diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml index df16b8e9c6..0a44e38548 100644 --- a/.github/workflows/build_and_deploy.yml +++ b/.github/workflows/build_and_deploy.yml @@ -11,6 +11,8 @@ on: jobs: build_test_and_upload: + env: + BUILD_ENVIRONMENT_IS_CI_OR_LOCAL: "CI" runs-on: ubuntu-20.04 container: ghcr.io/giganticminecraft/seichiassist-builder:1a64049 steps: @@ -79,6 +81,12 @@ jobs: touch -t "$timestamp" $proto done + - name: Check format with Scalafmt + run: sbt scalafmtCheckAll + + - name: Check lint with Scalafix + run: sbt "scalafix --check" + - name: Test and build artifact run: sbt assembly diff --git a/.gitignore b/.gitignore index 66ca205713..63be34873e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ /project/target /project/project /target +.bsp/ # Local files deployLocal.sh diff --git a/.scalafix.conf b/.scalafix.conf new file mode 100644 index 0000000000..1bcd40efe1 --- /dev/null +++ b/.scalafix.conf @@ -0,0 +1,8 @@ +rules = [ + NoAutoTupling, + RemoveUnused, + DisableSyntax, + LeakingImplicitClassVal, + NoValInForComprehension, + ProcedureSyntax, +] diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 0000000000..4f503d1b5a --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,29 @@ +version = 3.4.3 +preset=IntelliJ +runner.dialect = scala213 + +maxColumn = 96 + +includeNoParensInSelectChains = true + +optIn { + breakChainOnFirstMethodDot = false + forceBlankLineBeforeDocstring = true +} + +newlines { + beforeCurlyLambdaParams = multilineWithCaseOnly + afterCurlyLambda = squash + implicitParamListModifierPrefer = before + sometimesBeforeColonInMethodReturnType = true +} + +assumeStandardLibraryStripMargin = true +align.stripMargin = true + +docstrings { + style = Asterisk + oneline = unfold +} + +trailingCommas = never diff --git a/build.sbt b/build.sbt index 6a65728db1..e9dac3e4c3 100644 --- a/build.sbt +++ b/build.sbt @@ -3,13 +3,45 @@ import sbt.Keys.baseDirectory import java.io._ +// region 全プロジェクト共通のメタデータ + ThisBuild / scalaVersion := "2.13.1" -// ThisBuild / version はGitHub Actionsによって自動更新される。 +// ThisBuild / version はGitHub Actionsによって取得/自動更新される。 // 次の行は ThisBuild / version := "(\d*)" の形式でなければならない。 -ThisBuild / version := "34" +ThisBuild / version := "35" ThisBuild / organization := "click.seichi" ThisBuild / description := "ギガンティック☆整地鯖の独自要素を司るプラグイン" +// Scalafixが要求するため、semanticdbは有効化する +ThisBuild / semanticdbEnabled := true + +// endregion + +// region 雑多な設定 + +// kind-projector 構文を使いたいため +addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.13.2" cross CrossVersion.full) + +// Scalafixがsemanticdbを必要とするため +ThisBuild / semanticdbEnabled := true + +// CIビルドで詳細なログを確認するため +ThisBuild / logLevel := { + if (scala.sys.env.get("BUILD_ENVIRONMENT_IS_CI_OR_LOCAL").contains("CI")) { + Level.Debug + } else { + Level.Info + } +} + +// テストが落ちた時にスタックとレースを表示するため。 +// ScalaTest のオプションは https://www.scalatest.org/user_guide/using_the_runner を参照のこと。 +Compile / testOptions += Tests.Argument("-oS") + +// endregion + +// region 依存関係 + resolvers ++= Seq( "jitpack.io" at "https://jitpack.io", "maven.sk89q.com" at "https://maven.sk89q.com/repo/", @@ -75,28 +107,31 @@ val dependenciesToEmbed = Seq( "com.beachape" %% "enumeratum" % "1.5.13", // protobuf - "com.thesamet.scalapb" %% "scalapb-runtime" % scalapb.compiler.Version.scalapbVersion, + "com.thesamet.scalapb" %% "scalapb-runtime" % scalapb.compiler.Version.scalapbVersion ) -addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.13.2" cross CrossVersion.full) +// endregion + +// region assemblyで含む依存関係の処理 // localDependenciesはprovidedとして扱い、jarに埋め込まない assembly / assemblyExcludedJars := { - (assembly / fullClasspath).value - .filter { a => - def directoryContainsFile(directory: File, file: File) = - file.absolutePath.startsWith(directory.absolutePath) + (assembly / fullClasspath).value.filter { a => + def directoryContainsFile(directory: File, file: File) = + file.absolutePath.startsWith(directory.absolutePath) - directoryContainsFile(baseDirectory.value / "localDependencies", a.data) - } + directoryContainsFile(baseDirectory.value / "localDependencies", a.data) + } } -val tokenReplacementMap = settingKey[Map[String, String]]("Map specifying what tokens should be replaced to") +// endregion -tokenReplacementMap := Map( - "name" -> name.value, - "version" -> version.value -) +// region プラグインJarに埋め込むリソースの処理 + +val tokenReplacementMap = + settingKey[Map[String, String]]("Map specifying what tokens should be replaced to") + +tokenReplacementMap := Map("name" -> name.value, "version" -> version.value) val filesToBeReplacedInResourceFolder = Seq("plugin.yml") @@ -106,7 +141,8 @@ Compile / filteredResourceGenerator := filterResources( filesToBeReplacedInResourceFolder, tokenReplacementMap.value, - (Compile / resourceManaged).value, (Compile / resourceDirectory).value + (Compile / resourceManaged).value, + (Compile / resourceDirectory).value ) Compile / resourceGenerators += (Compile / filteredResourceGenerator) @@ -115,33 +151,39 @@ Compile / unmanagedResources += baseDirectory.value / "LICENSE" // トークン置換を行ったファイルをunmanagedResourcesのコピーから除外する unmanagedResources / excludeFilter := - filesToBeReplacedInResourceFolder.foldLeft((unmanagedResources / excludeFilter).value)(_.||(_)) + filesToBeReplacedInResourceFolder.foldLeft((unmanagedResources / excludeFilter).value)( + _.||(_) + ) + +// endregion -logLevel := Level.Debug +// region ScalaPBの設定 -// ScalaPBの設定 Compile / PB.protoSources := Seq(baseDirectory.value / "protocol") Compile / PB.targets := Seq(scalapb.gen() -> (Compile / sourceManaged).value / "scalapb") -Compile / testOptions += Tests.Argument("-oS") +// endregion + +// region 各プロジェクトの設定 + +lazy val root = (project in file(".")).settings( + name := "SeichiAssist", + assembly / assemblyOutputPath := baseDirectory.value / "target" / "build" / s"SeichiAssist.jar", + libraryDependencies := providedDependencies ++ testDependencies ++ dependenciesToEmbed, + excludeDependencies := Seq(ExclusionRule(organization = "org.bukkit", name = "bukkit")), + unmanagedBase := baseDirectory.value / "localDependencies", + scalacOptions ++= Seq( + "-encoding", + "utf8", + "-unchecked", + "-language:higherKinds", + "-deprecation", + "-Ypatmat-exhaust-depth", + "320", + "-Ymacro-annotations", + "-Ywarn-unused" + ), + javacOptions ++= Seq("-encoding", "utf8") +) -lazy val root = (project in file(".")) - .settings( - name := "SeichiAssist", - assembly / assemblyOutputPath := baseDirectory.value / "target" / "build" / s"SeichiAssist.jar", - libraryDependencies := providedDependencies ++ testDependencies ++ dependenciesToEmbed, - excludeDependencies := Seq( - ExclusionRule(organization = "org.bukkit", name = "bukkit") - ), - unmanagedBase := baseDirectory.value / "localDependencies", - scalacOptions ++= Seq( - "-encoding", "utf8", - "-unchecked", - "-language:higherKinds", - "-deprecation", - "-Ypatmat-exhaust-depth", "320", - "-Ymacro-annotations", - "-Ywarn-unused", - ), - javacOptions ++= Seq("-encoding", "utf8") - ) +// endregion diff --git a/project/plugins.sbt b/project/plugins.sbt index 26ac3e5846..38d40e2d5a 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1 +1,8 @@ +// プラグインJarを(依存関係にあるJarをすべて同梱して)出力するため addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.10") + +// Lintを掛けるため +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.34") + +// コードフォーマットするため +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 5763bb706a..7b7af6b431 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -117,6 +117,11 @@ commands: usage: / permission-message: *denied permission: seichiassist.hat + ec: + description: エンダーチェストを開く + usage: / + permission: seichiassist.ec + permission-message: *denied minestack: description: Toggle whether minestack collects applicable item automatically usage: /minestack @@ -148,7 +153,6 @@ permissions: - seichiassist.fd - seichiassist.present.* - seichiassist.hat - - seichiassist.minestack seichiassist.event: default: op seichiassist.shareinv: diff --git a/src/main/scala/com/github/unchama/buildassist/BuildAssist.scala b/src/main/scala/com/github/unchama/buildassist/BuildAssist.scala index 2a35a5cbc4..7f39b1f4fe 100644 --- a/src/main/scala/com/github/unchama/buildassist/BuildAssist.scala +++ b/src/main/scala/com/github/unchama/buildassist/BuildAssist.scala @@ -19,21 +19,22 @@ import java.util import java.util.UUID import scala.collection.mutable -class BuildAssist(plugin: Plugin) - (implicit - flyApi: ManagedFlyApi[SyncIO, Player], - buildCountAPI: subsystems.buildcount.BuildCountAPI[SyncIO, Player], - manaApi: ManaApi[IO, SyncIO, Player]) { +class BuildAssist(plugin: Plugin)( + implicit flyApi: ManagedFlyApi[SyncIO, Player], + buildCountAPI: subsystems.buildcount.BuildCountAPI[SyncIO, Player], + manaApi: ManaApi[IO, SyncIO, Player] +) { // TODO この辺のフィールドを整理する /** - * 永続化されない、プレーヤーのセッション内でのみ有効な一時データを管理するMap。 - * [[TemporaryDataInitializer]] によって初期化、削除される。 + * 永続化されない、プレーヤーのセッション内でのみ有効な一時データを管理するMap。 [[TemporaryDataInitializer]] によって初期化、削除される。 */ - val temporaryData: mutable.HashMap[UUID, TemporaryMutableBuildAssistPlayerData] = mutable.HashMap() + val temporaryData: mutable.HashMap[UUID, TemporaryMutableBuildAssistPlayerData] = + mutable.HashMap() - val buildAmountDataRepository: KeyedDataRepository[Player, ReadOnlyRef[SyncIO, BuildAmountData]] = + val buildAmountDataRepository + : KeyedDataRepository[Player, ReadOnlyRef[SyncIO, BuildAmountData]] = buildCountAPI.playerBuildAmountRepository { @@ -43,12 +44,15 @@ class BuildAssist(plugin: Plugin) def onEnable(): Unit = { implicit val menuRouter: BuildAssistMenuRouter[IO] = { - import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.{layoutPreparationContext, onMainThread} + import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.{ + layoutPreparationContext, + onMainThread + } BuildAssistMenuRouter.apply } - //コンフィグ系の設定は全てConfig.javaに移動 + // コンフィグ系の設定は全てConfig.javaに移動 BuildAssist.config = new BuildAssistConfig(plugin) BuildAssist.config.loadConfig() @@ -62,7 +66,7 @@ class BuildAssist(plugin: Plugin) new PlayerInventoryListener(), new TemporaryDataInitializer(this.temporaryData), new BlockLineUpTriggerListener[SyncIO], - new TilingSkillTriggerListener[SyncIO], + new TilingSkillTriggerListener[SyncIO] ) listeners.foreach { listener => @@ -77,194 +81,388 @@ class BuildAssist(plugin: Plugin) object BuildAssist { var instance: BuildAssist = _ - //範囲設置ブロックの対象リスト - val materiallist: java.util.Set[Material] = util.EnumSet.of( - - - Material.STONE //石 - , Material.GRASS //草 - , Material.DIRT //土 - , Material.COBBLESTONE //丸石 - , Material.WOOD //木 - , Material.SAND //砂 - , Material.GRAVEL //砂利 - , Material.GOLD_ORE //金鉱石 - , Material.IRON_ORE //鉄鉱石 - , Material.COAL_ORE //石炭鉱石 - , Material.LOG //原木 - , Material.GLASS //ガラス - , Material.LAPIS_ORE //ラピス鉱石 - , Material.LAPIS_BLOCK //ラピスB - , Material.SANDSTONE //砂岩 - , Material.WOOL //羊毛 - , Material.GOLD_BLOCK //金B - , Material.IRON_BLOCK //鉄B - , Material.BRICK //レンガB - , Material.BOOKSHELF //本棚 - , Material.MOSSY_COBBLESTONE //苔石 - , Material.OBSIDIAN //黒曜石 - , Material.DIAMOND_ORE //ダイヤ鉱石 - , Material.DIAMOND_BLOCK //ダイヤB - , Material.REDSTONE_ORE //赤鉱石 - , Material.ICE //氷 - , Material.SNOW_BLOCK //雪B - , Material.CLAY //粘土B - , Material.NETHERRACK //ネザーラック - , Material.SOUL_SAND //ソウルサンド - , Material.GLOWSTONE //グロウストーン - , Material.STAINED_GLASS //色付きガラス - , Material.SMOOTH_BRICK //石レンガ - , Material.MYCEL //菌糸 - , Material.NETHER_BRICK //ネザーレンガ - , Material.ENDER_STONE //エンドストーン - , Material.EMERALD_ORE //エメ鉱石 - , Material.EMERALD_BLOCK //エメB - , Material.COBBLE_WALL //丸石の壁 - , Material.QUARTZ_ORE //水晶鉱石 - , Material.QUARTZ_BLOCK //水晶B - , Material.STAINED_CLAY //色付き固焼き粘土 - , Material.LOG_2 //原木2 - , Material.PRISMARINE //プリズマリン - , Material.SEA_LANTERN //シーランタン - , Material.HARD_CLAY //固焼き粘土 - , Material.COAL_BLOCK //石炭B - , Material.PACKED_ICE //氷塊 - , Material.RED_SANDSTONE //赤い砂岩 - , Material.PURPUR_BLOCK //プルパーブ - , Material.PURPUR_PILLAR //柱状プルパーブ - , Material.END_BRICKS //エンドレンガB - , Material.RED_NETHER_BRICK //赤ネザーレンガB - , Material.BONE_BLOCK //骨B - , Material.NETHER_WART_BLOCK //ネザーウォートB - , Material.CONCRETE //コンクリート - , Material.CONCRETE_POWDER //コンクリートパウダー - , Material.ACACIA_STAIRS, Material.ACACIA_FENCE, Material.ACACIA_FENCE_GATE, - Material.BIRCH_WOOD_STAIRS, Material.BIRCH_FENCE, Material.BIRCH_FENCE_GATE, - Material.BONE_BLOCK, Material.BOOKSHELF, - Material.BRICK, Material.BRICK_STAIRS, - Material.CACTUS, Material.CHEST, - Material.CLAY_BRICK, - Material.DARK_OAK_STAIRS, Material.DARK_OAK_FENCE, Material.DARK_OAK_FENCE_GATE, - Material.END_BRICKS, - Material.FURNACE, Material.GLOWSTONE, Material.HARD_CLAY, - Material.JACK_O_LANTERN, Material.JUKEBOX, Material.JUNGLE_FENCE, Material.JUNGLE_FENCE_GATE, - Material.JUNGLE_WOOD_STAIRS, Material.LADDER, Material.LEAVES, Material.LEAVES_2, - Material.LOG, Material.LOG_2, Material.NETHER_BRICK, Material.NETHER_BRICK_STAIRS, - Material.NETHER_WART_BLOCK, Material.RED_NETHER_BRICK, - Material.OBSIDIAN, Material.PACKED_ICE, Material.PRISMARINE, - Material.PUMPKIN, Material.PURPUR_BLOCK, Material.PURPUR_SLAB, - Material.PURPUR_STAIRS, Material.PURPUR_PILLAR, - Material.QUARTZ_BLOCK, Material.QUARTZ_STAIRS, Material.QUARTZ, - Material.SANDSTONE, Material.SANDSTONE_STAIRS, Material.SEA_LANTERN, - Material.SLIME_BLOCK, Material.SMOOTH_BRICK, Material.SMOOTH_STAIRS, - Material.SNOW_BLOCK, Material.SPRUCE_FENCE, Material.SPRUCE_FENCE_GATE, - Material.SPRUCE_WOOD_STAIRS, Material.FENCE, Material.FENCE_GATE, - Material.STAINED_CLAY, Material.STAINED_GLASS, Material.STAINED_GLASS_PANE, - Material.STEP, Material.STONE, Material.STONE_SLAB2, Material.THIN_GLASS, - Material.TORCH, Material.WOOD, - Material.WOOD_STAIRS, Material.WOOD_STEP, - Material.WOOL, Material.CARPET, Material.WORKBENCH, - // #1008 - Material.LEAVES, Material.LEAVES_2 - ) + // 範囲設置ブロックの対象リスト + val materiallist: java.util.Set[Material] = util + .EnumSet + .of( + Material.STONE // 石 + , + Material.GRASS // 草 + , + Material.DIRT // 土 + , + Material.COBBLESTONE // 丸石 + , + Material.WOOD // 木 + , + Material.SAND // 砂 + , + Material.GRAVEL // 砂利 + , + Material.GOLD_ORE // 金鉱石 + , + Material.IRON_ORE // 鉄鉱石 + , + Material.COAL_ORE // 石炭鉱石 + , + Material.LOG // 原木 + , + Material.GLASS // ガラス + , + Material.LAPIS_ORE // ラピス鉱石 + , + Material.LAPIS_BLOCK // ラピスB + , + Material.SANDSTONE // 砂岩 + , + Material.WOOL // 羊毛 + , + Material.GOLD_BLOCK // 金B + , + Material.IRON_BLOCK // 鉄B + , + Material.BRICK // レンガB + , + Material.BOOKSHELF // 本棚 + , + Material.MOSSY_COBBLESTONE // 苔石 + , + Material.OBSIDIAN // 黒曜石 + , + Material.DIAMOND_ORE // ダイヤ鉱石 + , + Material.DIAMOND_BLOCK // ダイヤB + , + Material.REDSTONE_ORE // 赤鉱石 + , + Material.ICE // 氷 + , + Material.SNOW_BLOCK // 雪B + , + Material.CLAY // 粘土B + , + Material.NETHERRACK // ネザーラック + , + Material.SOUL_SAND // ソウルサンド + , + Material.GLOWSTONE // グロウストーン + , + Material.STAINED_GLASS // 色付きガラス + , + Material.SMOOTH_BRICK // 石レンガ + , + Material.MYCEL // 菌糸 + , + Material.NETHER_BRICK // ネザーレンガ + , + Material.ENDER_STONE // エンドストーン + , + Material.EMERALD_ORE // エメ鉱石 + , + Material.EMERALD_BLOCK // エメB + , + Material.COBBLE_WALL // 丸石の壁 + , + Material.QUARTZ_ORE // 水晶鉱石 + , + Material.QUARTZ_BLOCK // 水晶B + , + Material.STAINED_CLAY // 色付き固焼き粘土 + , + Material.LOG_2 // 原木2 + , + Material.PRISMARINE // プリズマリン + , + Material.SEA_LANTERN // シーランタン + , + Material.HARD_CLAY // 固焼き粘土 + , + Material.COAL_BLOCK // 石炭B + , + Material.PACKED_ICE // 氷塊 + , + Material.RED_SANDSTONE // 赤い砂岩 + , + Material.PURPUR_BLOCK // プルパーブ + , + Material.PURPUR_PILLAR // 柱状プルパーブ + , + Material.END_BRICKS // エンドレンガB + , + Material.RED_NETHER_BRICK // 赤ネザーレンガB + , + Material.BONE_BLOCK // 骨B + , + Material.NETHER_WART_BLOCK // ネザーウォートB + , + Material.CONCRETE // コンクリート + , + Material.CONCRETE_POWDER // コンクリートパウダー + , + Material.ACACIA_STAIRS, + Material.ACACIA_FENCE, + Material.ACACIA_FENCE_GATE, + Material.BIRCH_WOOD_STAIRS, + Material.BIRCH_FENCE, + Material.BIRCH_FENCE_GATE, + Material.BONE_BLOCK, + Material.BOOKSHELF, + Material.BRICK, + Material.BRICK_STAIRS, + Material.CACTUS, + Material.CHEST, + Material.CLAY_BRICK, + Material.DARK_OAK_STAIRS, + Material.DARK_OAK_FENCE, + Material.DARK_OAK_FENCE_GATE, + Material.END_BRICKS, + Material.FURNACE, + Material.GLOWSTONE, + Material.HARD_CLAY, + Material.JACK_O_LANTERN, + Material.JUKEBOX, + Material.JUNGLE_FENCE, + Material.JUNGLE_FENCE_GATE, + Material.JUNGLE_WOOD_STAIRS, + Material.LADDER, + Material.LEAVES, + Material.LEAVES_2, + Material.LOG, + Material.LOG_2, + Material.NETHER_BRICK, + Material.NETHER_BRICK_STAIRS, + Material.NETHER_WART_BLOCK, + Material.RED_NETHER_BRICK, + Material.OBSIDIAN, + Material.PACKED_ICE, + Material.PRISMARINE, + Material.PUMPKIN, + Material.PURPUR_BLOCK, + Material.PURPUR_SLAB, + Material.PURPUR_STAIRS, + Material.PURPUR_PILLAR, + Material.QUARTZ_BLOCK, + Material.QUARTZ_STAIRS, + Material.QUARTZ, + Material.SANDSTONE, + Material.SANDSTONE_STAIRS, + Material.SEA_LANTERN, + Material.SLIME_BLOCK, + Material.SMOOTH_BRICK, + Material.SMOOTH_STAIRS, + Material.SNOW_BLOCK, + Material.SPRUCE_FENCE, + Material.SPRUCE_FENCE_GATE, + Material.SPRUCE_WOOD_STAIRS, + Material.FENCE, + Material.FENCE_GATE, + Material.STAINED_CLAY, + Material.STAINED_GLASS, + Material.STAINED_GLASS_PANE, + Material.STEP, + Material.STONE, + Material.STONE_SLAB2, + Material.THIN_GLASS, + Material.TORCH, + Material.WOOD, + Material.WOOD_STAIRS, + Material.WOOD_STEP, + Material.WOOL, + Material.CARPET, + Material.WORKBENCH, + // #1008 + Material.LEAVES, + Material.LEAVES_2 + ) - //直列設置ブロックの対象リスト - val materiallist2: java.util.Set[Material] = util.EnumSet.of( - Material.STONE //石 - , Material.GRASS //草 - , Material.DIRT //土 - , Material.COBBLESTONE //丸石 - , Material.WOOD //木 - , Material.SAND //砂 - , Material.GRAVEL //砂利 - , Material.GOLD_ORE //金鉱石 - , Material.IRON_ORE //鉄鉱石 - , Material.COAL_ORE //石炭鉱石 - , Material.LOG //原木 - , Material.GLASS //ガラス - , Material.LAPIS_ORE //ラピス鉱石 - , Material.LAPIS_BLOCK //ラピスB - , Material.SANDSTONE //砂岩 - , Material.WOOL //羊毛 - , Material.GOLD_BLOCK //金B - , Material.IRON_BLOCK //鉄B - , Material.BRICK //レンガB - , Material.BOOKSHELF //本棚 - , Material.MOSSY_COBBLESTONE //苔石 - , Material.OBSIDIAN //黒曜石 - , Material.DIAMOND_ORE //ダイヤ鉱石 - , Material.DIAMOND_BLOCK //ダイヤB - , Material.REDSTONE_ORE //赤鉱石 - , Material.ICE //氷 - , Material.SNOW_BLOCK //雪B - , Material.CLAY //粘土B - , Material.NETHERRACK //ネザーラック - , Material.SOUL_SAND //ソウルサンド - , Material.GLOWSTONE //グロウストーン - , Material.STAINED_GLASS //色付きガラス - , Material.SMOOTH_BRICK //石レンガ - , Material.MYCEL //菌糸 - , Material.NETHER_BRICK //ネザーレンガ - , Material.ENDER_STONE //エンドストーン - , Material.EMERALD_ORE //エメ鉱石 - , Material.EMERALD_BLOCK //エメB - , Material.COBBLE_WALL //丸石の壁 - , Material.QUARTZ_ORE //水晶鉱石 - , Material.QUARTZ_BLOCK //水晶B - , Material.STAINED_CLAY //色付き固焼き粘土 - , Material.LOG_2 //原木2 - , Material.PRISMARINE //プリズマリン - , Material.SEA_LANTERN //シーランタン - , Material.HARD_CLAY //固焼き粘土 - , Material.COAL_BLOCK //石炭B - , Material.PACKED_ICE //氷塊 - , Material.RED_SANDSTONE //赤い砂岩 - , Material.PURPUR_BLOCK //プルパーブ - , Material.PURPUR_PILLAR //柱状プルパーブ - , Material.END_BRICKS //エンドレンガB - , Material.RED_NETHER_BRICK //赤ネザーレンガB - , Material.BONE_BLOCK //骨B - , Material.FENCE //オークフェンス - , Material.IRON_FENCE //鉄フェンス - , Material.THIN_GLASS //板ガラス - , Material.NETHER_FENCE //ネザーフェンス - , Material.STAINED_GLASS_PANE //色付き板ガラス - , Material.SLIME_BLOCK //スライムB - , Material.SPRUCE_FENCE //松フェンス - , Material.BIRCH_FENCE //白樺フェンス - , Material.JUNGLE_FENCE //ジャングルフェンス - , Material.DARK_OAK_FENCE //ダークオークフェンス - , Material.ACACIA_FENCE //アカシアフェンス - , Material.NETHER_WART_BLOCK //ネザーウォートB - , Material.CONCRETE //コンクリート - , Material.CONCRETE_POWDER //コンクリートパウダー - , Material.LEAVES, Material.LEAVES_2 - ) + // 直列設置ブロックの対象リスト + val materiallist2: java.util.Set[Material] = util + .EnumSet + .of( + Material.STONE // 石 + , + Material.GRASS // 草 + , + Material.DIRT // 土 + , + Material.COBBLESTONE // 丸石 + , + Material.WOOD // 木 + , + Material.SAND // 砂 + , + Material.GRAVEL // 砂利 + , + Material.GOLD_ORE // 金鉱石 + , + Material.IRON_ORE // 鉄鉱石 + , + Material.COAL_ORE // 石炭鉱石 + , + Material.LOG // 原木 + , + Material.GLASS // ガラス + , + Material.LAPIS_ORE // ラピス鉱石 + , + Material.LAPIS_BLOCK // ラピスB + , + Material.SANDSTONE // 砂岩 + , + Material.WOOL // 羊毛 + , + Material.GOLD_BLOCK // 金B + , + Material.IRON_BLOCK // 鉄B + , + Material.BRICK // レンガB + , + Material.BOOKSHELF // 本棚 + , + Material.MOSSY_COBBLESTONE // 苔石 + , + Material.OBSIDIAN // 黒曜石 + , + Material.DIAMOND_ORE // ダイヤ鉱石 + , + Material.DIAMOND_BLOCK // ダイヤB + , + Material.REDSTONE_ORE // 赤鉱石 + , + Material.ICE // 氷 + , + Material.SNOW_BLOCK // 雪B + , + Material.CLAY // 粘土B + , + Material.NETHERRACK // ネザーラック + , + Material.SOUL_SAND // ソウルサンド + , + Material.GLOWSTONE // グロウストーン + , + Material.STAINED_GLASS // 色付きガラス + , + Material.SMOOTH_BRICK // 石レンガ + , + Material.MYCEL // 菌糸 + , + Material.NETHER_BRICK // ネザーレンガ + , + Material.ENDER_STONE // エンドストーン + , + Material.EMERALD_ORE // エメ鉱石 + , + Material.EMERALD_BLOCK // エメB + , + Material.COBBLE_WALL // 丸石の壁 + , + Material.QUARTZ_ORE // 水晶鉱石 + , + Material.QUARTZ_BLOCK // 水晶B + , + Material.STAINED_CLAY // 色付き固焼き粘土 + , + Material.LOG_2 // 原木2 + , + Material.PRISMARINE // プリズマリン + , + Material.SEA_LANTERN // シーランタン + , + Material.HARD_CLAY // 固焼き粘土 + , + Material.COAL_BLOCK // 石炭B + , + Material.PACKED_ICE // 氷塊 + , + Material.RED_SANDSTONE // 赤い砂岩 + , + Material.PURPUR_BLOCK // プルパーブ + , + Material.PURPUR_PILLAR // 柱状プルパーブ + , + Material.END_BRICKS // エンドレンガB + , + Material.RED_NETHER_BRICK // 赤ネザーレンガB + , + Material.BONE_BLOCK // 骨B + , + Material.FENCE // オークフェンス + , + Material.IRON_FENCE // 鉄フェンス + , + Material.THIN_GLASS // 板ガラス + , + Material.NETHER_FENCE // ネザーフェンス + , + Material.STAINED_GLASS_PANE // 色付き板ガラス + , + Material.SLIME_BLOCK // スライムB + , + Material.SPRUCE_FENCE // 松フェンス + , + Material.BIRCH_FENCE // 白樺フェンス + , + Material.JUNGLE_FENCE // ジャングルフェンス + , + Material.DARK_OAK_FENCE // ダークオークフェンス + , + Material.ACACIA_FENCE // アカシアフェンス + , + Material.NETHER_WART_BLOCK // ネザーウォートB + , + Material.CONCRETE // コンクリート + , + Material.CONCRETE_POWDER // コンクリートパウダー + , + Material.LEAVES, + Material.LEAVES_2 + ) - //ハーフブロックとして扱うMaterial - val material_slab2: java.util.Set[Material] = util.EnumSet.of( - Material.STONE_SLAB2 //赤砂岩 - , Material.PURPUR_SLAB //プルパー - , Material.WOOD_STEP //木 - , Material.STEP //石 - ) + // ハーフブロックとして扱うMaterial + val material_slab2: java.util.Set[Material] = util + .EnumSet + .of( + Material.STONE_SLAB2 // 赤砂岩 + , + Material.PURPUR_SLAB // プルパー + , + Material.WOOD_STEP // 木 + , + Material.STEP // 石 + ) - val material_destruction: java.util.Set[Material] = util.EnumSet.of( - Material.LONG_GRASS //草 - , Material.DEAD_BUSH //枯れ木 - , Material.YELLOW_FLOWER //タンポポ - , Material.RED_ROSE //花9種 - , Material.BROWN_MUSHROOM //きのこ - , Material.RED_MUSHROOM //赤きのこ - , Material.TORCH //松明 - , Material.SNOW //雪 - , Material.DOUBLE_PLANT //高い花、草 - , Material.WATER //水 - , Material.STATIONARY_WATER //水 - , Material.LAVA // 溶岩 - , Material.STATIONARY_LAVA // 溶岩 - , Material.VINE // ツタ - ) + val material_destruction: java.util.Set[Material] = util + .EnumSet + .of( + Material.LONG_GRASS // 草 + , + Material.DEAD_BUSH // 枯れ木 + , + Material.YELLOW_FLOWER // タンポポ + , + Material.RED_ROSE // 花9種 + , + Material.BROWN_MUSHROOM // きのこ + , + Material.RED_MUSHROOM // 赤きのこ + , + Material.TORCH // 松明 + , + Material.SNOW // 雪 + , + Material.DOUBLE_PLANT // 高い花、草 + , + Material.WATER // 水 + , + Material.STATIONARY_WATER // 水 + , + Material.LAVA // 溶岩 + , + Material.STATIONARY_LAVA // 溶岩 + , + Material.VINE // ツタ + ) var plugin: Plugin = _ var config: BuildAssistConfig = _ diff --git a/src/main/scala/com/github/unchama/buildassist/BuildAssistConfig.scala b/src/main/scala/com/github/unchama/buildassist/BuildAssistConfig.scala index f9f4393718..46cfd4e8ff 100644 --- a/src/main/scala/com/github/unchama/buildassist/BuildAssistConfig.scala +++ b/src/main/scala/com/github/unchama/buildassist/BuildAssistConfig.scala @@ -10,37 +10,38 @@ class BuildAssistConfig(val plugin: Plugin) { saveDefaultConfig() - //コンフィグのロード + // コンフィグのロード def loadConfig(): Unit = { config = getConfig } - //plugin.ymlがない時にDefaultのファイルを生成 + // plugin.ymlがない時にDefaultのファイルを生成 private def saveDefaultConfig(): Unit = plugin.saveDefaultConfig() - //plugin.ymlファイルからの読み込み + // plugin.ymlファイルからの読み込み private def getConfig: FileConfiguration = plugin.getConfig def getFlyExp: Int = config.getString("flyexp").toInt - //直列設置開放LV + // 直列設置開放LV def getblocklineuplevel: Int = config.getString("blocklineup.level").toInt - //直列設置のマナ消費倍率 + // 直列設置のマナ消費倍率 def getblocklineupmana_mag: Double = config.getString("blocklineup.mana_mag").toDouble - //直列設置マインスタック優先開放LV + // 直列設置マインスタック優先開放LV def getblocklineupMinestacklevel: Int = config.getString("blocklineup.minestack_level").toInt def getZoneSetSkillLevel: Int = config.getString("ZoneSetSkill.level").toInt - //MineStackブロック一括クラフト開放LV - def getMinestackBlockCraftlevel(lv: Int): Int = config.getString("minestack_BlockCraft.level" + lv).toInt + // MineStackブロック一括クラフト開放LV + def getMinestackBlockCraftlevel(lv: Int): Int = + config.getString("minestack_BlockCraft.level" + lv).toInt - //ブロック設置カウントの1分上限 + // ブロック設置カウントの1分上限 def getBuildNum1minLimit: BigDecimal = BigDecimal(config.getString("BuildNum1minLimit")) - //ブロック範囲設置スキルのマインスタック優先解放レベル + // ブロック範囲設置スキルのマインスタック優先解放レベル def getZoneskillMinestacklevel: Int = config.getString("ZoneSetSkill.minestack").toInt -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/buildassist/MenuInventoryData.scala b/src/main/scala/com/github/unchama/buildassist/MenuInventoryData.scala index 06857382f6..f82986d53f 100644 --- a/src/main/scala/com/github/unchama/buildassist/MenuInventoryData.scala +++ b/src/main/scala/com/github/unchama/buildassist/MenuInventoryData.scala @@ -13,14 +13,15 @@ object MenuInventoryData { import scala.jdk.CollectionConverters._ def getSetBlockSkillData(p: Player): Inventory = { - //プレイヤーを取得 + // プレイヤーを取得 val player = p.getPlayer - //UUID取得 + // UUID取得 val uuid = player.getUniqueId - //プレイヤーデータ + // プレイヤーデータ val playerdata = BuildAssist.instance.temporaryData(uuid) - val inventory = Bukkit.getServer.createInventory(null, 4 * 9, s"$DARK_PURPLE$BOLD「範囲設置スキル」設定画面") + val inventory = + Bukkit.getServer.createInventory(null, 4 * 9, s"$DARK_PURPLE$BOLD「範囲設置スキル」設定画面") var itemstack = new ItemStack(Material.BARRIER, 1) var itemmeta = Bukkit.getItemFactory.getItemMeta(Material.BARRIER) var skullmeta = ItemMetaFactory.SKULL.getValue @@ -50,38 +51,37 @@ object MenuInventoryData { // FIXME: BAD NAME val ZSSkillA = playerdata.AREAint * 2 + 1 - //初期画面へ移動 + // 初期画面へ移動 itemstack.setDurability(3.toShort) itemmeta.setDisplayName(s"$YELLOW$UNDERLINE${BOLD}元のページへ") itemmeta.setLore(lore.asJava) itemstack.setItemMeta(itemmeta) inventory.setItem(0, itemstack) - //土設置のON/OFF + // 土設置のON/OFF itemstack = new ItemStack(Material.DIRT, 1) itemmeta = Bukkit.getItemFactory.getItemMeta(Material.STONE) itemmeta.setDisplayName(s"$YELLOW$UNDERLINE${BOLD}設置時に下の空洞を埋める機能") - lore = List( - s"$RESET$AQUA${UNDERLINE}機能の使用設定:$ZSDirt", - s"$RESET$AQUA${UNDERLINE}機能の範囲:地下5マスまで" - ) + lore = + List(s"$RESET$AQUA${UNDERLINE}機能の使用設定:$ZSDirt", s"$RESET$AQUA${UNDERLINE}機能の範囲:地下5マスまで") itemmeta.setLore(lore.asJava) itemstack.setItemMeta(itemmeta) inventory.setItem(4, itemstack) - //設定状況の表示 + // 設定状況の表示 itemstack = new ItemStack(Material.STONE, 1) itemmeta = Bukkit.getItemFactory.getItemMeta(Material.STONE) itemmeta.setDisplayName(s"$YELLOW$UNDERLINE${BOLD}現在の設定は以下の通りです") lore = List( s"$RESET$AQUA${UNDERLINE}スキルの使用設定:$ZSSkill", s"$RESET$AQUA${UNDERLINE}スキルの範囲設定:$ZSSkillA×$ZSSkillA", - s"$RESET$AQUA${UNDERLINE}MineStack優先設定:$ZSSkill_Minestack") + s"$RESET$AQUA${UNDERLINE}MineStack優先設定:$ZSSkill_Minestack" + ) itemmeta.setLore(lore.asJava) itemstack.setItemMeta(itemmeta) inventory.setItem(13, itemstack) - //範囲をMAXへ + // 範囲をMAXへ itemstack = new ItemStack(Material.SKULL_ITEM, 11) itemstack.setDurability(3.toShort) skullmeta.setDisplayName(s"$RED$UNDERLINE${BOLD}範囲設定を最大値に変更") @@ -94,7 +94,7 @@ object MenuInventoryData { itemstack.setItemMeta(skullmeta) AsyncInventorySetter.setItemAsync(inventory, 19, itemstack) - //範囲を一段階増加 + // 範囲を一段階増加 itemstack = new ItemStack(Material.SKULL_ITEM, 7) skullmeta = ItemMetaFactory.SKULL.getValue itemstack.setDurability(3.toShort) @@ -109,21 +109,19 @@ object MenuInventoryData { itemstack.setItemMeta(skullmeta) AsyncInventorySetter.setItemAsync(inventory, 20, itemstack) - //範囲を初期値へ + // 範囲を初期値へ itemstack = new ItemStack(Material.SKULL_ITEM, 5) skullmeta = ItemMetaFactory.SKULL.getValue itemstack.setDurability(3.toShort) skullmeta.setDisplayName(s"$RED$UNDERLINE${BOLD}範囲設定を初期値に変更") - lore = List( - s"$RESET${AQUA}現在の範囲設定:$ZSSkillA×$ZSSkillA", - s"$RESET$AQUA${UNDERLINE}変更後の範囲設定:5×5" - ) + lore = + List(s"$RESET${AQUA}現在の範囲設定:$ZSSkillA×$ZSSkillA", s"$RESET$AQUA${UNDERLINE}変更後の範囲設定:5×5") skullmeta.setLore(lore.asJava) skullmeta.setOwner("MHF_TNT") itemstack.setItemMeta(skullmeta) AsyncInventorySetter.setItemAsync(inventory, 22, itemstack) - //範囲を一段階減少 + // 範囲を一段階減少 itemstack = new ItemStack(Material.SKULL_ITEM, 3) skullmeta = ItemMetaFactory.SKULL.getValue itemstack.setDurability(3.toShort) @@ -138,23 +136,20 @@ object MenuInventoryData { itemstack.setItemMeta(skullmeta) AsyncInventorySetter.setItemAsync(inventory, 24, itemstack) - - //範囲をMINへ + // 範囲をMINへ itemstack = new ItemStack(Material.SKULL_ITEM, 1) skullmeta = ItemMetaFactory.SKULL.getValue itemstack.setDurability(3.toShort) skullmeta.setDisplayName(s"$RED$UNDERLINE${BOLD}範囲設定を最小値に変更") - lore = List( - s"$RESET${AQUA}現在の範囲設定:$ZSSkillA×$ZSSkillA", - s"$RESET$AQUA${UNDERLINE}変更後の範囲設定:3×3" - ) + lore = + List(s"$RESET${AQUA}現在の範囲設定:$ZSSkillA×$ZSSkillA", s"$RESET$AQUA${UNDERLINE}変更後の範囲設定:3×3") skullmeta.setLore(lore.asJava) skullmeta.setOwner("MHF_ArrowDown") itemstack.setItemMeta(skullmeta) AsyncInventorySetter.setItemAsync(inventory, 25, itemstack) - //35番目にMineStack優先設定を追加 - //MineStackの方を優先して消費する設定 + // 35番目にMineStack優先設定を追加 + // MineStackの方を優先して消費する設定 itemstack = new ItemStack(Material.CHEST, 1) itemmeta = Bukkit.getItemFactory.getItemMeta(Material.CHEST) itemmeta.setDisplayName(s"$YELLOW$UNDERLINE${BOLD}MineStack優先設定:$ZSSkill_Minestack") @@ -171,13 +166,13 @@ object MenuInventoryData { inventory } - //ブロックを並べる設定メニュー + // ブロックを並べる設定メニュー def getBlockLineUpData(p: Player): Inventory = { - //プレイヤーを取得 + // プレイヤーを取得 val player = p.getPlayer - //UUID取得 + // UUID取得 val uuid = player.getUniqueId - //プレイヤーデータ + // プレイヤーデータ val playerdata = BuildAssist.instance.temporaryData(uuid) val inventory = Bukkit.getServer.createInventory(null, 4 * 9, s"$DARK_PURPLE$BOLD「直列設置」設定") @@ -194,9 +189,11 @@ object MenuInventoryData { itemstack.setItemMeta(skullmeta) AsyncInventorySetter.setItemAsync(inventory, 27, itemstack) - //直列設置設定 + // 直列設置設定 itemstack = new ItemStack(Material.WOOD, 1) - itemmeta.setDisplayName(s"$YELLOW$UNDERLINE${BOLD}直列設置 :${BuildAssist.line_up_str(playerdata.line_up_flg)}") + itemmeta.setDisplayName( + s"$YELLOW$UNDERLINE${BOLD}直列設置 :${BuildAssist.line_up_str(playerdata.line_up_flg)}" + ) lore = List( s"$RESET${GRAY}オフハンドに木の棒、メインハンドに設置したいブロックを持って", s"$RESET${GRAY}左クリックすると向いてる方向に並べて設置します。", @@ -207,22 +204,23 @@ object MenuInventoryData { itemstack.setItemMeta(itemmeta) inventory.setItem(0, itemstack) - //直列設置ハーフブロック設定 + // 直列設置ハーフブロック設定 itemstack = new ItemStack(Material.STEP, 1) itemmeta = Bukkit.getItemFactory.getItemMeta(Material.STEP) - itemmeta.setDisplayName(s"$YELLOW$UNDERLINE${BOLD}ハーフブロック設定 :${BuildAssist.line_up_step_str(playerdata.line_up_step_flg)}") - lore = List( - s"$RESET${GRAY}ハーフブロックを並べる時の位置を決めます。", - s"$RESET${GRAY}クリックで切り替え" + itemmeta.setDisplayName( + s"$YELLOW$UNDERLINE${BOLD}ハーフブロック設定 :${BuildAssist.line_up_step_str(playerdata.line_up_step_flg)}" ) + lore = List(s"$RESET${GRAY}ハーフブロックを並べる時の位置を決めます。", s"$RESET${GRAY}クリックで切り替え") itemmeta.setLore(lore.asJava) itemstack.setItemMeta(itemmeta) inventory.setItem(1, itemstack) - //直列設置一部ブロックを破壊して並べる設定 + // 直列設置一部ブロックを破壊して並べる設定 itemstack = new ItemStack(Material.TNT, 1) itemmeta = Bukkit.getItemFactory.getItemMeta(Material.TNT) - itemmeta.setDisplayName(s"$YELLOW$UNDERLINE${BOLD}破壊設定 :${BuildAssist.line_up_off_on_str(playerdata.line_up_des_flg)}") + itemmeta.setDisplayName( + s"$YELLOW$UNDERLINE${BOLD}破壊設定 :${BuildAssist.line_up_off_on_str(playerdata.line_up_des_flg)}" + ) lore = List( s"$RESET${GRAY}ブロックを並べるとき特定のブロックを破壊して並べます。", s"$RESET${GRAY}破壊対象ブロック:草、花、水、雪、松明、きのこ、マグマ、ツタ", @@ -232,10 +230,12 @@ object MenuInventoryData { itemstack.setItemMeta(itemmeta) inventory.setItem(2, itemstack) - //MineStackの方を優先して消費する設定 + // MineStackの方を優先して消費する設定 itemstack = new ItemStack(Material.CHEST, 1) itemmeta = Bukkit.getItemFactory.getItemMeta(Material.CHEST) - itemmeta.setDisplayName(s"$YELLOW$UNDERLINE${BOLD}MineStack優先設定 :${BuildAssist.line_up_off_on_str(playerdata.line_up_minestack_flg)}") + itemmeta.setDisplayName( + s"$YELLOW$UNDERLINE${BOLD}MineStack優先設定 :${BuildAssist.line_up_off_on_str(playerdata.line_up_minestack_flg)}" + ) lore = List( s"$RESET${GRAY}スキルでブロックを並べるとき", s"$RESET${GRAY}MineStackの在庫を優先して消費します。", diff --git a/src/main/scala/com/github/unchama/buildassist/PlayerInventoryListener.scala b/src/main/scala/com/github/unchama/buildassist/PlayerInventoryListener.scala index 4df81f16a9..539535777f 100644 --- a/src/main/scala/com/github/unchama/buildassist/PlayerInventoryListener.scala +++ b/src/main/scala/com/github/unchama/buildassist/PlayerInventoryListener.scala @@ -11,16 +11,18 @@ import org.bukkit.event.inventory.{InventoryClickEvent, InventoryType} import org.bukkit.event.{EventHandler, Listener} import org.bukkit.{Material, Sound} -class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, - ioCanOpenBuildMainMenu: IO CanOpen BuildMainMenu.type) extends Listener { +class PlayerInventoryListener( + implicit effectEnvironment: EffectEnvironment, + ioCanOpenBuildMainMenu: IO CanOpen BuildMainMenu.type +) extends Listener { import com.github.unchama.targetedeffect._ import com.github.unchama.util.syntax.Nullability.NullabilityExtensionReceiver - //直列設置設定画面 + // 直列設置設定画面 @EventHandler def onPlayerClickBlockLineUpEvent(event: InventoryClickEvent): Unit = { - //外枠のクリック処理なら終了 + // 外枠のクリック処理なら終了 if (event.getClickedInventory == null) { return } @@ -28,15 +30,15 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, val itemstackcurrent = event.getCurrentItem val view = event.getView val he = view.getPlayer - //インベントリを開けたのがプレイヤーではない時終了 + // インベントリを開けたのがプレイヤーではない時終了 if (he.getType != EntityType.PLAYER) return val topinventory = view.getTopInventory.ifNull { return } - //インベントリが存在しない時終了 - //インベントリサイズが36でない時終了 + // インベントリが存在しない時終了 + // インベントリサイズが36でない時終了 if (topinventory.getSize != 36) { return } @@ -45,23 +47,29 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, val uuid = player.getUniqueId val playerdata = BuildAssist.instance.temporaryData(uuid) - val playerLevel = BuildAssist.instance.buildAmountDataRepository(player).read.unsafeRunSync().levelCorrespondingToExp.level + val playerLevel = BuildAssist + .instance + .buildAmountDataRepository(player) + .read + .unsafeRunSync() + .levelCorrespondingToExp + .level - //プレイヤーデータが無い場合は処理終了 + // プレイヤーデータが無い場合は処理終了 - //インベントリ名が以下の時処理 + // インベントリ名が以下の時処理 if (topinventory.getTitle == s"${DARK_PURPLE.toString}$BOLD「直列設置」設定") { event.setCancelled(true) - //プレイヤーインベントリのクリックの場合終了 + // プレイヤーインベントリのクリックの場合終了 if (event.getClickedInventory.getType == InventoryType.PLAYER) { return } /* - * クリックしたボタンに応じた各処理内容の記述ここから - */ + * クリックしたボタンに応じた各処理内容の記述ここから + */ if (itemstackcurrent.getType == Material.SKULL_ITEM) { - //ホームメニューへ帰還 + // ホームメニューへ帰還 effectEnvironment.unsafeRunAsyncTargetedEffect(player)( SequentialEffect( @@ -71,41 +79,50 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, "BuildMainMenuを開く" ) } else if (itemstackcurrent.getType == Material.WOOD) { - //直列設置設定 + // 直列設置設定 if (playerLevel < BuildAssist.config.getblocklineuplevel) { player.sendMessage(RED.toString + "建築Lvが足りません") } else { playerdata.line_up_flg = (playerdata.line_up_flg + 1) % 3 - player.sendMessage(s"${GREEN.toString}直列設置 :${BuildAssist.line_up_str.apply(playerdata.line_up_flg)}") + player.sendMessage( + s"${GREEN.toString}直列設置 :${BuildAssist.line_up_str.apply(playerdata.line_up_flg)}" + ) player.playSound(player.getLocation, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) player.openInventory(MenuInventoryData.getBlockLineUpData(player)) } } else if (itemstackcurrent.getType == Material.STEP) { - //直列設置ハーフブロック設定 + // 直列設置ハーフブロック設定 if (playerdata.line_up_step_flg >= 2) { playerdata.line_up_step_flg = 0 } else { playerdata.line_up_step_flg += 1 } - player.sendMessage(s"${GREEN.toString}ハーフブロック設定 :${BuildAssist.line_up_step_str(playerdata.line_up_step_flg)}") + player.sendMessage( + s"${GREEN.toString}ハーフブロック設定 :${BuildAssist.line_up_step_str(playerdata.line_up_step_flg)}" + ) player.playSound(player.getLocation, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) player.openInventory(MenuInventoryData.getBlockLineUpData(player)) } else if (itemstackcurrent.getType == Material.TNT) { - //直列設置一部ブロックを破壊して並べる設定 + // 直列設置一部ブロックを破壊して並べる設定 playerdata.line_up_des_flg = if (playerdata.line_up_des_flg == 0) 1 else 0 - player.sendMessage(s"${GREEN.toString}破壊設定 :${BuildAssist.line_up_off_on_str(playerdata.line_up_des_flg)}") + player.sendMessage( + s"${GREEN.toString}破壊設定 :${BuildAssist.line_up_off_on_str(playerdata.line_up_des_flg)}" + ) player.playSound(player.getLocation, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) player.openInventory(MenuInventoryData.getBlockLineUpData(player)) } else if (itemstackcurrent.getType == Material.CHEST) { - //マインスタックの方を優先して消費する設定 + // マインスタックの方を優先して消費する設定 if (playerLevel < BuildAssist.config.getblocklineupMinestacklevel) { player.sendMessage(s"${RED.toString}建築Lvが足りません") } else { playerdata.line_up_minestack_flg = if (playerdata.line_up_minestack_flg == 0) 1 else 0 - player.sendMessage(GREEN.toString + "マインスタック優先設定 :" + BuildAssist.line_up_off_on_str(playerdata.line_up_minestack_flg)) + player.sendMessage( + GREEN.toString + "マインスタック優先設定 :" + BuildAssist + .line_up_off_on_str(playerdata.line_up_minestack_flg) + ) player.playSound(player.getLocation, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) player.openInventory(MenuInventoryData.getBlockLineUpData(player)) } diff --git a/src/main/scala/com/github/unchama/buildassist/Util.scala b/src/main/scala/com/github/unchama/buildassist/Util.scala index ede13e62a3..141ac5cdbf 100644 --- a/src/main/scala/com/github/unchama/buildassist/Util.scala +++ b/src/main/scala/com/github/unchama/buildassist/Util.scala @@ -5,7 +5,7 @@ import org.bukkit.Bukkit object Util { - //ワールドガードAPIを返す + // ワールドガードAPIを返す def getWorldGuard: WorldGuardPlugin = { Bukkit.getServer.getPluginManager.getPlugin("WorldGuard") match { case plugin: WorldGuardPlugin => plugin diff --git a/src/main/scala/com/github/unchama/buildassist/listener/BlockLineUpTriggerListener.scala b/src/main/scala/com/github/unchama/buildassist/listener/BlockLineUpTriggerListener.scala index 2cb653c473..63a18e4ad4 100644 --- a/src/main/scala/com/github/unchama/buildassist/listener/BlockLineUpTriggerListener.scala +++ b/src/main/scala/com/github/unchama/buildassist/listener/BlockLineUpTriggerListener.scala @@ -18,10 +18,9 @@ import org.bukkit.{Material, Sound} import scala.util.control.Breaks class BlockLineUpTriggerListener[ - F[_] - : IncrementBuildExpWhenBuiltWithSkill[*[_], Player] - : SyncEffect -](implicit manaApi: ManaApi[IO, SyncIO, Player]) extends Listener { + F[_]: IncrementBuildExpWhenBuiltWithSkill[*[_], Player]: SyncEffect +](implicit manaApi: ManaApi[IO, SyncIO, Player]) + extends Listener { import scala.jdk.CollectionConverters._ @@ -36,44 +35,48 @@ class BlockLineUpTriggerListener[ val playerMineStack = seichiAssistData.minestack - //スキルOFFなら終了 + // スキルOFFなら終了 if (buildAssistData.line_up_flg == 0) return - //スキル利用可能でないワールドの場合終了 + // スキル利用可能でないワールドの場合終了 if (!player.getWorld.isBlockLineUpSkillEnabled) return - //左クリックの処理 + // 左クリックの処理 if (!(action == Action.LEFT_CLICK_AIR || action == Action.LEFT_CLICK_BLOCK)) return - //プレイヤーインベントリを取得 + // プレイヤーインベントリを取得 val inventory = player.getInventory - //メインハンドとオフハンドを取得 + // メインハンドとオフハンドを取得 val mainHandItem = inventory.getItemInMainHand val offhandItem = inventory.getItemInOffHand - //メインハンドに設置対象ブロックがある場合 - if (!(BuildAssist.materiallist2.contains(mainHandItem.getType) || BuildAssist.material_slab2.contains(mainHandItem.getType))) return + // メインハンドに設置対象ブロックがある場合 + if ( + !(BuildAssist.materiallist2.contains(mainHandItem.getType) || BuildAssist + .material_slab2 + .contains(mainHandItem.getType)) + ) return - //オフハンドに木の棒を持ってるときのみ発動させる + // オフハンドに木の棒を持ってるときのみ発動させる if (offhandItem.getType != Material.STICK) return val pl = player.getLocation val mainHandItemType = mainHandItem.getType val mainHandItemData = mainHandItem.getData.getData - //仰角は下向きがプラスで上向きがマイナス + // 仰角は下向きがプラスで上向きがマイナス val pitch = pl.getPitch val yaw = (pl.getYaw + 360) % 360 var step_x = 0 var step_y = 0 var step_z = 0 - //プレイヤーの足の座標を取得 + // プレイヤーの足の座標を取得 var px = pl.getBlockX var py = (pl.getY + 1.6).toInt var pz = pl.getBlockZ - //プレイヤーの向いてる方向を判定 + // プレイヤーの向いてる方向を判定 if (pitch > 45) { step_y = -1 py = pl.getBlockY @@ -81,16 +84,16 @@ class BlockLineUpTriggerListener[ step_y = 1 } else { if (buildAssistData.line_up_flg == 2) { - //下設置設定の場合は一段下げる + // 下設置設定の場合は一段下げる py -= 1 } - if (yaw > 315 || yaw < 45) { //南 + if (yaw > 315 || yaw < 45) { // 南 step_z = 1 - } else if (yaw < 135) { //西 + } else if (yaw < 135) { // 西 step_x = -1 - } else if (yaw < 225) { //北 + } else if (yaw < 225) { // 北 step_z = -1 - } else { //東 + } else { // 東 step_x = 1 } } @@ -106,11 +109,13 @@ class BlockLineUpTriggerListener[ val maxBlockUsage = { val availableOnHand = mainHandItem.getAmount.toLong - val availableInMineStack = mineStackObjectToBeUsed.map { - playerMineStack.getStackedAmountOf - }.getOrElse { - 0L - } + val availableInMineStack = mineStackObjectToBeUsed + .map { + playerMineStack.getStackedAmountOf + } + .getOrElse { + 0L + } val available = availableOnHand + availableInMineStack @@ -124,18 +129,19 @@ class BlockLineUpTriggerListener[ } Seq(Some(available), manaCap, Some(64L)).flatten.min - }.toInt + }.toInt def slabToDoubleSlab(material: Material) = material match { case Material.STONE_SLAB2 => Material.DOUBLE_STONE_SLAB2 case Material.PURPUR_SLAB => Material.PURPUR_DOUBLE_SLAB - case Material.WOOD_STEP => Material.WOOD_DOUBLE_STEP - case Material.STEP => Material.DOUBLE_STEP - case _ => mainHandItemType + case Material.WOOD_STEP => Material.WOOD_DOUBLE_STEP + case Material.STEP => Material.DOUBLE_STEP + case _ => mainHandItemType } val playerHoldsSlabBlock = BuildAssist.material_slab2.contains(mainHandItemType) - val doesHoldLeaves = (mainHandItemType eq Material.LEAVES) || (mainHandItemType eq Material.LEAVES_2) + val doesHoldLeaves = + (mainHandItemType eq Material.LEAVES) || (mainHandItemType eq Material.LEAVES_2) val slabLineUpStepMode = buildAssistData.line_up_step_flg val shouldPlaceDoubleSlabs = playerHoldsSlabBlock && slabLineUpStepMode == 2 @@ -155,23 +161,27 @@ class BlockLineUpTriggerListener[ else (mainHandItemType, 1, maxBlockUsage) - //設置した数 + // 設置した数 var placedBlockCount = 0 val b = new Breaks b.breakable { - while (placedBlockCount < placementIteration) { //設置ループ + while (placedBlockCount < placementIteration) { // 設置ループ px += step_x py += step_y pz += step_z val block = playerWorld.getBlockAt(px, py, pz) - //他人の保護がかかっている場合は設置終わり + // 他人の保護がかかっている場合は設置終わり if (!ExternalPlugins.getWorldGuard.canBuild(player, block.getLocation)) b.break if (block.getType != Material.AIR) { - //空気以外にぶつかり、ブロック破壊をしないならば終わる - if (!BuildAssist.material_destruction.contains(block.getType) || buildAssistData.line_up_des_flg == 0) { + // 空気以外にぶつかり、ブロック破壊をしないならば終わる + if ( + !BuildAssist + .material_destruction + .contains(block.getType) || buildAssistData.line_up_des_flg == 0 + ) { b.break } @@ -209,7 +219,7 @@ class BlockLineUpTriggerListener[ if (mainHandItem.getAmount - consumptionFromMainHand > 0) { mainHandItem.setAmount(mainHandItem.getAmount - consumptionFromMainHand) } else { - //アイテム数が0になっても消えないので自前で消す + // アイテム数が0になっても消えないので自前で消す inventory.setItemInMainHand(new ItemStack(Material.AIR, -1)) } diff --git a/src/main/scala/com/github/unchama/buildassist/listener/TemporaryDataInitializer.scala b/src/main/scala/com/github/unchama/buildassist/listener/TemporaryDataInitializer.scala index 4c7fee0485..943e3c3eab 100644 --- a/src/main/scala/com/github/unchama/buildassist/listener/TemporaryDataInitializer.scala +++ b/src/main/scala/com/github/unchama/buildassist/listener/TemporaryDataInitializer.scala @@ -7,7 +7,9 @@ import org.bukkit.event.{EventHandler, EventPriority, Listener} import java.util.UUID import scala.collection.mutable -class TemporaryDataInitializer(dataMap: mutable.Map[UUID, TemporaryMutableBuildAssistPlayerData]) extends Listener { +class TemporaryDataInitializer( + dataMap: mutable.Map[UUID, TemporaryMutableBuildAssistPlayerData] +) extends Listener { @EventHandler(priority = EventPriority.LOWEST) def onPreLogin(event: AsyncPlayerPreLoginEvent): Unit = { diff --git a/src/main/scala/com/github/unchama/buildassist/listener/TilingSkillTriggerListener.scala b/src/main/scala/com/github/unchama/buildassist/listener/TilingSkillTriggerListener.scala index 30a8913afd..1511de81c1 100644 --- a/src/main/scala/com/github/unchama/buildassist/listener/TilingSkillTriggerListener.scala +++ b/src/main/scala/com/github/unchama/buildassist/listener/TilingSkillTriggerListener.scala @@ -17,9 +17,7 @@ import scala.util.chaining._ import scala.util.control.Breaks class TilingSkillTriggerListener[ - F[_] - : IncrementBuildExpWhenBuiltWithSkill[*[_], Player] - : SyncEffect + F[_]: IncrementBuildExpWhenBuiltWithSkill[*[_], Player]: SyncEffect ] extends Listener { // 範囲設置スキルの発動を担うハンドラメソッド @@ -38,24 +36,28 @@ class TilingSkillTriggerListener[ event.getAction match { case Action.LEFT_CLICK_BLOCK => - case _ => return + case _ => return } - if (!(player.isSneaking && - BuildAssist.materiallist.contains(offHandItem.getType) && - buildAssistPlayerData.ZoneSetSkillFlag)) return + if ( + !(player.isSneaking && + BuildAssist.materiallist.contains(offHandItem.getType) && + buildAssistPlayerData.ZoneSetSkillFlag) + ) return val clickedBlock = event.getClickedBlock val offHandItemSelector = offHandItem.getData.getData - if (!(offHandItem.getType == clickedBlock.getType && offHandItemSelector == clickedBlock.getData)) { + if ( + !(offHandItem.getType == clickedBlock.getType && offHandItemSelector == clickedBlock.getData) + ) { player.sendMessage(s"$RED「オフハンドと同じブロック」をクリックしてください。(基準になります)") return } - //スキルの範囲設定 + // スキルの範囲設定 val areaInt = buildAssistPlayerData.AREAint - //設置範囲の基準となる座標 + // 設置範囲の基準となる座標 val centerX = clickedBlock.getX val surfaceY = clickedBlock.getY val centerZ = clickedBlock.getZ @@ -65,7 +67,8 @@ class TilingSkillTriggerListener[ var placementCount = 0 val minestackObjectToUse = - MineStackObjectList.minestacklist + MineStackObjectList + .minestacklist .find { obj => offHandItem.getType == obj.material && offHandItemSelector.toInt == obj.durability } @@ -111,14 +114,15 @@ class TilingSkillTriggerListener[ def fillBelowSurfaceWithDirt(): Unit = { (1 to 5).foreach { setBlockYOffsetBelow => - val fillLocation = new Location(playerWorld, targetX, surfaceY - setBlockYOffsetBelow, targetZ) + val fillLocation = + new Location(playerWorld, targetX, surfaceY - setBlockYOffsetBelow, targetZ) val blockToBeReplaced = fillLocation.getBlock if (fillTargetMaterials.contains(blockToBeReplaced.getType)) { if (Util.getWorldGuard.canBuild(player, fillLocation)) { blockToBeReplaced.setType(Material.DIRT) } else { - //他人の保護がかかっている場合は通知を行う + // 他人の保護がかかっている場合は通知を行う player.sendMessage(s"${RED}付近に誰かの保護がかかっているようです") } } @@ -137,7 +141,8 @@ class TilingSkillTriggerListener[ } def consumeOnePlacementItemFromInventory(): Option[Unit] = { - @scala.annotation.tailrec def forever(block: => Unit): Nothing = { + @scala.annotation.tailrec + def forever(block: => Unit): Nothing = { block; forever(block) } @@ -149,7 +154,7 @@ class TilingSkillTriggerListener[ if (consumptionSource != null && consumptionSource.isSimilar(offHandItem)) { val sourceStackAmount = consumptionSource.getAmount - //取得したインベントリデータから数量を1ひき、インベントリに反映する + // 取得したインベントリデータから数量を1ひき、インベントリに反映する val updatedItem = if (sourceStackAmount == 1) new ItemStack(Material.AIR) @@ -171,7 +176,7 @@ class TilingSkillTriggerListener[ } if (replaceableMaterials.contains(targetSurfaceBlock.getType)) { - //他人の保護がかかっている場合は処理を終了 + // 他人の保護がかかっている場合は処理を終了 if (!Util.getWorldGuard.canBuild(player, targetSurfaceLocation)) { player.sendMessage(s"${RED}付近に誰かの保護がかかっているようです") b1.break() @@ -179,7 +184,9 @@ class TilingSkillTriggerListener[ minestackObjectToUse match { case Some(mineStackObject) => - if (seichiAssistPlayerData.minestack.getStackedAmountOf(mineStackObject) > 0) { + if ( + seichiAssistPlayerData.minestack.getStackedAmountOf(mineStackObject) > 0 + ) { seichiAssistPlayerData.minestack.subtractStackedAmountOf(mineStackObject, 1) commitPlacement() diff --git a/src/main/scala/com/github/unchama/buildassist/menu/BlockPlacementSkillMenu.scala b/src/main/scala/com/github/unchama/buildassist/menu/BlockPlacementSkillMenu.scala index 55e234bd2c..3435ace74f 100644 --- a/src/main/scala/com/github/unchama/buildassist/menu/BlockPlacementSkillMenu.scala +++ b/src/main/scala/com/github/unchama/buildassist/menu/BlockPlacementSkillMenu.scala @@ -11,7 +11,12 @@ import com.github.unchama.seichiassist.effects.player.CommonSoundEffects import com.github.unchama.seichiassist.menus.BuildMainMenu import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.player.FocusedSoundEffect -import com.github.unchama.targetedeffect.{DeferredEffect, SequentialEffect, TargetedEffect, UnfocusedEffect} +import com.github.unchama.targetedeffect.{ + DeferredEffect, + SequentialEffect, + TargetedEffect, + UnfocusedEffect +} import com.github.unchama.{menuinventory, targetedeffect} import org.bukkit.ChatColor._ import org.bukkit.entity.Player @@ -19,11 +24,13 @@ import org.bukkit.{Material, Sound} object BlockPlacementSkillMenu extends Menu { - import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.{asyncShift, onMainThread} + import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.{ + asyncShift, + onMainThread + } import menuinventory.syntax._ - class Environment(implicit - val canOpenMainMenu: CanOpen[IO, BuildMainMenu.type]) + class Environment(implicit val canOpenMainMenu: CanOpen[IO, BuildMainMenu.type]) override val frame: MenuFrame = MenuFrame(4.chestRows, s"$DARK_PURPLE$BOLD「範囲設置スキル」設定画面") @@ -134,9 +141,7 @@ object BlockPlacementSkillMenu extends Menu { .lore { List(s"$RESET${AQUA}現在の範囲設定: $currentRange×$currentRange").concat( if (playerData.AREAint == 5) { - Seq( - s"$RESET${RED}これ以上範囲設定を大きくできません。" - ) + Seq(s"$RESET${RED}これ以上範囲設定を大きくできません。") } else { Seq( s"$RESET$AQUA${UNDERLINE}変更後の範囲設定: $changedRange×$changedRange", @@ -150,22 +155,18 @@ object BlockPlacementSkillMenu extends Menu { Button( iconItemStack, - LeftClickButtonEffect( - DeferredEffect( - IO { - if (playerData.AREAint < 5) - SequentialEffect( - FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f), - UnfocusedEffect { - playerData.AREAint += 1 - }, - MessageEffect(s"${RED}現在の範囲設定は $changedRange×$changedRange です"), - open - ) - else TargetedEffect.emptyEffect - } - ) - ) + LeftClickButtonEffect(DeferredEffect(IO { + if (playerData.AREAint < 5) + SequentialEffect( + FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f), + UnfocusedEffect { + playerData.AREAint += 1 + }, + MessageEffect(s"${RED}現在の範囲設定は $changedRange×$changedRange です"), + open + ) + else TargetedEffect.emptyEffect + })) ) } @@ -203,40 +204,33 @@ object BlockPlacementSkillMenu extends Menu { val iconItemStack = new SkullItemStackBuilder("MHF_ArrowDown") .title(s"$YELLOW$UNDERLINE${BOLD}範囲設定を一段階小さくする") .lore( - List(s"$RESET${AQUA}現在の範囲設定: $currentRange×$currentRange").concat( - if (playerData.AREAint == 1) { - List( - s"${RED}これ以上範囲設定を小さくできません。" - ) + List(s"$RESET${AQUA}現在の範囲設定: $currentRange×$currentRange") + .concat(if (playerData.AREAint == 1) { + List(s"${RED}これ以上範囲設定を小さくできません。") } else { List( s"$RESET$AQUA${UNDERLINE}変更後の範囲設定: $changedRange×$changedRange", s"$RESET$RED※範囲設定の最小値は3×3※" ) - } - ) + }) ) .amount(3) .build() Button( iconItemStack, - LeftClickButtonEffect( - DeferredEffect( - IO { - if (playerData.AREAint > 1) - SequentialEffect( - FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f), - UnfocusedEffect { - playerData.AREAint -= 1 - }, - MessageEffect(s"${RED}現在の範囲設定は $changedRange×$changedRange です"), - open - ) - else TargetedEffect.emptyEffect - } - ) - ) + LeftClickButtonEffect(DeferredEffect(IO { + if (playerData.AREAint > 1) + SequentialEffect( + FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f), + UnfocusedEffect { + playerData.AREAint -= 1 + }, + MessageEffect(s"${RED}現在の範囲設定は $changedRange×$changedRange です"), + open + ) + else TargetedEffect.emptyEffect + })) ) } @@ -274,7 +268,9 @@ object BlockPlacementSkillMenu extends Menu { val currentStatus = playerData.zs_minestack_flag val iconItemStackBuilder = new IconItemStackBuilder(Material.CHEST) - .title(s"$YELLOW$UNDERLINE${BOLD}MineStack優先設定: ${if (currentStatus) "ON" else "OFF"}") + .title( + s"$YELLOW$UNDERLINE${BOLD}MineStack優先設定: ${if (currentStatus) "ON" else "OFF"}" + ) .lore( s"$RESET${GRAY}スキルでブロックを並べるとき", s"$RESET${GRAY}MineStackの在庫を優先して消費します。", @@ -289,7 +285,11 @@ object BlockPlacementSkillMenu extends Menu { FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f), DeferredEffect { IO { - if (amountData.levelCorrespondingToExp.level < BuildAssist.config.getZoneskillMinestacklevel) + if ( + amountData + .levelCorrespondingToExp + .level < BuildAssist.config.getZoneskillMinestacklevel + ) MessageEffect(s"${RED}建築Lvが足りません") else SequentialEffect( @@ -308,13 +308,13 @@ object BlockPlacementSkillMenu extends Menu { } } - override def computeMenuLayout(player: Player)(implicit environment: Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { val computations = ButtonComputations(player) import computations._ - val constantPart = Map( - 0 -> buttonToOpenPreviousPage - ) + val constantPart = Map(0 -> buttonToOpenPreviousPage) import cats.implicits._ @@ -328,9 +328,7 @@ object BlockPlacementSkillMenu extends Menu { 24 -> computeButtonToDecreaseRange(), 25 -> computeButtonToMinimizeRange(), 35 -> computeButtonToToggleConsumingMineStack() - ) - .map(_.sequence) - .sequence + ).map(_.sequence).sequence for { dynamicPart <- dynamicPartComputation diff --git a/src/main/scala/com/github/unchama/buildassist/menu/BuildAssistMenuRouter.scala b/src/main/scala/com/github/unchama/buildassist/menu/BuildAssistMenuRouter.scala index 53b2e6748c..ca94d3bd36 100644 --- a/src/main/scala/com/github/unchama/buildassist/menu/BuildAssistMenuRouter.scala +++ b/src/main/scala/com/github/unchama/buildassist/menu/BuildAssistMenuRouter.scala @@ -13,16 +13,22 @@ trait BuildAssistMenuRouter[F[_]] { } object BuildAssistMenuRouter { - def apply(implicit - flyApi: ManagedFlyApi[SyncIO, Player], - layoutPreparationContext: LayoutPreparationContext, - onMainThread: OnMinecraftServerThread[IO]): BuildAssistMenuRouter[IO] = new BuildAssistMenuRouter[IO] { - implicit lazy val blockPlacementSkillMenuEnvironment: BlockPlacementSkillMenu.Environment = new BlockPlacementSkillMenu.Environment - implicit lazy val buildMainMenuEnvironment: BuildMainMenu.Environment = new BuildMainMenu.Environment - implicit lazy val mineStackMassCraftMenuEnvironment: MineStackMassCraftMenu.Environment = new MineStackMassCraftMenu.Environment + def apply( + implicit flyApi: ManagedFlyApi[SyncIO, Player], + layoutPreparationContext: LayoutPreparationContext, + onMainThread: OnMinecraftServerThread[IO] + ): BuildAssistMenuRouter[IO] = new BuildAssistMenuRouter[IO] { + implicit lazy val blockPlacementSkillMenuEnvironment: BlockPlacementSkillMenu.Environment = + new BlockPlacementSkillMenu.Environment + implicit lazy val buildMainMenuEnvironment: BuildMainMenu.Environment = + new BuildMainMenu.Environment + implicit lazy val mineStackMassCraftMenuEnvironment: MineStackMassCraftMenu.Environment = + new MineStackMassCraftMenu.Environment - implicit lazy val canOpenBlockPlacementSkillMenu: CanOpen[IO, BlockPlacementSkillMenu.type] = _.open - implicit lazy val canOpenMineStackMassCraftMenu: CanOpen[IO, MineStackMassCraftMenu] = _.open + implicit lazy val canOpenBlockPlacementSkillMenu + : CanOpen[IO, BlockPlacementSkillMenu.type] = _.open + implicit lazy val canOpenMineStackMassCraftMenu: CanOpen[IO, MineStackMassCraftMenu] = + _.open override implicit lazy val canOpenBuildMainMenu: CanOpen[IO, BuildMainMenu.type] = _.open } diff --git a/src/main/scala/com/github/unchama/buildassist/menu/MineStackMassCraftMenu.scala b/src/main/scala/com/github/unchama/buildassist/menu/MineStackMassCraftMenu.scala index f77169147f..5a020d2d86 100644 --- a/src/main/scala/com/github/unchama/buildassist/menu/MineStackMassCraftMenu.scala +++ b/src/main/scala/com/github/unchama/buildassist/menu/MineStackMassCraftMenu.scala @@ -24,16 +24,23 @@ import java.util.Locale object MineStackMassCraftMenu { - import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.{layoutPreparationContext, onMainThread} + import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.{ + layoutPreparationContext, + onMainThread + } type MineStackItemId = String - class Environment(implicit - val canOpenBuildMainMenu: CanOpen[IO, BuildMainMenu.type], - val canOpenItself: CanOpen[IO, MineStackMassCraftMenu]) + class Environment( + implicit val canOpenBuildMainMenu: CanOpen[IO, BuildMainMenu.type], + val canOpenItself: CanOpen[IO, MineStackMassCraftMenu] + ) + + case class MassCraftRecipe( + ingredients: NonEmptyList[(MineStackItemId, Int)], + products: NonEmptyList[(MineStackItemId, Int)] + ) { - case class MassCraftRecipe(ingredients: NonEmptyList[(MineStackItemId, Int)], - products: NonEmptyList[(MineStackItemId, Int)]) { /** * このレシピのクラフトを `scale` 回実行するような新たなレシピを作成する。 */ @@ -49,14 +56,16 @@ object MineStackMassCraftMenu { /** * 押すとこのレシピのクラフトが実行されるようなボタンを計算する。 * - * プレーヤーが`requiredBuildLevel`に達していない場合にこのボタンを押すと、 - * クラフトは実行されず適切なメッセージがプレーヤーに通達される。 + * プレーヤーが`requiredBuildLevel`に達していない場合にこのボタンを押すと、 クラフトは実行されず適切なメッセージがプレーヤーに通達される。 * - * @param requiredMassCraftLevel レシピの実行に必要なクラフトレベル - * @param menuPageNumber このボタンが表示される一括クラフト画面のページ番号 + * @param requiredMassCraftLevel + * レシピの実行に必要なクラフトレベル + * @param menuPageNumber + * このボタンが表示される一括クラフト画面のページ番号 */ - def computeButton(player: Player, requiredMassCraftLevel: Int, menuPageNumber: Int) - (implicit environment: Environment): IO[Button] = { + def computeButton(player: Player, requiredMassCraftLevel: Int, menuPageNumber: Int)( + implicit environment: Environment + ): IO[Button] = { import cats.implicits._ def queryAmountOf(mineStackObj: MineStackObj): IO[Long] = IO { @@ -69,7 +78,8 @@ object MineStackMassCraftMenu { def enumerateChunkDetails(chunks: NonEmptyList[(MineStackObj, Int)]): String = chunks.map { case (obj, amount) => s"${obj.uiName.get}${amount}個" }.mkString_("+") - val requiredBuildLevel = BuildAssist.config.getMinestackBlockCraftlevel(requiredMassCraftLevel) + val requiredBuildLevel = + BuildAssist.config.getMinestackBlockCraftlevel(requiredMassCraftLevel) val ingredientObjects = ingredients.map(toMineStackObjectChunk) val productObjects = products.map(toMineStackObjectChunk) @@ -90,13 +100,11 @@ object MineStackMassCraftMenu { for { possessionDisplayBlock <- - (ingredientObjects.toList ++ productObjects.toList) - .map(_._1) - .traverse { obj => - queryAmountOf(obj).map { amount => - s"$RESET$GRAY${obj.uiName.get}の数:${NumberFormat.getNumberInstance(Locale.US).format(amount)}" - } + (ingredientObjects.toList ++ productObjects.toList).map(_._1).traverse { obj => + queryAmountOf(obj).map { amount => + s"$RESET$GRAY${obj.uiName.get}の数:${NumberFormat.getNumberInstance(Locale.US).format(amount)}" } + } } yield { val lore = List( List(loreHeading), @@ -141,19 +149,22 @@ object MineStackMassCraftMenu { } else { onMainThread.runAction[SyncIO, Unit] { val allIngredientsAvailable = - ingredientObjects.forall { case (obj, amount) => - mineStack.getStackedAmountOf(obj) >= amount + ingredientObjects.forall { + case (obj, amount) => + mineStack.getStackedAmountOf(obj) >= amount } if (!allIngredientsAvailable) MessageEffectF[SyncIO](s"${RED}クラフト材料が足りません").apply(player) else SyncIO { - ingredientObjects.toList.foreach { case (obj, amount) => - mineStack.subtractStackedAmountOf(obj, amount) + ingredientObjects.toList.foreach { + case (obj, amount) => + mineStack.subtractStackedAmountOf(obj, amount) } - productObjects.toList.foreach { case (obj, amount) => - mineStack.addStackedAmountOf(obj, amount) + productObjects.toList.foreach { + case (obj, amount) => + mineStack.addStackedAmountOf(obj, amount) } } >> { val message = @@ -180,25 +191,31 @@ object MineStackMassCraftMenu { } } - case class MassCraftRecipeBlock(recipe: MassCraftRecipe, recipeScales: List[Int], requiredBuildLevel: Int) { - def toLayout(player: Player, beginIndex: Int, pageNumber: Int) - (implicit environment: Environment): IO[List[(Int, Slot)]] = { + case class MassCraftRecipeBlock( + recipe: MassCraftRecipe, + recipeScales: List[Int], + requiredBuildLevel: Int + ) { + def toLayout(player: Player, beginIndex: Int, pageNumber: Int)( + implicit environment: Environment + ): IO[List[(Int, Slot)]] = { import cats.implicits._ - recipeScales.zipWithIndex - .traverse { case (scale, scaleIndex) => + recipeScales.zipWithIndex.traverse { + case (scale, scaleIndex) => for { - button <- recipe.scaleBy(scale).computeButton(player, requiredBuildLevel, pageNumber) + button <- recipe + .scaleBy(scale) + .computeButton(player, requiredBuildLevel, pageNumber) } yield (beginIndex + scaleIndex, button) - } + } } } /** * 各メニューページのレシピブロックを持つ`Seq`。 * - * `Seq`の`i`番目の`List`には、 - * メニュー`(i+1)`番目のインベントリ内のスロットへの参照とレシピブロックの組が格納されている。 + * `Seq`の`i`番目の`List`には、 メニュー`(i+1)`番目のインベントリ内のスロットへの参照とレシピブロックの組が格納されている。 */ val recipeBlocks: Seq[List[(Int, MassCraftRecipeBlock)]] = { import eu.timepit.refined.auto._ @@ -210,289 +227,366 @@ object MineStackMassCraftMenu { Seq( List( ChestSlotRef(0, 0) -> MassCraftRecipeBlock( - MassCraftRecipe( - NonEmptyList.of(("stone", 10)), - NonEmptyList.of(("step0", 20)) - ), oneToTenThousand, 1 + MassCraftRecipe(NonEmptyList.of(("stone", 10)), NonEmptyList.of(("step0", 20))), + oneToTenThousand, + 1 ), ChestSlotRef(1, 0) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("stone", 10)), NonEmptyList.of(("smooth_brick0", 10)) - ), oneToTenThousand, 1 + ), + oneToTenThousand, + 1 ), ChestSlotRef(2, 0) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("granite", 10)), NonEmptyList.of(("polished_granite", 10)) - ), oneToThousand, 2 + ), + oneToThousand, + 2 ), ChestSlotRef(2, 5) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("diorite", 10)), NonEmptyList.of(("polished_diorite", 10)) - ), oneToThousand, 2 + ), + oneToThousand, + 2 ), ChestSlotRef(3, 0) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("andesite", 10)), NonEmptyList.of(("polished_andesite", 10)) - ), oneToThousand, 2 + ), + oneToThousand, + 2 ), ChestSlotRef(3, 5) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("quartz", 40)), NonEmptyList.of(("quartz_block", 10)) - ), oneToThousand, 2 + ), + oneToThousand, + 2 ), ChestSlotRef(4, 0) -> MassCraftRecipeBlock( - MassCraftRecipe( - NonEmptyList.of(("brick_item", 40)), - NonEmptyList.of(("brick", 10)) - ), oneToThousand, 2 + MassCraftRecipe(NonEmptyList.of(("brick_item", 40)), NonEmptyList.of(("brick", 10))), + oneToThousand, + 2 ), ChestSlotRef(4, 5) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("nether_brick_item", 40)), NonEmptyList.of(("nether_brick", 10)) - ), oneToThousand, 2 - ), + ), + oneToThousand, + 2 + ) ), List( ChestSlotRef(0, 0) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("snow_ball", 40)), NonEmptyList.of(("snow_block", 10)) - ), oneToThousand, 2 + ), + oneToThousand, + 2 ), ChestSlotRef(0, 5) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("nether_stalk", 20), ("nether_brick_item", 20)), NonEmptyList.of(("red_nether_brick", 10)) - ), oneToThousand, 2 + ), + oneToThousand, + 2 ), ChestSlotRef(1, 0) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("iron_ore", 40), ("coal", 10)), NonEmptyList.of(("iron_ingot", 40)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(1, 6) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("iron_ore", 50), ("lava_bucket", 1)), NonEmptyList.of(("iron_ingot", 50)) - ), oneToHundred, 3 + ), + oneToHundred, + 3 ), ChestSlotRef(2, 0) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("gold_ore", 40), ("coal", 10)), NonEmptyList.of(("gold_ingot", 40)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(2, 6) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("gold_ore", 50), ("lava_bucket", 1)), NonEmptyList.of(("gold_ingot", 50)) - ), oneToHundred, 3 + ), + oneToHundred, + 3 ), ChestSlotRef(3, 1) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("sand", 4), ("coal", 1)), NonEmptyList.of(("glass", 4)) - ), oneToHundred, 3 + ), + oneToHundred, + 3 ), ChestSlotRef(3, 6) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("sand", 50), ("lava_bucket", 1)), NonEmptyList.of(("glass", 50)) - ), oneToHundred, 3 + ), + oneToHundred, + 3 ), ChestSlotRef(4, 1) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("netherrack", 4), ("coal", 1)), NonEmptyList.of(("nether_brick_item", 4)) - ), oneToHundred, 3 + ), + oneToHundred, + 3 ), ChestSlotRef(4, 6) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("netherrack", 50), ("lava_bucket", 1)), NonEmptyList.of(("nether_brick_item", 50)) - ), oneToHundred, 3 - ), + ), + oneToHundred, + 3 + ) ), List( ChestSlotRef(0, 1) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("clay_ball", 4), ("coal", 1)), NonEmptyList.of(("brick_item", 4)) - ), oneToHundred, 3 + ), + oneToHundred, + 3 ), ChestSlotRef(0, 6) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("clay_ball", 50), ("lava_bucket", 1)), NonEmptyList.of(("brick_item", 50)) - ), oneToHundred, 3 + ), + oneToHundred, + 3 ), ChestSlotRef(1, 0) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("ender_stone", 4)), NonEmptyList.of(("end_bricks", 4)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(1, 5) -> MassCraftRecipeBlock( - MassCraftRecipe( - NonEmptyList.of(("coal", 9)), - NonEmptyList.of(("coal_block", 1)) - ), oneToThousand, 3 + MassCraftRecipe(NonEmptyList.of(("coal", 9)), NonEmptyList.of(("coal_block", 1))), + oneToThousand, + 3 ), ChestSlotRef(2, 0) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("iron_ingot", 9)), NonEmptyList.of(("iron_block", 1)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(2, 5) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("gold_ingot", 9)), NonEmptyList.of(("gold_block", 1)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(3, 0) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("lapis_lazuli", 9)), NonEmptyList.of(("lapis_block", 1)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(3, 5) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("redstone", 9)), NonEmptyList.of(("redstone_block", 1)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(4, 0) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("emerald", 9)), NonEmptyList.of(("emerald_block", 1)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(4, 5) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("diamond", 9)), NonEmptyList.of(("diamond_block", 1)) - ), oneToThousand, 3 - ), + ), + oneToThousand, + 3 + ) ), List( ChestSlotRef(0, 0) -> MassCraftRecipeBlock( - MassCraftRecipe( - NonEmptyList.of(("clay", 1)), - NonEmptyList.of(("clay_ball", 4)) - ), oneToThousand, 3 + MassCraftRecipe(NonEmptyList.of(("clay", 1)), NonEmptyList.of(("clay_ball", 4))), + oneToThousand, + 3 ), ChestSlotRef(0, 5) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("magma", 8), ("bucket", 1)), NonEmptyList.of(("lava_bucket", 1)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(1, 0) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("quartz_ore", 4), ("coal", 1)), NonEmptyList.of(("quartz", 4)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(1, 5) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("quartz_ore", 50), ("lava_bucket", 1)), NonEmptyList.of(("quartz", 50)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(2, 0) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("quartz_block", 1)), NonEmptyList.of(("quartz_block1", 1)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(2, 5) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("quartz_block", 2)), NonEmptyList.of(("quartz_block2", 1)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(3, 0) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("redstone_ore", 4), ("coal", 1)), NonEmptyList.of(("redstone", 4)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(3, 5) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("redstone_ore", 50), ("lava_bucket", 1)), NonEmptyList.of(("redstone", 50)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(4, 0) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("lapis_ore", 4), ("coal", 1)), NonEmptyList.of(("lapis_lazuli", 4)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(4, 5) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("lapis_ore", 50), ("lava_bucket", 1)), NonEmptyList.of(("lapis_lazuli", 50)) - ), oneToThousand, 3 - ), + ), + oneToThousand, + 3 + ) ), List( ChestSlotRef(0, 0) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("emerald_ore", 4), ("coal", 1)), NonEmptyList.of(("emerald", 4)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(0, 5) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("emerald_ore", 50), ("lava_bucket", 1)), NonEmptyList.of(("emerald", 50)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(1, 0) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("diamond_ore", 4), ("coal", 1)), NonEmptyList.of(("diamond", 4)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(1, 5) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("diamond_ore", 50), ("lava_bucket", 1)), NonEmptyList.of(("diamond", 50)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(2, 0) -> MassCraftRecipeBlock( - MassCraftRecipe( - NonEmptyList.of(("iron_ingot", 3)), - NonEmptyList.of(("bucket", 1)) - ), oneToThousand, 3 + MassCraftRecipe(NonEmptyList.of(("iron_ingot", 3)), NonEmptyList.of(("bucket", 1))), + oneToThousand, + 3 ), ChestSlotRef(2, 5) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("sandstone", 4), ("coal", 1)), NonEmptyList.of(("sandstone2", 4)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(3, 0) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("chorus_fruit", 4), ("coal", 1)), NonEmptyList.of(("popped_chorus_fruit", 4)) - ), oneToThousand, 3 + ), + oneToThousand, + 3 ), ChestSlotRef(3, 5) -> MassCraftRecipeBlock( MassCraftRecipe( NonEmptyList.of(("popped_chorus_fruit", 4)), NonEmptyList.of(("purpur_block", 4)) - ), oneToThousand, 3 - ), + ), + oneToThousand, + 3 + ) ) ) } @@ -508,10 +602,15 @@ case class MineStackMassCraftMenu(pageNumber: Int = 1) extends Menu { import eu.timepit.refined.auto._ - override def computeMenuLayout(player: Player)(implicit environment: Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { import environment._ - def buttonToTransferTo(newPageNumber: Int, skullOwnerReference: SkullOwnerReference): Button = + def buttonToTransferTo( + newPageNumber: Int, + skullOwnerReference: SkullOwnerReference + ): Button = CommonButtons.transferButton( new SkullItemStackBuilder(skullOwnerReference), s"${newPageNumber}ページ目へ", @@ -536,7 +635,8 @@ case class MineStackMassCraftMenu(pageNumber: Int = 1) extends Menu { Map( ChestSlotRef(5, 0) -> CommonButtons.transferButton( new SkullItemStackBuilder(SkullOwners.MHF_ArrowLeft), - "ホームへ", BuildMainMenu + "ホームへ", + BuildMainMenu ) ) @@ -544,10 +644,10 @@ case class MineStackMassCraftMenu(pageNumber: Int = 1) extends Menu { for { recipeSectionBlocks <- - MineStackMassCraftMenu.recipeBlocks(pageNumber - 1) - .traverse { case (beginIndex, recipeBlock) => + MineStackMassCraftMenu.recipeBlocks(pageNumber - 1).traverse { + case (beginIndex, recipeBlock) => recipeBlock.toLayout(player, beginIndex, pageNumber) - } + } recipeSection = recipeSectionBlocks.flatten.toMap } yield { diff --git a/src/main/scala/com/github/unchama/bungeesemaphoreresponder/System.scala b/src/main/scala/com/github/unchama/bungeesemaphoreresponder/System.scala index c4fb530fce..f000f137d7 100644 --- a/src/main/scala/com/github/unchama/bungeesemaphoreresponder/System.scala +++ b/src/main/scala/com/github/unchama/bungeesemaphoreresponder/System.scala @@ -9,10 +9,10 @@ import com.github.unchama.bungeesemaphoreresponder.infrastructure.redis.RedisBun import org.bukkit.entity.Player import org.bukkit.event.Listener -class System[ - F[_] : ConcurrentEffect : Timer -](val finalizer: PlayerDataFinalizer[F, Player], messagePublishingContext: ContextShift[IO]) - (implicit configuration: Configuration, _akkaSystem: ActorSystem) { +class System[F[_]: ConcurrentEffect: Timer]( + val finalizer: PlayerDataFinalizer[F, Player], + messagePublishingContext: ContextShift[IO] +)(implicit configuration: Configuration, _akkaSystem: ActorSystem) { // We wish to be more explicit on the context shift that will be used within this system, // so we don't receive it as an implicit parameter implicit val _contextShift: ContextShift[IO] = messagePublishingContext @@ -21,8 +21,6 @@ class System[ implicit val _synchronization: BungeeSemaphoreSynchronization[F[Unit], PlayerName] = { new RedisBungeeSemaphoreSynchronization[F]() } - Seq( - new BungeeSemaphoreCooperator[F](finalizer) - ) + Seq(new BungeeSemaphoreCooperator[F](finalizer)) } } diff --git a/src/main/scala/com/github/unchama/bungeesemaphoreresponder/bukkit/listeners/BungeeSemaphoreCooperator.scala b/src/main/scala/com/github/unchama/bungeesemaphoreresponder/bukkit/listeners/BungeeSemaphoreCooperator.scala index 7ab05117fb..9244387613 100644 --- a/src/main/scala/com/github/unchama/bungeesemaphoreresponder/bukkit/listeners/BungeeSemaphoreCooperator.scala +++ b/src/main/scala/com/github/unchama/bungeesemaphoreresponder/bukkit/listeners/BungeeSemaphoreCooperator.scala @@ -12,11 +12,12 @@ import org.bukkit.event.{EventHandler, EventPriority, Listener} import scala.concurrent.duration.{Duration, FiniteDuration} -class BungeeSemaphoreCooperator[ - F[_] : ConcurrentEffect : Timer -](finalizer: PlayerDataFinalizer[F, Player]) - (implicit synchronization: BungeeSemaphoreSynchronization[F[Unit], PlayerName], - configuration: Configuration) extends Listener { +class BungeeSemaphoreCooperator[F[_]: ConcurrentEffect: Timer]( + finalizer: PlayerDataFinalizer[F, Player] +)( + implicit synchronization: BungeeSemaphoreSynchronization[F[Unit], PlayerName], + configuration: Configuration +) extends Listener { import cats.effect.implicits._ import cats.implicits._ @@ -27,7 +28,7 @@ class BungeeSemaphoreCooperator[ val name = PlayerName(player.getName) val timeout = configuration.saveTimeoutDuration match { case duration: FiniteDuration => Timer[F].sleep(duration) - case _: Duration.Infinite => Async[F].never + case _: Duration.Infinite => Async[F].never } val program = for { diff --git a/src/main/scala/com/github/unchama/bungeesemaphoreresponder/domain/PlayerDataFinalizer.scala b/src/main/scala/com/github/unchama/bungeesemaphoreresponder/domain/PlayerDataFinalizer.scala index 8169928a35..02201da084 100644 --- a/src/main/scala/com/github/unchama/bungeesemaphoreresponder/domain/PlayerDataFinalizer.scala +++ b/src/main/scala/com/github/unchama/bungeesemaphoreresponder/domain/PlayerDataFinalizer.scala @@ -5,22 +5,22 @@ import cats.{Applicative, ApplicativeError, ~>} import com.github.unchama.generic.ContextCoercion /** - * The type of a pure callback object that should be invoked when a player quits the Minecraft server. + * The type of a pure callback object that should be invoked when a player quits the Minecraft + * server. * - * This is an interface to a controller object; - * within `onQuitOf` the object should do "finalization" of - * any data that is associated with player's login session. + * This is an interface to a controller object; within `onQuitOf` the object should do + * "finalization" of any data that is associated with player's login session. */ trait PlayerDataFinalizer[F[_], Player] { /** * A finalizing action that must be run when the given [[Player]] quits the server. * - * The action returned by this function can be run - * either on the server main thread or asynchronously at any other thread. + * The action returned by this function can be run either on the server main thread or + * asynchronously at any other thread. * - * Therefore the implementation of this method must not assume the execution on the server main thread. - * If required, the action may shift the execution context. + * Therefore the implementation of this method must not assume the execution on the server + * main thread. If required, the action may shift the execution context. */ def onQuitOf(player: Player): F[Unit] @@ -28,29 +28,27 @@ trait PlayerDataFinalizer[F[_], Player] { PlayerDataFinalizer(player => trans.apply(onQuitOf(player))) } - def coerceContextTo[G[_] : ContextCoercion[F, *[_]]]: PlayerDataFinalizer[G, Player] = transformContext[G](implicitly) + def coerceContextTo[G[_]: ContextCoercion[F, *[_]]]: PlayerDataFinalizer[G, Player] = + transformContext[G](implicitly) } object PlayerDataFinalizer { - def apply[F[_], Player](f: Player => F[Unit]): PlayerDataFinalizer[F, Player] = (player: Player) => f(player) + def apply[F[_], Player](f: Player => F[Unit]): PlayerDataFinalizer[F, Player] = + (player: Player) => f(player) import cats.effect.implicits._ import cats.implicits._ - def sequence[ - F[_] : Applicative, - Player - ](finalizers: List[PlayerDataFinalizer[F, Player]]): PlayerDataFinalizer[F, Player] = - PlayerDataFinalizer { - player => finalizers.traverse(_.onQuitOf(player)).as(()) - } + def sequence[F[_]: Applicative, Player]( + finalizers: List[PlayerDataFinalizer[F, Player]] + ): PlayerDataFinalizer[F, Player] = + PlayerDataFinalizer { player => finalizers.traverse(_.onQuitOf(player)).as(()) } - def concurrently[ - F[_] : ConcurrentEffect, - Player - ](finalizers: List[PlayerDataFinalizer[F, Player]]): PlayerDataFinalizer[F, Player] = + def concurrently[F[_]: ConcurrentEffect, Player]( + finalizers: List[PlayerDataFinalizer[F, Player]] + ): PlayerDataFinalizer[F, Player] = PlayerDataFinalizer { player => for { fibers <- finalizers.traverse(_.onQuitOf(player).attempt.start) diff --git a/src/main/scala/com/github/unchama/bungeesemaphoreresponder/infrastructure/redis/RedisBungeeSemaphoreSynchronization.scala b/src/main/scala/com/github/unchama/bungeesemaphoreresponder/infrastructure/redis/RedisBungeeSemaphoreSynchronization.scala index a867d7ab4f..b4ac3262ca 100644 --- a/src/main/scala/com/github/unchama/bungeesemaphoreresponder/infrastructure/redis/RedisBungeeSemaphoreSynchronization.scala +++ b/src/main/scala/com/github/unchama/bungeesemaphoreresponder/infrastructure/redis/RedisBungeeSemaphoreSynchronization.scala @@ -5,13 +5,17 @@ import cats.effect.{ContextShift, Effect, IO} import com.github.unchama.bungeesemaphoreresponder.Configuration import com.github.unchama.bungeesemaphoreresponder.domain.PlayerName import com.github.unchama.bungeesemaphoreresponder.domain.actions.BungeeSemaphoreSynchronization -import com.github.unchama.bungeesemaphoreresponder.infrastructure.redis.SignalFormat.{BungeeSemaphoreMessage, DataSaveFailed, ReleaseDataLock} +import com.github.unchama.bungeesemaphoreresponder.infrastructure.redis.SignalFormat.{ + BungeeSemaphoreMessage, + DataSaveFailed, + ReleaseDataLock +} -class RedisBungeeSemaphoreSynchronization[F[_] : Effect](implicit - publishingContext: ContextShift[IO], - configuration: Configuration, - actorSystem: ActorSystem) - extends BungeeSemaphoreSynchronization[F[Unit], PlayerName] { +class RedisBungeeSemaphoreSynchronization[F[_]: Effect]( + implicit publishingContext: ContextShift[IO], + configuration: Configuration, + actorSystem: ActorSystem +) extends BungeeSemaphoreSynchronization[F[Unit], PlayerName] { private val client = ConfiguredRedisClient() @@ -32,8 +36,12 @@ class RedisBungeeSemaphoreSynchronization[F[_] : Effect](implicit } } - override def confirmSaveCompletionOf(player: PlayerName): Action = sendMessage(ReleaseDataLock(player)) + override def confirmSaveCompletionOf(player: PlayerName): Action = sendMessage( + ReleaseDataLock(player) + ) - override def notifySaveFailureOf(player: PlayerName): Action = sendMessage(DataSaveFailed(player)) + override def notifySaveFailureOf(player: PlayerName): Action = sendMessage( + DataSaveFailed(player) + ) } diff --git a/src/main/scala/com/github/unchama/chatinterceptor/ChatInterceptor.scala b/src/main/scala/com/github/unchama/chatinterceptor/ChatInterceptor.scala index 1aaee08676..3b0c66a351 100644 --- a/src/main/scala/com/github/unchama/chatinterceptor/ChatInterceptor.scala +++ b/src/main/scala/com/github/unchama/chatinterceptor/ChatInterceptor.scala @@ -14,7 +14,7 @@ class ChatInterceptor(val scopes: List[ChatInterceptionScope]) extends Listener .unsafeRunSync() } - @EventHandler(priority=EventPriority.LOWEST) + @EventHandler(priority = EventPriority.LOWEST) def onPlayerChat(event: AsyncPlayerChatEvent): Unit = { import cats.implicits._ @@ -24,7 +24,7 @@ class ChatInterceptor(val scopes: List[ChatInterceptionScope]) extends Listener .unsafeRunSync() .find(_ == InterceptorResponse.Intercepted) match { case Some(_) => event.setCancelled(true) - case None => + case None => } } } diff --git a/src/main/scala/com/github/unchama/chatinterceptor/InterceptionScope.scala b/src/main/scala/com/github/unchama/chatinterceptor/InterceptionScope.scala index 1ac514af4a..8c86d55de1 100644 --- a/src/main/scala/com/github/unchama/chatinterceptor/InterceptionScope.scala +++ b/src/main/scala/com/github/unchama/chatinterceptor/InterceptionScope.scala @@ -23,11 +23,10 @@ class InterceptionScope[K, R](implicit val cs: ContextShift[IO]) { private val map: mutable.Map[K, Deferred[IO, InterceptionResult[R]]] = mutable.HashMap() def cancelAnyInterception(key: K, reason: CancellationReason): IO[Unit] = - IO { map.remove(key) } - .flatMap { - case Some(deferred) => deferred.complete(Right(reason)) - case None => IO.pure(()) - } + IO { map.remove(key) }.flatMap { + case Some(deferred) => deferred.complete(Right(reason)) + case None => IO.pure(()) + } def interceptFrom(key: K): IO[InterceptionResult[R]] = for { deferred <- Deferred[IO, InterceptionResult[R]] @@ -38,9 +37,10 @@ class InterceptionScope[K, R](implicit val cs: ContextShift[IO]) { def suggestInterception(key: K, response: R): IO[InterceptorResponse] = IO { map.remove(key) }.flatMap { - case Some(deferred) => for { - _ <- deferred.complete(Left(response)) - } yield Intercepted + case Some(deferred) => + for { + _ <- deferred.complete(Left(response)) + } yield Intercepted case None => IO.pure(Ignored) } } diff --git a/src/main/scala/com/github/unchama/concurrent/NonServerThreadContextShift.scala b/src/main/scala/com/github/unchama/concurrent/NonServerThreadContextShift.scala index 67772548fd..3a5549bab2 100644 --- a/src/main/scala/com/github/unchama/concurrent/NonServerThreadContextShift.scala +++ b/src/main/scala/com/github/unchama/concurrent/NonServerThreadContextShift.scala @@ -2,6 +2,7 @@ package com.github.unchama.concurrent object NonServerThreadContextShift { - def apply[F[_]](implicit cs: NonServerThreadContextShift[F]): NonServerThreadContextShift[F] = cs + def apply[F[_]](implicit cs: NonServerThreadContextShift[F]): NonServerThreadContextShift[F] = + cs } diff --git a/src/main/scala/com/github/unchama/concurrent/RepeatingRoutine.scala b/src/main/scala/com/github/unchama/concurrent/RepeatingRoutine.scala index 80fe788af0..d6675cea1e 100644 --- a/src/main/scala/com/github/unchama/concurrent/RepeatingRoutine.scala +++ b/src/main/scala/com/github/unchama/concurrent/RepeatingRoutine.scala @@ -12,78 +12,81 @@ object RepeatingRoutine { import cats.implicits._ - private def sleepWith[F[_] : Timer : Monad](getInterval: F[FiniteDuration]): F[Unit] = + private def sleepWith[F[_]: Timer: Monad](getInterval: F[FiniteDuration]): F[Unit] = getInterval >>= (Timer[F].sleep(_)) - def permanentRoutine[F[_] : Timer : Sync, U](getInterval: F[FiniteDuration], action: F[U]): F[Nothing] = { + def permanentRoutine[F[_]: Timer: Sync, U]( + getInterval: F[FiniteDuration], + action: F[U] + ): F[Nothing] = { Slf4jLogger.create.flatMap[Nothing] { implicit logger => foreverMRecovering[F, U, Nothing](action)(getInterval) } } - def foreverMRecovering[ - F[_] : Timer : MonadError[*[_], Throwable] : ErrorLogger, - U, R - ](action: F[U])(getInterval: F[FiniteDuration]): F[R] = { + def foreverMRecovering[F[_]: Timer: MonadError[*[_], Throwable]: ErrorLogger, U, R]( + action: F[U] + )(getInterval: F[FiniteDuration]): F[R] = { val recoveringAction: F[Unit] = - ApplicativeErrorThrowableExtra.recoverWithStackTrace(action.as(()))( - "繰り返し実行タスクの実行に失敗しました", - () - ) + ApplicativeErrorThrowableExtra + .recoverWithStackTrace(action.as(()))("繰り返し実行タスクの実行に失敗しました", ()) Monad[F].foreverM(sleepWith(getInterval) >> recoveringAction) } - def whileReferencedRecovering[ - F[_] : Timer : MonadError[*[_], Throwable] : ErrorLogger, - R <: AnyRef, U - ](reference: WeakRef[F, R], action: R => F[U], getInterval: F[FiniteDuration]): F[Unit] = { + def whileReferencedRecovering[F[_]: Timer: MonadError[ + *[_], + Throwable + ]: ErrorLogger, R <: AnyRef, U]( + reference: WeakRef[F, R], + action: R => F[U], + getInterval: F[FiniteDuration] + ): F[Unit] = { whileDefinedMRecovering(())(_ => reference.get.flatMap[Option[Unit]] { case Some(value) => action(value).as(Some(())) - case None => Monad[F].pure(None) + case None => Monad[F].pure(None) } )(getInterval) } /** - * 初期状態 `init` から、`getInterval` によってスリープしたのち `action` にて - * 状態を副作用付きで更新するという操作を、 `action` がNoneを返すまで繰り返す + * 初期状態 `init` から、`getInterval` によってスリープしたのち `action` にて 状態を副作用付きで更新するという操作を、 `action` + * がNoneを返すまで繰り返す * * よりロギングに関する制御を求める時は [[whileDefinedMRecovering]] を使うこと。 * - * @tparam State ループにて保持される状態の型 + * @tparam State + * ループにて保持される状態の型 * @return */ - def recMTask[F[_] : Timer : Sync, State](init: State)(action: State => F[Option[State]]) - (getInterval: F[FiniteDuration])(implicit context: RepeatingTaskContext): F[Unit] = { - Slf4jLogger.create.flatMap(implicit logger => - whileDefinedMRecovering(init)(action)(getInterval) - ) + def recMTask[F[_]: Timer: Sync, State](init: State)( + action: State => F[Option[State]] + )(getInterval: F[FiniteDuration])(implicit context: RepeatingTaskContext): F[Unit] = { + Slf4jLogger + .create + .flatMap(implicit logger => whileDefinedMRecovering(init)(action)(getInterval)) } /** - * 初期状態 `init` から、`getInterval` によってスリープしたのち `action` にて - * 状態を副作用付きで更新するという操作を、 `action` がNoneを返すまで繰り返す + * 初期状態 `init` から、`getInterval` によってスリープしたのち `action` にて 状態を副作用付きで更新するという操作を、 `action` + * がNoneを返すまで繰り返す * - * @tparam State ループにて保持される状態の型 + * @tparam State + * ループにて保持される状態の型 * @return */ - def whileDefinedMRecovering[ - F[_] : MonadError[*[_], Throwable] : Timer : ErrorLogger, - State - ](init: State)(action: State => F[Option[State]]) - (getInterval: F[FiniteDuration]): F[Unit] = { + def whileDefinedMRecovering[F[_]: MonadError[*[_], Throwable]: Timer: ErrorLogger, State]( + init: State + )(action: State => F[Option[State]])(getInterval: F[FiniteDuration]): F[Unit] = { val recoveringAction: State => F[Option[State]] = s => - ApplicativeErrorThrowableExtra.recoverWithStackTrace(action(s))( - "繰り返し実行タスクの実行に失敗しました", - None - ) + ApplicativeErrorThrowableExtra + .recoverWithStackTrace(action(s))("繰り返し実行タスクの実行に失敗しました", None) Monad[F].tailRecM(init) { state => sleepWith(getInterval) >> recoveringAction(state) map { case Some(value) => Left(value) - case None => Right(()) + case None => Right(()) } } } diff --git a/src/main/scala/com/github/unchama/concurrent/syntax/MinecraftTimeUnitSyntax.scala b/src/main/scala/com/github/unchama/concurrent/syntax/MinecraftTimeUnitSyntax.scala index 9582ec2650..dda9b48ff7 100644 --- a/src/main/scala/com/github/unchama/concurrent/syntax/MinecraftTimeUnitSyntax.scala +++ b/src/main/scala/com/github/unchama/concurrent/syntax/MinecraftTimeUnitSyntax.scala @@ -4,11 +4,14 @@ import scala.concurrent.duration.FiniteDuration import scala.language.implicitConversions trait MinecraftTimeUnitSyntax { - implicit def intTickAmountToMinecraftTimeUnitOps(int: Int): MinecraftTimeUnitOps = MinecraftTimeUnitOps(int.toLong) + implicit def intTickAmountToMinecraftTimeUnitOps(int: Int): MinecraftTimeUnitOps = + MinecraftTimeUnitOps(int.toLong) - implicit def longTickAmountToMinecraftTimeUnitOps(long: Long): MinecraftTimeUnitOps = MinecraftTimeUnitOps(long) + implicit def longTickAmountToMinecraftTimeUnitOps(long: Long): MinecraftTimeUnitOps = + MinecraftTimeUnitOps(long) } case class MinecraftTimeUnitOps(tickAmount: Long) extends AnyVal { - def ticks: FiniteDuration = FiniteDuration(tickAmount * 50, scala.concurrent.duration.MILLISECONDS) + def ticks: FiniteDuration = + FiniteDuration(tickAmount * 50, scala.concurrent.duration.MILLISECONDS) } diff --git a/src/main/scala/com/github/unchama/concurrent/syntax/package.scala b/src/main/scala/com/github/unchama/concurrent/syntax/package.scala index c46b57758f..6b2b794fd5 100644 --- a/src/main/scala/com/github/unchama/concurrent/syntax/package.scala +++ b/src/main/scala/com/github/unchama/concurrent/syntax/package.scala @@ -1,4 +1,3 @@ package com.github.unchama.concurrent -package object syntax - extends MinecraftTimeUnitSyntax +package object syntax extends MinecraftTimeUnitSyntax diff --git a/src/main/scala/com/github/unchama/contextualexecutor/Contexts.scala b/src/main/scala/com/github/unchama/contextualexecutor/Contexts.scala index 6fcf896071..a57e05aa8e 100644 --- a/src/main/scala/com/github/unchama/contextualexecutor/Contexts.scala +++ b/src/main/scala/com/github/unchama/contextualexecutor/Contexts.scala @@ -10,25 +10,34 @@ case class ExecutedCommand(command: Command, aliasUsed: String) /** * コマンドの実行に関わる一連の生パラメータの情報 */ -case class RawCommandContext(sender: CommandSender, - command: ExecutedCommand, - args: List[String]) +case class RawCommandContext( + sender: CommandSender, + command: ExecutedCommand, + args: List[String] +) /** * 変換されたコマンド引数の情報 * - * @param parsed コマンド引数のうち, [Any?]を型上限とするオブジェクトに変換されたもの. - * @param yetToBeParsed コマンド引数のうち, [parsed]へと変換されていない文字列. + * @param parsed + * コマンド引数のうち, [Any?]を型上限とするオブジェクトに変換されたもの. + * @param yetToBeParsed + * コマンド引数のうち, [parsed]へと変換されていない文字列. */ case class PartiallyParsedArgs(parsed: List[Any], yetToBeParsed: List[String]) /** * コマンドの実行時のコマンド引数や実行者などの情報を変換, 加工したデータ. * - * @tparam CS [CommandSender]オブジェクトの型上限. [sender]は[CS]であることまでが保証されている. - * @param command 実行コマンドに関する情報 - * @param args 引数情報 + * @tparam CS + * [CommandSender]オブジェクトの型上限. [sender]は[CS]であることまでが保証されている. + * @param command + * 実行コマンドに関する情報 + * @param args + * 引数情報 */ -case class ParsedArgCommandContext[+CS <: CommandSender](sender: CS, - command: ExecutedCommand, - args: PartiallyParsedArgs) +case class ParsedArgCommandContext[+CS <: CommandSender]( + sender: CS, + command: ExecutedCommand, + args: PartiallyParsedArgs +) diff --git a/src/main/scala/com/github/unchama/contextualexecutor/ContextualExecutor.scala b/src/main/scala/com/github/unchama/contextualexecutor/ContextualExecutor.scala index 179dba7242..43cfbd69be 100644 --- a/src/main/scala/com/github/unchama/contextualexecutor/ContextualExecutor.scala +++ b/src/main/scala/com/github/unchama/contextualexecutor/ContextualExecutor.scala @@ -7,6 +7,7 @@ import org.bukkit.command.{Command, CommandSender, TabExecutor} * コマンド実行時に[TabExecutor]へ渡される情報をラップした[RawCommandContext]を用いて処理を行うオブジェクトへのtrait. */ trait ContextualExecutor { + /** * [rawContext] に基づいて, 作用を計算する. * @@ -24,6 +25,7 @@ trait ContextualExecutor { object ContextualExecutor { implicit class ContextualTabExecutor(val contextualExecutor: ContextualExecutor) { + /** * この[ContextualExecutor]を[TabExecutor]オブジェクトへ変換する. * @@ -31,7 +33,12 @@ object ContextualExecutor { * 同期的な実行を期待する場合には[ContextualExecutor.executeWith]側で実行するコンテキストを指定せよ. */ def asNonBlockingTabExecutor(): TabExecutor = new TabExecutor { - override def onCommand(sender: CommandSender, command: Command, alias: String, args: Array[String]): Boolean = { + override def onCommand( + sender: CommandSender, + command: Command, + alias: String, + args: Array[String] + ): Boolean = { val context = RawCommandContext(sender, ExecutedCommand(command, alias), args.toList) contextualExecutor.executeWith(context).unsafeRunAsync { @@ -47,12 +54,17 @@ object ContextualExecutor { import scala.jdk.CollectionConverters._ - override def onTabComplete(sender: CommandSender, command: Command, alias: String, args: Array[String]): java.util.List[String] = { + override def onTabComplete( + sender: CommandSender, + command: Command, + alias: String, + args: Array[String] + ): java.util.List[String] = { val context = RawCommandContext(sender, ExecutedCommand(command, alias), args.toList) contextualExecutor.tabCandidatesFor(context) - }.asJava + }.asJava } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/contextualexecutor/builder/ContextualExecutorBuilder.scala b/src/main/scala/com/github/unchama/contextualexecutor/builder/ContextualExecutorBuilder.scala index 5a8d7f7d0a..def137269c 100644 --- a/src/main/scala/com/github/unchama/contextualexecutor/builder/ContextualExecutorBuilder.scala +++ b/src/main/scala/com/github/unchama/contextualexecutor/builder/ContextualExecutorBuilder.scala @@ -1,9 +1,14 @@ package com.github.unchama.contextualexecutor.builder import cats.data.{Kleisli, OptionT} -import cats.effect.{ConcurrentEffect, IO} +import cats.effect.{Effect, IO} import com.github.unchama.contextualexecutor.executors.PrintUsageExecutor -import com.github.unchama.contextualexecutor.{ContextualExecutor, ParsedArgCommandContext, PartiallyParsedArgs, RawCommandContext} +import com.github.unchama.contextualexecutor.{ + ContextualExecutor, + ParsedArgCommandContext, + PartiallyParsedArgs, + RawCommandContext +} import com.github.unchama.targetedeffect.TargetedEffect import com.github.unchama.targetedeffect.TargetedEffect.emptyEffect import com.github.unchama.targetedeffect.commandsender.MessageEffect @@ -17,36 +22,51 @@ import scala.reflect.ClassTag * 各引数はビルドされる[ContextualExecutor]において異常系を見つけるとすぐに[RawCommandContext.sender]に応答を送り返す. * この副作用を内包させるために[[IO]]への関数として宣言されている. * - * @tparam CS 生成するExecutorが受け付ける[CommandSender]のサブタイプの上限 - * @param senderTypeValidation [CommandSender]の[CS]へのダウンキャストを試みる関数 - * @param argumentsParser [RawCommandContext]から[PartiallyParsedArgs]の作成を試みる関数 - * @param contextualExecution [ParsedArgCommandContext]に基づいてコマンドの副作用を計算する関数 + * @tparam CS + * 生成するExecutorが受け付ける[CommandSender]のサブタイプの上限 + * @param senderTypeValidation + * [CommandSender]の[CS]へのダウンキャストを試みる関数 + * @param argumentsParser + * [RawCommandContext]から[PartiallyParsedArgs]の作成を試みる関数 + * @param contextualExecution + * [ParsedArgCommandContext]に基づいてコマンドの副作用を計算する関数 */ -case class ContextualExecutorBuilder[CS <: CommandSender](senderTypeValidation: SenderTypeValidation[CS], - argumentsParser: CommandArgumentsParser[CS], - contextualExecution: ScopedContextualExecution[CS]) { +case class ContextualExecutorBuilder[CS <: CommandSender]( + senderTypeValidation: SenderTypeValidation[CS], + argumentsParser: CommandArgumentsParser[CS], + contextualExecution: ScopedContextualExecution[CS] +) { /** - * @param parsers i番目にi番目の引数の変換を試みるような関数が入ったリスト - * @param onMissingArguments 引数がパーサに対して不足しているときに[RawCommandContext]を用いて実行する[ContextualExecutor] - * @return [argumentsParser]に, [parsers]と[onMissingArguments]が組み合わされた関数が入った新しい[ContextualExecutorBuilder]. + * @param parsers + * i番目にi番目の引数の変換を試みるような関数が入ったリスト + * @param onMissingArguments + * 引数がパーサに対して不足しているときに[RawCommandContext]を用いて実行する[ContextualExecutor] + * @return + * [argumentsParser]に, + * [parsers]と[onMissingArguments]が組み合わされた関数が入った新しい[ContextualExecutorBuilder]. */ - def argumentsParsers(parsers: List[SingleArgumentParser], - onMissingArguments: ContextualExecutor = PrintUsageExecutor): ContextualExecutorBuilder[CS] = { + def argumentsParsers( + parsers: List[SingleArgumentParser], + onMissingArguments: ContextualExecutor = PrintUsageExecutor + ): ContextualExecutorBuilder[CS] = { val combinedParser: CommandArgumentsParser[CS] = { case (refinedSender, context: RawCommandContext) => @scala.annotation.tailrec - def parse(remainingParsers: List[SingleArgumentParser], - remainingArgs: List[String], - reverseAccumulator: List[Any] = List()): Either[IO[Unit], PartiallyParsedArgs] = { + def parse( + remainingParsers: List[SingleArgumentParser], + remainingArgs: List[String], + reverseAccumulator: List[Any] = List() + ): Either[IO[Unit], PartiallyParsedArgs] = { val (parserHead, parserTail) = remainingParsers match { case ::(head, next) => (head, next) - case Nil => return Right(PartiallyParsedArgs(reverseAccumulator.reverse, remainingArgs)) + case Nil => + return Right(PartiallyParsedArgs(reverseAccumulator.reverse, remainingArgs)) } val (argHead, argTail) = remainingArgs match { case ::(head, next) => (head, next) - case Nil => return Left(onMissingArguments.executeWith(context)) + case Nil => return Left(onMissingArguments.executeWith(context)) } parserHead(argHead) match { @@ -57,7 +77,7 @@ case class ContextualExecutorBuilder[CS <: CommandSender](senderTypeValidation: parse(parsers, context.args) match { case Right(partiallyParsed) => IO.pure(Some(partiallyParsed)) - case Left(effect) => effect.map(_ => None) + case Left(effect) => effect.map(_ => None) } } @@ -78,14 +98,23 @@ case class ContextualExecutorBuilder[CS <: CommandSender](senderTypeValidation: * * [ContextualExecutor]の制約にあるとおり, [execution]は任意スレッドでの実行に対応しなければならない. */ - def executionF[F[_]: ConcurrentEffect, U](execution: ExecutionF[F, CS, U]): ContextualExecutorBuilder[CS] = - this.copy( - contextualExecution = context => { - ConcurrentEffect[F] - .toIO(execution(context)) - .as(TargetedEffect.emptyEffect) - } - ) + def executionF[F[_]: Effect, U]( + execution: ExecutionF[F, CS, U] + ): ContextualExecutorBuilder[CS] = + this.copy(contextualExecution = context => { + Effect[F].toIO(execution(context)).as(TargetedEffect.emptyEffect) + }) + + /** + * [contextualExecution]に[execution]に相当する関数が入った新しい[ContextualExecutorBuilder]を作成する. + * ここで、`execution` はコンテキストを受け取って、 コマンド実行者に対する作用(`Kleisli[F, CS, U]`) を起こすようなプログラムである. + * + * [ContextualExecutor]の制約にあるとおり, [execution]は任意スレッドでの実行に対応しなければならない. + */ + def executionCSEffect[F[_]: Effect, U]( + execution: ExecutionCSEffect[F, CS, U] + ): ContextualExecutorBuilder[CS] = + executionF[F, U](context => execution(context).run(context.sender)) /** * [[contextualExecution]]に、コンテキストを利用せずに走る `effect` が入った @@ -97,33 +126,39 @@ case class ContextualExecutorBuilder[CS <: CommandSender](senderTypeValidation: execution(_ => IO.pure(effect.map(_ => ()))) /** - * @return [CS]を[CS1]へ狭めるキャストを試み, - * 失敗すると[message]がエラーメッセージとして返る[senderTypeValidation]が入った - * 新しい[ContextualExecutorBuilder] + * @return + * [CS]を[CS1]へ狭めるキャストを試み, 失敗すると[message]がエラーメッセージとして返る[senderTypeValidation]が入った + * 新しい[ContextualExecutorBuilder] */ - def refineSenderWithError[CS1 <: CS : ClassTag](message: String): ContextualExecutorBuilder[CS1] = + def refineSenderWithError[CS1 <: CS: ClassTag]( + message: String + ): ContextualExecutorBuilder[CS1] = refineSender(MessageEffect(message)) /** - * @return [CS]を[CS1]へ狭めるキャストを試み, - * 失敗すると[messages]がエラーメッセージとして返る[senderTypeValidation]が入った - * 新しい[ContextualExecutorBuilder] + * @return + * [CS]を[CS1]へ狭めるキャストを試み, 失敗すると[messages]がエラーメッセージとして返る[senderTypeValidation]が入った + * 新しい[ContextualExecutorBuilder] */ - def refineSenderWithError[CS1 <: CS : ClassTag](messages: List[String]): ContextualExecutorBuilder[CS1] = + def refineSenderWithError[CS1 <: CS: ClassTag]( + messages: List[String] + ): ContextualExecutorBuilder[CS1] = refineSender(MessageEffect(messages)) /** - * @return [CS]を[CS1]へ狭めるキャストを試み, - * 失敗したら[errorMessageOnFail]が返るような[senderTypeValidation]が入った - * 新しい[ContextualExecutorBuilder] + * @return + * [CS]を[CS1]へ狭めるキャストを試み, 失敗したら[errorMessageOnFail]が返るような[senderTypeValidation]が入った + * 新しい[ContextualExecutorBuilder] */ - def refineSender[CS1 <: CS : ClassTag](effectOnFail: TargetedEffect[CommandSender]): ContextualExecutorBuilder[CS1] = { + def refineSender[CS1 <: CS: ClassTag]( + effectOnFail: TargetedEffect[CommandSender] + ): ContextualExecutorBuilder[CS1] = { val newSenderTypeValidation: SenderTypeValidation[CS1] = { sender => val verificationProgram = for { refined1 <- OptionT(senderTypeValidation(sender)) refined2: CS1 <- refined1 match { case refined1: CS1 => OptionT.pure[IO](refined1) - case _ => OptionT[IO, Nothing](effectOnFail(sender).map(_ => None)) + case _ => OptionT[IO, Nothing](effectOnFail(sender).map(_ => None)) } } yield refined2 @@ -138,10 +173,9 @@ case class ContextualExecutorBuilder[CS <: CommandSender](senderTypeValidation: * * 生成された[ContextualExecutor]は, * - * - 先ず, 送信者が[CS]であるかを確認する - * - 次に, 引数のパースを試みる - * - 最後に, 変換された引数を用いて[ParsedArgCommandContext]を作成し, - * それを用いて[contextualExecution]で指定される動作を行う + * - 先ず, 送信者が[CS]であるかを確認する + * - 次に, 引数のパースを試みる + * - 最後に, 変換された引数を用いて[ParsedArgCommandContext]を作成し, それを用いて[contextualExecution]で指定される動作を行う * * 処理を[ContextualExecutor.executeWith]内で行う. */ @@ -162,7 +196,9 @@ object ContextualExecutorBuilder { private val defaultArgumentParser: CommandArgumentsParser[CommandSender] = { case (_, context) => IO.pure(Some(PartiallyParsedArgs(List(), context.args))) } - private val defaultExecution: ScopedContextualExecution[CommandSender] = { _ => IO(emptyEffect) } + private val defaultExecution: ScopedContextualExecution[CommandSender] = { _ => + IO(emptyEffect) + } private val defaultSenderValidation: SenderTypeValidation[CommandSender] = { sender: CommandSender => IO.pure(Some(sender)) } diff --git a/src/main/scala/com/github/unchama/contextualexecutor/builder/ParserResponse.scala b/src/main/scala/com/github/unchama/contextualexecutor/builder/ParserResponse.scala index f31808da48..6cef574bdf 100644 --- a/src/main/scala/com/github/unchama/contextualexecutor/builder/ParserResponse.scala +++ b/src/main/scala/com/github/unchama/contextualexecutor/builder/ParserResponse.scala @@ -6,6 +6,7 @@ import com.github.unchama.targetedeffect.commandsender.MessageEffect import org.bukkit.command.CommandSender object ParserResponse { + /** * メッセージなしで「失敗」を表す[ResponseEffectOrResult]を作成する. */ @@ -14,14 +15,20 @@ object ParserResponse { /** * メッセージ付きの「失敗」を表す[ResponseEffectOrResult]を作成する. */ - def failWith(message: String): ResponseEffectOrResult[CommandSender, Nothing] = failWith(MessageEffect(message)) + def failWith(message: String): ResponseEffectOrResult[CommandSender, Nothing] = failWith( + MessageEffect(message) + ) - def failWith[CS](effect: TargetedEffect[CS]): ResponseEffectOrResult[CS, Nothing] = Left(effect) + def failWith[CS](effect: TargetedEffect[CS]): ResponseEffectOrResult[CS, Nothing] = Left( + effect + ) /** * メッセージ付きの「失敗」を表す[ResponseEffectOrResult]を作成する. */ - def failWith(message: List[String]): ResponseEffectOrResult[CommandSender, Any] = failWith(MessageEffect(message)) + def failWith(message: List[String]): ResponseEffectOrResult[CommandSender, Any] = failWith( + MessageEffect(message) + ) /** * [result]により「成功」したことを示す[ResponseEffectOrResult]を作成する. diff --git a/src/main/scala/com/github/unchama/contextualexecutor/builder/Parsers.scala b/src/main/scala/com/github/unchama/contextualexecutor/builder/Parsers.scala index cfec6bb76b..067623f2bc 100644 --- a/src/main/scala/com/github/unchama/contextualexecutor/builder/Parsers.scala +++ b/src/main/scala/com/github/unchama/contextualexecutor/builder/Parsers.scala @@ -11,15 +11,20 @@ object Parsers { succeedWith(_) } - def nonNegativeInteger(failureMessage: TargetedEffect[CommandSender] = emptyEffect): SingleArgumentParser = + def nonNegativeInteger( + failureMessage: TargetedEffect[CommandSender] = emptyEffect + ): SingleArgumentParser = closedRangeInt(0, Int.MaxValue, failureMessage) /** - * @return [smallEnd]より大きいか等しく[largeEnd]より小さいか等しい整数のパーサ + * @return + * [smallEnd]より大きいか等しく[largeEnd]より小さいか等しい整数のパーサ */ def closedRangeInt( - smallEnd: Int, largeEnd: Int, - failureMessage: TargetedEffect[CommandSender] = emptyEffect): SingleArgumentParser = { arg => + smallEnd: Int, + largeEnd: Int, + failureMessage: TargetedEffect[CommandSender] = emptyEffect + ): SingleArgumentParser = { arg => integer(failureMessage)(arg).flatMap { parsed => if ((smallEnd to largeEnd).contains(parsed.asInstanceOf[Int])) succeedWith(parsed) @@ -28,22 +33,28 @@ object Parsers { } } - def integer(failureEffect: TargetedEffect[CommandSender] = emptyEffect): SingleArgumentParser = { arg => + def integer( + failureEffect: TargetedEffect[CommandSender] = emptyEffect + ): SingleArgumentParser = { arg => arg.toIntOption match { case Some(value) => succeedWith(value) - case None => failWith(failureEffect) + case None => failWith(failureEffect) } } - def double(failureEffect: TargetedEffect[CommandSender] = emptyEffect): SingleArgumentParser = { arg => + def double( + failureEffect: TargetedEffect[CommandSender] = emptyEffect + ): SingleArgumentParser = { arg => arg.toDoubleOption match { case Some(value) => succeedWith(value) - case None => failWith(failureEffect) + case None => failWith(failureEffect) } } - def fromOptionParser[T](fromString: String => Option[T], - failureMessage: TargetedEffect[CommandSender] = emptyEffect): SingleArgumentParser = { + def fromOptionParser[T]( + fromString: String => Option[T], + failureMessage: TargetedEffect[CommandSender] = emptyEffect + ): SingleArgumentParser = { fromString.andThen(_.toRight(failureMessage)) } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/contextualexecutor/builder/package.scala b/src/main/scala/com/github/unchama/contextualexecutor/builder/package.scala index 286fd384b1..5234c923cd 100644 --- a/src/main/scala/com/github/unchama/contextualexecutor/builder/package.scala +++ b/src/main/scala/com/github/unchama/contextualexecutor/builder/package.scala @@ -1,5 +1,6 @@ package com.github.unchama.contextualexecutor +import cats.data.Kleisli import cats.effect.IO import com.github.unchama.targetedeffect.TargetedEffect import org.bukkit.command.CommandSender @@ -15,7 +16,12 @@ package object builder { type CommandArgumentsParser[-CS] = (CS, RawCommandContext) => IO[Option[PartiallyParsedArgs]] - type ScopedContextualExecution[-CS <: CommandSender] = ParsedArgCommandContext[CS] => IO[TargetedEffect[CS]] + type ScopedContextualExecution[-CS <: CommandSender] = + ParsedArgCommandContext[CS] => IO[TargetedEffect[CS]] type ExecutionF[F[_], CS <: CommandSender, U] = ParsedArgCommandContext[CS] => F[U] + + // コンテキストから、 CS に対して実行できる作用への関数 + type ExecutionCSEffect[F[_], CS <: CommandSender, U] = + ParsedArgCommandContext[CS] => Kleisli[F, CS, U] } diff --git a/src/main/scala/com/github/unchama/contextualexecutor/executors/BranchedExecutor.scala b/src/main/scala/com/github/unchama/contextualexecutor/executors/BranchedExecutor.scala index 780f407c06..ceed5809ec 100644 --- a/src/main/scala/com/github/unchama/contextualexecutor/executors/BranchedExecutor.scala +++ b/src/main/scala/com/github/unchama/contextualexecutor/executors/BranchedExecutor.scala @@ -6,20 +6,22 @@ import com.github.unchama.contextualexecutor.{ContextualExecutor, RawCommandCont /** * コマンドの枝分かれでのルーティングを静的に行うアクションを返す[ContextualExecutor] */ -case class BranchedExecutor(branches: Map[String, ContextualExecutor], - whenArgInsufficient: Option[ContextualExecutor] = Some(PrintUsageExecutor), - whenBranchNotFound: Option[ContextualExecutor] = Some(PrintUsageExecutor)) extends ContextualExecutor { +case class BranchedExecutor( + branches: Map[String, ContextualExecutor], + whenArgInsufficient: Option[ContextualExecutor] = Some(PrintUsageExecutor), + whenBranchNotFound: Option[ContextualExecutor] = Some(PrintUsageExecutor) +) extends ContextualExecutor { override def executeWith(rawContext: RawCommandContext): IO[Unit] = { def executeOptionally(executor: Option[ContextualExecutor]): IO[Unit] = executor match { case Some(executor) => executor.executeWith(rawContext) - case None => IO.pure(()) + case None => IO.pure(()) } val (argHead, argTail) = rawContext.args match { case ::(head, tl) => (head, tl) - case Nil => return executeOptionally(whenArgInsufficient) + case Nil => return executeOptionally(whenArgInsufficient) } val branch = branches.getOrElse(argHead, return executeOptionally(whenBranchNotFound)) @@ -39,4 +41,4 @@ case class BranchedExecutor(branches: Map[String, ContextualExecutor], } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/contextualexecutor/executors/EchoExecutor.scala b/src/main/scala/com/github/unchama/contextualexecutor/executors/EchoExecutor.scala index d5dec4d92f..68204c8c91 100644 --- a/src/main/scala/com/github/unchama/contextualexecutor/executors/EchoExecutor.scala +++ b/src/main/scala/com/github/unchama/contextualexecutor/executors/EchoExecutor.scala @@ -10,4 +10,4 @@ import org.bukkit.command.CommandSender */ case class EchoExecutor(effect: TargetedEffect[CommandSender]) extends ContextualExecutor { override def executeWith(rawContext: RawCommandContext): IO[Unit] = effect(rawContext.sender) -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/datarepository/KeyedDataRepository.scala b/src/main/scala/com/github/unchama/datarepository/KeyedDataRepository.scala index 6ca537000f..a2d026ac98 100644 --- a/src/main/scala/com/github/unchama/datarepository/KeyedDataRepository.scala +++ b/src/main/scala/com/github/unchama/datarepository/KeyedDataRepository.scala @@ -20,7 +20,9 @@ object KeyedDataRepository { implicit def functor[K]: Functor[KeyedDataRepository[K, *]] = new Functor[KeyedDataRepository[K, *]] { - override def map[A, B](fa: KeyedDataRepository[K, A])(f: A => B): KeyedDataRepository[K, B] = { + override def map[A, B]( + fa: KeyedDataRepository[K, A] + )(f: A => B): KeyedDataRepository[K, B] = { new KeyedDataRepository[K, B] { override def isDefinedAt(x: K): Boolean = fa.isDefinedAt(x) diff --git a/src/main/scala/com/github/unchama/datarepository/bukkit/player/BukkitRepositoryControls.scala b/src/main/scala/com/github/unchama/datarepository/bukkit/player/BukkitRepositoryControls.scala index 6a2fe56b60..fe292bf311 100644 --- a/src/main/scala/com/github/unchama/datarepository/bukkit/player/BukkitRepositoryControls.scala +++ b/src/main/scala/com/github/unchama/datarepository/bukkit/player/BukkitRepositoryControls.scala @@ -5,20 +5,25 @@ import cats.{Monad, ~>} import com.github.unchama.bungeesemaphoreresponder.domain.PlayerDataFinalizer import com.github.unchama.datarepository.template._ import com.github.unchama.datarepository.template.finalization.RepositoryFinalization -import com.github.unchama.datarepository.template.initialization.{PrefetchResult, SinglePhasedRepositoryInitialization, TwoPhasedRepositoryInitialization} +import com.github.unchama.datarepository.template.initialization.{ + PrefetchResult, + SinglePhasedRepositoryInitialization, + TwoPhasedRepositoryInitialization +} import com.github.unchama.generic.ContextCoercion import org.bukkit.entity.Player import org.bukkit.event.player.{AsyncPlayerPreLoginEvent, PlayerJoinEvent} import org.bukkit.event.{EventHandler, EventPriority, Listener} import java.util.UUID -import scala.annotation.tailrec import scala.collection.concurrent.TrieMap -case class BukkitRepositoryControls[F[_], R](repository: PlayerDataRepository[R], - initializer: Listener, - backupProcess: F[Unit], - finalizer: PlayerDataFinalizer[F, Player]) { +case class BukkitRepositoryControls[F[_], R]( + repository: PlayerDataRepository[R], + initializer: Listener, + backupProcess: F[Unit], + finalizer: PlayerDataFinalizer[F, Player] +) { def transformFinalizationContext[G[_]](trans: F ~> G): BukkitRepositoryControls[G, R] = BukkitRepositoryControls( @@ -33,7 +38,8 @@ case class BukkitRepositoryControls[F[_], R](repository: PlayerDataRepository[R] def map[S](f: R => S): BukkitRepositoryControls[F, S] = BukkitRepositoryControls(repository.map(f), initializer, backupProcess, finalizer) - def coerceFinalizationContextTo[G[_] : ContextCoercion[F, *[_]]]: BukkitRepositoryControls[G, R] = + def coerceFinalizationContextTo[G[_]: ContextCoercion[F, *[_]]] + : BukkitRepositoryControls[G, R] = transformFinalizationContext(ContextCoercion.asFunctionK) } @@ -49,12 +55,10 @@ object BukkitRepositoryControls { } private object Initializers { - def singlePhased[ - F[_] : SyncEffect, R - ](initialization: SinglePhasedRepositoryInitialization[F, R]) - (tapOnJoin: (Player, R) => F[Unit]) - (dataMap: TrieMap[UUID, R]): PreLoginListener = { - //noinspection ScalaUnusedSymbol + def singlePhased[F[_]: SyncEffect, R]( + initialization: SinglePhasedRepositoryInitialization[F, R] + )(tapOnJoin: (Player, R) => F[Unit])(dataMap: TrieMap[UUID, R]): PreLoginListener = { + // noinspection ScalaUnusedSymbol new PreLoginListener { @EventHandler(priority = EventPriority.LOWEST) override final def onPlayerPreLogin(event: AsyncPlayerPreLoginEvent): Unit = { @@ -80,38 +84,40 @@ object BukkitRepositoryControls { final def onPlayerJoin(event: PlayerJoinEvent): Unit = { val player = event.getPlayer - tapOnJoin(player, dataMap(player.getUniqueId)) - .runSync[SyncIO] - .unsafeRunSync() + tapOnJoin(player, dataMap(player.getUniqueId)).runSync[SyncIO].unsafeRunSync() } } } - def twoPhased[ - F[_] : SyncEffect, R - ](initialization: TwoPhasedRepositoryInitialization[F, Player, R]) - (temporaryDataMap: TrieMap[UUID, initialization.IntermediateData], dataMap: TrieMap[Player, R]): Listener = { + def twoPhased[F[_]: SyncEffect, R]( + initialization: TwoPhasedRepositoryInitialization[F, Player, R] + )( + temporaryDataMap: TrieMap[UUID, initialization.IntermediateData], + dataMap: TrieMap[Player, R] + ): Listener = { val temporaryDataMapInitializer = - singlePhased(initialization.prefetchIntermediateValue(_, _))((_, _) => Monad[F].unit)(temporaryDataMap) + singlePhased(initialization.prefetchIntermediateValue(_, _))((_, _) => Monad[F].unit)( + temporaryDataMap + ) new Listener { - //noinspection ScalaUnusedSymbol + // noinspection ScalaUnusedSymbol @EventHandler(priority = EventPriority.LOWEST) final def onPlayerPreLogin(event: AsyncPlayerPreLoginEvent): Unit = temporaryDataMapInitializer.onPlayerPreLogin(event) - //noinspection ScalaUnusedSymbol + // noinspection ScalaUnusedSymbol @EventHandler(priority = EventPriority.LOWEST) final def onPlayerJoin(event: PlayerJoinEvent): Unit = { val player = event.getPlayer temporaryDataMap.get(player.getUniqueId) match { case Some(temporaryData) => - dataMap(player) = - initialization.prepareData(player, temporaryData) - .runSync[SyncIO] - .unsafeRunSync() + dataMap(player) = initialization + .prepareData(player, temporaryData) + .runSync[SyncIO] + .unsafeRunSync() case None => val message = @@ -129,69 +135,82 @@ object BukkitRepositoryControls { } private object Finalizers { - def singlePhased[F[_] : Sync, Key, R, T](finalization: RepositoryFinalization[F, Key, R]) - (dataMap: TrieMap[Key, R]): PlayerDataFinalizer[F, Key] = { - player => - for { - finalData <- Sync[F].delay { - dataMap.remove(player).get - } - _ <- finalization.persistPair(player, finalData) - _ <- finalization.finalizeBeforeUnload(player, finalData) - } yield () + def singlePhased[F[_]: Sync, Key, R, T]( + finalization: RepositoryFinalization[F, Key, R] + )(dataMap: TrieMap[Key, R]): PlayerDataFinalizer[F, Key] = { player => + for { + finalData <- Sync[F].delay { + dataMap.remove(player).get + } + _ <- finalization.persistPair(player, finalData) + _ <- finalization.finalizeBeforeUnload(player, finalData) + } yield () } - def twoPhased[F[_] : Sync, R, T](finalization: RepositoryFinalization[F, Player, R]) - (temporaryDataMap: TrieMap[UUID, T], - dataMap: TrieMap[Player, R]): PlayerDataFinalizer[F, Player] = { - player => - for { - _ <- Sync[F].delay { - temporaryDataMap.remove(player.getUniqueId) - } - _ <- singlePhased(finalization)(dataMap).onQuitOf(player) - } yield () + def twoPhased[F[_]: Sync, R, T](finalization: RepositoryFinalization[F, Player, R])( + temporaryDataMap: TrieMap[UUID, T], + dataMap: TrieMap[Player, R] + ): PlayerDataFinalizer[F, Player] = { player => + for { + _ <- Sync[F].delay { + temporaryDataMap.remove(player.getUniqueId) + } + _ <- singlePhased(finalization)(dataMap).onQuitOf(player) + } yield () } } - private def backupProcess[F[_] : Sync, Key, R](finalization: RepositoryFinalization[F, Key, R]) - (dataMap: TrieMap[Key, R]): F[Unit] = { + private def backupProcess[F[_]: Sync, Key, R]( + finalization: RepositoryFinalization[F, Key, R] + )(dataMap: TrieMap[Key, R]): F[Unit] = { Sync[F].suspend { dataMap.toList.traverse(finalization.persistPair.tupled).as(()) } } - def createHandles[F[_] : SyncEffect, R](definition: RepositoryDefinition[F, Player, R]): F[BukkitRepositoryControls[F, R]] = { + def createHandles[F[_]: SyncEffect, R]( + definition: RepositoryDefinition[F, Player, R] + ): F[BukkitRepositoryControls[F, R]] = { import cats.implicits._ definition match { - case RepositoryDefinition.Phased.SinglePhased(initialization, tappingAction, finalization) => Sync[F].delay { - TrieMap.empty[UUID, R] - }.map { dataMap => - // workaround of https://youtrack.jetbrains.com/issue/SCL-18638 - val i: initialization.type = initialization - - BukkitRepositoryControls( - PlayerDataRepository.unlift(player => dataMap.get(player.getUniqueId)), - Initializers.singlePhased(i)(tappingAction)(dataMap), - backupProcess(finalization)(dataMap), - player => Finalizers.singlePhased(finalization)(dataMap).onQuitOf(player.getUniqueId) - ) - } + case RepositoryDefinition + .Phased + .SinglePhased(initialization, tappingAction, finalization) => + Sync[F] + .delay { + TrieMap.empty[UUID, R] + } + .map { dataMap => + // workaround of https://youtrack.jetbrains.com/issue/SCL-18638 + val i: initialization.type = initialization + + BukkitRepositoryControls( + PlayerDataRepository.unlift(player => dataMap.get(player.getUniqueId)), + Initializers.singlePhased(i)(tappingAction)(dataMap), + backupProcess(finalization)(dataMap), + player => + Finalizers.singlePhased(finalization)(dataMap).onQuitOf(player.getUniqueId) + ) + } - case RepositoryDefinition.Phased.TwoPhased(initialization, finalization) => Sync[F].delay { - (TrieMap.empty[Player, R], TrieMap.empty[UUID, initialization.IntermediateData]) - }.map { case (dataMap, temporaryDataMap) => - // workaround of https://youtrack.jetbrains.com/issue/SCL-18638 - val i: initialization.type = initialization - - BukkitRepositoryControls( - PlayerDataRepository.unlift(player => dataMap.get(player)), - Initializers.twoPhased(i)(temporaryDataMap, dataMap), - backupProcess(finalization)(dataMap), - Finalizers.twoPhased(finalization)(temporaryDataMap, dataMap) - ) - } + case RepositoryDefinition.Phased.TwoPhased(initialization, finalization) => + Sync[F] + .delay { + (TrieMap.empty[Player, R], TrieMap.empty[UUID, initialization.IntermediateData]) + } + .map { + case (dataMap, temporaryDataMap) => + // workaround of https://youtrack.jetbrains.com/issue/SCL-18638 + val i: initialization.type = initialization + + BukkitRepositoryControls( + PlayerDataRepository.unlift(player => dataMap.get(player)), + Initializers.twoPhased(i)(temporaryDataMap, dataMap), + backupProcess(finalization)(dataMap), + Finalizers.twoPhased(finalization)(temporaryDataMap, dataMap) + ) + } case rd: RepositoryDefinition.Mapped[F, Player, s, R] => createHandles[F, s](rd.source).map(_.map(rd.sr)) diff --git a/src/main/scala/com/github/unchama/datarepository/bukkit/player/PlayerDataRepository.scala b/src/main/scala/com/github/unchama/datarepository/bukkit/player/PlayerDataRepository.scala index d532ed5c78..fbab33e0a2 100644 --- a/src/main/scala/com/github/unchama/datarepository/bukkit/player/PlayerDataRepository.scala +++ b/src/main/scala/com/github/unchama/datarepository/bukkit/player/PlayerDataRepository.scala @@ -9,13 +9,13 @@ import scala.annotation.tailrec /** * ログイン中の[[Player]]に対して必ず `R` を返せるようなリポジトリ。 * - * どの期間についてプレーヤーに対して値を返せるかどうかは実装に依存するが、 - * 遅くても `PlayerJoinEvent` の `EventPriority.HIGH` から、 - * 早くても `PlayerQuitEvent` の `EventPriority.HIGHEST` まではデータが存在することが保証されている。 + * どの期間についてプレーヤーに対して値を返せるかどうかは実装に依存するが、 遅くても `PlayerJoinEvent` の `EventPriority.HIGH` から、 早くても + * `PlayerQuitEvent` の `EventPriority.HIGHEST` まではデータが存在することが保証されている。 * * プレーヤーがログインしている間は、[[Player]]を適用して得られる値が不変であることを保証する。 * - * @tparam R レポジトリが [[Player]] に関連付ける値の型 + * @tparam R + * レポジトリが [[Player]] に関連付ける値の型 */ trait PlayerDataRepository[R] extends KeyedDataRepository[Player, R] { @@ -32,38 +32,43 @@ object PlayerDataRepository { override def isDefinedAt(x: Player): Boolean = f(x).isDefined } - implicit def playerDataRefRepositoryMonad: Monad[PlayerDataRepository] = new Monad[PlayerDataRepository] { - override def pure[A](x: A): PlayerDataRepository[A] = { - new PlayerDataRepository[A] { - override def apply(player: Player): A = x + implicit def playerDataRefRepositoryMonad: Monad[PlayerDataRepository] = + new Monad[PlayerDataRepository] { + override def pure[A](x: A): PlayerDataRepository[A] = { + new PlayerDataRepository[A] { + override def apply(player: Player): A = x - override def isDefinedAt(x: Player): Boolean = true + override def isDefinedAt(x: Player): Boolean = true + } } - } - override def flatMap[A, B](fa: PlayerDataRepository[A])(f: A => PlayerDataRepository[B]): PlayerDataRepository[B] = { - new PlayerDataRepository[B] { - override def apply(player: Player): B = f(fa(player))(player) + override def flatMap[A, B]( + fa: PlayerDataRepository[A] + )(f: A => PlayerDataRepository[B]): PlayerDataRepository[B] = { + new PlayerDataRepository[B] { + override def apply(player: Player): B = f(fa(player))(player) - override def isDefinedAt(x: Player): Boolean = fa.isDefinedAt(x) + override def isDefinedAt(x: Player): Boolean = fa.isDefinedAt(x) + } } - } - override def tailRecM[A, B](a: A)(f: A => PlayerDataRepository[Either[A, B]]): PlayerDataRepository[B] = { - @tailrec - def go(player: Player)(current: A): Option[B] = - f(current).lift(player) match { - case Some(Left(nextA)) => go(player)(nextA) - case Some(Right(value)) => Some(value) - case None => None + override def tailRecM[A, B]( + a: A + )(f: A => PlayerDataRepository[Either[A, B]]): PlayerDataRepository[B] = { + @tailrec + def go(player: Player)(current: A): Option[B] = + f(current).lift(player) match { + case Some(Left(nextA)) => go(player)(nextA) + case Some(Right(value)) => Some(value) + case None => None + } + + new PlayerDataRepository[B] { + override def apply(player: Player): B = go(player)(a).get + + override def isDefinedAt(x: Player): Boolean = go(x)(a).isDefined } - - new PlayerDataRepository[B] { - override def apply(player: Player): B = go(player)(a).get - - override def isDefinedAt(x: Player): Boolean = go(x)(a).isDefined } } - } } diff --git a/src/main/scala/com/github/unchama/datarepository/definitions/FiberAdjoinedRepositoryDefinition.scala b/src/main/scala/com/github/unchama/datarepository/definitions/FiberAdjoinedRepositoryDefinition.scala index e31890ecd3..4d898b1997 100644 --- a/src/main/scala/com/github/unchama/datarepository/definitions/FiberAdjoinedRepositoryDefinition.scala +++ b/src/main/scala/com/github/unchama/datarepository/definitions/FiberAdjoinedRepositoryDefinition.scala @@ -12,24 +12,21 @@ object FiberAdjoinedRepositoryDefinition { type FiberAdjoined[R, F[_]] = (R, Deferred[F, Fiber[F, Nothing]]) - def extending[ - G[_] : Sync, - F[_] : ConcurrentEffect, - Player, R - ](definition: RepositoryDefinition.Phased[G, Player, R]): definition.Self[R FiberAdjoined F] = - definition - .flatXmapWithIntermediateEffects(r => - Deferred - .in[G, F, Fiber[F, Nothing]] - .map(r -> _) - ) { case (r, _) => + def extending[G[_]: Sync, F[_]: ConcurrentEffect, Player, R]( + definition: RepositoryDefinition.Phased[G, Player, R] + ): definition.Self[R FiberAdjoined F] = + definition.flatXmapWithIntermediateEffects(r => + Deferred.in[G, F, Fiber[F, Nothing]].map(r -> _) + ) { + case (r, _) => Monad[G].pure(r) - } { case (r, fiber) => + } { + case (r, fiber) => // 終了時にファイバーの開始を待ち、開始されたものをすぐにcancelする EffectExtra .runAsyncAndForget[F, G, Unit] { fiber.get.flatMap(_.cancel) } .as(r) - } + } } diff --git a/src/main/scala/com/github/unchama/datarepository/definitions/MutexRepositoryDefinition.scala b/src/main/scala/com/github/unchama/datarepository/definitions/MutexRepositoryDefinition.scala index 65056f811c..1901d17570 100644 --- a/src/main/scala/com/github/unchama/datarepository/definitions/MutexRepositoryDefinition.scala +++ b/src/main/scala/com/github/unchama/datarepository/definitions/MutexRepositoryDefinition.scala @@ -7,9 +7,9 @@ import com.github.unchama.generic.effect.concurrent.Mutex object MutexRepositoryDefinition { - def over[ - F[_] : Concurrent, G[_] : Sync : ContextCoercion[*[_], F], Player, R - ](underlying: RepositoryDefinition.Phased[G, Player, R]): underlying.Self[Mutex[F, G, R]] = + def over[F[_]: Concurrent, G[_]: Sync: ContextCoercion[*[_], F], Player, R]( + underlying: RepositoryDefinition.Phased[G, Player, R] + ): underlying.Self[Mutex[F, G, R]] = underlying.flatXmap(r => Mutex.of[F, G, R](r))(_.readLatest) } diff --git a/src/main/scala/com/github/unchama/datarepository/definitions/RefDictBackedRepositoryDefinition.scala b/src/main/scala/com/github/unchama/datarepository/definitions/RefDictBackedRepositoryDefinition.scala index 6c4d22036e..254941c81c 100644 --- a/src/main/scala/com/github/unchama/datarepository/definitions/RefDictBackedRepositoryDefinition.scala +++ b/src/main/scala/com/github/unchama/datarepository/definitions/RefDictBackedRepositoryDefinition.scala @@ -3,7 +3,10 @@ package com.github.unchama.datarepository.definitions import cats.Monad import com.github.unchama.datarepository.template.RepositoryDefinition import com.github.unchama.datarepository.template.finalization.RepositoryFinalization -import com.github.unchama.datarepository.template.initialization.{PrefetchResult, SinglePhasedRepositoryInitialization} +import com.github.unchama.datarepository.template.initialization.{ + PrefetchResult, + SinglePhasedRepositoryInitialization +} import com.github.unchama.generic.RefDict import java.util.UUID @@ -12,17 +15,18 @@ object RefDictBackedRepositoryDefinition { import cats.implicits._ - def usingUuidRefDictWithEffectfulDefault[ - F[_] : Monad, Player, R - ](refDict: RefDict[F, UUID, R])(getDefaultValue: F[R]): RepositoryDefinition.Phased.SinglePhased[F, Player, R] = { + def usingUuidRefDictWithEffectfulDefault[F[_]: Monad, Player, R]( + refDict: RefDict[F, UUID, R] + )(getDefaultValue: F[R]): RepositoryDefinition.Phased.SinglePhased[F, Player, R] = { val initialization: SinglePhasedRepositoryInitialization[F, R] = - (uuid, _) => refDict - .read(uuid) - .flatMap { - case Some(value) => Monad[F].pure(value) - case None => getDefaultValue - } - .map(PrefetchResult.Success.apply) + (uuid, _) => + refDict + .read(uuid) + .flatMap { + case Some(value) => Monad[F].pure(value) + case None => getDefaultValue + } + .map(PrefetchResult.Success.apply) val finalization: RepositoryFinalization[F, UUID, R] = RepositoryFinalization.withoutAnyFinalization((uuid, r) => refDict.write(uuid, r)) @@ -30,26 +34,23 @@ object RefDictBackedRepositoryDefinition { RepositoryDefinition.Phased.SinglePhased.withoutTappingAction(initialization, finalization) } - def usingUuidRefDictWithoutDefault[ - F[_] : Monad, Player, R - ](refDict: RefDict[F, UUID, R]): RepositoryDefinition.Phased.SinglePhased[F, Player, Option[R]] = { + def usingUuidRefDictWithoutDefault[F[_]: Monad, Player, R]( + refDict: RefDict[F, UUID, R] + ): RepositoryDefinition.Phased.SinglePhased[F, Player, Option[R]] = { val initialization: SinglePhasedRepositoryInitialization[F, Option[R]] = - (uuid, _) => refDict - .read(uuid) - .map(PrefetchResult.Success.apply) + (uuid, _) => refDict.read(uuid).map(PrefetchResult.Success.apply) val finalization: RepositoryFinalization[F, UUID, Option[R]] = - RepositoryFinalization.withoutAnyFinalization( - (uuid, optR) => - optR.fold(Monad[F].pure(()))(r => refDict.write(uuid, r)) + RepositoryFinalization.withoutAnyFinalization((uuid, optR) => + optR.fold(Monad[F].pure(()))(r => refDict.write(uuid, r)) ) RepositoryDefinition.Phased.SinglePhased.withoutTappingAction(initialization, finalization) } - - def usingUuidRefDict[F[_] : Monad, Player, R](refDict: RefDict[F, UUID, R]) - (defaultValue: R): RepositoryDefinition.Phased.SinglePhased[F, Player, R] = + def usingUuidRefDict[F[_]: Monad, Player, R](refDict: RefDict[F, UUID, R])( + defaultValue: R + ): RepositoryDefinition.Phased.SinglePhased[F, Player, R] = usingUuidRefDictWithEffectfulDefault(refDict)(Monad[F].pure(defaultValue)) } diff --git a/src/main/scala/com/github/unchama/datarepository/definitions/SessionMutexRepositoryDefinition.scala b/src/main/scala/com/github/unchama/datarepository/definitions/SessionMutexRepositoryDefinition.scala index a5185319fa..0886629c20 100644 --- a/src/main/scala/com/github/unchama/datarepository/definitions/SessionMutexRepositoryDefinition.scala +++ b/src/main/scala/com/github/unchama/datarepository/definitions/SessionMutexRepositoryDefinition.scala @@ -14,18 +14,21 @@ object SessionMutexRepositoryDefinition { import cats.implicits._ - def withRepositoryContext[ - F[_] : ConcurrentEffect, - G[_] : Sync : ContextCoercion[*[_], F], - Player - ]: RepositoryDefinition[G, Player, SessionMutex[F, G]] = { - RepositoryDefinition.Phased.SinglePhased.withoutTappingAction( - SinglePhasedRepositoryInitialization.withSupplier(SessionMutex.newIn[F, G]), - RepositoryFinalization.withoutAnyPersistence[G, UUID, SessionMutex[F, G]] { (_, mutex) => - EffectExtra.runAsyncAndForget[F, G, Unit] { - mutex.stopAnyFiber.as(()) + def withRepositoryContext[F[_]: ConcurrentEffect, G[_]: Sync: ContextCoercion[ + *[_], + F + ], Player]: RepositoryDefinition[G, Player, SessionMutex[F, G]] = { + RepositoryDefinition + .Phased + .SinglePhased + .withoutTappingAction( + SinglePhasedRepositoryInitialization.withSupplier(SessionMutex.newIn[F, G]), + RepositoryFinalization.withoutAnyPersistence[G, UUID, SessionMutex[F, G]] { + (_, mutex) => + EffectExtra.runAsyncAndForget[F, G, Unit] { + mutex.stopAnyFiber.as(()) + } } - } - ) + ) } } diff --git a/src/main/scala/com/github/unchama/datarepository/definitions/SignallingRepositoryDefinition.scala b/src/main/scala/com/github/unchama/datarepository/definitions/SignallingRepositoryDefinition.scala index d57c27ddd2..36265ba634 100644 --- a/src/main/scala/com/github/unchama/datarepository/definitions/SignallingRepositoryDefinition.scala +++ b/src/main/scala/com/github/unchama/datarepository/definitions/SignallingRepositoryDefinition.scala @@ -17,39 +17,42 @@ object SignallingRepositoryDefinition { import FiberAdjoinedRepositoryDefinition.FiberAdjoined import cats.implicits._ - def withPublishSink[ - G[_] : Sync, - F[_] : ConcurrentEffect : ContextCoercion[G, *[_]] : ErrorLogger, - Player: HasUuid, T - ](publishSink: Pipe[F, (Player, T), Unit]) - (definition: RepositoryDefinition.Phased[G, Player, T]): Phased.TwoPhased[G, Player, Ref[G, T] FiberAdjoined F] = { - FiberAdjoinedRepositoryDefinition.extending(definition.toTwoPhased).flatXmapWithPlayer { - player => { case (initialValue, fiberPromise) => - AsymmetricSignallingRef[G, F, T](initialValue) - .flatTap { ref => - EffectExtra.runAsyncAndForget[F, G, Unit] { - Concurrent[F].start[Nothing] { - ref - .valuesAwait - .use[F, Nothing] { stream => - StreamExtra.compileToRestartingStream[F, Nothing]("[SignallingRepositoryDefinition]") { - stream.map(player -> _).through(publishSink) + def withPublishSink[G[_]: Sync, F[_]: ConcurrentEffect: ContextCoercion[ + G, + *[_] + ]: ErrorLogger, Player: HasUuid, T](publishSink: Pipe[F, (Player, T), Unit])( + definition: RepositoryDefinition.Phased[G, Player, T] + ): Phased.TwoPhased[G, Player, Ref[G, T] FiberAdjoined F] = { + FiberAdjoinedRepositoryDefinition + .extending(definition.toTwoPhased) + .flatXmapWithPlayer { player => + { + case (initialValue, fiberPromise) => + AsymmetricSignallingRef[G, F, T](initialValue) + .flatTap { ref => + EffectExtra.runAsyncAndForget[F, G, Unit] { + Concurrent[F].start[Nothing] { + ref.valuesAwait.use[F, Nothing] { stream => + StreamExtra.compileToRestartingStream[F, Nothing]( + "[SignallingRepositoryDefinition]" + ) { + stream.map(player -> _).through(publishSink) + } } - } - } >>= fiberPromise.complete - } - } - .widen[Ref[G, T]] - .map(_ -> fiberPromise) - } - } { case (ref, fiberPromise) => ref.get.map(_ -> fiberPromise) } + } >>= fiberPromise.complete + } + } + .widen[Ref[G, T]] + .map(_ -> fiberPromise) + } + } { case (ref, fiberPromise) => ref.get.map(_ -> fiberPromise) } } - def withPublishSinkHidden[ - G[_] : Sync, - F[_] : ConcurrentEffect : ContextCoercion[G, *[_]] : ErrorLogger, - Player: HasUuid, T - ](publishSink: Pipe[F, (Player, T), Unit]) - (definition: RepositoryDefinition.Phased[G, Player, T]): RepositoryDefinition[G, Player, Ref[G, T]] = + def withPublishSinkHidden[G[_]: Sync, F[_]: ConcurrentEffect: ContextCoercion[ + G, + *[_] + ]: ErrorLogger, Player: HasUuid, T](publishSink: Pipe[F, (Player, T), Unit])( + definition: RepositoryDefinition.Phased[G, Player, T] + ): RepositoryDefinition[G, Player, Ref[G, T]] = withPublishSink(publishSink)(definition).map(_._1) } diff --git a/src/main/scala/com/github/unchama/datarepository/template/RepositoryDefinition.scala b/src/main/scala/com/github/unchama/datarepository/template/RepositoryDefinition.scala index c5d9286daa..723caa4053 100644 --- a/src/main/scala/com/github/unchama/datarepository/template/RepositoryDefinition.scala +++ b/src/main/scala/com/github/unchama/datarepository/template/RepositoryDefinition.scala @@ -4,13 +4,17 @@ import cats.effect.Sync import cats.effect.concurrent.Ref import cats.{Applicative, Apply, Monad} import com.github.unchama.datarepository.template.finalization.RepositoryFinalization -import com.github.unchama.datarepository.template.initialization.{SinglePhasedRepositoryInitialization, TwoPhasedRepositoryInitialization} +import com.github.unchama.datarepository.template.initialization.{ + SinglePhasedRepositoryInitialization, + TwoPhasedRepositoryInitialization +} import com.github.unchama.minecraft.algebra.HasUuid import java.util.UUID sealed trait RepositoryDefinition[F[_], Player, R] { - final def map[S](f: R => S): RepositoryDefinition[F, Player, S] = RepositoryDefinition.Mapped(this, f) + final def map[S](f: R => S): RepositoryDefinition[F, Player, S] = + RepositoryDefinition.Mapped(this, f) } object RepositoryDefinition { @@ -23,14 +27,18 @@ object RepositoryDefinition { sealed trait Phased[F[_], Player, R] extends RepositoryDefinition[F, Player, R] { type Self[S] <: RepositoryDefinition[F, Player, S] - def flatXmapWithIntermediateEffects[S](f: R => F[S]) - (beforePersisting: S => F[R])(beforeFinalization: S => F[R]) - (implicit F: Monad[F]): Self[S] + def flatXmapWithIntermediateEffects[S](f: R => F[S])(beforePersisting: S => F[R])( + beforeFinalization: S => F[R] + )(implicit F: Monad[F]): Self[S] - final def toTwoPhased(implicit F: Monad[F], playerHasUuid: HasUuid[Player]): Phased.TwoPhased[F, Player, R] = + final def toTwoPhased( + implicit F: Monad[F], + playerHasUuid: HasUuid[Player] + ): Phased.TwoPhased[F, Player, R] = this match { - case s@Phased.SinglePhased(_, _, _) => s.augmentToTwoPhased((_, r) => F.pure(r))(F.pure[R]) - case t@Phased.TwoPhased(_, _) => t + case s @ Phased.SinglePhased(_, _, _) => + s.augmentToTwoPhased((_, r) => F.pure(r))(F.pure[R]) + case t @ Phased.TwoPhased(_, _) => t } final def flatXmap[S](f: R => F[S])(g: S => F[R])(implicit F: Monad[F]): Self[S] = @@ -48,29 +56,32 @@ object RepositoryDefinition { object Phased { - case class SinglePhased[F[_], Player, R](initialization: SinglePhasedRepositoryInitialization[F, R], - tappingAction: (Player, R) => F[Unit], - finalization: RepositoryFinalization[F, UUID, R]) - extends Phased[F, Player, R] { + case class SinglePhased[F[_], Player, R]( + initialization: SinglePhasedRepositoryInitialization[F, R], + tappingAction: (Player, R) => F[Unit], + finalization: RepositoryFinalization[F, UUID, R] + ) extends Phased[F, Player, R] { override type Self[S] = SinglePhased[F, Player, S] - override def flatXmapWithIntermediateEffects[S](f: R => F[S]) - (beforePersisting: S => F[R])(beforeFinalization: S => F[R]) - (implicit F: Monad[F]): SinglePhased[F, Player, S] = + override def flatXmapWithIntermediateEffects[S](f: R => F[S])( + beforePersisting: S => F[R] + )(beforeFinalization: S => F[R])(implicit F: Monad[F]): SinglePhased[F, Player, S] = SinglePhased( (uuid, name) => initialization.prepareData(uuid, name).flatMap(_.traverse(f)), (player, s) => beforePersisting(s).flatMap(tappingAction(player, _)), finalization.withIntermediateEffects(beforePersisting)(beforeFinalization) ) - def withAnotherTappingAction[U](another: (Player, R) => F[U]) - (implicit F: Apply[F]): SinglePhased[F, Player, R] = this.copy( - tappingAction = (player, r) => F.productR(tappingAction(player, r))(another(player, r).as(())) + def withAnotherTappingAction[U]( + another: (Player, R) => F[U] + )(implicit F: Apply[F]): SinglePhased[F, Player, R] = this.copy(tappingAction = + (player, r) => F.productR(tappingAction(player, r))(another(player, r).as(())) ) - def augmentToTwoPhased[T](prepareFinalData: (Player, R) => F[T])(revertOnFinalization: T => F[R]) - (implicit F: Monad[F], playerHasUuid: HasUuid[Player]): TwoPhased[F, Player, T] = + def augmentToTwoPhased[T](prepareFinalData: (Player, R) => F[T])( + revertOnFinalization: T => F[R] + )(implicit F: Monad[F], playerHasUuid: HasUuid[Player]): TwoPhased[F, Player, T] = TwoPhased( TwoPhasedRepositoryInitialization.augment(initialization)((player, r) => tappingAction(player, r) >> prepareFinalData(player, r) @@ -82,45 +93,51 @@ object RepositoryDefinition { } object SinglePhased { - def withoutTappingAction[ - F[_] : Applicative, Player, R - ](initialization: SinglePhasedRepositoryInitialization[F, R], - finalization: RepositoryFinalization[F, UUID, R]): SinglePhased[F, Player, R] = { + def withoutTappingAction[F[_]: Applicative, Player, R]( + initialization: SinglePhasedRepositoryInitialization[F, R], + finalization: RepositoryFinalization[F, UUID, R] + ): SinglePhased[F, Player, R] = { SinglePhased(initialization, (_, _) => Applicative[F].unit, finalization) } - def trivial[F[_] : Applicative, Player]: SinglePhased[F, Player, Unit] = withoutTappingAction( - SinglePhasedRepositoryInitialization.constant(()), - RepositoryFinalization.trivial - ) + def trivial[F[_]: Applicative, Player]: SinglePhased[F, Player, Unit] = + withoutTappingAction( + SinglePhasedRepositoryInitialization.constant(()), + RepositoryFinalization.trivial + ) - def withSupplierAndTrivialFinalization[F[_] : Monad, Player, R](supplier: F[R]): SinglePhased[F, Player, R] = + def withSupplierAndTrivialFinalization[F[_]: Monad, Player, R]( + supplier: F[R] + ): SinglePhased[F, Player, R] = trivial[F, Player].flatXmap(_ => supplier)(_ => Applicative[F].unit) } - case class TwoPhased[F[_], Player, R](initialization: TwoPhasedRepositoryInitialization[F, Player, R], - finalization: RepositoryFinalization[F, Player, R]) - extends Phased[F, Player, R] { + case class TwoPhased[F[_], Player, R]( + initialization: TwoPhasedRepositoryInitialization[F, Player, R], + finalization: RepositoryFinalization[F, Player, R] + ) extends Phased[F, Player, R] { override type Self[S] = TwoPhased[F, Player, S] - def flatXmapWithPlayerAndIntermediateEffects[S](f: Player => R => F[S]) - (beforePersisting: S => F[R])(beforeFinalization: S => F[R]) - (implicit F: Monad[F]): TwoPhased[F, Player, S] = + def flatXmapWithPlayerAndIntermediateEffects[S](f: Player => R => F[S])( + beforePersisting: S => F[R] + )(beforeFinalization: S => F[R])(implicit F: Monad[F]): TwoPhased[F, Player, S] = TwoPhased( initialization.extendPreparation(f), finalization.withIntermediateEffects(beforePersisting)(beforeFinalization) ) - override def flatXmapWithIntermediateEffects[S](f: R => F[S]) - (beforePersisting: S => F[R])(beforeFinalization: S => F[R]) - (implicit F: Monad[F]): TwoPhased[F, Player, S] = + override def flatXmapWithIntermediateEffects[S](f: R => F[S])( + beforePersisting: S => F[R] + )(beforeFinalization: S => F[R])(implicit F: Monad[F]): TwoPhased[F, Player, S] = flatXmapWithPlayerAndIntermediateEffects(_ => f)(beforePersisting)(beforeFinalization) - def flatXmapWithPlayer[S](f: Player => R => F[S])(g: S => F[R])(implicit F: Monad[F]): TwoPhased[F, Player, S] = + def flatXmapWithPlayer[S](f: Player => R => F[S])(g: S => F[R])( + implicit F: Monad[F] + ): TwoPhased[F, Player, S] = flatXmapWithPlayerAndIntermediateEffects(f)(g)(g) } } case class Mapped[F[_], Player, S, R](source: RepositoryDefinition[F, Player, S], sr: S => R) - extends RepositoryDefinition[F, Player, R] + extends RepositoryDefinition[F, Player, R] } diff --git a/src/main/scala/com/github/unchama/datarepository/template/finalization/RepositoryFinalization.scala b/src/main/scala/com/github/unchama/datarepository/template/finalization/RepositoryFinalization.scala index dd7c0e8040..448a3db25e 100644 --- a/src/main/scala/com/github/unchama/datarepository/template/finalization/RepositoryFinalization.scala +++ b/src/main/scala/com/github/unchama/datarepository/template/finalization/RepositoryFinalization.scala @@ -6,8 +6,8 @@ import cats.{Applicative, FlatMap} * データレポジトリの終了処理を記述するオブジェクト。 * * このオブジェクトが記述するのは、 - * - 定期的、又は不定期的にプレーヤーのデータを永続化層に書き込み - * - 必要ならば、プレーヤーが退出する際に永続化層に書き込む処理とは別にクリーンアップを行う + * - 定期的、又は不定期的にプレーヤーのデータを永続化層に書き込み + * - 必要ならば、プレーヤーが退出する際に永続化層に書き込む処理とは別にクリーンアップを行う * * ようなデータレポジトリの処理である。 */ @@ -22,9 +22,9 @@ trait RepositoryFinalization[F[_], Player, R] { self => /** * 永続化前に `beforePersisting` を、終了処理前に `beforeFinalization` を実行するような終了処理を定義する。 */ - def withIntermediateEffects[S](beforePersisting: S => F[R]) - (beforeFinalization: S => F[R]) - (implicit F: FlatMap[F]): RepositoryFinalization[F, Player, S] = + def withIntermediateEffects[S](beforePersisting: S => F[R])( + beforeFinalization: S => F[R] + )(implicit F: FlatMap[F]): RepositoryFinalization[F, Player, S] = new RepositoryFinalization[F, Player, S] { override val persistPair: (Player, S) => F[Unit] = (p, s) => beforePersisting(s).flatMap(r => self.persistPair(p, r)) @@ -35,40 +35,43 @@ trait RepositoryFinalization[F[_], Player, R] { self => def contraMap[S](sr: S => R): RepositoryFinalization[F, Player, S] = new RepositoryFinalization[F, Player, S] { override val persistPair: (Player, S) => F[Unit] = (p, s) => self.persistPair(p, sr(s)) - override val finalizeBeforeUnload: (Player, S) => F[Unit] = (p, s) => self.finalizeBeforeUnload(p, sr(s)) + override val finalizeBeforeUnload: (Player, S) => F[Unit] = (p, s) => + self.finalizeBeforeUnload(p, sr(s)) } def contraMapKey[K](kp: K => Player): RepositoryFinalization[F, K, R] = new RepositoryFinalization[F, K, R] { override val persistPair: (K, R) => F[Unit] = (k, r) => self.persistPair(kp(k), r) - override val finalizeBeforeUnload: (K, R) => F[Unit] = (k, r) => self.finalizeBeforeUnload(kp(k), r) + override val finalizeBeforeUnload: (K, R) => F[Unit] = (k, r) => + self.finalizeBeforeUnload(kp(k), r) } - def withIntermediateEffect[S](sFr: S => F[R]) - (implicit F: FlatMap[F]): RepositoryFinalization[F, Player, S] = + def withIntermediateEffect[S](sFr: S => F[R])( + implicit F: FlatMap[F] + ): RepositoryFinalization[F, Player, S] = withIntermediateEffects(sFr)(sFr) } object RepositoryFinalization { - def withoutAnyPersistence[ - F[_] : Applicative, Player, R - ](finalization: (Player, R) => F[Unit]): RepositoryFinalization[F, Player, R] = + def withoutAnyPersistence[F[_]: Applicative, Player, R]( + finalization: (Player, R) => F[Unit] + ): RepositoryFinalization[F, Player, R] = new RepositoryFinalization[F, Player, R] { override val persistPair: (Player, R) => F[Unit] = (_, _) => Applicative[F].unit override val finalizeBeforeUnload: (Player, R) => F[Unit] = finalization } - def withoutAnyFinalization[ - F[_] : Applicative, Player, R - ](persist: (Player, R) => F[Unit]): RepositoryFinalization[F, Player, R] = + def withoutAnyFinalization[F[_]: Applicative, Player, R]( + persist: (Player, R) => F[Unit] + ): RepositoryFinalization[F, Player, R] = new RepositoryFinalization[F, Player, R] { override val persistPair: (Player, R) => F[Unit] = persist override val finalizeBeforeUnload: (Player, R) => F[Unit] = (_, _) => Applicative[F].unit } - def trivial[F[_] : Applicative, Player, R]: RepositoryFinalization[F, Player, R] = + def trivial[F[_]: Applicative, Player, R]: RepositoryFinalization[F, Player, R] = withoutAnyPersistence((_, _) => Applicative[F].unit) } diff --git a/src/main/scala/com/github/unchama/datarepository/template/initialization/PrefetchResult.scala b/src/main/scala/com/github/unchama/datarepository/template/initialization/PrefetchResult.scala index 32acb24c64..1550dbe2a5 100644 --- a/src/main/scala/com/github/unchama/datarepository/template/initialization/PrefetchResult.scala +++ b/src/main/scala/com/github/unchama/datarepository/template/initialization/PrefetchResult.scala @@ -4,8 +4,8 @@ import cats.{Applicative, Eval, Traverse} /** * データリポジトリの中間データの生成結果。 - * - [[R]] の値が入っている - * - 読み込みが失敗して(プレーヤーに表示するための)メッセージが入っている + * - [[R]] の値が入っている + * - 読み込みが失敗して(プレーヤーに表示するための)メッセージが入っている * * のどちらかである。 */ @@ -18,25 +18,25 @@ object PrefetchResult { case class Success[+R](data: R) extends PrefetchResult[R] implicit val traverseInstance: Traverse[PrefetchResult] = new Traverse[PrefetchResult] { - override def traverse[G[_], A, B](fa: PrefetchResult[A]) - (f: A => G[B]) - (implicit G: Applicative[G]): G[PrefetchResult[B]] = + override def traverse[G[_], A, B]( + fa: PrefetchResult[A] + )(f: A => G[B])(implicit G: Applicative[G]): G[PrefetchResult[B]] = fa match { - case f@Failed(_) => G.pure(f) + case f @ Failed(_) => G.pure(f) case Success(data) => G.map(f(data))(PrefetchResult.Success.apply) } - override def foldLeft[A, B](fa: PrefetchResult[A], b: B) - (f: (B, A) => B): B = + override def foldLeft[A, B](fa: PrefetchResult[A], b: B)(f: (B, A) => B): B = fa match { - case Failed(_) => b + case Failed(_) => b case Success(data) => f(b, data) } - override def foldRight[A, B](fa: PrefetchResult[A], lb: Eval[B]) - (f: (A, Eval[B]) => Eval[B]): Eval[B] = + override def foldRight[A, B](fa: PrefetchResult[A], lb: Eval[B])( + f: (A, Eval[B]) => Eval[B] + ): Eval[B] = fa match { - case Failed(_) => lb + case Failed(_) => lb case Success(data) => f(data, lb) } } diff --git a/src/main/scala/com/github/unchama/datarepository/template/initialization/SinglePhasedRepositoryInitialization.scala b/src/main/scala/com/github/unchama/datarepository/template/initialization/SinglePhasedRepositoryInitialization.scala index eff4372876..defc7080bb 100644 --- a/src/main/scala/com/github/unchama/datarepository/template/initialization/SinglePhasedRepositoryInitialization.scala +++ b/src/main/scala/com/github/unchama/datarepository/template/initialization/SinglePhasedRepositoryInitialization.scala @@ -9,11 +9,10 @@ import java.util.UUID /** * データレポジトリの初期化処理を記述するオブジェクト。 * - * マインクラフトのサーバーの仕様として、プレーヤーがサーバーに実際に参加する前に - * プレーヤーのUUID/名前ペアを受け取れることになっている。 + * マインクラフトのサーバーの仕様として、プレーヤーがサーバーに実際に参加する前に プレーヤーのUUID/名前ペアを受け取れることになっている。 * * このオブジェクトが記述するのは、そのような状況下において - * - ログイン処理後にUUID/名前を受け取り次第 [[R]] を生成する + * - ログイン処理後にUUID/名前を受け取り次第 [[R]] を生成する * * ようなデータリポジトリの処理である。 */ @@ -28,7 +27,9 @@ trait SinglePhasedRepositoryInitialization[F[_], R] { import cats.implicits._ - def extendPreparation[S](f: (UUID, String) => R => F[S])(implicit F: Monad[F]): SinglePhasedRepositoryInitialization[F, S] = + def extendPreparation[S](f: (UUID, String) => R => F[S])( + implicit F: Monad[F] + ): SinglePhasedRepositoryInitialization[F, S] = (uuid, name) => prepareData(uuid, name) >>= (_.traverse(f(uuid, name))) } @@ -37,24 +38,24 @@ object SinglePhasedRepositoryInitialization { import cats.implicits._ - implicit def functorInstance[F[_] : Functor]: Functor[SinglePhasedRepositoryInitialization[F, *]] = + implicit def functorInstance[F[_]: Functor] + : Functor[SinglePhasedRepositoryInitialization[F, *]] = new Functor[SinglePhasedRepositoryInitialization[F, *]] { - override def map[A, B](fa: SinglePhasedRepositoryInitialization[F, A]) - (f: A => B): SinglePhasedRepositoryInitialization[F, B] = + override def map[A, B](fa: SinglePhasedRepositoryInitialization[F, A])( + f: A => B + ): SinglePhasedRepositoryInitialization[F, B] = (uuid, name) => fa.prepareData(uuid, name).map(_.map(f)) } - import cats.implicits._ - - def withSupplier[F[_] : Functor, R](fr: F[R]): SinglePhasedRepositoryInitialization[F, R] = + def withSupplier[F[_]: Functor, R](fr: F[R]): SinglePhasedRepositoryInitialization[F, R] = (_, _) => Functor[F].map(fr)(PrefetchResult.Success.apply) - def constant[F[_] : Applicative, R](v: R): SinglePhasedRepositoryInitialization[F, R] = + def constant[F[_]: Applicative, R](v: R): SinglePhasedRepositoryInitialization[F, R] = withSupplier(Applicative[F].pure(v)) - def forRefCell[ - F[_] : Sync, R - ](initialization: SinglePhasedRepositoryInitialization[F, R]): SinglePhasedRepositoryInitialization[F, Ref[F, R]] = + def forRefCell[F[_]: Sync, R]( + initialization: SinglePhasedRepositoryInitialization[F, R] + ): SinglePhasedRepositoryInitialization[F, Ref[F, R]] = (uuid, name) => initialization.prepareData(uuid, name) >>= (_.traverse(Ref[F].of(_))) } diff --git a/src/main/scala/com/github/unchama/datarepository/template/initialization/TwoPhasedRepositoryInitialization.scala b/src/main/scala/com/github/unchama/datarepository/template/initialization/TwoPhasedRepositoryInitialization.scala index 256b5a3b5e..7a4266f5da 100644 --- a/src/main/scala/com/github/unchama/datarepository/template/initialization/TwoPhasedRepositoryInitialization.scala +++ b/src/main/scala/com/github/unchama/datarepository/template/initialization/TwoPhasedRepositoryInitialization.scala @@ -7,12 +7,11 @@ import java.util.UUID /** * データレポジトリの初期化処理を記述するオブジェクト。 * - * マインクラフトのサーバーの仕様として、プレーヤーがサーバーに実際に参加する前に - * プレーヤーのUUID/名前ペアを受け取れることになっている。 + * マインクラフトのサーバーの仕様として、プレーヤーがサーバーに実際に参加する前に プレーヤーのUUID/名前ペアを受け取れることになっている。 * * このオブジェクトが記述するのは、そのような状況下において - * - ログイン処理後にUUID/名前を受け取り、中間データを作成する - * - 実際に [[Player]] のインスタンスが得られ次第 [[R]] を生成する + * - ログイン処理後にUUID/名前を受け取り、中間データを作成する + * - 実際に [[Player]] のインスタンスが得られ次第 [[R]] を生成する * * ようなデータリポジトリの処理である。 */ @@ -32,10 +31,13 @@ trait TwoPhasedRepositoryInitialization[F[_], Player, R] { import cats.implicits._ - def extendPreparation[S](f: Player => R => F[S])(implicit F: Monad[F]): TwoPhasedRepositoryInitialization[F, Player, S] = + def extendPreparation[S]( + f: Player => R => F[S] + )(implicit F: Monad[F]): TwoPhasedRepositoryInitialization[F, Player, S] = new TwoPhasedRepositoryInitialization[F, Player, S] { override type IntermediateData = self.IntermediateData - override val prefetchIntermediateValue: (UUID, String) => F[PrefetchResult[IntermediateData]] = + override val prefetchIntermediateValue + : (UUID, String) => F[PrefetchResult[IntermediateData]] = self.prefetchIntermediateValue override val prepareData: (Player, IntermediateData) => F[S] = (player, i) => self.prepareData(player, i).flatMap(f(player)) @@ -46,21 +48,25 @@ object TwoPhasedRepositoryInitialization { import cats.implicits._ - implicit def functorInstance[F[_] : Functor, Player]: Functor[TwoPhasedRepositoryInitialization[F, Player, *]] = + implicit def functorInstance[F[_]: Functor, Player] + : Functor[TwoPhasedRepositoryInitialization[F, Player, *]] = new Functor[TwoPhasedRepositoryInitialization[F, Player, *]] { - override def map[A, B](fa: TwoPhasedRepositoryInitialization[F, Player, A]) - (f: A => B): TwoPhasedRepositoryInitialization[F, Player, B] = + override def map[A, B]( + fa: TwoPhasedRepositoryInitialization[F, Player, A] + )(f: A => B): TwoPhasedRepositoryInitialization[F, Player, B] = new TwoPhasedRepositoryInitialization[F, Player, B] { override type IntermediateData = fa.IntermediateData - override val prefetchIntermediateValue: (UUID, String) => F[PrefetchResult[IntermediateData]] = + override val prefetchIntermediateValue + : (UUID, String) => F[PrefetchResult[IntermediateData]] = fa.prefetchIntermediateValue override val prepareData: (Player, IntermediateData) => F[B] = (player, i) => fa.prepareData(player, i).map(f) } } - def augment[F[_], Player, R, T](singlePhasedRepositoryInitialization: SinglePhasedRepositoryInitialization[F, T]) - (prepareFinalData: (Player, T) => F[R]): TwoPhasedRepositoryInitialization[F, Player, R] = { + def augment[F[_], Player, R, T]( + singlePhasedRepositoryInitialization: SinglePhasedRepositoryInitialization[F, T] + )(prepareFinalData: (Player, T) => F[R]): TwoPhasedRepositoryInitialization[F, Player, R] = { new TwoPhasedRepositoryInitialization[F, Player, R] { override type IntermediateData = T override val prefetchIntermediateValue: (UUID, String) => F[PrefetchResult[T]] = @@ -69,13 +75,15 @@ object TwoPhasedRepositoryInitialization { } } - def canonicallyFrom[ - F[_] : Applicative, Player, R - ](initialization: SinglePhasedRepositoryInitialization[F, R]): TwoPhasedRepositoryInitialization[F, Player, R] = + def canonicallyFrom[F[_]: Applicative, Player, R]( + initialization: SinglePhasedRepositoryInitialization[F, R] + ): TwoPhasedRepositoryInitialization[F, Player, R] = augment(initialization) { case (_, r) => Applicative[F].pure(r) } - def withoutPrefetching[ - F[_] : Applicative, Player, R - ](f: Player => F[R]): TwoPhasedRepositoryInitialization[F, Player, R] = - augment(SinglePhasedRepositoryInitialization.constant[F, Unit](())) { case (player, _) => f(player) } + def withoutPrefetching[F[_]: Applicative, Player, R]( + f: Player => F[R] + ): TwoPhasedRepositoryInitialization[F, Player, R] = + augment(SinglePhasedRepositoryInitialization.constant[F, Unit](())) { + case (player, _) => f(player) + } } diff --git a/src/main/scala/com/github/unchama/fs2/workaround/fs3/Fs3Channel.scala b/src/main/scala/com/github/unchama/fs2/workaround/fs3/Fs3Channel.scala index f96d57c7a6..98c3d0f4af 100644 --- a/src/main/scala/com/github/unchama/fs2/workaround/fs3/Fs3Channel.scala +++ b/src/main/scala/com/github/unchama/fs2/workaround/fs3/Fs3Channel.scala @@ -31,83 +31,79 @@ import cats.effect.implicits._ import cats.syntax.all._ import fs2.{Chunk, Pipe, Pull, Stream} -/** Stream aware, multiple producer, single consumer closeable channel. +/** + * Stream aware, multiple producer, single consumer closeable channel. */ sealed trait Fs3Channel[F[_], A] { - /** Sends all the elements of the input stream through this channel, - * and closes it after. + /** + * Sends all the elements of the input stream through this channel, and closes it after. * Especially useful if the channel is single producer. */ def sendAll: Pipe[F, A, Nothing] - /** Sends an element through this channel. + /** + * Sends an element through this channel. * - * It can be called concurrently by multiple producers, and it may - * semantically block if the channel is bounded or synchronous. + * It can be called concurrently by multiple producers, and it may semantically block if the + * channel is bounded or synchronous. * * No-op if the channel is closed, see [[close]] for further info. */ def send(a: A): F[Either[Fs3Channel.Closed, Unit]] - /** The stream of elements sent through this channel. - * It terminates if [[close]] is called and all elements in the channel - * have been emitted (see [[close]] for futher info). + /** + * The stream of elements sent through this channel. It terminates if [[close]] is called and + * all elements in the channel have been emitted (see [[close]] for futher info). * - * This method CANNOT be called concurrently by multiple consumers, if - * you do so, one of the consumers might become permanently - * deadlocked. + * This method CANNOT be called concurrently by multiple consumers, if you do so, one of the + * consumers might become permanently deadlocked. * - * It is possible to call `stream` again once the previous - * one has terminated, but be aware that some element might get lost - * in the process, e.g if the first call to `stream` got 5 elements off - * the channel, and terminated after emitting 2, when the second call - * to `stream` starts it won't see those 3 elements. + * It is possible to call `stream` again once the previous one has terminated, but be aware + * that some element might get lost in the process, e.g if the first call to `stream` got 5 + * elements off the channel, and terminated after emitting 2, when the second call to `stream` + * starts it won't see those 3 elements. * - * Every time `stream` is pulled, it will serve all the elements that - * are queued up in a single chunk, including those from producers - * that might be semantically blocked on a bounded channel, which will - * then become unblocked. That is, a bound on a channel represents - * the maximum number of elements that can be queued up before a - * producer blocks, and not the maximum number of elements that will - * be received by `stream` at once. + * Every time `stream` is pulled, it will serve all the elements that are queued up in a + * single chunk, including those from producers that might be semantically blocked on a + * bounded channel, which will then become unblocked. That is, a bound on a channel represents + * the maximum number of elements that can be queued up before a producer blocks, and not the + * maximum number of elements that will be received by `stream` at once. */ def stream: Stream[F, A] - /** This method achieves graceful shutdown: when the channel gets - * closed, `stream` will terminate naturally after consuming all - * currently enqueued elements, including the ones by producers blocked - * on a bound. + /** + * This method achieves graceful shutdown: when the channel gets closed, `stream` will + * terminate naturally after consuming all currently enqueued elements, including the ones by + * producers blocked on a bound. * - * "Termination" here means that `stream` will no longer - * wait for new elements on the channel, and not that it will be - * interrupted while performing another action: if you want to - * interrupt `stream` immediately, without first processing enqueued - * elements, you should use `interruptWhen` on it instead. + * "Termination" here means that `stream` will no longer wait for new elements on the channel, + * and not that it will be interrupted while performing another action: if you want to + * interrupt `stream` immediately, without first processing enqueued elements, you should use + * `interruptWhen` on it instead. * - * After a call to `close`, any further calls to `send` or `close` - * will be no-ops. + * After a call to `close`, any further calls to `send` or `close` will be no-ops. * - * Note that `close` does not automatically unblock producers which - * might be blocked on a bound, they will only become unblocked if - * `stream` is executing. + * Note that `close` does not automatically unblock producers which might be blocked on a + * bound, they will only become unblocked if `stream` is executing. * - * In other words, if `close` is called while `stream` is - * executing, blocked producers will eventually become unblocked, - * before `stream` terminates and further `send` calls become - * no-ops. - * However, if `close` is called after `stream` has terminated (e.g - * because it was interrupted, or had a `.take(n)`), then blocked - * producers will stay blocked unless they get explicitly - * unblocked, either by a further call to `stream` to drain the - * channel, or by a a `race` with `closed`. + * In other words, if `close` is called while `stream` is executing, blocked producers will + * eventually become unblocked, before `stream` terminates and further `send` calls become + * no-ops. However, if `close` is called after `stream` has terminated (e.g because it was + * interrupted, or had a `.take(n)`), then blocked producers will stay blocked unless they get + * explicitly unblocked, either by a further call to `stream` to drain the channel, or by a a + * `race` with `closed`. */ def close: F[Either[Fs3Channel.Closed, Unit]] - /** Returns true if this channel is closed */ + /** + * Returns true if this channel is closed + */ def isClosed: F[Boolean] - /** Semantically blocks until the channel gets closed. */ + /** + * Semantically blocks until the channel gets closed. + */ def closed: F[Unit] } @@ -123,26 +119,20 @@ object Fs3Channel { def bounded[F[_], A](capacity: Int)(implicit F: Concurrent[F]): F[Fs3Channel[F, A]] = { case class State( - values: Vector[A], - size: Int, - waiting: Option[Deferred[F, Unit]], - producers: Vector[(A, Deferred[F, Unit])], - closed: Boolean - ) + values: Vector[A], + size: Int, + waiting: Option[Deferred[F, Unit]], + producers: Vector[(A, Deferred[F, Unit])], + closed: Boolean + ) val initial = State(Vector.empty, 0, None, Vector.empty, false) - ( - Ref[F].of(initial), - Deferred.tryable[F, Unit] - ).mapN { (state, closedGate) => + (Ref[F].of(initial), Deferred.tryable[F, Unit]).mapN { (state, closedGate) => new Fs3Channel[F, A] { def sendAll: Pipe[F, A, Nothing] = { in => - (in ++ Stream.eval_(close.void)) - .evalMap(send) - .takeWhile(_.isRight) - .drain + (in ++ Stream.eval_(close.void)).evalMap(send).takeWhile(_.isRight).drain } def send(a: A): F[Either[Closed, Unit]] = { @@ -198,37 +188,37 @@ object Fs3Channel { Pull.eval { Deferred[F, Unit].flatMap { waiting => state - .modify { case State(values, size, ignorePreviousWaiting @ _, producers, closed) => - if (values.nonEmpty || producers.nonEmpty) { - var unblock_ = F.unit - var allValues_ = values - - producers.foreach { case (value, producer) => - allValues_ = allValues_ :+ value - unblock_ = unblock_ >> producer.complete(()).void - } - - val unblock = unblock_ - val allValues = allValues_ - - val toEmit = Chunk.vector(allValues) - - ( - State(Vector(), 0, None, Vector.empty, closed), - // unblock needs to execute in F, so we can make it uncancelable - unblock.as( - Pull.output(toEmit) >> consumeLoop + .modify { + case State(values, size, ignorePreviousWaiting @ _, producers, closed) => + if (values.nonEmpty || producers.nonEmpty) { + var unblock_ = F.unit + var allValues_ = values + + producers.foreach { + case (value, producer) => + allValues_ = allValues_ :+ value + unblock_ = unblock_ >> producer.complete(()).void + } + + val unblock = unblock_ + val allValues = allValues_ + + val toEmit = Chunk.vector(allValues) + + ( + State(Vector(), 0, None, Vector.empty, closed), + // unblock needs to execute in F, so we can make it uncancelable + unblock.as(Pull.output(toEmit) >> consumeLoop) ) - ) - } else { - ( - State(values, size, waiting.some, producers, closed), - F.pure( - if (closed) Pull.done - else (Pull.eval(waiting.get) >> consumeLoop) + } else { + ( + State(values, size, waiting.some, producers, closed), + F.pure( + if (closed) Pull.done + else (Pull.eval(waiting.get) >> consumeLoop) + ) ) - ) - } + } } .flatten .uncancelable @@ -246,4 +236,4 @@ object Fs3Channel { // allocate once private final val closed: Either[Closed, Unit] = Left(Closed) private final val rightUnit: Either[Closed, Unit] = Right(()) -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/fs2/workaround/fs3/Fs3Topic.scala b/src/main/scala/com/github/unchama/fs2/workaround/fs3/Fs3Topic.scala index f14e9e9c40..2e97036708 100644 --- a/src/main/scala/com/github/unchama/fs2/workaround/fs3/Fs3Topic.scala +++ b/src/main/scala/com/github/unchama/fs2/workaround/fs3/Fs3Topic.scala @@ -32,95 +32,97 @@ import scala.collection.immutable.LongMap * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** Topic allows you to distribute `A`s published by an arbitrary - * number of publishers to an arbitrary number of subscribers. +/** + * Topic allows you to distribute `A`s published by an arbitrary number of publishers to an + * arbitrary number of subscribers. * - * Topic has built-in back-pressure support implemented as the maximum - * number of elements (`maxQueued`) that a subscriber is allowed to enqueue. + * Topic has built-in back-pressure support implemented as the maximum number of elements + * (`maxQueued`) that a subscriber is allowed to enqueue. * - * Once that bound is hit, any publishing action will semantically - * block until the lagging subscriber consumes some of its queued - * elements. + * Once that bound is hit, any publishing action will semantically block until the lagging + * subscriber consumes some of its queued elements. */ trait Fs3Topic[F[_], A] { self => - /** Publishes elements from source of `A` to this topic. - * [[Pipe]] equivalent of `publish1`. - * Closes the topic when the input stream terminates. - * Especially useful when the topic has a single producer. + /** + * Publishes elements from source of `A` to this topic. [[Pipe]] equivalent of `publish1`. + * Closes the topic when the input stream terminates. Especially useful when the topic has a + * single producer. */ def publish: Pipe[F, A, Nothing] - /** Publishes one `A` to topic. - * No-op if the channel is closed, see [[close]] for further info. + /** + * Publishes one `A` to topic. No-op if the channel is closed, see [[close]] for further info. * - * This operation does not complete until after the given element - * has been enqued on all subscribers, which means that if any - * subscriber is at its `maxQueued` limit, `publish1` will - * semantically block until that subscriber consumes an element. + * This operation does not complete until after the given element has been enqued on all + * subscribers, which means that if any subscriber is at its `maxQueued` limit, `publish1` + * will semantically block until that subscriber consumes an element. * - * A semantically blocked publication can be interrupted, but there is - * no guarantee of atomicity, and it could result in the `A` being - * received by some subscribers only. + * A semantically blocked publication can be interrupted, but there is no guarantee of + * atomicity, and it could result in the `A` being received by some subscribers only. * - * Note: if `publish1` is called concurrently by multiple producers, - * different subscribers may receive messages from different producers - * in a different order. + * Note: if `publish1` is called concurrently by multiple producers, different subscribers may + * receive messages from different producers in a different order. */ def publish1(a: A): F[Either[Fs3Topic.Closed, Unit]] - /** Subscribes for `A` values that are published to this topic. + /** + * Subscribes for `A` values that are published to this topic. * - * Pulling on the returned stream opens a "subscription", which allows up to - * `maxQueued` elements to be enqueued as a result of publication. + * Pulling on the returned stream opens a "subscription", which allows up to `maxQueued` + * elements to be enqueued as a result of publication. * - * If at any point, the queue backing the subscription has `maxQueued` elements in it, - * any further publications semantically block until elements are dequeued from the - * subscription queue. + * If at any point, the queue backing the subscription has `maxQueued` elements in it, any + * further publications semantically block until elements are dequeued from the subscription + * queue. * - * @param maxQueued maximum number of elements to enqueue to the subscription - * queue before blocking publishers + * @param maxQueued + * maximum number of elements to enqueue to the subscription queue before blocking + * publishers */ def subscribe(maxQueued: Int): Stream[F, A] - /** Like `subscribe`, but represents the subscription explicitly as - * a `Resource` which returns after the subscriber is subscribed, - * but before it has started pulling elements. + /** + * Like `subscribe`, but represents the subscription explicitly as a `Resource` which returns + * after the subscriber is subscribed, but before it has started pulling elements. */ def subscribeAwait(maxQueued: Int): Resource[F, Stream[F, A]] - /** Signal of current active subscribers. + /** + * Signal of current active subscribers. */ def subscribers: Stream[F, Int] - /** This method achieves graceful shutdown: when the topics gets - * closed, its subscribers will terminate naturally after consuming all - * currently enqueued elements. + /** + * This method achieves graceful shutdown: when the topics gets closed, its subscribers will + * terminate naturally after consuming all currently enqueued elements. * - * "Termination" here means that subscribers no longer - * wait for new elements on the topic, and not that they will be - * interrupted while performing another action: if you want to - * interrupt a subscriber, without first processing enqueued - * elements, you should use `interruptWhen` on it instead. + * "Termination" here means that subscribers no longer wait for new elements on the topic, and + * not that they will be interrupted while performing another action: if you want to interrupt + * a subscriber, without first processing enqueued elements, you should use `interruptWhen` on + * it instead. * - * After a call to `close`, any further calls to `publish1` or `close` - * will be no-ops. + * After a call to `close`, any further calls to `publish1` or `close` will be no-ops. * - * Note that `close` does not automatically unblock producers which - * might be blocked on a bound, they will only become unblocked - * if/when subscribers naturally finish to consume the respective elements. - * You can `race` the publish with `close` to interrupt them immediately. + * Note that `close` does not automatically unblock producers which might be blocked on a + * bound, they will only become unblocked if/when subscribers naturally finish to consume the + * respective elements. You can `race` the publish with `close` to interrupt them immediately. */ def close: F[Unit] - /** Returns true if this topic is closed */ + /** + * Returns true if this topic is closed + */ def isClosed: F[Boolean] - /** Semantically blocks until the topic gets closed. */ + /** + * Semantically blocks until the topic gets closed. + */ def closed: F[Unit] - /** Returns an alternate view of this `Topic` where its elements are of type `B`, - * given two functions, `A => B` and `B => A`. + /** + * Returns an alternate view of this `Topic` where its elements are of type `B`, given two + * functions, `A => B` and `B => A`. */ def imap[B](f: A => B)(g: B => A)(implicit F: Applicative[F]): Fs3Topic[F, B] = new Fs3Topic[F, B] { @@ -143,84 +145,83 @@ object Fs3Topic { import cats.implicits._ - def apply[F[_], A](initial: A)(implicit F: Concurrent[F]): F[Fs3Topic[F, A]] = in[F, F, A](initial) + def apply[F[_], A](initial: A)(implicit F: Concurrent[F]): F[Fs3Topic[F, A]] = + in[F, F, A](initial) - /** Constructs a Topic */ - //noinspection ScalaUnusedSymbol + /** + * Constructs a Topic + */ + // noinspection ScalaUnusedSymbol def in[G[_], F[_], A](initial: A)(implicit G: Sync[G], F: Concurrent[F]): G[Fs3Topic[F, A]] = ( Ref.in[G, F, (LongMap[Fs3Channel[F, A]], Long)](LongMap.empty[Fs3Channel[F, A]] -> 1L), SignallingRef.in[G, F, Int](0), AsymmetricTryableDeferred.concurrentIn[F, G, Unit] - ).mapN { case (state, subscriberCount, signalClosure) => - new Fs3Topic[F, A] { - def foreach[B](lm: LongMap[B])(f: B => F[Unit]): F[Unit] = - lm.foldLeft(F.unit) { case (op, (_, b)) => op >> f(b) } - - def publish1(a: A): F[Either[Fs3Topic.Closed, Unit]] = - signalClosure.tryGet[F].flatMap { - case Some(_) => Fs3Topic.closed.pure[F] - case None => - state.get - .flatMap { case (subs, _) => foreach(subs)(_.send(a).void) } - .as(Fs3Topic.rightUnit) + ).mapN { + case (state, subscriberCount, signalClosure) => + new Fs3Topic[F, A] { + def foreach[B](lm: LongMap[B])(f: B => F[Unit]): F[Unit] = + lm.foldLeft(F.unit) { case (op, (_, b)) => op >> f(b) } + + def publish1(a: A): F[Either[Fs3Topic.Closed, Unit]] = + signalClosure.tryGet[F].flatMap { + case Some(_) => Fs3Topic.closed.pure[F] + case None => + state + .get + .flatMap { case (subs, _) => foreach(subs)(_.send(a).void) } + .as(Fs3Topic.rightUnit) + } + + def subscribeAwait(maxQueued: Int): Resource[F, Stream[F, A]] = Resource.suspend { + for { + channel <- Fs3Channel.bounded[F, A](maxQueued) + } yield { + val subscribe = state.modify { + case (subs, id) => + (subs.updated(id, channel), id + 1) -> id + } <* subscriberCount.update(_ + 1) + + def unsubscribe(id: Long) = + state.modify { + case (subs, nextId) => + // _After_ we remove the bounded channel for this + // subscriber, we need to drain it to unblock to + // publish loop which might have already enqueued + // something. + def drainChannel: F[Unit] = + subs.get(id).traverse_ { chan => chan.close >> chan.stream.compile.drain } + + (subs - id, nextId) -> drainChannel + }.flatten >> subscriberCount.update(_ - 1) + + Resource.make(subscribe)(unsubscribe).as(channel.stream) + } } - def subscribeAwait(maxQueued: Int): Resource[F, Stream[F, A]] = Resource.suspend { - for { - channel <- Fs3Channel.bounded[F, A](maxQueued) - } yield { - val subscribe = state.modify { case (subs, id) => - (subs.updated(id, channel), id + 1) -> id - } <* subscriberCount.update(_ + 1) - - def unsubscribe(id: Long) = - state.modify { case (subs, nextId) => - // _After_ we remove the bounded channel for this - // subscriber, we need to drain it to unblock to - // publish loop which might have already enqueued - // something. - def drainChannel: F[Unit] = - subs.get(id).traverse_ { chan => - chan.close >> chan.stream.compile.drain - } - - (subs - id, nextId) -> drainChannel - }.flatten >> subscriberCount.update(_ - 1) - - Resource - .make(subscribe)(unsubscribe) - .as(channel.stream) + def publish: Pipe[F, A, INothing] = { in => + (in ++ Stream.eval_(close)).evalMap(publish1).takeWhile(_.isRight).drain } - } - def publish: Pipe[F, A, INothing] = { in => - (in ++ Stream.eval_(close)) - .evalMap(publish1) - .takeWhile(_.isRight) - .drain - } - - def subscribe(maxQueued: Int): Stream[F, A] = - Stream.resource(subscribeAwait(maxQueued)).flatten + def subscribe(maxQueued: Int): Stream[F, A] = + Stream.resource(subscribeAwait(maxQueued)).flatten - def subscribers: Stream[F, Int] = subscriberCount.discrete + def subscribers: Stream[F, Int] = subscriberCount.discrete - def close: F[Unit] = { - Bracket[F, Throwable].uncancelable { - signalClosure - .complete(()) - .flatMap { _ => - state.get + def close: F[Unit] = { + Bracket[F, Throwable].uncancelable { + signalClosure.complete(()).flatMap { _ => + state + .get .flatMap { case (subs, _) => foreach(subs)(_.close.void) } .as(Fs3Topic.rightUnit) } + } } - } - def closed: F[Unit] = signalClosure.get - def isClosed: F[Boolean] = signalClosure.tryGet[F].map(_.isDefined) - } + def closed: F[Unit] = signalClosure.get + def isClosed: F[Boolean] = signalClosure.tryGet[F].map(_.isDefined) + } } private final val closed: Either[Closed, Unit] = Left(Closed) diff --git a/src/main/scala/com/github/unchama/generic/ApplicativeErrorThrowableExtra.scala b/src/main/scala/com/github/unchama/generic/ApplicativeErrorThrowableExtra.scala index a7089431bd..d64936441f 100644 --- a/src/main/scala/com/github/unchama/generic/ApplicativeErrorThrowableExtra.scala +++ b/src/main/scala/com/github/unchama/generic/ApplicativeErrorThrowableExtra.scala @@ -7,12 +7,9 @@ object ApplicativeErrorThrowableExtra { import cats.implicits._ - def recoverWithStackTrace[ - F[_] : ApplicativeError[*[_], Throwable] : ErrorLogger, - A - ](action: F[A])(message: String, recover: => A): F[A] = - action.handleErrorWith { error => - ErrorLogger[F].error(error)(message).as(recover) - } + def recoverWithStackTrace[F[_]: ApplicativeError[*[_], Throwable]: ErrorLogger, A]( + action: F[A] + )(message: String, recover: => A): F[A] = + action.handleErrorWith { error => ErrorLogger[F].error(error)(message).as(recover) } } diff --git a/src/main/scala/com/github/unchama/generic/ContextCoercion.scala b/src/main/scala/com/github/unchama/generic/ContextCoercion.scala index 92760bfa56..8909842243 100644 --- a/src/main/scala/com/github/unchama/generic/ContextCoercion.scala +++ b/src/main/scala/com/github/unchama/generic/ContextCoercion.scala @@ -7,8 +7,8 @@ import cats.~> /** * 文脈FからGへの(自明な)変換を与える型クラス。 * - * [[cats.arrow.FunctionK]] と同じだが、より自明な、 - * 例えば [[cats.effect.SyncIO]] から [[cats.effect.IO]] のような変換を与えるオブジェクトとして機能する。 + * [[cats.arrow.FunctionK]] と同じだが、より自明な、 例えば [[cats.effect.SyncIO]] から [[cats.effect.IO]] + * のような変換を与えるオブジェクトとして機能する。 */ trait ContextCoercion[F[_], G[_]] extends (F ~> G) @@ -20,12 +20,15 @@ private[generic] abstract class ContextCoercionOps { import scala.language.implicitConversions - implicit def coercibleComputation[F[_], A](fa: F[A]): CoercibleComputation[F, A] = new CoercibleComputation(fa) + implicit def coercibleComputation[F[_], A](fa: F[A]): CoercibleComputation[F, A] = + new CoercibleComputation(fa) } object ContextCoercion extends ContextCoercionOps { - def apply[F[_], G[_], A](fa: F[A])(implicit coercion: ContextCoercion[F, G]): G[A] = coercion(fa) + def apply[F[_], G[_], A](fa: F[A])(implicit coercion: ContextCoercion[F, G]): G[A] = coercion( + fa + ) def asFunctionK[F[_], G[_]](implicit ev: ContextCoercion[F, G]): F ~> G = ev @@ -37,7 +40,7 @@ object ContextCoercion extends ContextCoercionOps { implicit def identityCoercion[F[_]]: ContextCoercion[F, F] = fromFunctionK(FunctionK.id) - implicit def syncEffectToSync[F[_] : SyncEffect, G[_] : Sync]: ContextCoercion[F, G] = { + implicit def syncEffectToSync[F[_]: SyncEffect, G[_]: Sync]: ContextCoercion[F, G] = { import cats.effect.implicits._ fromFunctionK(λ[F ~> G] { fa => diff --git a/src/main/scala/com/github/unchama/generic/Diff.scala b/src/main/scala/com/github/unchama/generic/Diff.scala index 8d9abf0488..3d517bb6d5 100644 --- a/src/main/scala/com/github/unchama/generic/Diff.scala +++ b/src/main/scala/com/github/unchama/generic/Diff.scala @@ -5,16 +5,16 @@ import cats.Eq /** * 異なる二つの値を保持するタプル。 * - * [[A]] に関連付いた [[Eq]] インスタンスによって - * [[left]] と [[right]] が等価でないと判定されることが保証される。 + * [[A]] に関連付いた [[Eq]] インスタンスによって [[left]] と [[right]] が等価でないと判定されることが保証される。 */ -case class Diff[A: Eq] private(left: A, right: A) +case class Diff[A: Eq] private (left: A, right: A) object Diff { import cats.implicits._ - def ofPairBy[A, B: Eq](pair: (A, A))(f: A => B): Option[Diff[B]] = fromValues(f(pair._1), f(pair._2)) + def ofPairBy[A, B: Eq](pair: (A, A))(f: A => B): Option[Diff[B]] = + fromValues(f(pair._1), f(pair._2)) def fromValues[A: Eq](left: A, right: A): Option[Diff[A]] = if (left neqv right) Some(Diff(left, right)) else None diff --git a/src/main/scala/com/github/unchama/generic/EitherExtra.scala b/src/main/scala/com/github/unchama/generic/EitherExtra.scala index 018abdda3a..3840581c99 100644 --- a/src/main/scala/com/github/unchama/generic/EitherExtra.scala +++ b/src/main/scala/com/github/unchama/generic/EitherExtra.scala @@ -4,16 +4,16 @@ object EitherExtra { def unassociate[A, B, C](eaebc: Either[A, Either[B, C]]): Either[Either[A, B], C] = eaebc match { - case Left(a) => Left(Left(a)) - case Right(Left(b)) => Left(Right(b)) + case Left(a) => Left(Left(a)) + case Right(Left(b)) => Left(Right(b)) case Right(Right(c)) => Right(c) } def associate[A, B, C](eeabc: Either[Either[A, B], C]): Either[A, Either[B, C]] = eeabc match { - case Left(Left(a)) => Left(a) + case Left(Left(a)) => Left(a) case Left(Right(b)) => Right(Left(b)) - case Right(c) => Right(Right(c)) + case Right(c) => Right(Right(c)) } } diff --git a/src/main/scala/com/github/unchama/generic/MapExtra.scala b/src/main/scala/com/github/unchama/generic/MapExtra.scala index 502a3c6300..13dbe32c01 100644 --- a/src/main/scala/com/github/unchama/generic/MapExtra.scala +++ b/src/main/scala/com/github/unchama/generic/MapExtra.scala @@ -9,34 +9,40 @@ object MapExtra { /** * `grouping` によりキーを分類し、 [[CommutativeSemigroup]] にて分類された値を合成する。 * - * 任意の `g: K => T`、 `k: K`、 `map: Map[K, V]` について、 - * `collapseKeys(g)(map).get(k) == map.filterKeys(k1 => g(k) == g(k1)).combineAll` - * を満たす。 + * 任意の `g: K => T`、 `k: K`、 `map: Map[K, V]` について、 `collapseKeys(g)(map).get(k) == + * map.filterKeys(k1 => g(k) == g(k1)).combineAll` を満たす。 */ - def collapseKeysThrough[T, K, V: CommutativeSemigroup](grouping: K => T) - (map: Map[K, V]): Map[K, V] = + def collapseKeysThrough[T, K, V: CommutativeSemigroup]( + grouping: K => T + )(map: Map[K, V]): Map[K, V] = map .groupBy { case (k, _) => grouping(k) } .toList - .mapFilter { case (_, groupedMap) => - groupedMap.toList match { - case ::((k, v), remaining) => Some { - val values: List[V] = remaining.map(_._2) + .mapFilter { + case (_, groupedMap) => + groupedMap.toList match { + case ::((k, v), remaining) => + Some { + val values: List[V] = remaining.map(_._2) - k -> values.foldLeft(v)(CommutativeSemigroup[V].combine) + k -> values.foldLeft(v)(CommutativeSemigroup[V].combine) + } + case Nil => None // 到達不能であるはず } - case Nil => None // 到達不能であるはず - } } .toMap /** - * - * @param map 元となるMap - * @param base キーの集合 - * @param default 埋める値 - * @tparam K キー - * @tparam V 値 + * @param map + * 元となるMap + * @param base + * キーの集合 + * @param default + * 埋める値 + * @tparam K + * キー + * @tparam V + * 値 * @return */ def fillOnBaseSet[K, V](map: Map[K, V], base: Set[K], default: V): Map[K, V] = { diff --git a/src/main/scala/com/github/unchama/generic/OptionTExtra.scala b/src/main/scala/com/github/unchama/generic/OptionTExtra.scala index c95d72c2dd..f80574ec30 100644 --- a/src/main/scala/com/github/unchama/generic/OptionTExtra.scala +++ b/src/main/scala/com/github/unchama/generic/OptionTExtra.scala @@ -4,6 +4,7 @@ import cats.Applicative import cats.data.OptionT object OptionTExtra { + /** * `failCondition` が true のとき失敗するような計算を返す。 */ diff --git a/src/main/scala/com/github/unchama/generic/ReadWrite.scala b/src/main/scala/com/github/unchama/generic/ReadWrite.scala index 735698a374..0e47cd69da 100644 --- a/src/main/scala/com/github/unchama/generic/ReadWrite.scala +++ b/src/main/scala/com/github/unchama/generic/ReadWrite.scala @@ -15,6 +15,6 @@ object ReadWrite { override def write(a: A): F[Unit] = _write(a) } - def liftUnsafe[F[_] : Sync, A](_read: => A, _write: => A => Unit): ReadWrite[F, A] = + def liftUnsafe[F[_]: Sync, A](_read: => A, _write: => A => Unit): ReadWrite[F, A] = from(Sync[F].delay(_read), a => Sync[F].delay(_write(a))) } diff --git a/src/main/scala/com/github/unchama/generic/RefDict.scala b/src/main/scala/com/github/unchama/generic/RefDict.scala index f7aaa69da8..9ffc367952 100644 --- a/src/main/scala/com/github/unchama/generic/RefDict.scala +++ b/src/main/scala/com/github/unchama/generic/RefDict.scala @@ -3,11 +3,9 @@ package com.github.unchama.generic import cats.{Contravariant, ~>} /** - * [[Key]] をキーとした参照セルの辞書型データ構造の抽象。 - * 読み書きは [[F]] のコンテキストで行われる作用として記述される。 + * [[Key]] をキーとした参照セルの辞書型データ構造の抽象。 読み書きは [[F]] のコンテキストで行われる作用として記述される。 * - * (特にDBや別マシンにあるKVストアなどでは)アクセスが並列に行われる可能性があることから、 - * このI/Fは一切の等式を保証しない。 + * (特にDBや別マシンにあるKVストアなどでは)アクセスが並列に行われる可能性があることから、 このI/Fは一切の等式を保証しない。 */ trait RefDict[F[_], Key, Value] { self => @@ -23,7 +21,8 @@ trait RefDict[F[_], Key, Value] { override def write(key: Key, value: Value): G[Unit] = fK(self.write(key, value)) } - final def coerceContextTo[G[_]](implicit ev: ContextCoercion[F, G]): RefDict[G, Key, Value] = mapK(ev) + final def coerceContextTo[G[_]](implicit ev: ContextCoercion[F, G]): RefDict[G, Key, Value] = + mapK(ev) } object RefDict { diff --git a/src/main/scala/com/github/unchama/generic/Token.scala b/src/main/scala/com/github/unchama/generic/Token.scala index 8ec313139e..1be477f1ed 100644 --- a/src/main/scala/com/github/unchama/generic/Token.scala +++ b/src/main/scala/com/github/unchama/generic/Token.scala @@ -21,7 +21,9 @@ package com.github.unchama.generic * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** Represents a unique identifier (using object equality). */ +/** + * Represents a unique identifier (using object equality). + */ private[generic] final class Token extends Serializable { override def toString: String = s"Token(${hashCode.toHexString})" } diff --git a/src/main/scala/com/github/unchama/generic/WeakRef.scala b/src/main/scala/com/github/unchama/generic/WeakRef.scala index 5573d3059d..43d7fa7164 100644 --- a/src/main/scala/com/github/unchama/generic/WeakRef.scala +++ b/src/main/scala/com/github/unchama/generic/WeakRef.scala @@ -14,18 +14,10 @@ sealed trait WeakRef[F[_], A <: AnyRef] { * 参照しているオブジェクトを取る。 * * `F[_]: Monad` の時、任意のアクション `f: F[U]` について、次が成り立つ: - * - {{{ - * get.flatMap(r1 => f.flatMap(_ => get.flatMap(r2 => - * // 空になった場合、その後はずっと空 - * !(r1.isEmpty && r2.nonempty) - * ))) == F.pure(true) - * }}} - * - {{{ - * get.flatMap(r1 => f.flatMap(_ => get.flatMap(r2 => - * // 二度 Some を返してきた場合、参照が等しい - * (r2.isEmpty) || (r1.get) eq (r2.get) - * ))) == F.pure(true) - * }}} + * - {{{ get.flatMap(r1 => f.flatMap(_ => get.flatMap(r2 => // 空になった場合、その後はずっと空 !(r1.isEmpty + * && r2.nonempty) ))) == F.pure(true) }}} + * - {{{ get.flatMap(r1 => f.flatMap(_ => get.flatMap(r2 => // 二度 Some を返してきた場合、参照が等しい + * (r2.isEmpty) || (r1.get) eq (r2.get) ))) == F.pure(true) }}} */ def get: F[Option[A]] @@ -44,10 +36,10 @@ sealed trait WeakRef[F[_], A <: AnyRef] { object WeakRef { /** - * 与えられたオブジェクト `value` を弱参照に包む。 - * `value` がガベージコレクトされた場合、返却された [[WeakRef]] の `get` 作用は [[None]] を返すようになる。 + * 与えられたオブジェクト `value` を弱参照に包む。 `value` がガベージコレクトされた場合、返却された [[WeakRef]] の `get` 作用は [[None]] + * を返すようになる。 */ - def of[F[_] : Sync, A <: AnyRef](value: A): WeakRef[F, A] = { + def of[F[_]: Sync, A <: AnyRef](value: A): WeakRef[F, A] = { val weakReference = new WeakReference(value) val getAction = Sync[F].delay(weakReference.get) diff --git a/src/main/scala/com/github/unchama/generic/algebra/typeclasses/HasSuccessor.scala b/src/main/scala/com/github/unchama/generic/algebra/typeclasses/HasSuccessor.scala index b2c1ec4bdd..fe79241f75 100644 --- a/src/main/scala/com/github/unchama/generic/algebra/typeclasses/HasSuccessor.scala +++ b/src/main/scala/com/github/unchama/generic/algebra/typeclasses/HasSuccessor.scala @@ -7,8 +7,7 @@ import simulacrum.typeclass * 後者関数 `successor` を兼ね揃えた全順序集合の型クラス。 * * この型クラスのインスタンスは、任意の `x: T` と `y: T` について - * - `x < y` ならば `x < x.successor <= y` - * を満たす。 + * - `x < y` ならば `x < x.successor <= y` を満たす。 */ @typeclass trait HasSuccessor[T] extends AnyRef { @@ -27,17 +26,14 @@ import simulacrum.typeclass /** * `lower` 以上 `upper` 以下の要素の順序付けられた [[LazyList]] を作成する。 * - * 返される [[LazyList]] を `l` とし、 `0 <= i < (l.size - 1)`、 - * `0 <= j < l.size` であるとき、 - * - `i == 0` ならば、 `l(i) = lower` - * - `successor(l(i)) = l(i + 1)` - * - `lower <= l(j) <= upper` - * を満たす。 + * 返される [[LazyList]] を `l` とし、 `0 <= i < (l.size - 1)`、 `0 <= j < l.size` であるとき、 + * - `i == 0` ならば、 `l(i) = lower` + * - `successor(l(i)) = l(i + 1)` + * - `lower <= l(j) <= upper` を満たす。 * - * この関数が返す [[LazyList]] は、 - * 必ずしもすべての `lower <= x < upper` となる要素を取りつくすわけではない。 - * 具体的には、自然数の二つ組の辞書式順序において `(1, 0)` はいかなる要素の後者でもないから、 - * `range((0, 0), (2, 0))` は (`(0, 0) < (1, 0) < (2, 0)` であるにもかかわらず) `(1, 0)` を含まない。 + * この関数が返す [[LazyList]] は、 必ずしもすべての `lower <= x < upper` となる要素を取りつくすわけではない。 + * 具体的には、自然数の二つ組の辞書式順序において `(1, 0)` はいかなる要素の後者でもないから、 `range((0, 0), (2, 0))` は (`(0, 0) < (1, + * 0) < (2, 0)` であるにもかかわらず) `(1, 0)` を含まない。 */ final def closedRange(lower: T, upper: T): LazyList[T] = { if (lower > upper) { diff --git a/src/main/scala/com/github/unchama/generic/algebra/typeclasses/OrderedMonus.scala b/src/main/scala/com/github/unchama/generic/algebra/typeclasses/OrderedMonus.scala index 3f9d0222d8..fca1d401ba 100644 --- a/src/main/scala/com/github/unchama/generic/algebra/typeclasses/OrderedMonus.scala +++ b/src/main/scala/com/github/unchama/generic/algebra/typeclasses/OrderedMonus.scala @@ -6,15 +6,13 @@ import cats.kernel.{CommutativeMonoid, Order} * 切り捨て減算(truncated subtraction)を提供する、全順序構造を備える可換モノイド。 * * 以下の法則を満たす。 - * - `x lteqv y` <=> `x + z = y` となる `z` が存在する。 - * - `x lteqv (y + z)` => `|-|(x, z) lteqv y` + * - `x lteqv y` <=> `x + z = y` となる `z` が存在する。 + * - `x lteqv (y + z)` => `|-|(x, z) lteqv y` */ trait OrderedMonus[A] extends Order[A] with CommutativeMonoid[A] { /** - * 切り捨て減算。 - * `x: A, y: A` について、 `z: A` = `|-|(x, y)` は - * `x <= y + z` となるような最小の `z` として定義される。 + * 切り捨て減算。 `x: A, y: A` について、 `z: A` = `|-|(x, y)` は `x <= y + z` となるような最小の `z` として定義される。 */ def |-|(x: A, y: A): A diff --git a/src/main/scala/com/github/unchama/generic/effect/ConcurrentExtra.scala b/src/main/scala/com/github/unchama/generic/effect/ConcurrentExtra.scala index 580d06fe5a..a7a5576702 100644 --- a/src/main/scala/com/github/unchama/generic/effect/ConcurrentExtra.scala +++ b/src/main/scala/com/github/unchama/generic/effect/ConcurrentExtra.scala @@ -9,8 +9,7 @@ object ConcurrentExtra { /** * `f` を並行的に開始し、 `f` の計算をキャンセルする計算を`f`に渡してから結果をawaitする計算を返す。 * - * `f` 内で渡された `CancelToken[F]` を実行した際の動作は未定義となる。 - * 実際、`f` 内のキャンセルはそれ自身の終了をブロックしながらawaitするため、 + * `f` 内で渡された `CancelToken[F]` を実行した際の動作は未定義となる。 実際、`f` 内のキャンセルはそれ自身の終了をブロックしながらawaitするため、 * ハングすることが予想される。 */ def withSelfCancellation[F[_]: Concurrent, A](f: CancelToken[F] => F[A]): F[A] = diff --git a/src/main/scala/com/github/unchama/generic/effect/EffectExtra.scala b/src/main/scala/com/github/unchama/generic/effect/EffectExtra.scala index 7afae4992b..664ff3f092 100644 --- a/src/main/scala/com/github/unchama/generic/effect/EffectExtra.scala +++ b/src/main/scala/com/github/unchama/generic/effect/EffectExtra.scala @@ -6,7 +6,7 @@ object EffectExtra { import cats.effect.implicits._ - def runAsyncAndForget[F[_] : Effect, G[_] : Sync, A](fa: F[A]): G[Unit] = + def runAsyncAndForget[F[_]: Effect, G[_]: Sync, A](fa: F[A]): G[Unit] = fa.runAsync(_ => IO.unit).runSync[G] } diff --git a/src/main/scala/com/github/unchama/generic/effect/ResourceScope.scala b/src/main/scala/com/github/unchama/generic/effect/ResourceScope.scala index f1daa89e5b..a631417048 100644 --- a/src/main/scala/com/github/unchama/generic/effect/ResourceScope.scala +++ b/src/main/scala/com/github/unchama/generic/effect/ResourceScope.scala @@ -9,28 +9,21 @@ import com.github.unchama.generic.{ContextCoercion, OptionTExtra} import scala.collection.immutable /** - * Spigotプラグインのようなユースケースでは、 - * ティックをまたぐような計算において任意のタイミングでサーバーが停止する可能性がある。 - * これにより、サーバー停止時の処理を適切に行うために + * Spigotプラグインのようなユースケースでは、 ティックをまたぐような計算において任意のタイミングでサーバーが停止する可能性がある。 これにより、サーバー停止時の処理を適切に行うために * プラグインインスタンス等の場所から「確保したリソースすべてに対する参照を持つ」必要性が発生するケースがある。 * 「あとで必ず破壊しなければいけない一時的に生成されたエンティティ」などはその最たる例である。 * * `ResourceScope`はこのような状況に対応するためのオブジェクトのtraitである。 * - * cats-effectで提供される代数データ型である`Resource[F, R]`は、 - * `R` 型のリソースの確保・解放の`F`による計算の組を扱いやすいようにまとめた構造である。 + * cats-effectで提供される代数データ型である`Resource[F, R]`は、 `R` 型のリソースの確保・解放の`F`による計算の組を扱いやすいようにまとめた構造である。 * `ResourceScope`オブジェクト`r`は、与えられたリソース `resource: Resource[F, R]` に対して、 - * 「`resource`により確保された資源を解放されるまで`r`の管理下に置く」ような - * 新たな `Resource` を `tracked` により生成する。 + * 「`resource`により確保された資源を解放されるまで`r`の管理下に置く」ような 新たな `Resource` を `tracked` により生成する。 * - * `release` によりスコープ内で使用している資源を強制的に解放することができる。 - * `release`は、資源を使用している `Fiber` に対してキャンセルのシグナルを送り、 + * `release` によりスコープ内で使用している資源を強制的に解放することができる。 `release`は、資源を使用している `Fiber` に対してキャンセルのシグナルを送り、 * 資源が解放されるのを待ち続ける。これにより、スコープ内の資源が解放されるのを待つことができる。 * - * `use` に渡した計算の中で `release` をした際の動作は未定義となる。 - * 実際、解放処理は資源の解放をawaitする、かつ資源を使用しているプログラムが - * 解放処理を要求すると、解放に必要なキャンセル処理を受け付けなくなるため、 - * ハングすることが想定される。 + * `use` に渡した計算の中で `release` をした際の動作は未定義となる。 実際、解放処理は資源の解放をawaitする、かつ資源を使用しているプログラムが + * 解放処理を要求すると、解放に必要なキャンセル処理を受け付けなくなるため、 ハングすることが想定される。 */ trait ResourceScope[ResourceUsageContext[_], DataAccessContext[_], ResourceHandler] { implicit val ResourceUsageContext: Monad[ResourceUsageContext] @@ -39,16 +32,18 @@ trait ResourceScope[ResourceUsageContext[_], DataAccessContext[_], ResourceHandl import cats.implicits._ /** - * 与えられた`resource`とそれを使用する関数`f`を引数に取る。 - * 確保した資源が`use`によって使われている最中にこのスコープ下に置くような計算を返す。 + * 与えられた`resource`とそれを使用する関数`f`を引数に取る。 確保した資源が`use`によって使われている最中にこのスコープ下に置くような計算を返す。 */ - def useTracked[R <: ResourceHandler, A](resource: Resource[ResourceUsageContext, R]) - (use: R => ResourceUsageContext[A]): ResourceUsageContext[A] + def useTracked[R <: ResourceHandler, A](resource: Resource[ResourceUsageContext, R])( + use: R => ResourceUsageContext[A] + ): ResourceUsageContext[A] /** * 「与えられたハンドラがこのスコープの管理下にあるならば解放する計算を、そうでなければ空の値を返す」計算を返す。 */ - def getCancelToken(handler: ResourceHandler): DataAccessContext[Option[CancelToken[ResourceUsageContext]]] + def getCancelToken( + handler: ResourceHandler + ): DataAccessContext[Option[CancelToken[ResourceUsageContext]]] /** * 確保されている資源の集合の計算 @@ -70,7 +65,9 @@ trait ResourceScope[ResourceUsageContext[_], DataAccessContext[_], ResourceHandl /** * 与えられたハンドラが管理下にあれば解放するような計算 */ - def getReleaseAction(handler: ResourceHandler): DataAccessContext[CancelToken[ResourceUsageContext]] = { + def getReleaseAction( + handler: ResourceHandler + ): DataAccessContext[CancelToken[ResourceUsageContext]] = { for { optionToken <- getCancelToken(handler) } yield { @@ -81,69 +78,74 @@ trait ResourceScope[ResourceUsageContext[_], DataAccessContext[_], ResourceHandl /** * 与えられたハンドラがこのスコープの管理下にあるかどうかを判定する計算 */ - def isTracked(handler: ResourceHandler): DataAccessContext[Boolean] = trackedHandlers.map(_.contains(handler)) + def isTracked(handler: ResourceHandler): DataAccessContext[Boolean] = + trackedHandlers.map(_.contains(handler)) } object ResourceScope { + /** * 新たな資源スコープを作成する。 * - * 参照透明性が無いためunsafe-接頭語がつけられている。 - * 実際、`unsafeCreate` を二回呼んだ時には異なるスコープが作成される。 + * 参照透明性が無いためunsafe-接頭語がつけられている。 実際、`unsafeCreate` を二回呼んだ時には異なるスコープが作成される。 * - * @tparam F リソースを扱う計算 - * @tparam R リソースハンドラの型 + * @tparam F + * リソースを扱う計算 + * @tparam R + * リソースハンドラの型 */ - def unsafeCreate[F[_] : Concurrent, G[_] : Sync : ContextCoercion[*[_], F], R]: ResourceScope[F, G, R] = { + def unsafeCreate[F[_]: Concurrent, G[_]: Sync: ContextCoercion[*[_], F], R] + : ResourceScope[F, G, R] = { new MultiDictResourceScope() } /** * 新たな資源スコープを作成する計算。 */ - def create[F[_] : Concurrent, G[_] : Sync : ContextCoercion[*[_], F], R]: F[ResourceScope[F, G, R]] = { + def create[F[_]: Concurrent, G[_]: Sync: ContextCoercion[*[_], F], R] + : F[ResourceScope[F, G, R]] = { Concurrent[F].delay(unsafeCreate[F, G, R]) } /** - * 新たな資源スコープを作成する。 - * 返される資源スコープ内では高々一つの資源しか確保されないことが保証される。 + * 新たな資源スコープを作成する。 返される資源スコープ内では高々一つの資源しか確保されないことが保証される。 * - * 参照透明性が無いためunsafe-接頭語がつけられている。 - * 実際、`unsafeCreateSingletonScope` を二回呼んだ時には異なるスコープが作成される。 + * 参照透明性が無いためunsafe-接頭語がつけられている。 実際、`unsafeCreateSingletonScope` を二回呼んだ時には異なるスコープが作成される。 * - * @tparam F リソースを扱う計算 - * @tparam R リソースハンドラの型 + * @tparam F + * リソースを扱う計算 + * @tparam R + * リソースハンドラの型 */ - def unsafeCreateSingletonScope[ - F[_] : Concurrent, - G[_] : Sync : ContextCoercion[*[_], F], - R - ]: SingleResourceScope[F, G, R] = new SingleResourceScope() + def unsafeCreateSingletonScope[F[_]: Concurrent, G[_]: Sync: ContextCoercion[*[_], F], R] + : SingleResourceScope[F, G, R] = new SingleResourceScope() /** * `ResourceScope` の標準的な実装。 */ - class MultiDictResourceScope[F[_], G[_], ResourceHandler] private[ResourceScope](implicit val ResourceUsageContext: Concurrent[F], - val DataAccessContext: Sync[G], - contextCoercion: ContextCoercion[G, F]) - extends ResourceScope[F, G, ResourceHandler] { + class MultiDictResourceScope[F[_], G[_], ResourceHandler] private[ResourceScope] ( + implicit val ResourceUsageContext: Concurrent[F], + val DataAccessContext: Sync[G], + contextCoercion: ContextCoercion[G, F] + ) extends ResourceScope[F, G, ResourceHandler] { /** * この`Map`の終域にある`CancelToken[F]`は、 - * - ハンドラを管理下から外し - * - ハンドラをリソースとして解放する - * 計算である。 + * - ハンドラを管理下から外し + * - ハンドラをリソースとして解放する 計算である。 * * ここで、管理下から外すというのは、単にこの`Map`からハンドラを取り除く処理である。 */ - private val handlerToCancelTokens: Ref[G, immutable.MultiDict[ResourceHandler, CancelToken[F]]] = + private val handlerToCancelTokens + : Ref[G, immutable.MultiDict[ResourceHandler, CancelToken[F]]] = Ref.unsafe(immutable.MultiDict.empty[ResourceHandler, CancelToken[F]]) import ResourceUsageContext._ import cats.implicits._ - override def useTracked[R <: ResourceHandler, A](resource: Resource[F, R])(use: R => F[A]): F[A] = { + override def useTracked[R <: ResourceHandler, A]( + resource: Resource[F, R] + )(use: R => F[A]): F[A] = { for { allocated <- resource.allocated (handler, releaseResource) = allocated @@ -153,7 +155,9 @@ object ResourceScope { val registerHandler = handlerToCancelTokens.update(_.add(handler, cancelToken)) val forgetUsage = handlerToCancelTokens.update(_.remove(handler, cancelToken)) - guarantee(ContextCoercion(registerHandler) >> use(handler))(ContextCoercion(forgetUsage) >> releaseResource) + guarantee(ContextCoercion(registerHandler) >> use(handler))( + ContextCoercion(forgetUsage) >> releaseResource + ) } } yield a } @@ -163,9 +167,7 @@ object ResourceScope { } override def getCancelToken(handler: ResourceHandler): G[Option[CancelToken[F]]] = - handlerToCancelTokens.get.map(dict => - dict.sets.get(handler).map(sequenceCancelToken) - ) + handlerToCancelTokens.get.map(dict => dict.sets.get(handler).map(sequenceCancelToken)) override val trackedHandlers: G[Set[ResourceHandler]] = handlerToCancelTokens.get.map(_.keySet.toSet) @@ -179,12 +181,10 @@ object ResourceScope { } } - class SingleResourceScope[ - F[_] : Concurrent, - G[_], - ResourceHandler - ] private[ResourceScope](implicit override val DataAccessContext: Sync[G], contextCoercion: ContextCoercion[G, F]) - extends ResourceScope[OptionT[F, *], G, ResourceHandler] { + class SingleResourceScope[F[_]: Concurrent, G[_], ResourceHandler] private[ResourceScope] ( + implicit override val DataAccessContext: Sync[G], + contextCoercion: ContextCoercion[G, F] + ) extends ResourceScope[OptionT[F, *], G, ResourceHandler] { type OptionF[a] = OptionT[F, a] @@ -196,24 +196,25 @@ object ResourceScope { * トップレベルのRefがリソースの「割り当て操作」に対応し、内側のRefが実際のリソースを持つ。 * * 具体的には、新しくリソースを作成したいとき、 - * - まずトップレベルのRefがNoneであることを確認してからSome(newRef)を入れ - * (ここでNoneではなかった場合リソース確保を失敗させる) + * - まずトップレベルのRefがNoneであることを確認してからSome(newRef)を入れ (ここでNoneではなかった場合リソース確保を失敗させる) * - それが成功したらリソース確保を実行し、 * - newRefの中に確保したリソースを入れる * * という手順を踏む。 */ - private val resourceSlot: Ref[G, Option[Ref[G, Option[(ResourceHandler, CancelToken[OptionF])]]]] = + private val resourceSlot + : Ref[G, Option[Ref[G, Option[(ResourceHandler, CancelToken[OptionF])]]]] = Ref.unsafe(None) import ContextCoercion._ import cats.implicits._ - override def useTracked[R <: ResourceHandler, A](resource: Resource[OptionF, R])(use: R => OptionF[A]): OptionF[A] = { + override def useTracked[R <: ResourceHandler, A]( + resource: Resource[OptionF, R] + )(use: R => OptionF[A]): OptionF[A] = { for { /** - * Refを作成し、それを `resourceSlot` に格納する試行をする。 - * 試行が成功して初めてリソースの確保を行ってから、確保したリソースでRefを埋める。 + * Refを作成し、それを `resourceSlot` に格納する試行をする。 試行が成功して初めてリソースの確保を行ってから、確保したリソースでRefを埋める。 */ newRef <- OptionT.liftF( Ref.of[G, Option[(ResourceHandler, CancelToken[OptionF])]](None).coerceTo[F] @@ -221,10 +222,12 @@ object ResourceScope { // `resourceSlot` が空で`newPromise` の格納が成功した場合のみSome(true)が結果となる promiseAllocation <- OptionT.liftF( - resourceSlot.tryModify { - case None => (Some(newRef), true) - case allocated@Some(_) => (allocated, false) - }.coerceTo[F] + resourceSlot + .tryModify { + case None => (Some(newRef), true) + case allocated @ Some(_) => (allocated, false) + } + .coerceTo[F] ) _ <- OptionTExtra.failIf[F](!promiseAllocation.contains(true)) @@ -233,14 +236,19 @@ object ResourceScope { // リソースの確保自体に失敗した場合は // `resourceSlot` を別のリソースの確保のために開ける。 - allocated <- resource.allocated(ResourceUsageContext).orElse(forgetUsage *> OptionT.none) + allocated <- resource + .allocated(ResourceUsageContext) + .orElse(forgetUsage *> OptionT.none) (handler, releaseResource) = allocated // use中に解放命令が入った時、useをキャンセルしforgetUsage >> releaseResourceを行うように a <- ConcurrentExtra.withSelfCancellation[OptionF, A] { cancelToken => - val registerHandler = OptionT.liftF(newRef.set(Some(handler, cancelToken)).coerceTo[F]) + val registerHandler = + OptionT.liftF(newRef.set(Some(handler, cancelToken)).coerceTo[F]) - ResourceUsageContext.guarantee(registerHandler >> use(handler))(forgetUsage >> releaseResource) + ResourceUsageContext.guarantee(registerHandler >> use(handler))( + forgetUsage >> releaseResource + ) } } yield a } @@ -263,15 +271,18 @@ object ResourceScope { override val trackedHandlers: G[Set[ResourceHandler]] = for { promiseOption <- resourceSlot.get tracked <- promiseOption match { - case Some(ref) => ref.get.map { - case Some((handler, _)) => Set(handler) // 確保済み - case None => Set.empty[ResourceHandler] // 確保が確定したが確保中 - } + case Some(ref) => + ref.get.map { + case Some((handler, _)) => Set(handler) // 確保済み + case None => Set.empty[ResourceHandler] // 確保が確定したが確保中 + } case None => Monad[G].pure(Set[ResourceHandler]()) // 確保予定のリソースが無い } } yield tracked - def useTrackedForSome[R <: ResourceHandler, A](resource: Resource[F, R])(f: R => F[A]): F[Option[A]] = + def useTrackedForSome[R <: ResourceHandler, A](resource: Resource[F, R])( + f: R => F[A] + ): F[Option[A]] = useTracked(resource.mapK(OptionT.liftK[F]))(f.andThen(OptionT.liftF(_))).value override lazy val getReleaseAllAction: G[CancelToken[OptionF]] = { diff --git a/src/main/scala/com/github/unchama/generic/effect/concurrent/AsymmetricSignallingRef.scala b/src/main/scala/com/github/unchama/generic/effect/concurrent/AsymmetricSignallingRef.scala index 8a425bd14c..a459a96293 100644 --- a/src/main/scala/com/github/unchama/generic/effect/concurrent/AsymmetricSignallingRef.scala +++ b/src/main/scala/com/github/unchama/generic/effect/concurrent/AsymmetricSignallingRef.scala @@ -14,15 +14,13 @@ import fs2.Stream /** * 更新が [[Stream]] により読め出せるような可変参照セル。 * - * [[fs2.concurrent.SignallingRef]] とほぼ同等だが、 - * 可変参照セルへの変更を行うコンテキストと更新を読みだすコンテキストが区別されている。 + * [[fs2.concurrent.SignallingRef]] とほぼ同等だが、 可変参照セルへの変更を行うコンテキストと更新を読みだすコンテキストが区別されている。 * したがって、このtraitは [[fs2.concurrent.SignallingRef]] を一般化している。 */ abstract class AsymmetricSignallingRef[G[_], F[_], A] extends Ref[G, A] { /** - * 更新値へのsubscriptionを [[Resource]] として表現したもの。 - * subscriptionが有効になった後に [[Resource]] として利用可能になり、 + * 更新値へのsubscriptionを [[Resource]] として表現したもの。 subscriptionが有効になった後に [[Resource]] として利用可能になり、 * 利用が終了した後に自動的にunsubscribeされる。 * * [[Resource]] として利用可能である間は更新が行われた順に更新値が全て得られることが保証される。 @@ -38,22 +36,21 @@ object AsymmetricSignallingRef { * * Copyright (c) 2013 Functional Streams for Scala * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. */ import cats.implicits._ @@ -61,81 +58,75 @@ object AsymmetricSignallingRef { /** * 指定された値で初期化された[[AsymmetricSignallingRef]]を作成する作用。 */ - def apply[ - G[_] : Sync, - F[_] : ConcurrentEffect : ContextCoercion[G, *[_]], - A - ](initial: A): G[AsymmetricSignallingRef[G, F, A]] = in[G, G, F, A](initial) + def apply[G[_]: Sync, F[_]: ConcurrentEffect: ContextCoercion[G, *[_]], A]( + initial: A + ): G[AsymmetricSignallingRef[G, F, A]] = in[G, G, F, A](initial) /** * 指定された値で初期化された[[AsymmetricSignallingRef]]を作成する作用。 * * [[apply]] とほぼ等価であるが、状態の作成を別の作用型の中で行う。 */ - def in[ - H[_] : Sync, - G[_] : Sync, - F[_] : ConcurrentEffect : ContextCoercion[G, *[_]], - A - ](initial: A): H[AsymmetricSignallingRef[G, F, A]] = { + def in[H[_]: Sync, G[_]: Sync, F[_]: ConcurrentEffect: ContextCoercion[G, *[_]], A]( + initial: A + ): H[AsymmetricSignallingRef[G, F, A]] = { val initialState = TimeStamped(new Token, new Token, initial) Applicative[H].map2( Ref.in[H, G, TimeStamped[A]](initialState), Fs3Topic.in[H, F, TimeStamped[A]](initialState) - ) { case (ref, topic) => - new AsymmetricSignallingRefImpl[G, F, A](ref, topic) + ) { + case (ref, topic) => + new AsymmetricSignallingRefImpl[G, F, A](ref, topic) } } - private final class AsymmetricSignallingRefImpl[ - G[_], - F[_], - A - ](state: Ref[G, TimeStamped[A]], changeTopic: Fs3Topic[F, TimeStamped[A]]) - (implicit G: Sync[G], F: ConcurrentEffect[F], GToF: ContextCoercion[G, F]) - extends AsymmetricSignallingRef[G, F, A] { + private final class AsymmetricSignallingRefImpl[G[_], F[_], A]( + state: Ref[G, TimeStamped[A]], + changeTopic: Fs3Topic[F, TimeStamped[A]] + )(implicit G: Sync[G], F: ConcurrentEffect[F], GToF: ContextCoercion[G, F]) + extends AsymmetricSignallingRef[G, F, A] { private val topicQueueSize = 10 override val valuesAwait: Resource[F, Stream[F, A]] = - changeTopic - .subscribeAwait(topicQueueSize) - .flatMap { subscription => - Resource.liftF { + changeTopic.subscribeAwait(topicQueueSize).flatMap { subscription => + Resource + .liftF { state.get.map { currentValue => - subscription.through(ReorderingPipe.withInitialToken[F, A](currentValue.nextStamp)) + subscription + .through(ReorderingPipe.withInitialToken[F, A](currentValue.nextStamp)) } - }.mapK(GToF) - } + } + .mapK(GToF) + } override def get: G[A] = state.get.map(_.value) override def set(a: A): G[Unit] = update(_ => a) /** - * 状態更新関数 `A => (A, B)` を基に、 - * 内部状態の置き換え `InternalState[A] => InternalState[A]` と + * 状態更新関数 `A => (A, B)` を基に、 内部状態の置き換え `InternalState[A] => InternalState[A]` と * それに伴って必要である通知作用 `A => G[B]` の積を作成する。 */ private def updateAndNotify[B](f: A => (A, B)): TimeStamped[A] => (TimeStamped[A], G[B]) = { case TimeStamped(_, nextStamp, a) => val (newA, result) = f(a) val newATimeStamped = TimeStamped(nextStamp, new Token, newA) - val action = EffectExtra.runAsyncAndForget[F, G, Unit](changeTopic.publish1(newATimeStamped).void) + val action = + EffectExtra.runAsyncAndForget[F, G, Unit](changeTopic.publish1(newATimeStamped).void) newATimeStamped -> action.as(result) } override def access: G[(A, A => G[Boolean])] = - state.access.map { case (snapshot, set) => - val setter = { (newValue: A) => - val (newState, notify) = updateAndNotify(_ => (newValue, ()))(snapshot) - set(newState).flatTap { succeeded => - notify.whenA(succeeded) + state.access.map { + case (snapshot, set) => + val setter = { (newValue: A) => + val (newState, notify) = updateAndNotify(_ => (newValue, ()))(snapshot) + set(newState).flatTap { succeeded => notify.whenA(succeeded) } } - } - (snapshot.value, setter) + (snapshot.value, setter) } override def tryUpdate(f: A => A): G[Boolean] = @@ -143,7 +134,7 @@ object AsymmetricSignallingRef { override def tryModify[B](f: A => (A, B)): G[Option[B]] = state.tryModify(updateAndNotify(f)).flatMap { - case None => G.pure(None) + case None => G.pure(None) case Some(action) => G.map(action)(Some(_)) } diff --git a/src/main/scala/com/github/unchama/generic/effect/concurrent/AsymmetricTryableDeferred.scala b/src/main/scala/com/github/unchama/generic/effect/concurrent/AsymmetricTryableDeferred.scala index 49e4639f1b..17373b4c1b 100644 --- a/src/main/scala/com/github/unchama/generic/effect/concurrent/AsymmetricTryableDeferred.scala +++ b/src/main/scala/com/github/unchama/generic/effect/concurrent/AsymmetricTryableDeferred.scala @@ -1,35 +1,35 @@ package com.github.unchama.generic.effect.concurrent -import java.util.concurrent.atomic.AtomicReference - import cats.effect.concurrent.Deferred import cats.effect.{Concurrent, Sync} +import java.util.concurrent.atomic.AtomicReference import scala.annotation.tailrec import scala.collection.immutable.LongMap /** * A [[Deferred]] to which one can query for the "current value". * - * While [[cats.effect.concurrent.TryableDeferred]] serves for this purpose, it restricts - * the `tryGet` operation to the same context the promise is being completed. - * This turns out to be too restrictive in some cases, especially when we want to immediately know - * if the promise is completed or not. + * While [[cats.effect.concurrent.TryableDeferred]] serves for this purpose, it restricts the + * `tryGet` operation to the same context the promise is being completed. This turns out to be + * too restrictive in some cases, especially when we want to immediately know if the promise is + * completed or not. * - * [[AsymmetricTryableDeferred]] is similar to [[cats.effect.concurrent.TryableDeferred]] in spirit, - * but allows one to query for the current value in a more relaxed context. + * [[AsymmetricTryableDeferred]] is similar to [[cats.effect.concurrent.TryableDeferred]] in + * spirit, but allows one to query for the current value in a more relaxed context. */ trait AsymmetricTryableDeferred[F[_], A] extends Deferred[F, A] { - def tryGet[G[_] : Sync]: G[Option[A]] + def tryGet[G[_]: Sync]: G[Option[A]] } object AsymmetricTryableDeferred { - def concurrent[F[_] : Concurrent, A]: F[AsymmetricTryableDeferred[F, A]] = concurrentIn[F, F, A] + def concurrent[F[_]: Concurrent, A]: F[AsymmetricTryableDeferred[F, A]] = + concurrentIn[F, F, A] - def concurrentIn[F[_] : Concurrent, G[_] : Sync, A]: G[AsymmetricTryableDeferred[F, A]] = + def concurrentIn[F[_]: Concurrent, G[_]: Sync, A]: G[AsymmetricTryableDeferred[F, A]] = Sync[G].delay { new ConcurrentAsymmetricDeferred(new AtomicReference(State.Unset(LinkedMap.empty))) } @@ -52,28 +52,43 @@ object AsymmetricTryableDeferred { * limitations under the License. */ - private[this] class LinkedMap[K, +V](val entries: Map[K, (V, Long)], - private[this] val insertionOrder: LongMap[K], - private[this] val nextId: Long) { - /** Returns a new map with the supplied key/value added. */ + private[this] class LinkedMap[K, +V]( + val entries: Map[K, (V, Long)], + private[this] val insertionOrder: LongMap[K], + private[this] val nextId: Long + ) { + + /** + * Returns a new map with the supplied key/value added. + */ def updated[V2 >: V](k: K, v: V2): LinkedMap[K, V2] = { - val insertionOrderOldRemoved = entries.get(k).fold(insertionOrder) { case (_, id) => insertionOrder - id } - new LinkedMap(entries.updated(k, (v, nextId)), insertionOrderOldRemoved.updated(nextId, k), nextId + 1) + val insertionOrderOldRemoved = + entries.get(k).fold(insertionOrder) { case (_, id) => insertionOrder - id } + new LinkedMap( + entries.updated(k, (v, nextId)), + insertionOrderOldRemoved.updated(nextId, k), + nextId + 1 + ) } - /** Removes the element at the specified key. */ + /** + * Removes the element at the specified key. + */ def -(k: K): LinkedMap[K, V] = - new LinkedMap(entries - k, - entries - .get(k) - .map { case (_, id) => insertionOrder - id } - .getOrElse(insertionOrder), - nextId) - - /** The keys in this map, in the order they were added. */ + new LinkedMap( + entries - k, + entries.get(k).map { case (_, id) => insertionOrder - id }.getOrElse(insertionOrder), + nextId + ) + + /** + * The keys in this map, in the order they were added. + */ def keys: Iterable[K] = insertionOrder.values - /** The values in this map, in the order they were added. */ + /** + * The values in this map, in the order they were added. + */ def values: Iterable[V] = keys.flatMap(k => entries.get(k).toList.map(_._1)) override def toString: String = @@ -100,8 +115,9 @@ object AsymmetricTryableDeferred { } - final private class ConcurrentAsymmetricDeferred[F[_], A](ref: AtomicReference[State[A]])(implicit F: Concurrent[F]) - extends AsymmetricTryableDeferred[F, A] { + final private class ConcurrentAsymmetricDeferred[F[_], A](ref: AtomicReference[State[A]])( + implicit F: Concurrent[F] + ) extends AsymmetricTryableDeferred[F, A] { def get: F[A] = F.suspend { ref.get match { @@ -115,7 +131,7 @@ object AsymmetricTryableDeferred { def unregister(): Unit = ref.get match { case State.Set(_) => () - case s@State.Unset(waiting) => + case s @ State.Unset(waiting) => val updated = State.Unset(waiting - id) if (ref.compareAndSet(s, updated)) () else unregister() @@ -126,10 +142,10 @@ object AsymmetricTryableDeferred { } } - def tryGet[G[_] : Sync]: G[Option[A]] = + def tryGet[G[_]: Sync]: G[Option[A]] = Sync[G].delay { ref.get match { - case State.Set(a) => Some(a) + case State.Set(a) => Some(a) case State.Unset(_) => None } } @@ -141,7 +157,7 @@ object AsymmetricTryableDeferred { def register(): Option[A] = ref.get match { case State.Set(a) => Some(a) - case s@State.Unset(waiting) => + case s @ State.Unset(waiting) => val updated = State.Unset(waiting.updated(id, (a: A) => cb(Right(a)))) if (ref.compareAndSet(s, updated)) None else register() @@ -158,9 +174,11 @@ object AsymmetricTryableDeferred { private def unsafeComplete(a: A): F[Unit] = ref.get match { case State.Set(_) => - throw new IllegalStateException("Attempting to complete a Deferred that has already been completed") + throw new IllegalStateException( + "Attempting to complete a Deferred that has already been completed" + ) - case s@State.Unset(_) => + case s @ State.Unset(_) => if (ref.compareAndSet(s, State.Set(a))) { val list = s.waiting.values if (list.nonEmpty) diff --git a/src/main/scala/com/github/unchama/generic/effect/concurrent/AsymmetricTryableFiber.scala b/src/main/scala/com/github/unchama/generic/effect/concurrent/AsymmetricTryableFiber.scala index f5322cb45d..dceaefdb7a 100644 --- a/src/main/scala/com/github/unchama/generic/effect/concurrent/AsymmetricTryableFiber.scala +++ b/src/main/scala/com/github/unchama/generic/effect/concurrent/AsymmetricTryableFiber.scala @@ -4,27 +4,30 @@ import cats.effect.{CancelToken, Concurrent, ExitCase, Fiber, Sync} import cats.{Functor, Monad} /** - * We can think of a [[cats.effect.concurrent.Deferred]] as a "mutable" Promise to which - * read and write operations are synchronized. Then [[cats.effect.concurrent.TryableDeferred]] is a - * [[cats.effect.concurrent.Deferred]] which can immediately tell whether it has been completed or not. + * We can think of a [[cats.effect.concurrent.Deferred]] as a "mutable" Promise to which read + * and write operations are synchronized. Then [[cats.effect.concurrent.TryableDeferred]] is a + * [[cats.effect.concurrent.Deferred]] which can immediately tell whether it has been completed + * or not. * - * Analogously then, [[Fiber]] is a read-only promise and we should be able to describe - * a [[Fiber]] which can tell its completion status. [[AsymmetricTryableFiber]] serves this purpose. + * Analogously then, [[Fiber]] is a read-only promise and we should be able to describe a + * [[Fiber]] which can tell its completion status. [[AsymmetricTryableFiber]] serves this + * purpose. * - * @tparam F the context in which the fiber is run + * @tparam F + * the context in which the fiber is run */ -abstract class AsymmetricTryableFiber[F[_] : Functor, A] extends Fiber[F, A] { +abstract class AsymmetricTryableFiber[F[_]: Functor, A] extends Fiber[F, A] { import AsymmetricTryableFiber._ /** * Obtains the current status of the `Fiber`. */ - def getCurrentStatus[G[_] : Sync]: G[FiberStatus[A]] + def getCurrentStatus[G[_]: Sync]: G[FiberStatus[A]] /** - * A computation that cancels the fiber if it is still running, - * returning whether the cancellation happened or not. + * A computation that cancels the fiber if it is still running, returning whether the + * cancellation happened or not. */ def cancelIfRunning: F[Boolean] @@ -37,19 +40,19 @@ abstract class AsymmetricTryableFiber[F[_] : Functor, A] extends Fiber[F, A] { override def cancel: CancelToken[F] = cancelIfRunning.as(()) - def isRunning[G[_] : Sync]: G[Boolean] = getCurrentStatus[G] map { + def isRunning[G[_]: Sync]: G[Boolean] = getCurrentStatus[G] map { case Running => true - case _ => false + case _ => false } - def isNotRunning[G[_] : Sync]: G[Boolean] = isRunning map (!_) + def isNotRunning[G[_]: Sync]: G[Boolean] = isRunning map (!_) - def isComplete[G[_] : Sync]: G[Boolean] = getCurrentStatus[G] map { + def isComplete[G[_]: Sync]: G[Boolean] = getCurrentStatus[G] map { case Completed(_) => true - case _ => false + case _ => false } - def isIncomplete[G[_] : Sync]: G[Boolean] = isComplete map (!_) + def isIncomplete[G[_]: Sync]: G[Boolean] = isComplete map (!_) } object AsymmetricTryableFiber { @@ -68,7 +71,8 @@ object AsymmetricTryableFiber { /** * Start concurrent execution of the source suspended in `F`. * - * @return a [[AsymmetricTryableFiber]] which can be used to cancel, join or tryJoin the result + * @return + * a [[AsymmetricTryableFiber]] which can be used to cancel, join or tryJoin the result */ def start[F[_], A](fa: F[A])(implicit F: Concurrent[F]): F[AsymmetricTryableFiber[F, A]] = { import cats.effect.implicits._ @@ -78,9 +82,7 @@ object AsymmetricTryableFiber { completionPromise <- AsymmetricTryableDeferred.concurrent[F, FiberResult[A]] fiber <- F.start { { - fa >>= { a => - completionPromise.complete(Completed(a)).as(a) - } + fa >>= { a => completionPromise.complete(Completed(a)).as(a) } }.guaranteeCase { case ExitCase.Error(e) => // when the action fa threw an error @@ -98,10 +100,10 @@ object AsymmetricTryableFiber { } } - override def getCurrentStatus[G[_] : Sync]: G[FiberStatus[A]] = { + override def getCurrentStatus[G[_]: Sync]: G[FiberStatus[A]] = { completionPromise.tryGet[G] map { case Some(value) => value - case None => Running + case None => Running } } @@ -116,7 +118,7 @@ object AsymmetricTryableFiber { */ def completed[F[_], A](a: A)(implicit F: Monad[F]): AsymmetricTryableFiber[F, A] = { new AsymmetricTryableFiber[F, A] { - override def getCurrentStatus[G[_] : Sync]: G[FiberStatus[A]] = Sync[G].pure(Completed(a)) + override def getCurrentStatus[G[_]: Sync]: G[FiberStatus[A]] = Sync[G].pure(Completed(a)) override def cancelIfRunning: F[Boolean] = F.pure(false) diff --git a/src/main/scala/com/github/unchama/generic/effect/concurrent/Mutex.scala b/src/main/scala/com/github/unchama/generic/effect/concurrent/Mutex.scala index 9363375f2a..6a3b1d7d67 100644 --- a/src/main/scala/com/github/unchama/generic/effect/concurrent/Mutex.scala +++ b/src/main/scala/com/github/unchama/generic/effect/concurrent/Mutex.scala @@ -6,32 +6,29 @@ import com.github.unchama.generic.ContextCoercion final class Mutex[ MutexContext[_], - ReadContext[_] : ContextCoercion[*[_], MutexContext], + ReadContext[_]: ContextCoercion[*[_], MutexContext], A -] private(mVar: MVar[MutexContext, A], previous: Ref[ReadContext, A]) - (implicit fBracket: Bracket[MutexContext, Throwable]) { +] private (mVar: MVar[MutexContext, A], previous: Ref[ReadContext, A])( + implicit fBracket: Bracket[MutexContext, Throwable] +) { import ContextCoercion._ import cats.implicits._ /** - * - 変数のロックを取得 - * - `use`により新たな値を計算 - * - 変数を更新 - * したうえで、`use` の計算結果を反映する `B` を返却するような計算。 + * - 変数のロックを取得 + * - `use`により新たな値を計算 + * - 変数を更新 したうえで、`use` の計算結果を反映する `B` を返却するような計算。 * - * `use` が失敗した場合、外側の計算も失敗するが、変数は元の状態へ戻ることが保証される。 - * また、複数の並行するプロセスがこの計算を行う場合、 - * ロック取得から変数更新の間にあるプロセスはどの時点でもただ一つしか存在しないことが保証され、 - * したがってロックを取得できるまで外側の計算が (意味論的な) ブロッキングを行うことになる。 + * `use` が失敗した場合、外側の計算も失敗するが、変数は元の状態へ戻ることが保証される。 また、複数の並行するプロセスがこの計算を行う場合、 + * ロック取得から変数更新の間にあるプロセスはどの時点でもただ一つしか存在しないことが保証され、 したがってロックを取得できるまで外側の計算が (意味論的な) + * ブロッキングを行うことになる。 */ def lockAndModify[B](use: A => MutexContext[(A, B)]): MutexContext[B] = Bracket[MutexContext, Throwable].bracketCase(mVar.take) { a => - use(a) >>= { case (newA, b) => - mVar - .put(newA) - .flatTap(_ => previous.set(newA).coerceTo[MutexContext]) - .as(b) + use(a) >>= { + case (newA, b) => + mVar.put(newA).flatTap(_ => previous.set(newA).coerceTo[MutexContext]).as(b) } } { case (_, ExitCase.Completed) => @@ -44,26 +41,19 @@ final class Mutex[ } /** - * - 変数のロックを取得 - * - `use`により新たな値を計算 - * - 変数を更新 - * し、新しいAの値を返す計算。 + * - 変数のロックを取得 + * - `use`により新たな値を計算 + * - 変数を更新 し、新しいAの値を返す計算。 * - * `use` が失敗した場合、外側の計算も失敗するが、変数は元の状態へ戻ることが保証される。 - * また、複数の並行するプロセスがこの計算を行う場合、 - * ロック取得から変数更新の間にあるプロセスはどの時点でもただ一つしか存在しないことが保証され、 - * したがってロックを取得できるまで外側の計算が (意味論的な) ブロッキングを行うことになる。 + * `use` が失敗した場合、外側の計算も失敗するが、変数は元の状態へ戻ることが保証される。 また、複数の並行するプロセスがこの計算を行う場合、 + * ロック取得から変数更新の間にあるプロセスはどの時点でもただ一つしか存在しないことが保証され、 したがってロックを取得できるまで外側の計算が (意味論的な) + * ブロッキングを行うことになる。 */ def lockAndUpdate(use: A => MutexContext[A]): MutexContext[A] = - lockAndModify { a => - use(a).map { newA => - (newA, newA) - } - } + lockAndModify { a => use(a).map { newA => (newA, newA) } } /** - * 最後にsetされた値を排他制御を無視して取得する計算。 - * ロックを取得する必要があるケースでは [[lockAndModify]] を使用すること。 + * 最後にsetされた値を排他制御を無視して取得する計算。 ロックを取得する必要があるケースでは [[lockAndModify]] を使用すること。 */ val readLatest: ReadContext[A] = previous.get } @@ -72,11 +62,9 @@ object Mutex { import cats.implicits._ - def of[ - F[_] : Concurrent, - G[_] : Sync : ContextCoercion[*[_], F], - A - ](initial: A): G[Mutex[F, G, A]] = { + def of[F[_]: Concurrent, G[_]: Sync: ContextCoercion[*[_], F], A]( + initial: A + ): G[Mutex[F, G, A]] = { for { ref <- Ref.of[G, A](initial) mVar <- MVar.in[G, F, A](initial) diff --git a/src/main/scala/com/github/unchama/generic/effect/concurrent/RecoveringSemaphore.scala b/src/main/scala/com/github/unchama/generic/effect/concurrent/RecoveringSemaphore.scala index 011e931c7c..0a735635d1 100644 --- a/src/main/scala/com/github/unchama/generic/effect/concurrent/RecoveringSemaphore.scala +++ b/src/main/scala/com/github/unchama/generic/effect/concurrent/RecoveringSemaphore.scala @@ -9,7 +9,7 @@ import scala.concurrent.duration.FiniteDuration /** * 一定時間をおいて利用可能になるようなセマフォ。 */ -final class RecoveringSemaphore[F[_] : Timer : Concurrent] private(semaphore: Semaphore[F]) { +final class RecoveringSemaphore[F[_]: Timer: Concurrent] private (semaphore: Semaphore[F]) { import cats.effect.implicits._ import cats.implicits._ @@ -23,9 +23,11 @@ final class RecoveringSemaphore[F[_] : Timer : Concurrent] private(semaphore: Se semaphore.tryAcquire.flatMap { acquired => if (acquired) - Bracket[F, Throwable].guarantee(action) { - releaseProgram.start.as(()) - }.as(()) + Bracket[F, Throwable] + .guarantee(action) { + releaseProgram.start.as(()) + } + .as(()) else Monad[F].unit } @@ -36,7 +38,7 @@ object RecoveringSemaphore { import cats.implicits._ - def newIn[G[_] : Sync, F[_] : Concurrent : Timer]: G[RecoveringSemaphore[F]] = + def newIn[G[_]: Sync, F[_]: Concurrent: Timer]: G[RecoveringSemaphore[F]] = Semaphore.in[G, F](1).map(new RecoveringSemaphore(_)) } diff --git a/src/main/scala/com/github/unchama/generic/effect/concurrent/SessionMutex.scala b/src/main/scala/com/github/unchama/generic/effect/concurrent/SessionMutex.scala index 17a5b1c2ea..b175a063b9 100644 --- a/src/main/scala/com/github/unchama/generic/effect/concurrent/SessionMutex.scala +++ b/src/main/scala/com/github/unchama/generic/effect/concurrent/SessionMutex.scala @@ -4,14 +4,13 @@ import cats.Monad import cats.effect.{Concurrent, Sync} import com.github.unchama.generic.ContextCoercion -class SessionMutex[F[_] : Monad, G[_]] private(underlying: Mutex[F, G, TryableFiber[F, Unit]]) { +class SessionMutex[F[_]: Monad, G[_]] private (underlying: Mutex[F, G, TryableFiber[F, Unit]]) { import cats.implicits._ /** - * - 実行中のFiberがあれば停止し、Mutexの中身を停止したFiberで上書きしfalseを返す - * - そうでなければ、Mutexの中身を `startNewFiber` により提供されるFiberにて上書きしtrueを返す - * ような計算を返す。 + * - 実行中のFiberがあれば停止し、Mutexの中身を停止したFiberで上書きしfalseを返す + * - そうでなければ、Mutexの中身を `startNewFiber` により提供されるFiberにて上書きしtrueを返す ような計算を返す。 */ def flipState(startNewFiber: F[TryableFiber[F, Unit]]): F[Boolean] = underlying.lockAndModify { fiber => @@ -23,9 +22,8 @@ class SessionMutex[F[_] : Monad, G[_]] private(underlying: Mutex[F, G, TryableFi } /** - * - 実行中のFiberを持っていればそれを止めtrueを返し - * - そうでなければfalseを返す - * ようなIOを返す + * - 実行中のFiberを持っていればそれを止めtrueを返し + * - そうでなければfalseを返す ようなIOを返す */ def stopAnyFiber: F[Boolean] = underlying.lockAndModify { fiber => @@ -39,7 +37,7 @@ object SessionMutex { import cats.implicits._ - def newIn[F[_] : Concurrent, G[_] : Sync : ContextCoercion[*[_], F]]: G[SessionMutex[F, G]] = + def newIn[F[_]: Concurrent, G[_]: Sync: ContextCoercion[*[_], F]]: G[SessionMutex[F, G]] = for { mutex <- Mutex.of[F, G, TryableFiber[F, Unit]](TryableFiber.unit[F]) } yield new SessionMutex(mutex) diff --git a/src/main/scala/com/github/unchama/generic/effect/concurrent/TryableFiber.scala b/src/main/scala/com/github/unchama/generic/effect/concurrent/TryableFiber.scala index e903e6093c..b2894c6a96 100644 --- a/src/main/scala/com/github/unchama/generic/effect/concurrent/TryableFiber.scala +++ b/src/main/scala/com/github/unchama/generic/effect/concurrent/TryableFiber.scala @@ -5,12 +5,13 @@ import cats.effect.{CancelToken, Concurrent, Fiber} import cats.{FlatMap, Monad} /** - * We can think of a [[cats.effect.concurrent.Deferred]] as a "mutable" Promise to which - * read and write operations are synchronized. Then [[cats.effect.concurrent.TryableDeferred]] is a - * [[cats.effect.concurrent.Deferred]] which can immediately tell whether it has been completed or not. + * We can think of a [[cats.effect.concurrent.Deferred]] as a "mutable" Promise to which read + * and write operations are synchronized. Then [[cats.effect.concurrent.TryableDeferred]] is a + * [[cats.effect.concurrent.Deferred]] which can immediately tell whether it has been completed + * or not. * - * Analogously then, [[Fiber]] is a read-only promise and we should be able to describe - * a [[Fiber]] which can tell its completion status. [[TryableFiber]] serves this purpose. + * Analogously then, [[Fiber]] is a read-only promise and we should be able to describe a + * [[Fiber]] which can tell its completion status. [[TryableFiber]] serves this purpose. */ trait TryableFiber[F[_], A] extends Fiber[F, A] { implicit val fFMap: FlatMap[F] @@ -27,8 +28,8 @@ trait TryableFiber[F[_], A] extends Fiber[F, A] { def isIncomplete: F[Boolean] = isComplete map (!_) /** - * A computation that cancels the fiber if it is not complete, - * returning whether the cancellation happened or not. + * A computation that cancels the fiber if it is not complete, returning whether the + * cancellation happened or not. */ def cancelIfIncomplete: F[Boolean] = { // cancellation does not alter completion status; @@ -39,10 +40,12 @@ trait TryableFiber[F[_], A] extends Fiber[F, A] { } object TryableFiber { + /** * Start concurrent execution of the source suspended in `F`. * - * @return a [[TryableFiber]] which can be used to cancel, join or tryJoin the result + * @return + * a [[TryableFiber]] which can be used to cancel, join or tryJoin the result */ def start[F[_], A](fa: F[A])(implicit fConc: Concurrent[F]): F[TryableFiber[F, A]] = { import cats.implicits._ diff --git a/src/main/scala/com/github/unchama/generic/effect/stream/ReorderingPipe.scala b/src/main/scala/com/github/unchama/generic/effect/stream/ReorderingPipe.scala index bf71fea41a..42018781da 100644 --- a/src/main/scala/com/github/unchama/generic/effect/stream/ReorderingPipe.scala +++ b/src/main/scala/com/github/unchama/generic/effect/stream/ReorderingPipe.scala @@ -1,7 +1,7 @@ package com.github.unchama.generic.effect.stream import com.github.unchama.generic.Token -import fs2.{Chunk, Pipe, Stream} +import fs2.{Chunk, Pipe} import scala.annotation.tailrec @@ -10,30 +10,40 @@ object ReorderingPipe { /** * シーケンスされたタイムスタンプ付きの値。 * - * @param currentStamp この値のタイムスタンプ - * @param nextStamp 次の値のタイムスタンプ + * @param currentStamp + * この値のタイムスタンプ + * @param nextStamp + * 次の値のタイムスタンプ */ case class TimeStamped[+A](currentStamp: Token, nextStamp: Token, value: A) /** * ReorderingPipeの内部状態。 * - * @param nextToken 次の値のタイムスタンプ - * @param waitMap [[TimeStamped.currentStamp]]をキーに、パイプにすでに到着した値とその次のタイムスタンプを保持する [[Map]] + * @param nextToken + * 次の値のタイムスタンプ + * @param waitMap + * [[TimeStamped.currentStamp]]をキーに、パイプにすでに到着した値とその次のタイムスタンプを保持する [[Map]] */ private case class WaitMap[A](nextToken: Token, waitMap: Map[Token, (A, Token)]) { def flushWith(nextChunk: Chunk[TimeStamped[A]]): (WaitMap[A], Chunk[A]) = { - @tailrec def go(map: Map[Token, (A, Token)], next: Token, accum: Vector[A]): (WaitMap[A], Chunk[A]) = { + @tailrec def go( + map: Map[Token, (A, Token)], + next: Token, + accum: Vector[A] + ): (WaitMap[A], Chunk[A]) = { map.get(next) match { case Some((a, newNext)) => go(map.removed(next), newNext, accum.appended(a)) - case None => (WaitMap(next, map), Chunk.vector(accum)) + case None => (WaitMap(next, map), Chunk.vector(accum)) } } val combinedMap: Map[Token, (A, Token)] = Map.from { waitMap.toVector.appendedAll { - nextChunk.toVector.map { case TimeStamped(stamp, nextStamp, value) => stamp -> (value, nextStamp) } + nextChunk.toVector.map { + case TimeStamped(stamp, nextStamp, value) => stamp -> (value, nextStamp) + } } } @@ -46,10 +56,12 @@ object ReorderingPipe { * * `token` よりもタイムスタンプが古い要素は返されるストリームに出力されない。 * - * @param token 出力 [[Stream]] の最初の要素となることを期待される入力が持つタイムスタンプ + * @param token + * 出力 [[Stream]] の最初の要素となることを期待される入力が持つタイムスタンプ */ def withInitialToken[F[_], A](token: Token): Pipe[F, TimeStamped[A], A] = - _.scanChunks(WaitMap[A](token, Map.empty)) { case (waitMap, nextChunk) => - waitMap.flushWith(nextChunk) + _.scanChunks(WaitMap[A](token, Map.empty)) { + case (waitMap, nextChunk) => + waitMap.flushWith(nextChunk) } } diff --git a/src/main/scala/com/github/unchama/generic/effect/stream/StreamExtra.scala b/src/main/scala/com/github/unchama/generic/effect/stream/StreamExtra.scala index c02789566f..343adf61e6 100644 --- a/src/main/scala/com/github/unchama/generic/effect/stream/StreamExtra.scala +++ b/src/main/scala/com/github/unchama/generic/effect/stream/StreamExtra.scala @@ -31,26 +31,31 @@ object StreamExtra { } /** - * 与えられた [[Stream]] の最初の空でないチャンクと、その後続の [[Stream]] を高々一度だけ流す [[Stream]] を作成する。 - * 与えられた [[Stream]] が空だった場合、空の [[Stream]] が返る。 + * 与えられた [[Stream]] の最初の空でないチャンクと、その後続の [[Stream]] を高々一度だけ流す [[Stream]] を作成する。 与えられた + * [[Stream]] が空だった場合、空の [[Stream]] が返る。 */ def uncons[F[_], O](stream: Stream[F, O]): Stream[F, (Chunk[O], Stream[F, O])] = - stream.pull.unconsNonEmpty.flatMap { - case Some((head, tail)) => Pull.output1(head, tail) - case None => Pull.done - }.stream + stream + .pull + .unconsNonEmpty + .flatMap { + case Some((head, tail)) => Pull.output1(head, tail) + case None => Pull.done + } + .stream /** * 与えられた `stream` に対してfoldの操作を行う。 * * 結果として返されるストリームは、 - * - 内部状態 [[B]] を持つ。 `initial` から開始し、 `stream` から値を受け取る度に `f` で内部状態を更新する。 - * - 離散的なストリームである `flushSignal` が出力するのと同じタイミングで内部状態を出力し、 - * 内部状態を `initial` へと戻す + * - 内部状態 [[B]] を持つ。 `initial` から開始し、 `stream` から値を受け取る度に `f` で内部状態を更新する。 + * - 離散的なストリームである `flushSignal` が出力するのと同じタイミングで内部状態を出力し、 内部状態を `initial` へと戻す */ - def foldGate[F[_] : Concurrent, A, B, U](stream: Stream[F, A], - flushSignal: Stream[F, U], - initial: B)(f: B => A => B): Stream[F, B] = { + def foldGate[F[_]: Concurrent, A, B, U]( + stream: Stream[F, A], + flushSignal: Stream[F, U], + initial: B + )(f: B => A => B): Stream[F, B] = { Stream.force { Ref[F].of(initial).map { ref => flushSignal @@ -63,28 +68,33 @@ object StreamExtra { /** * キーと出力の組の[[fs2.Stream]]から、キーが `filter` を満たす出力のみを取り出してストリームを作成する。 */ - def valuesWithKeyFilter[F[_], K, O](stream: Stream[F, (K, O)])(filter: K => Boolean): Stream[F, O] = + def valuesWithKeyFilter[F[_], K, O](stream: Stream[F, (K, O)])( + filter: K => Boolean + ): Stream[F, O] = stream.mapFilter { case (k, out) => Option.when(filter(k))(out) } /** * キーと出力の組の[[fs2.Stream]]から、キーが`key`と同じUUIDを持つ出力のみを取り出してストリームを作成する。 */ - def valuesWithKeyOfSameUuidAs[F[_], K: HasUuid, O](key: K)(stream: Stream[F, (K, O)]): Stream[F, O] = { + def valuesWithKeyOfSameUuidAs[F[_], K: HasUuid, O]( + key: K + )(stream: Stream[F, (K, O)]): Stream[F, O] = { val uuid = HasUuid[K].of(key) valuesWithKeyFilter(stream) { k => HasUuid[K].of(k) == uuid } } def keyedValueDiffs[F[_], K, O: Eq](stream: Stream[F, (K, O)]): Stream[F, (K, Diff[O])] = { stream - .mapAccumulate(Map.empty[K, O]) { case (map, (key, o)) => - val output: Option[(K, Diff[O])] = map.get(key) match { - case Some(previousValue) => - Diff.fromValues(previousValue, o).map(key -> _) - case None => - None - } + .mapAccumulate(Map.empty[K, O]) { + case (map, (key, o)) => + val output: Option[(K, Diff[O])] = map.get(key) match { + case Some(previousValue) => + Diff.fromValues(previousValue, o).map(key -> _) + case None => + None + } - (map.updated(key, o), output) + (map.updated(key, o), output) } .mapFilter(_._2) } @@ -92,13 +102,14 @@ object StreamExtra { /** * 与えられたストリームを、エラーが発生したときに再起動するストリームに変換してコンパイルする。 */ - def compileToRestartingStream[F[_] : Sync : ErrorLogger, A](context: String)(stream: Stream[F, _]): F[A] = { + def compileToRestartingStream[F[_]: Sync: ErrorLogger, A]( + context: String + )(stream: Stream[F, _]): F[A] = { Monad[F].foreverM { stream .handleErrorWith { error => Stream.eval { - ErrorLogger[F] - .error(error)(s"$context fs2.Stream が予期せぬエラーで終了しました。再起動します。") + ErrorLogger[F].error(error)(s"$context fs2.Stream が予期せぬエラーで終了しました。再起動します。") } } .compile diff --git a/src/main/scala/com/github/unchama/generic/effect/unsafe/EffectEnvironment.scala b/src/main/scala/com/github/unchama/generic/effect/unsafe/EffectEnvironment.scala index 962303a9fb..1932097d32 100644 --- a/src/main/scala/com/github/unchama/generic/effect/unsafe/EffectEnvironment.scala +++ b/src/main/scala/com/github/unchama/generic/effect/unsafe/EffectEnvironment.scala @@ -11,12 +11,11 @@ trait EffectEnvironment { /** * `program` を `context` の文脈にてunsafeに実行する。 * - * このメソッドは一般に副作用をもたらすためunsafeである。 - * 理想的には、これはプログラムの最も外側にて「一度だけ」呼び出されるべきである。 + * このメソッドは一般に副作用をもたらすためunsafeである。 理想的には、これはプログラムの最も外側にて「一度だけ」呼び出されるべきである。 * * このメソッドの実装は `context` を用いて実行に関するロギングを行ってよい。 */ - def unsafeRunEffectAsync[U, F[_] : Effect](context: String, program: F[U]): Unit + def unsafeRunEffectAsync[U, F[_]: Effect](context: String, program: F[U]): Unit /** * `receiver`を`effect`に適用して得られる`IO`を例外を補足して実行する。 @@ -24,11 +23,16 @@ trait EffectEnvironment { * * このメソッドは、呼び出し箇所での実行を`effect`から得られる`IO`が別コンテキストに移るまでブロックする。 * - * @param context effectが何をすることを期待しているかの記述 - * @tparam T レシーバの型 - * @deprecated use [[EffectEnvironment]] + * @param context + * effectが何をすることを期待しているかの記述 + * @tparam T + * レシーバの型 + * @deprecated + * use [[EffectEnvironment]] */ - def unsafeRunAsyncTargetedEffect[T](receiver: T)(effect: TargetedEffect[T], context: String): Unit = + def unsafeRunAsyncTargetedEffect[T]( + receiver: T + )(effect: TargetedEffect[T], context: String): Unit = unsafeRunEffectAsync(context, effect(receiver)) } diff --git a/src/main/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiter.scala b/src/main/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiter.scala index c58146b5a0..b9d2cd5b3a 100644 --- a/src/main/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiter.scala +++ b/src/main/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiter.scala @@ -12,10 +12,11 @@ object FixedWindowRateLimiter { import cats.implicits._ - def in[ - G[_] : Sync : Clock, - A: OrderedMonus - ](maxPermits: A, resetDuration: FiniteDuration, firstPermits: Option[A] = None): G[RateLimiter[G, A]] = { + def in[G[_]: Sync: Clock, A: OrderedMonus]( + maxPermits: A, + resetDuration: FiniteDuration, + firstPermits: Option[A] = None + ): G[RateLimiter[G, A]] = { case class RateLimiterState(lastResetTimeStampInMilli: Long, permitsUsedSoFar: A) val inMillis = TimeUnit.MILLISECONDS @@ -33,42 +34,44 @@ object FixedWindowRateLimiter { for { latestTime <- Clock[G].realTime(inMillis) - obtainedPermits <- stateRef.modify { case RateLimiterState(lastRestoredTimeStampInMilli, permitsUsedSoFar) => - // もし十分な時間が経っていれば、現在までのどこかの時点でカウントのリセットが発生しているはずだから、 - // そのようなリセットの数を数える。 - // - // currentTimeが十分に古く、floorDivの結果が負になる可能性があることに注意。 - // これは滅多に発生しないが、そのような状況はlatestTimeが取得されたよりも後にlastRestoredTimeStampInMilliが - // 更新されたことを示しており、この modify の中で我々が再度resetする理由は無い。 - val numberOfResetsHappenedSinceLastReset = - Math.max( - 0, - Math.floorDiv( - latestTime - lastRestoredTimeStampInMilli, - resetDurationInMillis + obtainedPermits <- stateRef.modify { + case RateLimiterState(lastRestoredTimeStampInMilli, permitsUsedSoFar) => + // もし十分な時間が経っていれば、現在までのどこかの時点でカウントのリセットが発生しているはずだから、 + // そのようなリセットの数を数える。 + // + // currentTimeが十分に古く、floorDivの結果が負になる可能性があることに注意。 + // これは滅多に発生しないが、そのような状況はlatestTimeが取得されたよりも後にlastRestoredTimeStampInMilliが + // 更新されたことを示しており、この modify の中で我々が再度resetする理由は無い。 + val numberOfResetsHappenedSinceLastReset = + Math.max( + 0, + Math + .floorDiv(latestTime - lastRestoredTimeStampInMilli, resetDurationInMillis) ) - ) - val resetHappened = numberOfResetsHappenedSinceLastReset > 0 + val resetHappened = numberOfResetsHappenedSinceLastReset > 0 - val basePermitsCount = if (resetHappened) zero else permitsUsedSoFar + val basePermitsCount = if (resetHappened) zero else permitsUsedSoFar - val newPermitsCount = (basePermitsCount |+| a) min maxPermits - val obtainedPermits = newPermitsCount |-| basePermitsCount + val newPermitsCount = (basePermitsCount |+| a) min maxPermits + val obtainedPermits = newPermitsCount |-| basePermitsCount - // 最後にカウントのリセットが発生したタイムスタンプ。 - val restoredTimeStamp = if (!resetHappened) lastRestoredTimeStampInMilli else { - // `lastRestoredTimeStampInMilli + n * resetDuration` で、 - // `latestTime` 以下となる最大のものを探せばよいが、これは次の式で計算できる - lastRestoredTimeStampInMilli + numberOfResetsHappenedSinceLastReset * resetDurationInMillis - } + // 最後にカウントのリセットが発生したタイムスタンプ。 + val restoredTimeStamp = + if (!resetHappened) lastRestoredTimeStampInMilli + else { + // `lastRestoredTimeStampInMilli + n * resetDuration` で、 + // `latestTime` 以下となる最大のものを探せばよいが、これは次の式で計算できる + lastRestoredTimeStampInMilli + numberOfResetsHappenedSinceLastReset * resetDurationInMillis + } - (RateLimiterState(restoredTimeStamp, newPermitsCount), obtainedPermits) + (RateLimiterState(restoredTimeStamp, newPermitsCount), obtainedPermits) } } yield obtainedPermits } - override def peekAvailablePermissions: G[A] = stateRef.get.map(maxPermits |-| _.permitsUsedSoFar) -} + override def peekAvailablePermissions: G[A] = + stateRef.get.map(maxPermits |-| _.permitsUsedSoFar) + } } } diff --git a/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala b/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala index 7d82aff472..41adc00f65 100644 --- a/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala +++ b/src/main/scala/com/github/unchama/generic/ratelimiting/RateLimiter.scala @@ -3,19 +3,15 @@ package com.github.unchama.generic.ratelimiting import com.github.unchama.generic.algebra.typeclasses.OrderedMonus /** - * Rate Limitの実装を提供するオブジェクト。 - * 内部状態を持ち、一定時間当たりにいくらかの量の「リクエスト」が送信されることを許可する。 + * Rate Limitの実装を提供するオブジェクト。 内部状態を持ち、一定時間当たりにいくらかの量の「リクエスト」が送信されることを許可する。 * - * 例えばデータ転送等のコンテキストにおいて、 - * 時間当たり一定量のバイト数だけ送信を許可したいという要求がある。 - * ここでの転送するデータに相当する概念を「リクエスト」と呼ぶことにする。 + * 例えばデータ転送等のコンテキストにおいて、 時間当たり一定量のバイト数だけ送信を許可したいという要求がある。 ここでの転送するデータに相当する概念を「リクエスト」と呼ぶことにする。 * - * このオブジェクトの使用者は、一定の量のリクエストの送信を申請する。 - * このオブジェクトはリクエスト量の制限に基づいて、 - * あとどれくらいリクエストを送って良いかという指標を返す。 + * このオブジェクトの使用者は、一定の量のリクエストの送信を申請する。 このオブジェクトはリクエスト量の制限に基づいて、 あとどれくらいリクエストを送って良いかという指標を返す。 * リクエスト量にどのような制限を掛けるかは実装依存である。 * - * @tparam A リクエスト量を記述できる型 + * @tparam A + * リクエスト量を記述できる型 */ trait RateLimiter[F[_], A] { @@ -25,18 +21,19 @@ trait RateLimiter[F[_], A] { protected val A: OrderedMonus[A] /** - * [[A]] の値によって指定されるリクエスト量を送る申請をする作用。 - * 作用の結果として、送って良いリクエスト量を表す [[A]] の値が返される。 + * [[A]] の値によって指定されるリクエスト量を送る申請をする作用。 作用の結果として、送って良いリクエスト量を表す [[A]] の値が返される。 * - * @param a 申請するリクエスト送信量 - * @return [[A]]の値を返す作用 + * @param a + * 申請するリクエスト送信量 + * @return + * [[A]]の値を返す作用 */ def requestPermission(a: A): F[A] /** - * 次にリセットされるまで送っても良いリクエスト量を取得する作用を返す。 - * この作用の発火が、送って良いリクエスト量に影響することはない。 - * @return [[A]] によって記述される、次にリセットされるまで送っても良いリクエスト量を取得する作用 + * 次にリセットされるまで送っても良いリクエスト量を取得する作用を返す。 この作用の発火が、送って良いリクエスト量に影響することはない。 + * @return + * [[A]] によって記述される、次にリセットされるまで送っても良いリクエスト量を取得する作用 */ def peekAvailablePermissions: F[A] } diff --git a/src/main/scala/com/github/unchama/itemmigration/application/ItemMigrationStateRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/itemmigration/application/ItemMigrationStateRepositoryDefinitions.scala index c8fc94a63e..c37ea586b2 100644 --- a/src/main/scala/com/github/unchama/itemmigration/application/ItemMigrationStateRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/itemmigration/application/ItemMigrationStateRepositoryDefinitions.scala @@ -8,10 +8,12 @@ import com.github.unchama.itemmigration.domain.PlayerMigrationState object ItemMigrationStateRepositoryDefinitions { - def initialization[F[_] : Sync]: SinglePhasedRepositoryInitialization[F, PlayerMigrationState[F]] = + def initialization[F[_]: Sync] + : SinglePhasedRepositoryInitialization[F, PlayerMigrationState[F]] = SinglePhasedRepositoryInitialization.withSupplier(PlayerMigrationState.newIn[F]) - def finalization[F[_] : Applicative, Player]: RepositoryFinalization[F, Player, PlayerMigrationState[F]] = + def finalization[F[_]: Applicative, Player] + : RepositoryFinalization[F, Player, PlayerMigrationState[F]] = RepositoryFinalization.trivial } diff --git a/src/main/scala/com/github/unchama/itemmigration/bukkit/controllers/player/PlayerItemMigrationController.scala b/src/main/scala/com/github/unchama/itemmigration/bukkit/controllers/player/PlayerItemMigrationController.scala index fc13fbb7e2..3d96fe200e 100644 --- a/src/main/scala/com/github/unchama/itemmigration/bukkit/controllers/player/PlayerItemMigrationController.scala +++ b/src/main/scala/com/github/unchama/itemmigration/bukkit/controllers/player/PlayerItemMigrationController.scala @@ -9,19 +9,22 @@ import com.github.unchama.itemmigration.service.ItemMigrationService import org.bukkit.entity.Player import org.bukkit.event.block.BlockPlaceEvent import org.bukkit.event.inventory.InventoryOpenEvent -import org.bukkit.event.player.{PlayerDropItemEvent, PlayerEvent, PlayerItemConsumeEvent, PlayerJoinEvent} +import org.bukkit.event.player.{ + PlayerDropItemEvent, + PlayerEvent, + PlayerItemConsumeEvent, + PlayerJoinEvent +} import org.bukkit.event.{Cancellable, EventHandler, EventPriority, Listener} /** - * プレーヤーのアイテムマイグレーション処理中に、 - * 該当プレーヤーの行動を制御するためのリスナオブジェクトのクラス + * プレーヤーのアイテムマイグレーション処理中に、 該当プレーヤーの行動を制御するためのリスナオブジェクトのクラス */ -class PlayerItemMigrationController[ - F[_] : ConcurrentEffect, - G[_] : SyncEffect -](migrationState: KeyedDataRepository[Player, PlayerMigrationState[G]], - migrations: ItemMigrations, service: ItemMigrationService[F, PlayerInventoriesData[F]]) - extends Listener { +class PlayerItemMigrationController[F[_]: ConcurrentEffect, G[_]: SyncEffect]( + migrationState: KeyedDataRepository[Player, PlayerMigrationState[G]], + migrations: ItemMigrations, + service: ItemMigrationService[F, PlayerInventoriesData[F]] +) extends Listener { import cats.effect.implicits._ @@ -38,7 +41,7 @@ class PlayerItemMigrationController[ def onInventoryOpen(e: InventoryOpenEvent): Unit = { e.getPlayer match { case player: Player => cancelIfLockActive(player, e) - case _ => + case _ => } } diff --git a/src/main/scala/com/github/unchama/itemmigration/bukkit/targets/PlayerInventoriesData.scala b/src/main/scala/com/github/unchama/itemmigration/bukkit/targets/PlayerInventoriesData.scala index a3aa0d9899..7f445a0974 100644 --- a/src/main/scala/com/github/unchama/itemmigration/bukkit/targets/PlayerInventoriesData.scala +++ b/src/main/scala/com/github/unchama/itemmigration/bukkit/targets/PlayerInventoriesData.scala @@ -8,7 +8,7 @@ import org.bukkit.entity.Player /** * アイテムマイグレーションを行う対象としてのプレーヤーインベントリを表すオブジェクト */ -case class PlayerInventoriesData[F[_] : Sync](player: Player) extends ItemMigrationTarget[F] { +case class PlayerInventoriesData[F[_]: Sync](player: Player) extends ItemMigrationTarget[F] { override def runMigration(conversion: ItemStackConversion): F[Unit] = Sync[F].delay { MigrationHelper.convertEachStackIn(player.getInventory)(conversion) diff --git a/src/main/scala/com/github/unchama/itemmigration/bukkit/targets/WorldLevelData.scala b/src/main/scala/com/github/unchama/itemmigration/bukkit/targets/WorldLevelData.scala index 105a4d17f8..54664f1147 100644 --- a/src/main/scala/com/github/unchama/itemmigration/bukkit/targets/WorldLevelData.scala +++ b/src/main/scala/com/github/unchama/itemmigration/bukkit/targets/WorldLevelData.scala @@ -17,12 +17,16 @@ import org.slf4j.Logger * * `enumerateChunkCoordinates` は、ワールドを渡すとそのワールド内で変換すべきチャンク座標(x, z)を列挙する。 * - * @param getWorlds 変換対象であるワールドを列挙するプログラム - * @param enumerateChunkCoordinates ワールド内で変換すべきチャンク座標を列挙するプログラム + * @param getWorlds + * 変換対象であるワールドを列挙するプログラム + * @param enumerateChunkCoordinates + * ワールド内で変換すべきチャンク座標を列挙するプログラム */ -case class WorldLevelData[F[_]](getWorlds: F[IndexedSeq[World]], - enumerateChunkCoordinates: World => F[Seq[(Int, Int)]]) - (implicit metricsLogger: Logger, F: Concurrent[F]) extends ItemMigrationTarget[F] { +case class WorldLevelData[F[_]]( + getWorlds: F[IndexedSeq[World]], + enumerateChunkCoordinates: World => F[Seq[(Int, Int)]] +)(implicit metricsLogger: Logger, F: Concurrent[F]) + extends ItemMigrationTarget[F] { override def runMigration(conversion: ItemStackConversion): F[Unit] = { import cats.implicits._ @@ -50,16 +54,20 @@ object WorldLevelData { import cats.implicits._ - private implicit class ListHasAtEvery[F[_]](val list: List[F[Unit]]) extends AnyVal { - def atEvery(interval: Int)(actionAt: Int => F[Unit])(implicit F: Monad[F]): List[F[Unit]] = { - list.mapWithIndex { case (x, index) => - if ((index + 1) % interval == 0) x >> actionAt(index) else x + private implicit class ListHasAtEvery[F[_]](private val list: List[F[Unit]]) extends AnyVal { + def atEvery( + interval: Int + )(actionAt: Int => F[Unit])(implicit F: Monad[F]): List[F[Unit]] = { + list.mapWithIndex { + case (x, index) => + if ((index + 1) % interval == 0) x >> actionAt(index) else x } } } - private def reloadWorld[F[_]](worldRef: Ref[F, World]) - (implicit logger: Logger, F: Sync[F]): F[Unit] = { + private def reloadWorld[F[_]]( + worldRef: Ref[F, World] + )(implicit logger: Logger, F: Sync[F]): F[Unit] = { worldRef.get >>= { world => F.delay { logger.info(s"${world.getName}を再読み込みします…") @@ -69,14 +77,13 @@ object WorldLevelData { logger.warn(s"${world.getName}はアンロードされませんでした。") } Bukkit.createWorld(creator) - }.flatTap { - newWorld => F.delay(logger.info(s"${newWorld.getName}を再読み込みしました")) - } + }.flatTap { newWorld => F.delay(logger.info(s"${newWorld.getName}を再読み込みしました")) } } >>= worldRef.set } - private def migrateChunk[F[_]](conversion: ItemStackConversion, chunkCoordinate: (Int, Int)) - (world: World)(implicit F: Sync[F], logger: Logger): F[Unit] = F.delay { + private def migrateChunk[F[_]](conversion: ItemStackConversion, chunkCoordinate: (Int, Int))( + world: World + )(implicit F: Sync[F], logger: Logger): F[Unit] = F.delay { val chunk = world.getChunkAt(chunkCoordinate._1, chunkCoordinate._2) chunk.getTileEntities.foreach { @@ -106,21 +113,24 @@ object WorldLevelData { F.delay { logger.info("チャンクの保存キューの処理を要求します…") - } >> F.start { - WorldChunkSaving.relaxFileIOThreadThrottle[F] >> F.delay { - logger.info("チャンクの保存キューが処理されました") + } >> F + .start { + WorldChunkSaving.relaxFileIOThreadThrottle[F] >> F.delay { + logger.info("チャンクの保存キューが処理されました") + } } - }.as(()) + .as(()) } - private def flushEntityRemovalQueue[F[_] : Sync](worldRef: Ref[F, World]): F[Unit] = { + private def flushEntityRemovalQueue[F[_]: Sync](worldRef: Ref[F, World]): F[Unit] = { import com.github.unchama.util.nms.v1_12_2.world.WorldChunkSaving worldRef.get >>= WorldChunkSaving.flushEntityRemovalQueue[F] } - private def logProgress[F[_]](chunkIndex: Int, totalChunks: Int)(worldRef: Ref[F, World]) - (implicit F: Sync[F], logger: Logger): F[Unit] = { + private def logProgress[F[_]](chunkIndex: Int, totalChunks: Int)( + worldRef: Ref[F, World] + )(implicit F: Sync[F], logger: Logger): F[Unit] = { worldRef.get >>= { world => F.delay { val processed = chunkIndex + 1 @@ -133,17 +143,18 @@ object WorldLevelData { private final val progressLogInterval = 1000 private final val reloadWorldInterval = 10000 - def convertChunkWise[F[_]](originalWorld: World, targetChunks: Seq[(Int, Int)], conversion: ItemStack => ItemStack) - (implicit F: Concurrent[F], logger: Logger): F[Unit] = + def convertChunkWise[F[_]]( + originalWorld: World, + targetChunks: Seq[(Int, Int)], + conversion: ItemStack => ItemStack + )(implicit F: Concurrent[F], logger: Logger): F[Unit] = for { worldRef <- Ref.of(originalWorld) _ <- { val chunkConversionEffects: List[F[Unit]] = { - targetChunks - .map { chunkCoordinate => - worldRef.get >>= migrateChunk[F](conversion, chunkCoordinate) - } - .toList + targetChunks.map { chunkCoordinate => + worldRef.get >>= migrateChunk[F](conversion, chunkCoordinate) + }.toList } /* @@ -159,8 +170,12 @@ object WorldLevelData { * GC Rootからの参照パスを特定することを推奨する。 */ chunkConversionEffects - .atEvery(chunkSaverQueueFlushInterval)(_ => flushEntityRemovalQueue(worldRef) >> queueChunkSaverFlush) - .atEvery(progressLogInterval)(index => logProgress(index, chunkConversionEffects.size)(worldRef)) + .atEvery(chunkSaverQueueFlushInterval)(_ => + flushEntityRemovalQueue(worldRef) >> queueChunkSaverFlush + ) + .atEvery(progressLogInterval)(index => + logProgress(index, chunkConversionEffects.size)(worldRef) + ) .atEvery(reloadWorldInterval)(_ => reloadWorld(worldRef)) .sequence >> reloadWorld(worldRef) } diff --git a/src/main/scala/com/github/unchama/itemmigration/bukkit/util/MigrationHelper.scala b/src/main/scala/com/github/unchama/itemmigration/bukkit/util/MigrationHelper.scala index c0ffd9f963..4b2cad1204 100644 --- a/src/main/scala/com/github/unchama/itemmigration/bukkit/util/MigrationHelper.scala +++ b/src/main/scala/com/github/unchama/itemmigration/bukkit/util/MigrationHelper.scala @@ -20,37 +20,37 @@ object MigrationHelper { } /** - * シュルカーボックス等の内部にインベントリを持つアイテムについて、 - * インベントリ内部のアイテムに対して変換を行うように `conversion` をラップする。 + * シュルカーボックス等の内部にインベントリを持つアイテムについて、 インベントリ内部のアイテムに対して変換を行うように `conversion` をラップする。 */ def delegateConversionForContainers(conversion: ItemStackConversion): ItemStackConversion = { - itemStack => { - val cloned = itemStack.clone() - cloned.getItemMeta match { - case meta: BlockStateMeta if meta.hasBlockState => - meta.getBlockState match { - case state: InventoryHolder => - - /** - * [このスレッド](https://www.spigotmc.org/threads/unable-to-modify-shulker-box-inventory-contents.320665/) - * を参考に実装。 - * - * [[BlockStateMeta]] の `.getBlockState` メソッドは - * - * > The state is a copy, it must be set back (or to another item) with setBlockState(BlockState) - * - * にあるように状態のコピーを返してくるので、それに付属したインベントリ(`state.getInventory`) - * に変更を行った後は `state` を `meta` に返して、 `meta` を `cloned` に返す必要がある。 - */ - convertEachStackIn(state.getInventory)(conversion) - meta.setBlockState(state) - cloned.setItemMeta(meta) - cloned - case _ => conversion(itemStack) - } - case _ => conversion(itemStack) + itemStack => + { + val cloned = itemStack.clone() + cloned.getItemMeta match { + case meta: BlockStateMeta if meta.hasBlockState => + meta.getBlockState match { + case state: InventoryHolder => + /** + * [このスレッド](https://www.spigotmc.org/threads/unable-to-modify-shulker-box-inventory-contents.320665/) + * を参考に実装。 + * + * [[BlockStateMeta]] の `.getBlockState` メソッドは + * + * > The state is a copy, it must be set back (or to another item) with + * setBlockState(BlockState) + * + * にあるように状態のコピーを返してくるので、それに付属したインベントリ(`state.getInventory`) に変更を行った後は `state` を + * `meta` に返して、 `meta` を `cloned` に返す必要がある。 + */ + convertEachStackIn(state.getInventory)(conversion) + meta.setBlockState(state) + cloned.setItemMeta(meta) + cloned + case _ => conversion(itemStack) + } + case _ => conversion(itemStack) + } } - } } } diff --git a/src/main/scala/com/github/unchama/itemmigration/domain/ItemMigrationLogger.scala b/src/main/scala/com/github/unchama/itemmigration/domain/ItemMigrationLogger.scala index e0a2fe3885..6ddfda0d22 100644 --- a/src/main/scala/com/github/unchama/itemmigration/domain/ItemMigrationLogger.scala +++ b/src/main/scala/com/github/unchama/itemmigration/domain/ItemMigrationLogger.scala @@ -5,6 +5,9 @@ trait ItemMigrationLogger[F[_], -T <: ItemMigrationTarget[F]] { /** * `target` に対して、 `versions` をバージョンに持つマイグレーションを適用する旨をログ等で管理者に通知する。 */ - def logMigrationVersionsToBeApplied(versions: IndexedSeq[ItemMigrationVersionNumber], target: T): F[Unit] + def logMigrationVersionsToBeApplied( + versions: IndexedSeq[ItemMigrationVersionNumber], + target: T + ): F[Unit] } diff --git a/src/main/scala/com/github/unchama/itemmigration/domain/ItemMigrationVersionNumber.scala b/src/main/scala/com/github/unchama/itemmigration/domain/ItemMigrationVersionNumber.scala index 85b99abd52..e850dc7a0b 100644 --- a/src/main/scala/com/github/unchama/itemmigration/domain/ItemMigrationVersionNumber.scala +++ b/src/main/scala/com/github/unchama/itemmigration/domain/ItemMigrationVersionNumber.scala @@ -14,8 +14,10 @@ object ItemMigrationVersionNumber { import eu.timepit.refined._ import eu.timepit.refined.numeric.NonNegative - def apply(versionHead: ItemMigrationVersionComponent, - versionRest: ItemMigrationVersionComponent*): ItemMigrationVersionNumber = { + def apply( + versionHead: ItemMigrationVersionComponent, + versionRest: ItemMigrationVersionComponent* + ): ItemMigrationVersionNumber = { ItemMigrationVersionNumber(NonEmptyList.of(versionHead, versionRest: _*)) } @@ -26,14 +28,10 @@ object ItemMigrationVersionNumber { .map(_.toIntOption) .sequence .flatMap { components => - components - .map(component => refineV[NonNegative](component)) - .sequence - .toOption - }.flatMap { componentList => - NonEmptyList - .fromList(componentList) - .map(ItemMigrationVersionNumber(_)) - } + components.map(component => refineV[NonNegative](component)).sequence.toOption + } + .flatMap { componentList => + NonEmptyList.fromList(componentList).map(ItemMigrationVersionNumber(_)) + } } diff --git a/src/main/scala/com/github/unchama/itemmigration/domain/ItemMigrationVersionRepository.scala b/src/main/scala/com/github/unchama/itemmigration/domain/ItemMigrationVersionRepository.scala index 55e1e0485f..6b35dda8c1 100644 --- a/src/main/scala/com/github/unchama/itemmigration/domain/ItemMigrationVersionRepository.scala +++ b/src/main/scala/com/github/unchama/itemmigration/domain/ItemMigrationVersionRepository.scala @@ -5,8 +5,10 @@ import cats.effect.Resource /** * 対象 `T` に関するアイテムマイグレーションの適用済みバージョンのリポジトリ。 * - * @tparam F 計算を行うコンテキスト - * @tparam T このオブジェクトがマイグレーションを実行できるオブジェクト達の型の上界 + * @tparam F + * 計算を行うコンテキスト + * @tparam T + * このオブジェクトがマイグレーションを実行できるオブジェクト達の型の上界 */ trait ItemMigrationVersionRepository[F[_], -T <: ItemMigrationTarget[F]] { @@ -23,11 +25,16 @@ trait ItemMigrationVersionRepository[F[_], -T <: ItemMigrationTarget[F]] { /** * `target` にすでに適用されたマイグレーションの番号の集合を取得する計算。 */ - def getVersionsAppliedTo(target: T): PersistenceLock[target.type] => F[Set[ItemMigrationVersionNumber]] + def getVersionsAppliedTo( + target: T + ): PersistenceLock[target.type] => F[Set[ItemMigrationVersionNumber]] /** * `target` に適用したバージョンを永続化する計算。 */ - def persistVersionsAppliedTo(target: T, versions: Iterable[ItemMigrationVersionNumber]): PersistenceLock[target.type] => F[Unit] + def persistVersionsAppliedTo( + target: T, + versions: Iterable[ItemMigrationVersionNumber] + ): PersistenceLock[target.type] => F[Unit] } diff --git a/src/main/scala/com/github/unchama/itemmigration/domain/ItemMigrations.scala b/src/main/scala/com/github/unchama/itemmigration/domain/ItemMigrations.scala index 88b66f8960..eddec48851 100644 --- a/src/main/scala/com/github/unchama/itemmigration/domain/ItemMigrations.scala +++ b/src/main/scala/com/github/unchama/itemmigration/domain/ItemMigrations.scala @@ -12,7 +12,8 @@ case class ItemMigrations(migrations: IndexedSeq[ItemMigration]) { } /** - * @return マイグレーションをバージョン番号でソートした新しい `ItemMigrations` + * @return + * マイグレーションをバージョン番号でソートした新しい `ItemMigrations` */ def sorted: ItemMigrations = { import Ordering.Implicits._ @@ -21,8 +22,8 @@ case class ItemMigrations(migrations: IndexedSeq[ItemMigration]) { } /** - * このオブジェクトが持つマイグレーションのうち、 - * `appliedVersions` にバージョンが含まれていないマイグレーションのみを含む新しい `ItemMigrations` を返す。 + * このオブジェクトが持つマイグレーションのうち、 `appliedVersions` にバージョンが含まれていないマイグレーションのみを含む新しい `ItemMigrations` + * を返す。 */ def yetToBeApplied(appliedVersions: Set[ItemMigrationVersionNumber]): ItemMigrations = ItemMigrations { @@ -30,7 +31,8 @@ case class ItemMigrations(migrations: IndexedSeq[ItemMigration]) { } /** - * @return マイグレーション列のアイテムスタック変換関数を順番に全て合成した関数。 + * @return + * マイグレーション列のアイテムスタック変換関数を順番に全て合成した関数。 */ def toSingleConversion: ItemStackConversion = { if (migrations.isEmpty) { diff --git a/src/main/scala/com/github/unchama/itemmigration/domain/PlayerMigrationState.scala b/src/main/scala/com/github/unchama/itemmigration/domain/PlayerMigrationState.scala index 36d820f3a9..529b247165 100644 --- a/src/main/scala/com/github/unchama/itemmigration/domain/PlayerMigrationState.scala +++ b/src/main/scala/com/github/unchama/itemmigration/domain/PlayerMigrationState.scala @@ -3,7 +3,7 @@ package com.github.unchama.itemmigration.domain import cats.effect.Sync import cats.effect.concurrent.Ref -class PlayerMigrationState[F[_]] private(flagRef: Ref[F, Boolean]) { +class PlayerMigrationState[F[_]] private (flagRef: Ref[F, Boolean]) { val setMigrated: F[Unit] = flagRef.set(true) @@ -15,7 +15,7 @@ object PlayerMigrationState { import cats.implicits._ - def newIn[F[_] : Sync]: F[PlayerMigrationState[F]] = + def newIn[F[_]: Sync]: F[PlayerMigrationState[F]] = for { ref <- Ref[F].of(false) } yield new PlayerMigrationState[F](ref) diff --git a/src/main/scala/com/github/unchama/itemmigration/service/ItemMigrationService.scala b/src/main/scala/com/github/unchama/itemmigration/service/ItemMigrationService.scala index c3b73fde26..90c0748120 100644 --- a/src/main/scala/com/github/unchama/itemmigration/service/ItemMigrationService.scala +++ b/src/main/scala/com/github/unchama/itemmigration/service/ItemMigrationService.scala @@ -1,11 +1,17 @@ package com.github.unchama.itemmigration.service import cats.effect.Bracket -import com.github.unchama.itemmigration.domain.{ItemMigrationLogger, ItemMigrationTarget, ItemMigrationVersionRepository, ItemMigrations} +import com.github.unchama.itemmigration.domain.{ + ItemMigrationLogger, + ItemMigrationTarget, + ItemMigrationVersionRepository, + ItemMigrations +} -case class ItemMigrationService[F[_], -T <: ItemMigrationTarget[F]](persistence: ItemMigrationVersionRepository[F, T], - logger: ItemMigrationLogger[F, T]) - (implicit F: Bracket[F, Throwable]) { +case class ItemMigrationService[F[_], -T <: ItemMigrationTarget[F]]( + persistence: ItemMigrationVersionRepository[F, T], + logger: ItemMigrationLogger[F, T] +)(implicit F: Bracket[F, Throwable]) { def runMigration(migrations: ItemMigrations)(target: T): F[Unit] = { import cats.implicits._ @@ -14,7 +20,8 @@ case class ItemMigrationService[F[_], -T <: ItemMigrationTarget[F]](persistence: for { appliedVersions <- persistence.getVersionsAppliedTo(target)(lock) migrationsToApply = migrations.yetToBeApplied(appliedVersions) - _ <- logger.logMigrationVersionsToBeApplied(migrationsToApply.migrations.map(_.version), target) + _ <- logger + .logMigrationVersionsToBeApplied(migrationsToApply.migrations.map(_.version), target) _ <- if (migrationsToApply.isEmpty) F.unit else target.runMigration(migrationsToApply.toSingleConversion) @@ -28,16 +35,22 @@ case class ItemMigrationService[F[_], -T <: ItemMigrationTarget[F]](persistence: object ItemMigrationService { /** - * Uses the [[https://typelevel.org/cats/guidelines.html#partially-applied-type-params Partially Applied Type Params technique]] for ergonomics. + * Uses the + * [[https://typelevel.org/cats/guidelines.html#partially-applied-type-params Partially Applied Type Params technique]] + * for ergonomics. */ - //noinspection ScalaUnusedSymbol - final private[service] class ItemMigrationServicePartiallyApplied[F[_]](private val dummy: Boolean = true) extends AnyVal { - def apply[T <: ItemMigrationTarget[F]](persistence: ItemMigrationVersionRepository[F, T], - logger: ItemMigrationLogger[F, T]) - (implicit F: Bracket[F, Throwable]): ItemMigrationService[F, T] = { + // noinspection ScalaUnusedSymbol + final private[service] class ItemMigrationServicePartiallyApplied[F[_]]( + private val dummy: Boolean = true + ) extends AnyVal { + def apply[T <: ItemMigrationTarget[F]]( + persistence: ItemMigrationVersionRepository[F, T], + logger: ItemMigrationLogger[F, T] + )(implicit F: Bracket[F, Throwable]): ItemMigrationService[F, T] = { ItemMigrationService[F, T](persistence, logger) } } - def inContextOf[F[_]]: ItemMigrationServicePartiallyApplied[F] = new ItemMigrationServicePartiallyApplied[F] -} \ No newline at end of file + def inContextOf[F[_]]: ItemMigrationServicePartiallyApplied[F] = + new ItemMigrationServicePartiallyApplied[F] +} diff --git a/src/main/scala/com/github/unchama/itemstackbuilder/AbstractItemStackBuilder.scala b/src/main/scala/com/github/unchama/itemstackbuilder/AbstractItemStackBuilder.scala index 149eb036d1..2048320be8 100644 --- a/src/main/scala/com/github/unchama/itemstackbuilder/AbstractItemStackBuilder.scala +++ b/src/main/scala/com/github/unchama/itemstackbuilder/AbstractItemStackBuilder.scala @@ -8,11 +8,15 @@ import org.bukkit.inventory.{ItemFlag, ItemStack} /** * ItemStackBuilderのベースとなる抽象クラス. * - * @tparam M 派生クラスが生成する[ItemStack]が持つべきであろう[ItemMeta]の型. - * @author karayuu + * @tparam M + * 派生クラスが生成する[ItemStack]が持つべきであろう[ItemMeta]の型. + * @author + * karayuu */ -abstract class AbstractItemStackBuilder[-M <: ItemMeta] protected -(material: Material, durability: Short) extends ItemStackBuilder { +abstract class AbstractItemStackBuilder[-M <: ItemMeta] protected ( + material: Material, + durability: Short +) extends ItemStackBuilder { private val component: IconComponent = new IconComponent(material, durability) diff --git a/src/main/scala/com/github/unchama/itemstackbuilder/IconItemStackBuilder.scala b/src/main/scala/com/github/unchama/itemstackbuilder/IconItemStackBuilder.scala index 6aa14febfd..1e1a3fa24b 100644 --- a/src/main/scala/com/github/unchama/itemstackbuilder/IconItemStackBuilder.scala +++ b/src/main/scala/com/github/unchama/itemstackbuilder/IconItemStackBuilder.scala @@ -7,18 +7,20 @@ import org.bukkit.inventory.meta.ItemMeta /** * ItemStack, 特にメニューに使用するスロットのIconを生成するBuilderです. * - * @param material ItemStackに設定するMaterial - * @param durability ダメージ値 - * Created by karayuu on 2019/03/30 + * @param material + * ItemStackに設定するMaterial + * @param durability + * ダメージ値 Created by karayuu on 2019/03/30 */ -class IconItemStackBuilder(material: Material, durability: Short = 0.toShort) extends - AbstractItemStackBuilder[ItemMeta](material, durability) { +class IconItemStackBuilder(material: Material, durability: Short = 0.toShort) + extends AbstractItemStackBuilder[ItemMeta](material, durability) { private var shouldShowAttribute: Boolean = false /** * ItemStack(IconItemStackBuilder)の各種情報を表示させます.(シャベルの採掘速度等) * - * @return このBuilder + * @return + * このBuilder */ def showAttribute(): this.type = { this.shouldShowAttribute = true diff --git a/src/main/scala/com/github/unchama/itemstackbuilder/ItemStackBuilder.scala b/src/main/scala/com/github/unchama/itemstackbuilder/ItemStackBuilder.scala index b45420d85f..98bac66c89 100644 --- a/src/main/scala/com/github/unchama/itemstackbuilder/ItemStackBuilder.scala +++ b/src/main/scala/com/github/unchama/itemstackbuilder/ItemStackBuilder.scala @@ -6,77 +6,94 @@ import org.bukkit.inventory.{ItemFlag, ItemStack} * [ItemStack] をBuildするBuilderを表すインターフェース. */ trait ItemStackBuilder { + /** * [ItemStack] の表示名を設定します. * - * @param title [ItemStack] の表示名 - * @return このBuilder + * @param title + * [ItemStack] の表示名 + * @return + * このBuilder */ def title(title: String): ItemStackBuilder /** * [ItemStack] のLoreを設定します. * - * @param lore [ItemStack] のLoreとして設定する [String] の [List] - * @return このBuilder + * @param lore + * [ItemStack] のLoreとして設定する [String] の [List] + * @return + * このBuilder */ def lore(lore: List[String]): ItemStackBuilder /** * [ItemStack] のLoreを設定します. * - * @param _lore [ItemStack] のLoreとして設定する [String] の [List] - * @return このBuilder + * @param _lore + * [ItemStack] のLoreとして設定する [String] の [List] + * @return + * このBuilder */ def lore(_lore: String*): ItemStackBuilder = lore(_lore.toList) /** * [ItemStack] にエンチャントを付与します. * - * @return このBuilder + * @return + * このBuilder */ def enchanted(): ItemStackBuilder /** * [ItemStack] の個数を指定します. * - * @param amount [ItemStack] の個数 - * @return このBuilder + * @param amount + * [ItemStack] の個数 + * @return + * このBuilder */ def amount(amount: Int): ItemStackBuilder /** * [ItemStack] にunbreakableを付与する * - * @return このBuilder + * @return + * このBuilder */ def unbreakable(): ItemStackBuilder /** * [ItemStack] に与えられた [ItemFlag] を付与する. * - * @return このBuilder + * @return + * このBuilder */ def flagged(flag: ItemFlag): ItemStackBuilder /** * [ItemStack] に与えられた全ての [ItemFlag] を付与する. * - * @return このBuilder + * @return + * このBuilder */ def flagged(flags: ItemFlag*): ItemStackBuilder = flagged(flags.toSet) /** * [ItemStack] に [Set] で与えられた [ItemFlag] を全て付与する. * - * @return このBuilder + * @return + * このBuilder */ - def flagged(flagSet: Set[ItemFlag]): ItemStackBuilder = flagSet.foldLeft(this) { case (acc, flag) => acc.flagged(flag) } + def flagged(flagSet: Set[ItemFlag]): ItemStackBuilder = flagSet.foldLeft(this) { + case (acc, flag) => acc.flagged(flag) + } /** * Builderによって指定された各引数を用いて [ItemStack] を生成します * - * @return 生成された [ItemStack] + * @return + * 生成された [ItemStack] */ def build(): ItemStack } diff --git a/src/main/scala/com/github/unchama/itemstackbuilder/SkullItemStackBuilder.scala b/src/main/scala/com/github/unchama/itemstackbuilder/SkullItemStackBuilder.scala index c50ae4813c..32a5326e42 100644 --- a/src/main/scala/com/github/unchama/itemstackbuilder/SkullItemStackBuilder.scala +++ b/src/main/scala/com/github/unchama/itemstackbuilder/SkullItemStackBuilder.scala @@ -1,33 +1,37 @@ package com.github.unchama.itemstackbuilder -import java.util.UUID - import com.mojang.authlib.GameProfile import com.mojang.authlib.properties.Property import org.bukkit.inventory.meta.SkullMeta import org.bukkit.{Bukkit, Material, SkullType} +import java.util.UUID + /** * Created by karayuu on 2019/04/09 */ -class SkullItemStackBuilder(private val owner: SkullOwnerReference) extends - AbstractItemStackBuilder[SkullMeta](Material.SKULL_ITEM, SkullType.PLAYER.ordinal.toShort) { +class SkullItemStackBuilder(private val owner: SkullOwnerReference) + extends AbstractItemStackBuilder[SkullMeta]( + Material.SKULL_ITEM, + SkullType.PLAYER.ordinal.toShort + ) { /** - * プレーヤーがサーバーに参加したことのない場合に - * 頭のスキンを読み込むことができないため、そのようなケースが想定されるされる箇所では + * プレーヤーがサーバーに参加したことのない場合に 頭のスキンを読み込むことができないため、そのようなケースが想定されるされる箇所では * プレーヤー名を[[String]]として取るコンストラクタを使用せよ。 * - * それ以外の場合はこのコンストラクタを使うようにせよ。 - * Bukkitは`Persistent storage of users should be by UUID`と記している。 + * それ以外の場合はこのコンストラクタを使うようにせよ。 Bukkitは`Persistent storage of users should be by UUID`と記している。 * - * @see SkullMeta.setOwner - * @param ownerUUID [Material.SKULL_ITEM] に表示するプレーヤーのUUID + * @see + * SkullMeta.setOwner + * @param ownerUUID + * [Material.SKULL_ITEM] に表示するプレーヤーのUUID */ def this(ownerUUID: UUID) = this(SkullOwnerUuid(ownerUUID)) /** - * @param ownerName [Material.SKULL_ITEM] に表示するプレーヤーの名前 + * @param ownerName + * [Material.SKULL_ITEM] に表示するプレーヤーの名前 */ def this(ownerName: String) = this(SkullOwnerName(ownerName)) @@ -36,16 +40,17 @@ class SkullItemStackBuilder(private val owner: SkullOwnerReference) extends case SkullOwnerUuid(uuid) => meta.setOwningPlayer(Bukkit.getOfflinePlayer(uuid)) case SkullOwnerName(name) => - /** * 参加したことのないプレーヤーはgetOfflinePlayerでデータが取れないのでこうするしか無い */ - //noinspection ScalaDeprecation + // noinspection ScalaDeprecation meta.setOwner(name) /** - * @see [[https://www.spigotmc.org/threads/1-12-2-applying-custom-textures-to-skulls.327361/ カスタムヘッドを生成するコード]] - * @see [[https://qiita.com/yuta0801/items/edb4804dfb867ea82c5a テクスチャへのリンク周り]] + * @see + * [[https://www.spigotmc.org/threads/1-12-2-applying-custom-textures-to-skulls.327361/ カスタムヘッドを生成するコード]] + * @see + * [[https://qiita.com/yuta0801/items/edb4804dfb867ea82c5a テクスチャへのリンク周り]] */ case SkullOwnerTextureValue(textureValue) => val uuid = UUID.randomUUID() diff --git a/src/main/scala/com/github/unchama/itemstackbuilder/TippedArrowItemStackBuilder.scala b/src/main/scala/com/github/unchama/itemstackbuilder/TippedArrowItemStackBuilder.scala index 8f51249a92..9eb3d79064 100644 --- a/src/main/scala/com/github/unchama/itemstackbuilder/TippedArrowItemStackBuilder.scala +++ b/src/main/scala/com/github/unchama/itemstackbuilder/TippedArrowItemStackBuilder.scala @@ -5,7 +5,7 @@ import org.bukkit.inventory.meta.PotionMeta import org.bukkit.potion.{PotionData, PotionType} class TippedArrowItemStackBuilder(val potionData: PotionData) - extends AbstractItemStackBuilder[PotionMeta](Material.TIPPED_ARROW, 0) { + extends AbstractItemStackBuilder[PotionMeta](Material.TIPPED_ARROW, 0) { def this(potionType: PotionType) = this(new PotionData(potionType)) diff --git a/src/main/scala/com/github/unchama/itemstackbuilder/component/IconComponent.scala b/src/main/scala/com/github/unchama/itemstackbuilder/component/IconComponent.scala index c19e8e0be7..f36c28c4b1 100644 --- a/src/main/scala/com/github/unchama/itemstackbuilder/component/IconComponent.scala +++ b/src/main/scala/com/github/unchama/itemstackbuilder/component/IconComponent.scala @@ -9,8 +9,7 @@ import org.bukkit.{Bukkit, Material} import scala.jdk.javaapi.CollectionConverters.asJava /** - * ビルダー内で保持されるアイテムスタックの情報をまとめて持つデータ型. - * ミュータブルな設計になっている. + * ビルダー内で保持されるアイテムスタックの情報をまとめて持つデータ型. ミュータブルな設計になっている. * * Created by karayuu on 2019/04/09 */ diff --git a/src/main/scala/com/github/unchama/menuinventory/ChestSlotRef.scala b/src/main/scala/com/github/unchama/menuinventory/ChestSlotRef.scala index 11a1f45210..1129d07878 100644 --- a/src/main/scala/com/github/unchama/menuinventory/ChestSlotRef.scala +++ b/src/main/scala/com/github/unchama/menuinventory/ChestSlotRef.scala @@ -9,9 +9,13 @@ object ChestSlotRef { /** * チェストインベントリでのスロットへの参照を計算する - * @param rowIndex 一番上の行から参照したいスロットを含む行まで移動する行数 - * @param columnIndex 一番左の列から参照したいスロットを含む列まで移動する列数 - * @return `rowIndex`と`columnIndex`により指定されたスロットのスロットid + * @param rowIndex + * 一番上の行から参照したいスロットを含む行まで移動する行数 + * @param columnIndex + * 一番左の列から参照したいスロットを含む列まで移動する列数 + * @return + * `rowIndex`と`columnIndex`により指定されたスロットのスロットid */ - def apply(rowIndex: ChestRowIndexRef, columnIndex: ChestColumnIndexRef): Int = rowIndex.value * 9 + columnIndex.value + def apply(rowIndex: ChestRowIndexRef, columnIndex: ChestColumnIndexRef): Int = + rowIndex.value * 9 + columnIndex.value } diff --git a/src/main/scala/com/github/unchama/menuinventory/InventoryRowSize.scala b/src/main/scala/com/github/unchama/menuinventory/InventoryRowSize.scala index 5076bd1932..1490fbae4a 100644 --- a/src/main/scala/com/github/unchama/menuinventory/InventoryRowSize.scala +++ b/src/main/scala/com/github/unchama/menuinventory/InventoryRowSize.scala @@ -8,6 +8,7 @@ import org.bukkit.event.inventory.InventoryType case class InventoryRowSize(rows: Int) extends AnyVal object InventoryRowSize { + /** * インベントリのサイズを表すデータ型. */ diff --git a/src/main/scala/com/github/unchama/menuinventory/Menu.scala b/src/main/scala/com/github/unchama/menuinventory/Menu.scala index a5f62a8a44..0460571dc8 100644 --- a/src/main/scala/com/github/unchama/menuinventory/Menu.scala +++ b/src/main/scala/com/github/unchama/menuinventory/Menu.scala @@ -9,14 +9,12 @@ import org.bukkit.entity.Player /** * 「メニュー」のtrait. * - * このtraitを実装するオブジェクトは, インベントリ上で展開される意味づけされたUIの情報を持っている. - * これらのUIをメニューインベントリ, または単にメニューと呼ぶこととする. + * このtraitを実装するオブジェクトは, インベントリ上で展開される意味づけされたUIの情報を持っている. これらのUIをメニューインベントリ, または単にメニューと呼ぶこととする. */ trait Menu { /** - * メニューを開く操作に必要な環境情報の型。 - * 例えば、メニューが利用するAPIなどをここを通して渡すことができる。 + * メニューを開く操作に必要な環境情報の型。 例えば、メニューが利用するAPIなどをここを通して渡すことができる。 */ type Environment @@ -26,16 +24,19 @@ trait Menu { val frame: MenuFrame /** - * @return `player`からメニューの[[MenuSlotLayout]]を計算する[[IO]] + * @return + * `player`からメニューの[[MenuSlotLayout]]を計算する[[IO]] */ def computeMenuLayout(player: Player)(implicit environment: Environment): IO[MenuSlotLayout] /** * メニューを[Player]に開かせる[TargetedEffect]. */ - def open(implicit environment: Environment, - ctx: LayoutPreparationContext, - onMainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = data.Kleisli { player => + def open( + implicit environment: Environment, + ctx: LayoutPreparationContext, + onMainThread: OnMinecraftServerThread[IO] + ): TargetedEffect[Player] = data.Kleisli { player => for { session <- MenuSession.createNewSessionWith[IO](frame) _ <- session.openInventory.run(player) @@ -45,4 +46,4 @@ trait Menu { } yield () } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/menuinventory/MenuFrame.scala b/src/main/scala/com/github/unchama/menuinventory/MenuFrame.scala index 17bb29e144..4703832cf4 100644 --- a/src/main/scala/com/github/unchama/menuinventory/MenuFrame.scala +++ b/src/main/scala/com/github/unchama/menuinventory/MenuFrame.scala @@ -5,8 +5,10 @@ import com.github.unchama.util.InventoryUtil._ import org.bukkit.inventory.{Inventory, InventoryHolder} /** - * @param size インベントリのサイズを決定するデータ - * @param title インベントリのタイトル + * @param size + * インベントリのサイズを決定するデータ + * @param title + * インベントリのタイトル */ case class MenuFrame(size: InventorySize, title: String) { private[menuinventory] def createConfiguredInventory(holder: InventoryHolder): Inventory = diff --git a/src/main/scala/com/github/unchama/menuinventory/MenuHandler.scala b/src/main/scala/com/github/unchama/menuinventory/MenuHandler.scala index 540a8746c3..85f239def0 100644 --- a/src/main/scala/com/github/unchama/menuinventory/MenuHandler.scala +++ b/src/main/scala/com/github/unchama/menuinventory/MenuHandler.scala @@ -11,24 +11,26 @@ import org.bukkit.event.{EventHandler, Listener} /** * [MenuInventoryView] に由来するインベントリ上のクリックイベントをビューに定義されたアクションに流すようなリスナーオブジェクト. * - * @author karayuu + * @author + * karayuu */ -class MenuHandler(implicit val cs: NonServerThreadContextShift[IO], env: EffectEnvironment) extends Listener { +class MenuHandler(implicit val cs: NonServerThreadContextShift[IO], env: EffectEnvironment) + extends Listener { @EventHandler(ignoreCancelled = true) def onInventoryClick(event: InventoryClickEvent): Unit = { val whoClicked = event.getWhoClicked match { case player: Player => player - case _ => return + case _ => return } - //メニュー外のクリック排除 + // メニュー外のクリック排除 val clickedInventory = event.getClickedInventory.ifNull { return } val holder = event.getWhoClicked.getOpenInventory.getTopInventory.getHolder match { case session: MenuSession => session - case _ => return + case _ => return } // 上インベントリ以外のクリックを排除 diff --git a/src/main/scala/com/github/unchama/menuinventory/MenuSession.scala b/src/main/scala/com/github/unchama/menuinventory/MenuSession.scala index 5412f840f0..98ab921f9d 100644 --- a/src/main/scala/com/github/unchama/menuinventory/MenuSession.scala +++ b/src/main/scala/com/github/unchama/menuinventory/MenuSession.scala @@ -15,7 +15,7 @@ import org.bukkit.inventory.{Inventory, InventoryHolder, ItemStack} /** * 共有された[sessionInventory]を作用付きの「メニュー」として扱うインベントリを保持するためのセッション. */ -class MenuSession private(private val frame: MenuFrame) extends InventoryHolder { +class MenuSession private (private val frame: MenuFrame) extends InventoryHolder { val currentLayout: Ref[IO, MenuSlotLayout] = Ref.unsafe(MenuSlotLayout()) @@ -24,12 +24,15 @@ class MenuSession private(private val frame: MenuFrame) extends InventoryHolder /** * このセッションが持つ共有インベントリを開く[TargetedEffect]を返します. */ - def openInventory(implicit onMainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = + def openInventory( + implicit onMainThread: OnMinecraftServerThread[IO] + ): TargetedEffect[Player] = PlayerEffects.openInventoryEffect(sessionInventory) - def overwriteViewWith(newLayout: MenuSlotLayout) - (implicit ctx: LayoutPreparationContext, - onMainThread: OnMinecraftServerThread[IO]): IO[Unit] = { + def overwriteViewWith(newLayout: MenuSlotLayout)( + implicit ctx: LayoutPreparationContext, + onMainThread: OnMinecraftServerThread[IO] + ): IO[Unit] = { type LayoutDiff = Map[Int, Option[Slot]] // 差分があるインデックスを列挙する @@ -39,10 +42,9 @@ class MenuSession private(private val frame: MenuFrame) extends InventoryHolder import cats.implicits._ - domain - .map(key => key -> newMap.get(key)) - .toMap - .filter { case (key, newValue) => oldMap.get(key) neqv newValue } + domain.map(key => key -> newMap.get(key)).toMap.filter { + case (key, newValue) => oldMap.get(key) neqv newValue + } } implicit val slotEq: Eq[Slot] = (x: Slot, y: Slot) => x eq y @@ -76,7 +78,7 @@ class MenuSession private(private val frame: MenuFrame) extends InventoryHolder sessionInventory.getViewers.asScala.toList.foreach { case player: Player => player.updateInventory() - case _ => + case _ => } } } @@ -89,6 +91,7 @@ class MenuSession private(private val frame: MenuFrame) extends InventoryHolder object MenuSession { - def createNewSessionWith[F[_] : Sync](frame: MenuFrame): F[MenuSession] = Sync[F].delay(new MenuSession(frame)) + def createNewSessionWith[F[_]: Sync](frame: MenuFrame): F[MenuSession] = + Sync[F].delay(new MenuSession(frame)) } diff --git a/src/main/scala/com/github/unchama/menuinventory/MenuSlotLayout.scala b/src/main/scala/com/github/unchama/menuinventory/MenuSlotLayout.scala index f72dab4932..a76cb11f8c 100644 --- a/src/main/scala/com/github/unchama/menuinventory/MenuSlotLayout.scala +++ b/src/main/scala/com/github/unchama/menuinventory/MenuSlotLayout.scala @@ -11,27 +11,33 @@ import org.bukkit.event.inventory.InventoryClickEvent * どのインデックスがどの[Slot]と関連付けられているかの情報を持つ[Map]のラッパークラス. */ case class MenuSlotLayout(private[menuinventory] val layoutMap: Map[Int, Slot]) { + /** - * @return クリックされた枠に対応した[Slot]が[InventoryClickEvent]に基づいて引き起こす作用 + * @return + * クリックされた枠に対応した[Slot]が[InventoryClickEvent]に基づいて引き起こす作用 */ - def effectOn(event: InventoryClickEvent)(implicit cs: ContextShift[IO]): TargetedEffect[Player] = + def effectOn( + event: InventoryClickEvent + )(implicit cs: ContextShift[IO]): TargetedEffect[Player] = layoutMap.get(event.getSlot) match { case Some(slot) => slot.effectOn(event) - case None => emptyEffect + case None => emptyEffect } /** * このレイアウトに[another]を「かぶせた」新しいレイアウトを作成する。 * - * 新しいレイアウトのスロットは, 同じ場所が[another]で埋まっている場合[another]のものが, - * そうでなければこのレイアウトのものがセットされている. + * 新しいレイアウトのスロットは, 同じ場所が[another]で埋まっている場合[another]のものが, そうでなければこのレイアウトのものがセットされている. */ - def merge(another: MenuSlotLayout): MenuSlotLayout = MenuSlotLayout(layoutMap ++ another.layoutMap) + def merge(another: MenuSlotLayout): MenuSlotLayout = MenuSlotLayout( + layoutMap ++ another.layoutMap + ) /** * [slotReplacement]でレイアウトの一箇所を置き換えた新しいレイアウトを計算する. */ - def altered(slotReplacement: (Int, Slot)): MenuSlotLayout = copy(layoutMap = layoutMap + slotReplacement) + def altered(slotReplacement: (Int, Slot)): MenuSlotLayout = + copy(layoutMap = layoutMap + slotReplacement) } object MenuSlotLayout { @@ -39,7 +45,9 @@ object MenuSlotLayout { def apply(): MenuSlotLayout = MenuSlotLayout(Map[Int, Slot]()) - @inline def singleSlotLayout(indexedSlot: => (Int, Slot)): MenuSlotLayout = MenuSlotLayout(indexedSlot) + @inline def singleSlotLayout(indexedSlot: => (Int, Slot)): MenuSlotLayout = MenuSlotLayout( + indexedSlot + ) def apply(mappings: (Int, Slot)*): MenuSlotLayout = MenuSlotLayout(Map(mappings: _*)) diff --git a/src/main/scala/com/github/unchama/menuinventory/slot/Slot.scala b/src/main/scala/com/github/unchama/menuinventory/slot/Slot.scala index 3975e063f4..edb94af4c7 100644 --- a/src/main/scala/com/github/unchama/menuinventory/slot/Slot.scala +++ b/src/main/scala/com/github/unchama/menuinventory/slot/Slot.scala @@ -11,9 +11,11 @@ import org.bukkit.inventory.ItemStack * * [effectOn]の作用はクリックにより発生した[InventoryClickEvent]をキャンセル状態にすることができます. * - * @author karayuu + * @author + * karayuu */ trait Slot { + /** * この [Slot] にセットされている [ItemStack]. */ @@ -24,8 +26,12 @@ trait Slot { * * このメソッド自体の呼び出しに副作用はありません. * - * @param event [InventoryClickEvent] - * @return クリックした[Player]へ及ぼすべき作用 + * @param event + * [InventoryClickEvent] + * @return + * クリックした[Player]へ及ぼすべき作用 */ - def effectOn(event: InventoryClickEvent)(implicit cs: ContextShift[IO]): TargetedEffect[Player] + def effectOn(event: InventoryClickEvent)( + implicit cs: ContextShift[IO] + ): TargetedEffect[Player] } diff --git a/src/main/scala/com/github/unchama/menuinventory/slot/button/Button.scala b/src/main/scala/com/github/unchama/menuinventory/slot/button/Button.scala index e95e34db0e..f116160687 100644 --- a/src/main/scala/com/github/unchama/menuinventory/slot/button/Button.scala +++ b/src/main/scala/com/github/unchama/menuinventory/slot/button/Button.scala @@ -4,7 +4,7 @@ import cats.data import cats.effect.{ContextShift, IO} import com.github.unchama.menuinventory.slot.Slot import com.github.unchama.menuinventory.slot.button.action.ButtonEffect -import com.github.unchama.targetedeffect.{TargetedEffect, UnfocusedEffect, _} +import com.github.unchama.targetedeffect._ import org.bukkit.Material import org.bukkit.entity.Player import org.bukkit.event.inventory.InventoryClickEvent @@ -17,13 +17,17 @@ import org.bukkit.inventory.ItemStack * * [effectOn]は常に与えられた[InventoryClickEvent]をキャンセルする副作用を含みます. * - * @param itemStack [Inventory] へセットする [ItemStack] - * @author karayuu + * @param itemStack + * [Inventory] へセットする [ItemStack] + * @author + * karayuu */ -case class Button(override val itemStack: ItemStack, - private val effects: List[ButtonEffect]) extends Slot { - override def effectOn(event: InventoryClickEvent)(implicit cs: ContextShift[IO]): TargetedEffect[Player] = { - import cats.implicits._ +case class Button(override val itemStack: ItemStack, private val effects: List[ButtonEffect]) + extends Slot { + override def effectOn( + event: InventoryClickEvent + )(implicit cs: ContextShift[IO]): TargetedEffect[Player] = { + import com.github.unchama.generic.syntax._ UnfocusedEffect { @@ -33,14 +37,17 @@ case class Button(override val itemStack: ItemStack, }) } - def withAnotherEffect(effect: ButtonEffect): Button = this.copy(effects = effects.appended(effect)) + def withAnotherEffect(effect: ButtonEffect): Button = + this.copy(effects = effects.appended(effect)) } case object Button { + /** * [effects]をひとつずつ作用として発生させる [Slot] を構築します. */ - def apply(itemStack: ItemStack, effects: ButtonEffect*): Button = Button(itemStack, effects.toList) + def apply(itemStack: ItemStack, effects: ButtonEffect*): Button = + Button(itemStack, effects.toList) val empty: Button = apply(new ItemStack(Material.AIR)) -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/menuinventory/slot/button/RecomputedButton.scala b/src/main/scala/com/github/unchama/menuinventory/slot/button/RecomputedButton.scala index 674b28999a..6150b32d1d 100644 --- a/src/main/scala/com/github/unchama/menuinventory/slot/button/RecomputedButton.scala +++ b/src/main/scala/com/github/unchama/menuinventory/slot/button/RecomputedButton.scala @@ -7,16 +7,19 @@ import com.github.unchama.menuinventory.slot.button.action.ButtonEffect import com.github.unchama.minecraft.actions.OnMinecraftServerThread object RecomputedButton { + /** * クリックされるたびに[buttonComputation]に基づいてスロット自体が更新される[Button]を作成する. */ - def apply(buttonComputation: IO[Button]) - (implicit ctx: LayoutPreparationContext, onMainThread: OnMinecraftServerThread[IO]): IO[Button] = + def apply(buttonComputation: IO[Button])( + implicit ctx: LayoutPreparationContext, + onMainThread: OnMinecraftServerThread[IO] + ): IO[Button] = buttonComputation.map { computedButton => val recomputation = - ButtonEffect(scope => Kleisli.liftF( - this (buttonComputation).flatMap(scope.overwriteCurrentSlotBy) - )) + ButtonEffect(scope => + Kleisli.liftF(this(buttonComputation).flatMap(scope.overwriteCurrentSlotBy)) + ) computedButton.withAnotherEffect(recomputation) } diff --git a/src/main/scala/com/github/unchama/menuinventory/slot/button/ReloadingButton.scala b/src/main/scala/com/github/unchama/menuinventory/slot/button/ReloadingButton.scala index a993215dbf..523b3ea8e1 100644 --- a/src/main/scala/com/github/unchama/menuinventory/slot/button/ReloadingButton.scala +++ b/src/main/scala/com/github/unchama/menuinventory/slot/button/ReloadingButton.scala @@ -8,24 +8,21 @@ import com.github.unchama.minecraft.actions.OnMinecraftServerThread import org.bukkit.entity.Player object ReloadingButton { + /** * クリックされるたびに[buttonComputation]に基づいてスロット自体が更新される[Button]を作成する. */ - def apply[M <: Menu](menu: M)(button: Button) - (implicit - environment: menu.Environment, - ctx: LayoutPreparationContext, - onMainThread: OnMinecraftServerThread[IO]): Button = { - button.withAnotherEffect( - ButtonEffect(scope => { - val clicker = scope.event.getWhoClicked.asInstanceOf[Player] - Kleisli.liftF( - for { - newLayout <- menu.computeMenuLayout(clicker) - _ <- scope.overwriteCurrentViewBy(newLayout) - } yield () - ) - }) - ) + def apply[M <: Menu](menu: M)(button: Button)( + implicit environment: menu.Environment, + ctx: LayoutPreparationContext, + onMainThread: OnMinecraftServerThread[IO] + ): Button = { + button.withAnotherEffect(ButtonEffect(scope => { + val clicker = scope.event.getWhoClicked.asInstanceOf[Player] + Kleisli.liftF(for { + newLayout <- menu.computeMenuLayout(clicker) + _ <- scope.overwriteCurrentViewBy(newLayout) + } yield ()) + })) } } diff --git a/src/main/scala/com/github/unchama/menuinventory/slot/button/action/ButtonEffect.scala b/src/main/scala/com/github/unchama/menuinventory/slot/button/action/ButtonEffect.scala index 9131c7e006..d4d3cd6de2 100644 --- a/src/main/scala/com/github/unchama/menuinventory/slot/button/action/ButtonEffect.scala +++ b/src/main/scala/com/github/unchama/menuinventory/slot/button/action/ButtonEffect.scala @@ -20,4 +20,3 @@ object ButtonEffect { def apply(effect: ButtonEffectScope => TargetedEffect[Player]): ButtonEffect = (event: InventoryClickEvent) => effect(ButtonEffectScope(event)) } - diff --git a/src/main/scala/com/github/unchama/menuinventory/slot/button/action/ButtonEffectScope.scala b/src/main/scala/com/github/unchama/menuinventory/slot/button/action/ButtonEffectScope.scala index 4977670aca..eaab2e4492 100644 --- a/src/main/scala/com/github/unchama/menuinventory/slot/button/action/ButtonEffectScope.scala +++ b/src/main/scala/com/github/unchama/menuinventory/slot/button/action/ButtonEffectScope.scala @@ -10,14 +10,16 @@ import org.bukkit.event.inventory.InventoryClickEvent * [ButtonEffect]に渡すScoped Lambdaの中で実行可能であるべきメソッドを提供するスコープオブジェクトのクラス. */ case class ButtonEffectScope(event: InventoryClickEvent) { - def overwriteCurrentViewBy(newLayout: MenuSlotLayout) - (implicit ctx: LayoutPreparationContext, - onMainThread: OnMinecraftServerThread[IO]): IO[Unit] = + def overwriteCurrentViewBy(newLayout: MenuSlotLayout)( + implicit ctx: LayoutPreparationContext, + onMainThread: OnMinecraftServerThread[IO] + ): IO[Unit] = event.getInventory.getHolder.asInstanceOf[MenuSession].overwriteViewWith(newLayout) - def overwriteCurrentSlotBy(newSlot: Slot) - (implicit ctx: LayoutPreparationContext, - onMainThread: OnMinecraftServerThread[IO]): IO[Unit] = { + def overwriteCurrentSlotBy(newSlot: Slot)( + implicit ctx: LayoutPreparationContext, + onMainThread: OnMinecraftServerThread[IO] + ): IO[Unit] = { val session = event.getInventory.getHolder.asInstanceOf[MenuSession] for { diff --git a/src/main/scala/com/github/unchama/menuinventory/slot/button/action/ClickEventFilter.scala b/src/main/scala/com/github/unchama/menuinventory/slot/button/action/ClickEventFilter.scala index f5e16def15..b5359cf2d4 100644 --- a/src/main/scala/com/github/unchama/menuinventory/slot/button/action/ClickEventFilter.scala +++ b/src/main/scala/com/github/unchama/menuinventory/slot/button/action/ClickEventFilter.scala @@ -3,14 +3,19 @@ package com.github.unchama.menuinventory.slot.button.action import org.bukkit.event.inventory.InventoryClickEvent /** - * @author karayuu + * @author + * karayuu */ -case class ClickEventFilter(private val predicate: InventoryClickEvent => Boolean) extends AnyVal { +case class ClickEventFilter(private val predicate: InventoryClickEvent => Boolean) + extends AnyVal { + /** * 与えられた [InventoryClickEvent] に対して動作を行うべきか返します. * - * @param event [InventoryClickEvent] - * @return true: 動作を行う / false: 動作を行わない + * @param event + * [InventoryClickEvent] + * @return + * true: 動作を行う / false: 動作を行わない */ def shouldReactTo(event: InventoryClickEvent): Boolean = predicate(event) } diff --git a/src/main/scala/com/github/unchama/menuinventory/slot/button/action/FilteredButtonEffect.scala b/src/main/scala/com/github/unchama/menuinventory/slot/button/action/FilteredButtonEffect.scala index 0a6fe19481..359bfe488c 100644 --- a/src/main/scala/com/github/unchama/menuinventory/slot/button/action/FilteredButtonEffect.scala +++ b/src/main/scala/com/github/unchama/menuinventory/slot/button/action/FilteredButtonEffect.scala @@ -8,13 +8,16 @@ import org.bukkit.event.inventory.InventoryClickEvent /** * "フィルタ"付きの[ButtonEffect] * - * @param clickEventFilter InventoryClickEventを受け取り動作を行わせるかを決定する [ClickEventFilter] - * @param effect InventoryClickEventを受け取り何かしらの作用を発生させる関数 + * @param clickEventFilter + * InventoryClickEventを受け取り動作を行わせるかを決定する [ClickEventFilter] + * @param effect + * InventoryClickEventを受け取り何かしらの作用を発生させる関数 * - * [effect]は[clickEventFilter] がtrueを返した際に発火されます. + * [effect]は[clickEventFilter] がtrueを返した際に発火されます. */ -case class FilteredButtonEffect(private val clickEventFilter: ClickEventFilter) - (private val effect: ButtonEffectScope => TargetedEffect[Player]) extends ButtonEffect { +case class FilteredButtonEffect(private val clickEventFilter: ClickEventFilter)( + private val effect: ButtonEffectScope => TargetedEffect[Player] +) extends ButtonEffect { /** * [ButtonEffectScope]に依存しない[TargetedEffect]を実行する[FilteredButtonEffect]を構築する. @@ -43,6 +46,11 @@ object LeftClickButtonEffect { /** * [ButtonEffectScope]に依存しない[TargetedEffect]を実行する[LeftClickButtonEffect]を構築する. */ - def apply(effect: TargetedEffect[Player], effects: TargetedEffect[Player]*): FilteredButtonEffect = - FilteredButtonEffect(ClickEventFilter.LEFT_CLICK)((_: ButtonEffectScope) => effect.followedBy(SequentialEffect(effects: _*))) + def apply( + effect: TargetedEffect[Player], + effects: TargetedEffect[Player]* + ): FilteredButtonEffect = + FilteredButtonEffect(ClickEventFilter.LEFT_CLICK)((_: ButtonEffectScope) => + effect.followedBy(SequentialEffect(effects: _*)) + ) } diff --git a/src/main/scala/com/github/unchama/menuinventory/syntax/AllSyntax.scala b/src/main/scala/com/github/unchama/menuinventory/syntax/AllSyntax.scala index c1947ce8e5..42ed0fb3bc 100644 --- a/src/main/scala/com/github/unchama/menuinventory/syntax/AllSyntax.scala +++ b/src/main/scala/com/github/unchama/menuinventory/syntax/AllSyntax.scala @@ -13,11 +13,10 @@ trait InventorySizeSyntax { implicit final class InventorySizeOps(val size: InventorySize) { def slotCount: Int = size match { - case Left(rowSize) => rowSize.rows * 9 + case Left(rowSize) => rowSize.rows * 9 case Right(inventoryType) => inventoryType.getDefaultSize } } } -trait AllSyntax extends InventorySizeIntSyntax - with InventorySizeSyntax +trait AllSyntax extends InventorySizeIntSyntax with InventorySizeSyntax diff --git a/src/main/scala/com/github/unchama/minecraft/actions/GetConnectedPlayers.scala b/src/main/scala/com/github/unchama/minecraft/actions/GetConnectedPlayers.scala index 556e2d95ee..6c5198f52c 100644 --- a/src/main/scala/com/github/unchama/minecraft/actions/GetConnectedPlayers.scala +++ b/src/main/scala/com/github/unchama/minecraft/actions/GetConnectedPlayers.scala @@ -18,6 +18,8 @@ trait GetConnectedPlayers[F[_], Player] { object GetConnectedPlayers { - def apply[F[_], Player](implicit ev: GetConnectedPlayers[F, Player]): GetConnectedPlayers[F, Player] = ev + def apply[F[_], Player]( + implicit ev: GetConnectedPlayers[F, Player] + ): GetConnectedPlayers[F, Player] = ev } diff --git a/src/main/scala/com/github/unchama/minecraft/actions/OnMinecraftServerThread.scala b/src/main/scala/com/github/unchama/minecraft/actions/OnMinecraftServerThread.scala index 6cfa072cef..d79b6994ed 100644 --- a/src/main/scala/com/github/unchama/minecraft/actions/OnMinecraftServerThread.scala +++ b/src/main/scala/com/github/unchama/minecraft/actions/OnMinecraftServerThread.scala @@ -7,7 +7,7 @@ trait OnMinecraftServerThread[F[_]] { /** * マインクラフトサーバーが走るスレッド上でアクションを実行する。 */ - def runAction[G[_] : SyncEffect, A](ga: G[A]): F[A] + def runAction[G[_]: SyncEffect, A](ga: G[A]): F[A] } diff --git a/src/main/scala/com/github/unchama/minecraft/actions/SendMinecraftMessage.scala b/src/main/scala/com/github/unchama/minecraft/actions/SendMinecraftMessage.scala index e1f6afb32e..9fedddbf67 100644 --- a/src/main/scala/com/github/unchama/minecraft/actions/SendMinecraftMessage.scala +++ b/src/main/scala/com/github/unchama/minecraft/actions/SendMinecraftMessage.scala @@ -14,16 +14,19 @@ trait SendMinecraftMessage[F[_], Target] { strings.traverse(string(player, _)).as(()) def mapK[G[_]](fk: F ~> G): SendMinecraftMessage[G, Target] = - (player: Target, s: String) => - fk(SendMinecraftMessage.this.string(player, s)) + (player: Target, s: String) => fk(SendMinecraftMessage.this.string(player, s)) } object SendMinecraftMessage { - def apply[F[_], Player](implicit ev: SendMinecraftMessage[F, Player]): SendMinecraftMessage[F, Player] = implicitly + def apply[F[_], Player]( + implicit ev: SendMinecraftMessage[F, Player] + ): SendMinecraftMessage[F, Player] = implicitly - implicit def coercion[F[_], G[_]](implicit ev: SendMinecraftMessage[F, Player], - fg: ContextCoercion[F, G]): SendMinecraftMessage[G, Player] = + implicit def coercion[F[_], G[_]]( + implicit ev: SendMinecraftMessage[F, Player], + fg: ContextCoercion[F, G] + ): SendMinecraftMessage[G, Player] = ev.mapK(fg) } diff --git a/src/main/scala/com/github/unchama/minecraft/bukkit/actions/BroadcastBukkitMessage.scala b/src/main/scala/com/github/unchama/minecraft/bukkit/actions/BroadcastBukkitMessage.scala index 000bd47998..a94d055081 100644 --- a/src/main/scala/com/github/unchama/minecraft/bukkit/actions/BroadcastBukkitMessage.scala +++ b/src/main/scala/com/github/unchama/minecraft/bukkit/actions/BroadcastBukkitMessage.scala @@ -4,7 +4,8 @@ import cats.effect.{Sync, SyncIO} import com.github.unchama.minecraft.actions.{BroadcastMinecraftMessage, OnMinecraftServerThread} import org.bukkit.Bukkit -class BroadcastBukkitMessage[F[_] : Sync : OnMinecraftServerThread] extends BroadcastMinecraftMessage[F] { +class BroadcastBukkitMessage[F[_]: Sync: OnMinecraftServerThread] + extends BroadcastMinecraftMessage[F] { import scala.jdk.CollectionConverters._ @@ -18,7 +19,7 @@ class BroadcastBukkitMessage[F[_] : Sync : OnMinecraftServerThread] extends Broa object BroadcastBukkitMessage { - def apply[F[_] : Sync : OnMinecraftServerThread]: BroadcastMinecraftMessage[F] = + def apply[F[_]: Sync: OnMinecraftServerThread]: BroadcastMinecraftMessage[F] = new BroadcastBukkitMessage[F] } diff --git a/src/main/scala/com/github/unchama/minecraft/bukkit/actions/GetConnectedBukkitPlayers.scala b/src/main/scala/com/github/unchama/minecraft/bukkit/actions/GetConnectedBukkitPlayers.scala index 98eb829706..0684e38bbf 100644 --- a/src/main/scala/com/github/unchama/minecraft/bukkit/actions/GetConnectedBukkitPlayers.scala +++ b/src/main/scala/com/github/unchama/minecraft/bukkit/actions/GetConnectedBukkitPlayers.scala @@ -5,9 +5,8 @@ import com.github.unchama.minecraft.actions.{GetConnectedPlayers, OnMinecraftSer import org.bukkit.Bukkit import org.bukkit.entity.Player -class GetConnectedBukkitPlayers[ - F[_] : Sync : OnMinecraftServerThread -] extends GetConnectedPlayers[F, Player] { +class GetConnectedBukkitPlayers[F[_]: Sync: OnMinecraftServerThread] + extends GetConnectedPlayers[F, Player] { import scala.jdk.CollectionConverters._ diff --git a/src/main/scala/com/github/unchama/minecraft/bukkit/actions/OnBukkitServerThread.scala b/src/main/scala/com/github/unchama/minecraft/bukkit/actions/OnBukkitServerThread.scala index dd38426012..71ca5629f6 100644 --- a/src/main/scala/com/github/unchama/minecraft/bukkit/actions/OnBukkitServerThread.scala +++ b/src/main/scala/com/github/unchama/minecraft/bukkit/actions/OnBukkitServerThread.scala @@ -7,15 +7,15 @@ import com.github.unchama.minecraft.actions.OnMinecraftServerThread import org.bukkit.Bukkit import org.bukkit.plugin.java.JavaPlugin -class OnBukkitServerThread[ - F[_] -](implicit hostPlugin: JavaPlugin, +class OnBukkitServerThread[F[_]]( + implicit hostPlugin: JavaPlugin, shift: ContextShift[F], - F: ConcurrentEffect[F]) extends OnMinecraftServerThread[F] { + F: ConcurrentEffect[F] +) extends OnMinecraftServerThread[F] { import cats.implicits._ - override def runAction[G[_] : SyncEffect, A](ga: G[A]): F[A] = { + override def runAction[G[_]: SyncEffect, A](ga: G[A]): F[A] = { val checkMainThread = Sync[G].delay { hostPlugin.getServer.isPrimaryThread } @@ -32,18 +32,19 @@ class OnBukkitServerThread[ // 実行結果が得られていない場合、メインスレッドに飛んで実行結果を戻す // メインスレッドに飛ぶアクション自体をcancelableにする - case None => F.cancelable[A] { cb => - val run: Runnable = () => { - // メインスレッド内でgaを実行、結果を取り出し、継続に渡す - val a = SyncEffect[G].runSync[SyncIO, A](ga).unsafeRunSync() - cb(Right(a)) + case None => + F.cancelable[A] { cb => + val run: Runnable = () => { + // メインスレッド内でgaを実行、結果を取り出し、継続に渡す + val a = SyncEffect[G].runSync[SyncIO, A](ga).unsafeRunSync() + cb(Right(a)) + } + + val task = Bukkit.getScheduler.runTask(hostPlugin, run) + + // runAction自体がキャンセル可能になるために、cancelableに対してtask.cancelを戻す + F.delay(task.cancel()) } - - val task = Bukkit.getScheduler.runTask(hostPlugin, run) - - // runAction自体がキャンセル可能になるために、cancelableに対してtask.cancelを戻す - F.delay(task.cancel()) - } } // 継続の実行がメインスレッドから外れるよう促す diff --git a/src/main/scala/com/github/unchama/minecraft/bukkit/actions/SendBukkitMessage.scala b/src/main/scala/com/github/unchama/minecraft/bukkit/actions/SendBukkitMessage.scala index 80e72c0800..73bd5c8922 100644 --- a/src/main/scala/com/github/unchama/minecraft/bukkit/actions/SendBukkitMessage.scala +++ b/src/main/scala/com/github/unchama/minecraft/bukkit/actions/SendBukkitMessage.scala @@ -4,7 +4,7 @@ import cats.effect.Sync import com.github.unchama.minecraft.actions.SendMinecraftMessage import org.bukkit.entity.Player -class SendBukkitMessage[F[_] : Sync] extends SendMinecraftMessage[F, Player] { +class SendBukkitMessage[F[_]: Sync] extends SendMinecraftMessage[F, Player] { // NOTE: プレーヤーがオフラインの時にこのアクションを実行しても問題ない override def string(player: Player, s: String): F[Unit] = Sync[F].delay(player.sendMessage(s)) @@ -12,6 +12,6 @@ class SendBukkitMessage[F[_] : Sync] extends SendMinecraftMessage[F, Player] { object SendBukkitMessage { - implicit def apply[F[_] : Sync]: SendMinecraftMessage[F, Player] = new SendBukkitMessage[F] + implicit def apply[F[_]: Sync]: SendMinecraftMessage[F, Player] = new SendBukkitMessage[F] } diff --git a/src/main/scala/com/github/unchama/minecraft/bukkit/objects/BukkitBossBar.scala b/src/main/scala/com/github/unchama/minecraft/bukkit/objects/BukkitBossBar.scala index 742ae7bb43..f216877dff 100644 --- a/src/main/scala/com/github/unchama/minecraft/bukkit/objects/BukkitBossBar.scala +++ b/src/main/scala/com/github/unchama/minecraft/bukkit/objects/BukkitBossBar.scala @@ -6,17 +6,23 @@ import com.github.unchama.minecraft.objects.MinecraftBossBar import org.bukkit.Bukkit import org.bukkit.boss.{BarColor, BarFlag, BarStyle, BossBar} -class BukkitBossBar[F[_]] private(instance: BossBar)(implicit F: Sync[F]) extends MinecraftBossBar[F] { +class BukkitBossBar[F[_]] private (instance: BossBar)(implicit F: Sync[F]) + extends MinecraftBossBar[F] { override type Player = org.bukkit.entity.Player override type Style = BarStyle override type Color = BarColor override type Flag = BarFlag - override val title: ReadWrite[F, String] = ReadWrite.liftUnsafe(instance.getTitle, instance.setTitle) - override val color: ReadWrite[F, BarColor] = ReadWrite.liftUnsafe(instance.getColor, instance.setColor) - override val style: ReadWrite[F, BarStyle] = ReadWrite.liftUnsafe(instance.getStyle, instance.setStyle) - override val progress: ReadWrite[F, Double] = ReadWrite.liftUnsafe(instance.getProgress, instance.setProgress) - override val visibility: ReadWrite[F, Boolean] = ReadWrite.liftUnsafe(instance.isVisible, instance.setVisible) + override val title: ReadWrite[F, String] = + ReadWrite.liftUnsafe(instance.getTitle, instance.setTitle) + override val color: ReadWrite[F, BarColor] = + ReadWrite.liftUnsafe(instance.getColor, instance.setColor) + override val style: ReadWrite[F, BarStyle] = + ReadWrite.liftUnsafe(instance.getStyle, instance.setStyle) + override val progress: ReadWrite[F, Double] = + ReadWrite.liftUnsafe(instance.getProgress, instance.setProgress) + override val visibility: ReadWrite[F, Boolean] = + ReadWrite.liftUnsafe(instance.isVisible, instance.setVisible) override val flags: FlagOperations = new FlagOperations { override def add(flag: BarFlag): F[Unit] = F.delay(instance.addFlag(flag)) @@ -40,9 +46,13 @@ class BukkitBossBar[F[_]] private(instance: BossBar)(implicit F: Sync[F]) extend object BukkitBossBar { - def apply[F[_] : Sync](title: String, color: BarColor, style: BarStyle): F[BukkitBossBar[F]] = + def apply[F[_]: Sync](title: String, color: BarColor, style: BarStyle): F[BukkitBossBar[F]] = in[F, F](title, color, style) - def in[G[_] : Sync, F[_] : Sync](title: String, color: BarColor, style: BarStyle): G[BukkitBossBar[F]] = + def in[G[_]: Sync, F[_]: Sync]( + title: String, + color: BarColor, + style: BarStyle + ): G[BukkitBossBar[F]] = Sync[G].delay(new BukkitBossBar(Bukkit.getServer.createBossBar(title, color, style))) } diff --git a/src/main/scala/com/github/unchama/seichiassist/Config.scala b/src/main/scala/com/github/unchama/seichiassist/Config.scala index 9099a5d0b5..afa86bb7c0 100644 --- a/src/main/scala/com/github/unchama/seichiassist/Config.scala +++ b/src/main/scala/com/github/unchama/seichiassist/Config.scala @@ -1,12 +1,28 @@ package com.github.unchama.seichiassist -import com.github.unchama.bungeesemaphoreresponder.{RedisConnectionSettings, Configuration => BungeeSemaphoreResponderConfiguration} +import com.github.unchama.bungeesemaphoreresponder.{ + RedisConnectionSettings, + Configuration => BungeeSemaphoreResponderConfiguration +} import com.github.unchama.seichiassist.domain.configuration.RedisBungeeRedisConfiguration -import com.github.unchama.seichiassist.subsystems.autosave.application.{SystemConfiguration => AutoSaveConfiguration} -import com.github.unchama.seichiassist.subsystems.buildcount.application.{BuildExpMultiplier, Configuration => BuildCountConfiguration} +import com.github.unchama.seichiassist.subsystems.anywhereender.{ + SystemConfiguration => AnywhereEnderConfiguration +} +import com.github.unchama.seichiassist.subsystems.autosave.application.{ + SystemConfiguration => AutoSaveConfiguration +} +import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.SeichiLevel +import com.github.unchama.seichiassist.subsystems.buildcount.application.{ + BuildExpMultiplier, + Configuration => BuildCountConfiguration +} import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.application.{Configuration => FastDiggingEffectConfiguration} -import com.github.unchama.seichiassist.subsystems.discordnotification.{SystemConfiguration => DiscordNotificationConfiguration} +import com.github.unchama.seichiassist.subsystems.discordnotification.{ + SystemConfiguration => DiscordNotificationConfiguration +} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.application.{ + Configuration => FastDiggingEffectConfiguration +} import org.bukkit.World import org.bukkit.configuration.file.FileConfiguration import org.bukkit.plugin.java.JavaPlugin @@ -23,7 +39,7 @@ object Config { } } -final class Config private(val config: FileConfiguration) { +final class Config private (val config: FileConfiguration) { def getFlyExp: Int = config.getString("flyexp").toInt // NOTE: @@ -41,14 +57,12 @@ final class Config private(val config: FileConfiguration) { def getDualBreaklevel: Int = getIntFailFast("dualbreaklevel") - def getMultipleIDBlockBreaklevel: Int = getIntFailFast("multipleidblockbreaklevel") + def getMultipleIDBlockBreakLevel: Int = getIntFailFast("multipleidblockbreaklevel") def getDropExplevel(i: Int): Double = getDoubleFailFast("dropexplevel" + i) def getPassivePortalInventorylevel: Int = getIntFailFast("passiveportalinventorylevel") - def getDokodemoEnderlevel: Int = getIntFailFast("dokodemoenderlevel") - def getMineStacklevel(i: Int): Int = getIntFailFast("minestacklevel" + i) def getDB: String = config.getString("db") @@ -69,14 +83,14 @@ final class Config private(val config: FileConfiguration) { s"jdbc:mysql://$hostComponent$portComponent" } - //サーバー番号取得 + // サーバー番号取得 def getServerNum: Int = getIntFailFast("servernum") def getServerId: String = config.getString("server-id") def chunkSearchCommandBase: String = config.getString("chunk-search-command-base") - //サブホーム最大数取得 + // サブホーム最大数取得 def getSubHomeMax: Int = getIntFailFast("subhomemax") def getDebugMode: Int = getIntFailFast("debugmode") @@ -88,10 +102,15 @@ final class Config private(val config: FileConfiguration) { /** * 木の棒メニュー内のグリッド式保護メニューによる保護が許可されたワールドか * - * @param world 対象のワールド - * @return 許可されているならtrue、許可されていないならfalse + * @param world + * 対象のワールド + * @return + * 許可されているならtrue、許可されていないならfalse */ - def isGridProtectionEnabled(world: World): Boolean = config.getStringList("GridProtectEnableWorld").parallelStream.anyMatch((name: String) => world.getName.equalsIgnoreCase(name)) + def isGridProtectionEnabled(world: World): Boolean = config + .getStringList("GridProtectEnableWorld") + .parallelStream + .anyMatch((name: String) => world.getName.equalsIgnoreCase(name)) /** * ワールドごとのグリッド保護上限値を返却。該当の設定値がなければデフォ値を返却 @@ -99,7 +118,8 @@ final class Config private(val config: FileConfiguration) { * @param world * @return */ - def getGridLimitPerWorld(world: String): Int = config.getString("GridLimitPerWorld." + world, config.getString("GridLimitDefault")).toInt + def getGridLimitPerWorld(world: String): Int = + config.getString("GridLimitPerWorld." + world, config.getString("GridLimitDefault")).toInt def getTemplateKeepAmount: Int = getIntFailFast("GridTemplateKeepAmount") @@ -127,15 +147,19 @@ final class Config private(val config: FileConfiguration) { /** * 各種URLを返します. * - * @param typeName Url以下の項目名 - * @return 該当URL.ただし, typeNameが誤っていた場合は""を返します. + * @param typeName + * Url以下の項目名 + * @return + * 該当URL.ただし, typeNameが誤っていた場合は""を返します. */ def getUrl(typeName: String): String = config.getString("Url." + typeName, "") def getFastDiggingEffectSystemConfiguration: FastDiggingEffectConfiguration = { new FastDiggingEffectConfiguration { override val amplifierPerBlockMined: Double = getDoubleFailFast("minutespeedamount") - override val amplifierPerPlayerConnection: Double = getDoubleFailFast("onlineplayersamount") + override val amplifierPerPlayerConnection: Double = getDoubleFailFast( + "onlineplayersamount" + ) } } @@ -186,13 +210,21 @@ final class Config private(val config: FileConfiguration) { } } + def getAnywhereEnderConfiguration: AnywhereEnderConfiguration = + new AnywhereEnderConfiguration { + override val requiredMinimumLevel: SeichiLevel = SeichiLevel( + getIntFailFast("dokodemoenderlevel") + ) + } + def getAutoSaveSystemConfiguration: AutoSaveConfiguration = new AutoSaveConfiguration { override val autoSaveEnabled: Boolean = config.getBoolean("AutoSave.Enable") } - def discordNotificationConfiguration: DiscordNotificationConfiguration = new DiscordNotificationConfiguration { - override val webhookUrl: String = config.getString("Url.webhook.notification") - } + def discordNotificationConfiguration: DiscordNotificationConfiguration = + new DiscordNotificationConfiguration { + override val webhookUrl: String = config.getString("Url.webhook.notification") + } def buildCountConfiguration: BuildCountConfiguration = new BuildCountConfiguration { override val multipliers: BuildExpMultiplier = new BuildExpMultiplier { @@ -202,4 +234,4 @@ final class Config private(val config: FileConfiguration) { override val oneMinuteBuildExpLimit: BuildExpAmount = BuildExpAmount(BigDecimal(config.getString("BuildNum1minLimit"))) } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/DefaultEffectEnvironment.scala b/src/main/scala/com/github/unchama/seichiassist/DefaultEffectEnvironment.scala index 93e8fa4dd7..24823286bd 100644 --- a/src/main/scala/com/github/unchama/seichiassist/DefaultEffectEnvironment.scala +++ b/src/main/scala/com/github/unchama/seichiassist/DefaultEffectEnvironment.scala @@ -7,15 +7,16 @@ import com.github.unchama.generic.effect.unsafe.EffectEnvironment // TODO prepare alternative environment that uses dedicated Logger for effect execution object DefaultEffectEnvironment extends EffectEnvironment { - override def unsafeRunEffectAsync[U, F[_] : Effect](context: String, program: F[U]): Unit = { + override def unsafeRunEffectAsync[U, F[_]: Effect](context: String, program: F[U]): Unit = { import cats.effect.implicits._ program .runAsync { - case Left(error) => IO { - println(s"${context}最中にエラーが発生しました。") - error.printStackTrace() - } + case Left(error) => + IO { + println(s"${context}最中にエラーが発生しました。") + error.printStackTrace() + } case Right(_) => IO.unit } .unsafeRunSync() diff --git a/src/main/scala/com/github/unchama/seichiassist/LevelThresholds.scala b/src/main/scala/com/github/unchama/seichiassist/LevelThresholds.scala index ce66e7f18f..5f8577beb2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/LevelThresholds.scala +++ b/src/main/scala/com/github/unchama/seichiassist/LevelThresholds.scala @@ -1,12 +1,9 @@ package com.github.unchama.seichiassist object LevelThresholds { - val giganticBerserkLevelList: List[Int] = List( - 20, 30, 40, 40, 50, 50, 60, 70, 80, 100, - 100, 110, 120, 130, 140, 150, 160, 170, 180, 200, - 250, 270, 300, 320, 350, 370, 400, 420, 450, 500, - 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1500, - 1500, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, - 10000, 20000, 30000, 40000, 50000, 60000, 70000, 85000, 100000 - ) + val giganticBerserkLevelList: List[Int] = List(20, 30, 40, 40, 50, 50, 60, 70, 80, 100, 100, + 110, 120, 130, 140, 150, 160, 170, 180, 200, 250, 270, 300, 320, 350, 370, 400, 420, 450, + 500, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1500, 1500, 2000, 3000, 4000, 5000, + 6000, 7000, 8000, 9000, 10000, 10000, 20000, 30000, 40000, 50000, 60000, 70000, 85000, + 100000) } diff --git a/src/main/scala/com/github/unchama/seichiassist/ManagedWorld.scala b/src/main/scala/com/github/unchama/seichiassist/ManagedWorld.scala index ea1475dab0..a7cc146843 100644 --- a/src/main/scala/com/github/unchama/seichiassist/ManagedWorld.scala +++ b/src/main/scala/com/github/unchama/seichiassist/ManagedWorld.scala @@ -29,18 +29,14 @@ case object ManagedWorld extends Enum[ManagedWorld] { case object WORLD_DOT extends ManagedWorld("world_dot", "ドット絵ワールド") - implicit class ManagedWorldOps(val managedWorld: ManagedWorld) extends AnyVal { + implicit class ManagedWorldOps(private val managedWorld: ManagedWorld) extends AnyVal { + /** - * 整地ワールドであるかどうか - * これらのワールドは整地スキルが利用可能であり、整地量をカウントするワールドである。 + * 整地ワールドであるかどうか これらのワールドは整地スキルが利用可能であり、整地量をカウントするワールドである。 */ def isSeichi: Boolean = managedWorld match { - case WORLD_SW - | WORLD_SW_2 - | WORLD_SW_3 - | WORLD_SW_4 - | WORLD_SW_NETHER - | WORLD_SW_END => true + case WORLD_SW | WORLD_SW_2 | WORLD_SW_3 | WORLD_SW_4 | WORLD_SW_NETHER | WORLD_SW_END => + true case _ => false } @@ -51,23 +47,16 @@ case object ManagedWorld extends Enum[ManagedWorld] { */ def isSeichiWorldWithWGRegions: Boolean = managedWorld match { case WORLD_SW_2 | WORLD_SW_4 => true - case _ => false + case _ => false } /** - * 建築量をカウントするワールドかどうか - * 建築スキルが使えるかどうかと等しい + * 建築量をカウントするワールドかどうか 建築スキルが使えるかどうかと等しい */ def shouldTrackBuildBlock: Boolean = managedWorld match { - case WORLD_2 - | WORLD_SW - | WORLD_SW_2 - | WORLD_SW_3 - | WORLD_SW_4 - | WORLD_SW_NETHER - | WORLD_SW_END - | WORLD_DOT - | WORLD_BUILD => true + case WORLD_2 | WORLD_SW | WORLD_SW_2 | WORLD_SW_3 | WORLD_SW_4 | WORLD_SW_NETHER | + WORLD_SW_END | WORLD_DOT | WORLD_BUILD => + true case _ => false } @@ -77,7 +66,7 @@ case object ManagedWorld extends Enum[ManagedWorld] { def isSeichiSkillAllowed: Boolean = isSeichi || { managedWorld match { case WORLD_2 | WORLD_BUILD => true - case _ => false + case _ => false } } @@ -98,12 +87,14 @@ case object ManagedWorld extends Enum[ManagedWorld] { def isSeichiSkillAllowed: Boolean = asManagedWorld().exists(_.isSeichiSkillAllowed) - def isBlockLineUpSkillEnabled: Boolean = asManagedWorld().exists(_.isBlockLineUpSkillEnabled) + def isBlockLineUpSkillEnabled: Boolean = + asManagedWorld().exists(_.isBlockLineUpSkillEnabled) } val seichiWorlds: IndexedSeq[ManagedWorld] = values.filter(_.isSeichi) def fromBukkitWorld(world: World): Option[ManagedWorld] = fromName(world.getName) - def fromName(worldName: String): Option[ManagedWorld] = values.find(_.alphabetName == worldName) + def fromName(worldName: String): Option[ManagedWorld] = + values.find(_.alphabetName == worldName) } diff --git a/src/main/scala/com/github/unchama/seichiassist/MaterialSets.scala b/src/main/scala/com/github/unchama/seichiassist/MaterialSets.scala index 78a580e676..6cf1cbf973 100644 --- a/src/main/scala/com/github/unchama/seichiassist/MaterialSets.scala +++ b/src/main/scala/com/github/unchama/seichiassist/MaterialSets.scala @@ -8,50 +8,147 @@ import org.bukkit.inventory.ItemStack object MaterialSets { val fortuneMaterials: Set[Material] = Set( - Material.COAL_ORE, Material.DIAMOND_ORE, - Material.LAPIS_ORE, Material.EMERALD_ORE, - Material.REDSTONE_ORE, Material.GLOWING_REDSTONE_ORE, + Material.COAL_ORE, + Material.DIAMOND_ORE, + Material.LAPIS_ORE, + Material.EMERALD_ORE, + Material.REDSTONE_ORE, + Material.GLOWING_REDSTONE_ORE, Material.QUARTZ_ORE ) // このMaterialは整地スキルに対応する val materials: Set[Material] = Set( - Material.STONE, Material.NETHERRACK, Material.NETHER_BRICK, Material.DIRT, Material.GRAVEL, Material.LOG, - Material.LOG_2, Material.GRASS, Material.IRON_ORE, Material.GOLD_ORE, Material.SAND, - Material.SANDSTONE, Material.END_BRICKS, Material.ENDER_STONE, Material.ICE, - Material.PACKED_ICE, Material.OBSIDIAN, Material.MAGMA, Material.SOUL_SAND, Material.LEAVES, Material.LEAVES_2, - Material.CLAY, Material.STAINED_CLAY, Material.COBBLESTONE, Material.MOSSY_COBBLESTONE, Material.HARD_CLAY, - Material.MONSTER_EGGS, Material.WEB, Material.WOOD, Material.FENCE, Material.DARK_OAK_FENCE, Material.RAILS, - Material.MYCEL, Material.SNOW_BLOCK, Material.HUGE_MUSHROOM_1, Material.HUGE_MUSHROOM_2, Material.BONE_BLOCK, - Material.PURPUR_BLOCK, Material.PURPUR_PILLAR, Material.SEA_LANTERN, Material.PRISMARINE, Material.SMOOTH_BRICK, - Material.GLOWSTONE, Material.STAINED_GLASS, Material.STAINED_GLASS_PANE, Material.THIN_GLASS, Material.GLASS, - Material.WOOD_STAIRS, Material.BIRCH_WOOD_STAIRS, Material.SPRUCE_WOOD_STAIRS, Material.ACACIA_STAIRS, - Material.DARK_OAK_STAIRS, Material.BIRCH_FENCE, Material.SPRUCE_FENCE, Material.ACACIA_FENCE, Material.FENCE_GATE, - Material.BIRCH_FENCE_GATE, Material.SPRUCE_FENCE_GATE, Material.ACACIA_FENCE_GATE, Material.DARK_OAK_FENCE_GATE, - Material.COBBLESTONE_STAIRS, Material.SANDSTONE_STAIRS, Material.BRICK_STAIRS, Material.QUARTZ_STAIRS, - Material.BOOKSHELF, Material.IRON_FENCE, Material.ICE, Material.WOOL, Material.GOLD_BLOCK, Material.END_ROD, - Material.PUMPKIN, Material.MELON_BLOCK, Material.STONE_SLAB2, Material.SPONGE, Material.SOIL, Material.GRASS_PATH, - Material.MOB_SPAWNER, Material.WORKBENCH, Material.FURNACE, Material.QUARTZ_BLOCK, Material.CHEST, - Material.TRAPPED_CHEST, Material.NETHER_FENCE, Material.NETHER_BRICK_STAIRS, Material.CAULDRON, Material.END_ROD, - Material.PURPUR_STAIRS, Material.END_BRICKS, Material.PURPUR_SLAB, Material.ENDER_CHEST, Material.PURPUR_SLAB, Material.STEP, - Material.DOUBLE_STEP, Material.ENDER_PORTAL_FRAME, Material.ENDER_PORTAL, Material.VINE, + Material.STONE, + Material.NETHERRACK, + Material.NETHER_BRICK, + Material.DIRT, + Material.GRAVEL, + Material.LOG, + Material.LOG_2, + Material.GRASS, + Material.IRON_ORE, + Material.GOLD_ORE, + Material.SAND, + Material.SANDSTONE, + Material.END_BRICKS, + Material.ENDER_STONE, + Material.ICE, + Material.PACKED_ICE, + Material.OBSIDIAN, + Material.MAGMA, + Material.SOUL_SAND, + Material.LEAVES, + Material.LEAVES_2, + Material.CLAY, + Material.STAINED_CLAY, + Material.COBBLESTONE, + Material.MOSSY_COBBLESTONE, + Material.HARD_CLAY, + Material.MONSTER_EGGS, + Material.WEB, + Material.WOOD, + Material.FENCE, + Material.DARK_OAK_FENCE, + Material.RAILS, + Material.MYCEL, + Material.SNOW_BLOCK, + Material.HUGE_MUSHROOM_1, + Material.HUGE_MUSHROOM_2, + Material.BONE_BLOCK, + Material.PURPUR_BLOCK, + Material.PURPUR_PILLAR, + Material.SEA_LANTERN, + Material.PRISMARINE, + Material.SMOOTH_BRICK, + Material.GLOWSTONE, + Material.STAINED_GLASS, + Material.STAINED_GLASS_PANE, + Material.THIN_GLASS, + Material.GLASS, + Material.WOOD_STAIRS, + Material.BIRCH_WOOD_STAIRS, + Material.SPRUCE_WOOD_STAIRS, + Material.ACACIA_STAIRS, + Material.DARK_OAK_STAIRS, + Material.BIRCH_FENCE, + Material.SPRUCE_FENCE, + Material.ACACIA_FENCE, + Material.FENCE_GATE, + Material.BIRCH_FENCE_GATE, + Material.SPRUCE_FENCE_GATE, + Material.ACACIA_FENCE_GATE, + Material.DARK_OAK_FENCE_GATE, + Material.COBBLESTONE_STAIRS, + Material.SANDSTONE_STAIRS, + Material.BRICK_STAIRS, + Material.QUARTZ_STAIRS, + Material.BOOKSHELF, + Material.IRON_FENCE, + Material.ICE, + Material.WOOL, + Material.GOLD_BLOCK, + Material.END_ROD, + Material.PUMPKIN, + Material.MELON_BLOCK, + Material.STONE_SLAB2, + Material.SPONGE, + Material.SOIL, + Material.GRASS_PATH, + Material.MOB_SPAWNER, + Material.WORKBENCH, + Material.FURNACE, + Material.QUARTZ_BLOCK, + Material.CHEST, + Material.TRAPPED_CHEST, + Material.NETHER_FENCE, + Material.NETHER_BRICK_STAIRS, + Material.CAULDRON, + Material.END_ROD, + Material.PURPUR_STAIRS, + Material.END_BRICKS, + Material.PURPUR_SLAB, + Material.ENDER_CHEST, + Material.PURPUR_SLAB, + Material.STEP, + Material.DOUBLE_STEP, + Material.ENDER_PORTAL_FRAME, + Material.ENDER_PORTAL, + Material.VINE, // #913 - Material.BED_BLOCK, Material.TRAP_DOOR, Material.IRON_TRAPDOOR, Material.CARPET, Material.IRON_DOOR_BLOCK, + Material.BED_BLOCK, + Material.TRAP_DOOR, + Material.IRON_TRAPDOOR, + Material.CARPET, + Material.IRON_DOOR_BLOCK, // 木のドアはそれぞれMaterialが別れている - Material.WOODEN_DOOR, Material.ACACIA_DOOR, Material.BIRCH_DOOR, Material.DARK_OAK_DOOR, - Material.JUNGLE_DOOR, Material.SPRUCE_DOOR, - Material.SMOOTH_STAIRS, Material.BREWING_STAND, Material.WOOD_STEP, Material.TNT, - //#1027,#1159 - Material.WOOD_STEP, Material.WOOD_DOUBLE_STEP, - Material.DISPENSER, Material.PISTON_STICKY_BASE, Material.WEB + Material.WOODEN_DOOR, + Material.ACACIA_DOOR, + Material.BIRCH_DOOR, + Material.DARK_OAK_DOOR, + Material.JUNGLE_DOOR, + Material.SPRUCE_DOOR, + Material.SMOOTH_STAIRS, + Material.BREWING_STAND, + Material.WOOD_STEP, + Material.TNT, + // #1027,#1159 + Material.WOOD_STEP, + Material.WOOD_DOUBLE_STEP, + Material.DISPENSER, + Material.PISTON_STICKY_BASE, + Material.WEB ) ++ fortuneMaterials // これらのマテリアルを持つブロックは破壊を整地量に計上しない val exclude: Set[Material] = Set( Material.GRASS_PATH, - Material.SOIL, Material.MOB_SPAWNER, - Material.CAULDRON, Material.ENDER_CHEST, - Material.ENDER_PORTAL_FRAME, Material.ENDER_PORTAL + Material.SOIL, + Material.MOB_SPAWNER, + Material.CAULDRON, + Material.ENDER_CHEST, + Material.ENDER_PORTAL_FRAME, + Material.ENDER_PORTAL ) val materialsToCountBlockBreak: Set[Material] = materials -- exclude @@ -59,36 +156,48 @@ object MaterialSets { /** * これらのマテリアルを用いてブロックの破壊試行を行う。 * - * 整地スキル使用時のブロックから取れるアイテムは、 - * プレーヤーの使用ツールのマテリアルをこれらに張り替えた時のドロップのmaxとして計算される。 + * 整地スキル使用時のブロックから取れるアイテムは、 プレーヤーの使用ツールのマテリアルをこれらに張り替えた時のドロップのmaxとして計算される。 * - * 例えば石をシャベルで掘った時にも、ツールのエンチャントを保ったままダイヤツルハシで掘ったものとして計算し、 - * 結果得られるスタック数が最大のものが結果として採用される。 + * 例えば石をシャベルで掘った時にも、ツールのエンチャントを保ったままダイヤツルハシで掘ったものとして計算し、 結果得られるスタック数が最大のものが結果として採用される。 */ - val breakTestToolMaterials: Seq[Material] = Seq( - Material.DIAMOND_PICKAXE, Material.DIAMOND_AXE, Material.DIAMOND_SPADE - ) + val breakTestToolMaterials: Seq[Material] = + Seq(Material.DIAMOND_PICKAXE, Material.DIAMOND_AXE, Material.DIAMOND_SPADE) val breakToolMaterials: Set[Material] = Set( - Material.WOOD_PICKAXE, Material.WOOD_SPADE, - Material.STONE_PICKAXE, Material.STONE_AXE, Material.STONE_SPADE, - Material.IRON_PICKAXE, Material.IRON_AXE, Material.IRON_SPADE, - Material.GOLD_PICKAXE, Material.GOLD_AXE, Material.GOLD_SPADE + Material.WOOD_PICKAXE, + Material.WOOD_SPADE, + Material.STONE_PICKAXE, + Material.STONE_AXE, + Material.STONE_SPADE, + Material.IRON_PICKAXE, + Material.IRON_AXE, + Material.IRON_SPADE, + Material.GOLD_PICKAXE, + Material.GOLD_AXE, + Material.GOLD_SPADE ) ++ breakTestToolMaterials val cancelledMaterials: Set[Material] = Set( - Material.CHEST, Material.ENDER_CHEST, Material.TRAPPED_CHEST, Material.ANVIL, Material.ARMOR_STAND, - Material.BEACON, Material.BIRCH_DOOR, Material.BIRCH_FENCE_GATE, Material.BIRCH_WOOD_STAIRS, - Material.BOAT, Material.FURNACE, Material.WORKBENCH, Material.HOPPER, Material.MINECART + Material.CHEST, + Material.ENDER_CHEST, + Material.TRAPPED_CHEST, + Material.ANVIL, + Material.ARMOR_STAND, + Material.BEACON, + Material.BIRCH_DOOR, + Material.BIRCH_FENCE_GATE, + Material.BIRCH_WOOD_STAIRS, + Material.BOAT, + Material.FURNACE, + Material.WORKBENCH, + Material.HOPPER, + Material.MINECART ) - val transparentMaterials: Set[Material] = Set( - Material.BEDROCK, Material.AIR - ) + val transparentMaterials: Set[Material] = Set(Material.BEDROCK, Material.AIR) - val gravityMaterials: Set[Material] = Set( - Material.LOG, Material.LOG_2, Material.LEAVES, Material.LEAVES_2 - ) + val gravityMaterials: Set[Material] = + Set(Material.LOG, Material.LOG_2, Material.LEAVES, Material.LEAVES_2) trait MaterialOf[S <: Set[Material]] @@ -98,13 +207,19 @@ object MaterialSets { type BreakTool = ItemStackOf[breakToolMaterials.type] type BlockBreakableBySkill = BlockOf[materials.type] - def refineItemStack(stack: ItemStack, set: collection.immutable.Set[Material]): Option[ItemStackOf[set.type]] = + def refineItemStack( + stack: ItemStack, + set: collection.immutable.Set[Material] + ): Option[ItemStackOf[set.type]] = if (set.contains(stack.getType)) Some(tag.apply[MaterialOf[set.type]][ItemStack](stack)) else None - def refineBlock(block: Block, set: collection.immutable.Set[Material]): Option[BlockOf[set.type]] = + def refineBlock( + block: Block, + set: collection.immutable.Set[Material] + ): Option[BlockOf[set.type]] = if (set.contains(block.getType)) Some(tag.apply[MaterialOf[set.type]][Block](block)) else diff --git a/src/main/scala/com/github/unchama/seichiassist/MineStackObjectList.scala b/src/main/scala/com/github/unchama/seichiassist/MineStackObjectList.scala index 3c2931c9c4..c593336e9d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/MineStackObjectList.scala +++ b/src/main/scala/com/github/unchama/seichiassist/MineStackObjectList.scala @@ -55,8 +55,22 @@ object MineStackObjectList { new MineStackObj(MOB_DROP, "blaze_powder", "ブレイズパウダー", 1, Material.BLAZE_POWDER, 0), new MineStackObj(MOB_DROP, "ghast_tear", "ガストの涙", 1, Material.GHAST_TEAR, 0), new MineStackObj(MOB_DROP, "magma_cream", "マグマクリーム", 1, Material.MAGMA_CREAM, 0), - new MineStackObj(MOB_DROP, "prismarine_shard", "プリズマリンの欠片", 1, Material.PRISMARINE_SHARD, 0), - new MineStackObj(MOB_DROP, "prismarine_crystals", "プリズマリンクリスタル", 1, Material.PRISMARINE_CRYSTALS, 0), + new MineStackObj( + MOB_DROP, + "prismarine_shard", + "プリズマリンの欠片", + 1, + Material.PRISMARINE_SHARD, + 0 + ), + new MineStackObj( + MOB_DROP, + "prismarine_crystals", + "プリズマリンクリスタル", + 1, + Material.PRISMARINE_CRYSTALS, + 0 + ), new MineStackObj(MOB_DROP, "feather", "羽", 1, Material.FEATHER, 0), new MineStackObj(MOB_DROP, "leather", "革", 1, Material.LEATHER, 0), new MineStackObj(MOB_DROP, "rabbit_hide", "ウサギの皮", 1, Material.RABBIT_HIDE, 0), @@ -65,7 +79,14 @@ object MineStackObjectList { new MineStackObj(MOB_DROP, "shulker_shell", "シュルカーの殻", 1, Material.SHULKER_SHELL, 0), new MineStackObj(MOB_DROP, "totem_of_undying", "不死のトーテム", 1, Material.TOTEM, 0), new MineStackObj(MOB_DROP, "dragon_head", "エンダードラゴンの頭", 1, Material.SKULL_ITEM, 5), - new MineStackObj(MOB_DROP, "wither_skeleton_skull", "ウィザースケルトンの頭", 1, Material.SKULL_ITEM, 1) + new MineStackObj( + MOB_DROP, + "wither_skeleton_skull", + "ウィザースケルトンの頭", + 1, + Material.SKULL_ITEM, + 1 + ) ) // 採掘で入手可能な農業系ブロック @@ -119,14 +140,28 @@ object MineStackObjectList { new MineStackObj(AGRICULTURAL, "beetroot_seeds", "ビートルートの種", 1, Material.BEETROOT_SEEDS, 0), new MineStackObj(AGRICULTURAL, "carrot_item", "ニンジン", 1, Material.CARROT_ITEM, 0), new MineStackObj(AGRICULTURAL, "potato_item", "ジャガイモ", 1, Material.POTATO_ITEM, 0), - new MineStackObj(AGRICULTURAL, "poisonous_potato", "青くなったジャガイモ", 1, Material.POISONOUS_POTATO, 0), + new MineStackObj( + AGRICULTURAL, + "poisonous_potato", + "青くなったジャガイモ", + 1, + Material.POISONOUS_POTATO, + 0 + ), new MineStackObj(AGRICULTURAL, "wheat", "小麦", 1, Material.WHEAT, 0), new MineStackObj(AGRICULTURAL, "pumpkin_seeds", "カボチャの種", 1, Material.PUMPKIN_SEEDS, 0), new MineStackObj(AGRICULTURAL, "melon_seeds", "スイカの種", 1, Material.MELON_SEEDS, 0), new MineStackObj(AGRICULTURAL, "nether_stalk", "ネザーウォート", 1, Material.NETHER_STALK, 0), new MineStackObj(AGRICULTURAL, "chorus_fruit", "コーラスフルーツ", 1, Material.CHORUS_FRUIT, 0), new MineStackObj(AGRICULTURAL, "chorus_flower", "コーラスフラワー", 1, Material.CHORUS_FLOWER, 0), - new MineStackObj(AGRICULTURAL, "popped_chorus_fruit", "焼いたコーラスフルーツ", 1, Material.CHORUS_FRUIT_POPPED, 0), + new MineStackObj( + AGRICULTURAL, + "popped_chorus_fruit", + "焼いたコーラスフルーツ", + 1, + Material.CHORUS_FRUIT_POPPED, + 0 + ), new MineStackObj(AGRICULTURAL, "egg", "卵", 1, Material.EGG, 0), new MineStackObj(AGRICULTURAL, "pork", "生の豚肉", 1, Material.PORK, 0), new MineStackObj(AGRICULTURAL, "cooked_porkchop", "焼き豚", 1, Material.GRILLED_PORK, 0), @@ -175,7 +210,14 @@ object MineStackObjectList { new MineStackObj(BUILDING, "log3", "ジャングルの原木", 1, Material.LOG, 3), new MineStackObj(BUILDING, "wood_3", "ジャングルの木材", 1, Material.WOOD, 3), new MineStackObj(BUILDING, "wood_step3", "ジャングルの木材ハーフブロック", 1, Material.WOOD_STEP, 3), - new MineStackObj(BUILDING, "jungle_stairs", "ジャングルの木の階段", 1, Material.JUNGLE_WOOD_STAIRS, 0), + new MineStackObj( + BUILDING, + "jungle_stairs", + "ジャングルの木の階段", + 1, + Material.JUNGLE_WOOD_STAIRS, + 0 + ), new MineStackObj(BUILDING, "jungle_fence", "ジャングルのフェンス", 1, Material.JUNGLE_FENCE, 0), new MineStackObj(BUILDING, "log_2", "アカシアの原木", 1, Material.LOG_2, 0), new MineStackObj(BUILDING, "wood_4", "アカシアの木材", 1, Material.WOOD, 4), @@ -185,7 +227,14 @@ object MineStackObjectList { new MineStackObj(BUILDING, "log_21", "ダークオークの原木", 1, Material.LOG_2, 1), new MineStackObj(BUILDING, "wood_5", "ダークオークの木材", 1, Material.WOOD, 5), new MineStackObj(BUILDING, "wood_step5", "ダークオークの木材ハーフブロック", 1, Material.WOOD_STEP, 5), - new MineStackObj(BUILDING, "dark_oak_stairs", "ダークオークの木の階段", 1, Material.DARK_OAK_STAIRS, 0), + new MineStackObj( + BUILDING, + "dark_oak_stairs", + "ダークオークの木の階段", + 1, + Material.DARK_OAK_STAIRS, + 0 + ), new MineStackObj(BUILDING, "dark_oak_fence", "ダークオークのフェンス", 1, Material.DARK_OAK_FENCE, 0), new MineStackObj(BUILDING, "cobblestone", "丸石", 1, Material.COBBLESTONE, 0), new MineStackObj(BUILDING, "step3", "丸石ハーフブロック", 1, Material.STEP, 3), @@ -210,7 +259,14 @@ object MineStackObjectList { new MineStackObj(BUILDING, "red_sand", "赤い砂", 1, Material.SAND, 1), new MineStackObj(BUILDING, "red_sandstone", "赤い砂岩", 1, Material.RED_SANDSTONE, 0), new MineStackObj(BUILDING, "stone_slab20", "赤い砂岩ハーフブロック", 1, Material.STONE_SLAB2, 0), - new MineStackObj(BUILDING, "red_sandstone_stairs", "赤い砂岩の階段", 1, Material.RED_SANDSTONE_STAIRS, 0), + new MineStackObj( + BUILDING, + "red_sandstone_stairs", + "赤い砂岩の階段", + 1, + Material.RED_SANDSTONE_STAIRS, + 0 + ), new MineStackObj(BUILDING, "red_sandstone1", "模様入りの赤い砂岩", 1, Material.RED_SANDSTONE, 1), new MineStackObj(BUILDING, "red_sandstone2", "なめらかな赤い砂岩", 1, Material.RED_SANDSTONE, 2), new MineStackObj(BUILDING, "clay_ball", "粘土", 1, Material.CLAY_BALL, 0), @@ -228,10 +284,31 @@ object MineStackObjectList { new MineStackObj(BUILDING, "nether_brick_item", "ネザーレンガ", 1, Material.NETHER_BRICK_ITEM, 0), new MineStackObj(BUILDING, "nether_brick", "ネザーレンガ(ブロック)", 1, Material.NETHER_BRICK, 0), new MineStackObj(BUILDING, "step6", "ネザーレンガハーフブロック", 1, Material.STEP, 6), - new MineStackObj(BUILDING, "nether_brick_stairs", "ネザーレンガの階段", 1, Material.NETHER_BRICK_STAIRS, 0), - new MineStackObj(BUILDING, "nether_brick_fence", "ネザーレンガのフェンス", 1, Material.NETHER_FENCE, 0), + new MineStackObj( + BUILDING, + "nether_brick_stairs", + "ネザーレンガの階段", + 1, + Material.NETHER_BRICK_STAIRS, + 0 + ), + new MineStackObj( + BUILDING, + "nether_brick_fence", + "ネザーレンガのフェンス", + 1, + Material.NETHER_FENCE, + 0 + ), new MineStackObj(BUILDING, "red_nether_brick", "赤いネザーレンガ", 1, Material.RED_NETHER_BRICK, 0), - new MineStackObj(BUILDING, "nether_wart_block", "ネザ-ウォートブロック", 1, Material.NETHER_WART_BLOCK, 0), + new MineStackObj( + BUILDING, + "nether_wart_block", + "ネザ-ウォートブロック", + 1, + Material.NETHER_WART_BLOCK, + 0 + ), new MineStackObj(BUILDING, "ender_stone", "エンドストーン", 1, Material.ENDER_STONE, 0), new MineStackObj(BUILDING, "end_bricks", "エンドストーンレンガ", 1, Material.END_BRICKS, 0), new MineStackObj(BUILDING, "purpur_block", "プルパーブロック", 1, Material.PURPUR_BLOCK, 0), @@ -294,7 +371,14 @@ object MineStackObjectList { new MineStackObj(BUILDING, "beacon", "ビーコン", 1, Material.BEACON, 0), new MineStackObj(BUILDING, "armor_stand", "アーマースタンド", 1, Material.ARMOR_STAND, 0), new MineStackObj(BUILDING, "end_crystal", "エンドクリスタル", 1, Material.END_CRYSTAL, 0), - new MineStackObj(BUILDING, "enchanting_table", "エンチャントテーブル", 1, Material.ENCHANTMENT_TABLE, 0), + new MineStackObj( + BUILDING, + "enchanting_table", + "エンチャントテーブル", + 1, + Material.ENCHANTMENT_TABLE, + 0 + ), new MineStackObj(BUILDING, "jukebox", "ジュークボックス", 1, Material.JUKEBOX, 0), new MineStackObj(BUILDING, "hard_clay", "テラコッタ", 1, Material.HARD_CLAY, 0), new MineStackObj(BUILDING, "stained_clay", "白色のテラコッタ", 1, Material.STAINED_CLAY, 0), @@ -329,38 +413,262 @@ object MineStackObjectList { new MineStackObj(BUILDING, "concrete13", "緑色のコンクリート", 1, Material.CONCRETE, 13), new MineStackObj(BUILDING, "concrete14", "赤色のコンクリート", 1, Material.CONCRETE, 14), new MineStackObj(BUILDING, "concrete15", "黒色のコンクリート", 1, Material.CONCRETE, 15), - new MineStackObj(BUILDING, "concrete_powder", "白色のコンクリートパウダー", 1, Material.CONCRETE_POWDER, 0), - new MineStackObj(BUILDING, "concrete_powder1", "橙色のコンクリートパウダー", 1, Material.CONCRETE_POWDER, 1), - new MineStackObj(BUILDING, "concrete_powder2", "赤紫色のコンクリートパウダー", 1, Material.CONCRETE_POWDER, 2), - new MineStackObj(BUILDING, "concrete_powder3", "空色のコンクリートパウダー", 1, Material.CONCRETE_POWDER, 3), - new MineStackObj(BUILDING, "concrete_powder4", "黄色のコンクリートパウダー", 1, Material.CONCRETE_POWDER, 4), - new MineStackObj(BUILDING, "concrete_powder5", "黄緑色のコンクリートパウダー", 1, Material.CONCRETE_POWDER, 5), - new MineStackObj(BUILDING, "concrete_powder6", "桃色のコンクリートパウダー", 1, Material.CONCRETE_POWDER, 6), - new MineStackObj(BUILDING, "concrete_powder7", "灰色のコンクリートパウダー", 1, Material.CONCRETE_POWDER, 7), - new MineStackObj(BUILDING, "concrete_powder8", "薄灰色のコンクリートパウダー", 1, Material.CONCRETE_POWDER, 8), - new MineStackObj(BUILDING, "concrete_powder9", "青緑色のコンクリートパウダー", 1, Material.CONCRETE_POWDER, 9), - new MineStackObj(BUILDING, "concrete_powder10", "紫色のコンクリートパウダー", 1, Material.CONCRETE_POWDER, 10), - new MineStackObj(BUILDING, "concrete_powder11", "青色のコンクリートパウダー", 1, Material.CONCRETE_POWDER, 11), - new MineStackObj(BUILDING, "concrete_powder12", "茶色のコンクリートパウダー", 1, Material.CONCRETE_POWDER, 12), - new MineStackObj(BUILDING, "concrete_powder13", "緑色のコンクリートパウダー", 1, Material.CONCRETE_POWDER, 13), - new MineStackObj(BUILDING, "concrete_powder14", "赤色のコンクリートパウダー", 1, Material.CONCRETE_POWDER, 14), - new MineStackObj(BUILDING, "concrete_powder15", "黒色のコンクリートパウダー", 1, Material.CONCRETE_POWDER, 15), - new MineStackObj(BUILDING, "white_glazed_terracotta", "白色の彩釉テラコッタ", 1, Material.WHITE_GLAZED_TERRACOTTA, 0), - new MineStackObj(BUILDING, "orange_glazed_terracotta", "橙色の彩釉テラコッタ", 1, Material.ORANGE_GLAZED_TERRACOTTA, 0), - new MineStackObj(BUILDING, "magenta_glazed_terracotta", "赤紫色の彩釉テラコッタ", 1, Material.MAGENTA_GLAZED_TERRACOTTA, 0), - new MineStackObj(BUILDING, "light_blue_glazed_terracotta", "空色の彩釉テラコッタ", 1, Material.LIGHT_BLUE_GLAZED_TERRACOTTA, 0), - new MineStackObj(BUILDING, "yellow_glazed_terracotta", "黄色の彩釉テラコッタ", 1, Material.YELLOW_GLAZED_TERRACOTTA, 0), - new MineStackObj(BUILDING, "lime_glazed_terracotta", "黄緑色の彩釉テラコッタ", 1, Material.LIME_GLAZED_TERRACOTTA, 0), - new MineStackObj(BUILDING, "pink_glazed_terracotta", "桃色の彩釉テラコッタ", 1, Material.PINK_GLAZED_TERRACOTTA, 0), - new MineStackObj(BUILDING, "gray_glazed_terracotta", "灰色の彩釉テラコッタ", 1, Material.GRAY_GLAZED_TERRACOTTA, 0), - new MineStackObj(BUILDING, "silver_glazed_terracotta", "薄灰色の彩釉テラコッタ", 1, Material.SILVER_GLAZED_TERRACOTTA, 0), - new MineStackObj(BUILDING, "cyan_glazed_terracotta", "青緑色の彩釉テラコッタ", 1, Material.CYAN_GLAZED_TERRACOTTA, 0), - new MineStackObj(BUILDING, "purple_glazed_terracotta", "紫色の彩釉テラコッタ", 1, Material.PURPLE_GLAZED_TERRACOTTA, 0), - new MineStackObj(BUILDING, "blue_glazed_terracotta", "青色の彩釉テラコッタ", 1, Material.BLUE_GLAZED_TERRACOTTA, 0), - new MineStackObj(BUILDING, "brown_glazed_terracotta", "茶色の彩釉テラコッタ", 1, Material.BROWN_GLAZED_TERRACOTTA, 0), - new MineStackObj(BUILDING, "green_glazed_terracotta", "緑色の彩釉テラコッタ", 1, Material.GREEN_GLAZED_TERRACOTTA, 0), - new MineStackObj(BUILDING, "red_glazed_terracotta", "赤色の彩釉テラコッタ", 1, Material.RED_GLAZED_TERRACOTTA, 0), - new MineStackObj(BUILDING, "black_glazed_terracotta", "黒色の彩釉テラコッタ", 1, Material.BLACK_GLAZED_TERRACOTTA, 0), + new MineStackObj( + BUILDING, + "concrete_powder", + "白色のコンクリートパウダー", + 1, + Material.CONCRETE_POWDER, + 0 + ), + new MineStackObj( + BUILDING, + "concrete_powder1", + "橙色のコンクリートパウダー", + 1, + Material.CONCRETE_POWDER, + 1 + ), + new MineStackObj( + BUILDING, + "concrete_powder2", + "赤紫色のコンクリートパウダー", + 1, + Material.CONCRETE_POWDER, + 2 + ), + new MineStackObj( + BUILDING, + "concrete_powder3", + "空色のコンクリートパウダー", + 1, + Material.CONCRETE_POWDER, + 3 + ), + new MineStackObj( + BUILDING, + "concrete_powder4", + "黄色のコンクリートパウダー", + 1, + Material.CONCRETE_POWDER, + 4 + ), + new MineStackObj( + BUILDING, + "concrete_powder5", + "黄緑色のコンクリートパウダー", + 1, + Material.CONCRETE_POWDER, + 5 + ), + new MineStackObj( + BUILDING, + "concrete_powder6", + "桃色のコンクリートパウダー", + 1, + Material.CONCRETE_POWDER, + 6 + ), + new MineStackObj( + BUILDING, + "concrete_powder7", + "灰色のコンクリートパウダー", + 1, + Material.CONCRETE_POWDER, + 7 + ), + new MineStackObj( + BUILDING, + "concrete_powder8", + "薄灰色のコンクリートパウダー", + 1, + Material.CONCRETE_POWDER, + 8 + ), + new MineStackObj( + BUILDING, + "concrete_powder9", + "青緑色のコンクリートパウダー", + 1, + Material.CONCRETE_POWDER, + 9 + ), + new MineStackObj( + BUILDING, + "concrete_powder10", + "紫色のコンクリートパウダー", + 1, + Material.CONCRETE_POWDER, + 10 + ), + new MineStackObj( + BUILDING, + "concrete_powder11", + "青色のコンクリートパウダー", + 1, + Material.CONCRETE_POWDER, + 11 + ), + new MineStackObj( + BUILDING, + "concrete_powder12", + "茶色のコンクリートパウダー", + 1, + Material.CONCRETE_POWDER, + 12 + ), + new MineStackObj( + BUILDING, + "concrete_powder13", + "緑色のコンクリートパウダー", + 1, + Material.CONCRETE_POWDER, + 13 + ), + new MineStackObj( + BUILDING, + "concrete_powder14", + "赤色のコンクリートパウダー", + 1, + Material.CONCRETE_POWDER, + 14 + ), + new MineStackObj( + BUILDING, + "concrete_powder15", + "黒色のコンクリートパウダー", + 1, + Material.CONCRETE_POWDER, + 15 + ), + new MineStackObj( + BUILDING, + "white_glazed_terracotta", + "白色の彩釉テラコッタ", + 1, + Material.WHITE_GLAZED_TERRACOTTA, + 0 + ), + new MineStackObj( + BUILDING, + "orange_glazed_terracotta", + "橙色の彩釉テラコッタ", + 1, + Material.ORANGE_GLAZED_TERRACOTTA, + 0 + ), + new MineStackObj( + BUILDING, + "magenta_glazed_terracotta", + "赤紫色の彩釉テラコッタ", + 1, + Material.MAGENTA_GLAZED_TERRACOTTA, + 0 + ), + new MineStackObj( + BUILDING, + "light_blue_glazed_terracotta", + "空色の彩釉テラコッタ", + 1, + Material.LIGHT_BLUE_GLAZED_TERRACOTTA, + 0 + ), + new MineStackObj( + BUILDING, + "yellow_glazed_terracotta", + "黄色の彩釉テラコッタ", + 1, + Material.YELLOW_GLAZED_TERRACOTTA, + 0 + ), + new MineStackObj( + BUILDING, + "lime_glazed_terracotta", + "黄緑色の彩釉テラコッタ", + 1, + Material.LIME_GLAZED_TERRACOTTA, + 0 + ), + new MineStackObj( + BUILDING, + "pink_glazed_terracotta", + "桃色の彩釉テラコッタ", + 1, + Material.PINK_GLAZED_TERRACOTTA, + 0 + ), + new MineStackObj( + BUILDING, + "gray_glazed_terracotta", + "灰色の彩釉テラコッタ", + 1, + Material.GRAY_GLAZED_TERRACOTTA, + 0 + ), + new MineStackObj( + BUILDING, + "silver_glazed_terracotta", + "薄灰色の彩釉テラコッタ", + 1, + Material.SILVER_GLAZED_TERRACOTTA, + 0 + ), + new MineStackObj( + BUILDING, + "cyan_glazed_terracotta", + "青緑色の彩釉テラコッタ", + 1, + Material.CYAN_GLAZED_TERRACOTTA, + 0 + ), + new MineStackObj( + BUILDING, + "purple_glazed_terracotta", + "紫色の彩釉テラコッタ", + 1, + Material.PURPLE_GLAZED_TERRACOTTA, + 0 + ), + new MineStackObj( + BUILDING, + "blue_glazed_terracotta", + "青色の彩釉テラコッタ", + 1, + Material.BLUE_GLAZED_TERRACOTTA, + 0 + ), + new MineStackObj( + BUILDING, + "brown_glazed_terracotta", + "茶色の彩釉テラコッタ", + 1, + Material.BROWN_GLAZED_TERRACOTTA, + 0 + ), + new MineStackObj( + BUILDING, + "green_glazed_terracotta", + "緑色の彩釉テラコッタ", + 1, + Material.GREEN_GLAZED_TERRACOTTA, + 0 + ), + new MineStackObj( + BUILDING, + "red_glazed_terracotta", + "赤色の彩釉テラコッタ", + 1, + Material.RED_GLAZED_TERRACOTTA, + 0 + ), + new MineStackObj( + BUILDING, + "black_glazed_terracotta", + "黒色の彩釉テラコッタ", + 1, + Material.BLACK_GLAZED_TERRACOTTA, + 0 + ), new MineStackObj(BUILDING, "wool_0", "羊毛", 1, Material.WOOL, 0), new MineStackObj(BUILDING, "wool_1", "橙色の羊毛", 1, Material.WOOL, 1), new MineStackObj(BUILDING, "wool_2", "赤紫色の羊毛", 1, Material.WOOL, 2), @@ -411,22 +719,134 @@ object MineStackObjectList { new MineStackObj(BUILDING, "stained_glass_14", "赤色の色付きガラス", 1, Material.STAINED_GLASS, 14), new MineStackObj(BUILDING, "stained_glass_15", "黒色の色付きガラス", 1, Material.STAINED_GLASS, 15), new MineStackObj(BUILDING, "glass_panel", "板ガラス", 1, Material.THIN_GLASS, 0), - new MineStackObj(BUILDING, "glass_panel_0", "白色の色付きガラス板", 1, Material.STAINED_GLASS_PANE, 0), - new MineStackObj(BUILDING, "glass_panel_1", "橙色の色付きガラス板", 1, Material.STAINED_GLASS_PANE, 1), - new MineStackObj(BUILDING, "glass_panel_2", "赤紫色の色付きガラス板", 1, Material.STAINED_GLASS_PANE, 2), - new MineStackObj(BUILDING, "glass_panel_3", "空色の色付きガラス板", 1, Material.STAINED_GLASS_PANE, 3), - new MineStackObj(BUILDING, "glass_panel_4", "黄色の色付きガラス板", 1, Material.STAINED_GLASS_PANE, 4), - new MineStackObj(BUILDING, "glass_panel_5", "黄緑色の色付きガラス板", 1, Material.STAINED_GLASS_PANE, 5), - new MineStackObj(BUILDING, "glass_panel_6", "桃色の色付きガラス板", 1, Material.STAINED_GLASS_PANE, 6), - new MineStackObj(BUILDING, "glass_panel_7", "灰色の色付きガラス板", 1, Material.STAINED_GLASS_PANE, 7), - new MineStackObj(BUILDING, "glass_panel_8", "薄灰色の色付きガラス板", 1, Material.STAINED_GLASS_PANE, 8), - new MineStackObj(BUILDING, "glass_panel_9", "青緑色の色付きガラス板", 1, Material.STAINED_GLASS_PANE, 9), - new MineStackObj(BUILDING, "glass_panel_10", "紫色の色付きガラス板", 1, Material.STAINED_GLASS_PANE, 10), - new MineStackObj(BUILDING, "glass_panel_11", "青色の色付きガラス板", 1, Material.STAINED_GLASS_PANE, 11), - new MineStackObj(BUILDING, "glass_panel_12", "茶色の色付きガラス板", 1, Material.STAINED_GLASS_PANE, 12), - new MineStackObj(BUILDING, "glass_panel_13", "緑色の色付きガラス板", 1, Material.STAINED_GLASS_PANE, 13), - new MineStackObj(BUILDING, "glass_panel_14", "赤色の色付きガラス板", 1, Material.STAINED_GLASS_PANE, 14), - new MineStackObj(BUILDING, "glass_panel_15", "黒色の色付きガラス板", 1, Material.STAINED_GLASS_PANE, 15), + new MineStackObj( + BUILDING, + "glass_panel_0", + "白色の色付きガラス板", + 1, + Material.STAINED_GLASS_PANE, + 0 + ), + new MineStackObj( + BUILDING, + "glass_panel_1", + "橙色の色付きガラス板", + 1, + Material.STAINED_GLASS_PANE, + 1 + ), + new MineStackObj( + BUILDING, + "glass_panel_2", + "赤紫色の色付きガラス板", + 1, + Material.STAINED_GLASS_PANE, + 2 + ), + new MineStackObj( + BUILDING, + "glass_panel_3", + "空色の色付きガラス板", + 1, + Material.STAINED_GLASS_PANE, + 3 + ), + new MineStackObj( + BUILDING, + "glass_panel_4", + "黄色の色付きガラス板", + 1, + Material.STAINED_GLASS_PANE, + 4 + ), + new MineStackObj( + BUILDING, + "glass_panel_5", + "黄緑色の色付きガラス板", + 1, + Material.STAINED_GLASS_PANE, + 5 + ), + new MineStackObj( + BUILDING, + "glass_panel_6", + "桃色の色付きガラス板", + 1, + Material.STAINED_GLASS_PANE, + 6 + ), + new MineStackObj( + BUILDING, + "glass_panel_7", + "灰色の色付きガラス板", + 1, + Material.STAINED_GLASS_PANE, + 7 + ), + new MineStackObj( + BUILDING, + "glass_panel_8", + "薄灰色の色付きガラス板", + 1, + Material.STAINED_GLASS_PANE, + 8 + ), + new MineStackObj( + BUILDING, + "glass_panel_9", + "青緑色の色付きガラス板", + 1, + Material.STAINED_GLASS_PANE, + 9 + ), + new MineStackObj( + BUILDING, + "glass_panel_10", + "紫色の色付きガラス板", + 1, + Material.STAINED_GLASS_PANE, + 10 + ), + new MineStackObj( + BUILDING, + "glass_panel_11", + "青色の色付きガラス板", + 1, + Material.STAINED_GLASS_PANE, + 11 + ), + new MineStackObj( + BUILDING, + "glass_panel_12", + "茶色の色付きガラス板", + 1, + Material.STAINED_GLASS_PANE, + 12 + ), + new MineStackObj( + BUILDING, + "glass_panel_13", + "緑色の色付きガラス板", + 1, + Material.STAINED_GLASS_PANE, + 13 + ), + new MineStackObj( + BUILDING, + "glass_panel_14", + "赤色の色付きガラス板", + 1, + Material.STAINED_GLASS_PANE, + 14 + ), + new MineStackObj( + BUILDING, + "glass_panel_15", + "黒色の色付きガラス板", + 1, + Material.STAINED_GLASS_PANE, + 15 + ), new MineStackObj(BUILDING, "dye_1", "赤色の染料", 1, Material.INK_SACK, 1), new MineStackObj(BUILDING, "dye_2", "緑色の染料", 1, Material.INK_SACK, 2), new MineStackObj(BUILDING, "dye_5", "紫色の染料", 1, Material.INK_SACK, 5), @@ -457,94 +877,531 @@ object MineStackObjectList { new MineStackObj(BUILDING, "bed_12", "茶色のベッド", 1, Material.BED, 12), new MineStackObj(BUILDING, "bed_13", "緑色のベッド", 1, Material.BED, 13), new MineStackObj(BUILDING, "bed_14", "赤色のベッド", 1, Material.BED, 14), - new MineStackObj(BUILDING, "bed_15", "黒色のベッド", 1, Material.BED, 15), - + new MineStackObj(BUILDING, "bed_15", "黒色のベッド", 1, Material.BED, 15) ) // レッドストーン系ブロック val minestacklistrs: List[MineStackObj] = List( - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "redstone", "レッドストーン", 1, Material.REDSTONE, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "stone_button", "石のボタン", 1, Material.STONE_BUTTON, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "wood_button", "木のボタン", 1, Material.WOOD_BUTTON, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "stone_plate", "石の感圧版", 1, Material.STONE_PLATE, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "wood_plate", "木の感圧版", 1, Material.WOOD_PLATE, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "fence_gate", "オークのフェンスゲート", 1, Material.FENCE_GATE, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "spruce_fence_gate", "マツのフェンスゲート", 1, Material.SPRUCE_FENCE_GATE, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "birch_fence_gate", "シラカバのフェンスゲート", 1, Material.BIRCH_FENCE_GATE, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "jungle_fence_gate", "ジャングルのフェンスゲート", 1, Material.JUNGLE_FENCE_GATE, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "dark_oak_fence_gate", "ダークオークのフェンスゲート", 1, Material.DARK_OAK_FENCE_GATE, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "acacia_fence_gate", "アカシアのフェンスゲート", 1, Material.ACACIA_FENCE_GATE, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "redstone_block", "レッドストーンブロック", 1, Material.REDSTONE_BLOCK, 0), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "redstone", + "レッドストーン", + 1, + Material.REDSTONE, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "stone_button", + "石のボタン", + 1, + Material.STONE_BUTTON, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "wood_button", + "木のボタン", + 1, + Material.WOOD_BUTTON, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "stone_plate", + "石の感圧版", + 1, + Material.STONE_PLATE, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "wood_plate", + "木の感圧版", + 1, + Material.WOOD_PLATE, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "fence_gate", + "オークのフェンスゲート", + 1, + Material.FENCE_GATE, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "spruce_fence_gate", + "マツのフェンスゲート", + 1, + Material.SPRUCE_FENCE_GATE, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "birch_fence_gate", + "シラカバのフェンスゲート", + 1, + Material.BIRCH_FENCE_GATE, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "jungle_fence_gate", + "ジャングルのフェンスゲート", + 1, + Material.JUNGLE_FENCE_GATE, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "dark_oak_fence_gate", + "ダークオークのフェンスゲート", + 1, + Material.DARK_OAK_FENCE_GATE, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "acacia_fence_gate", + "アカシアのフェンスゲート", + 1, + Material.ACACIA_FENCE_GATE, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "redstone_block", + "レッドストーンブロック", + 1, + Material.REDSTONE_BLOCK, + 0 + ), new MineStackObj(REDSTONE_AND_TRANSPORTATION, "lever", "レバー", 1, Material.LEVER, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "redstone_torch_on", "レッドストーントーチ", 1, Material.REDSTONE_TORCH_ON, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "trap_door", "木のトラップドア", 1, Material.TRAP_DOOR, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "iron_trapdoor", "鉄のトラップドア", 1, Material.IRON_TRAPDOOR, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "gold_plate", "重量感圧版 (軽) ", 1, Material.GOLD_PLATE, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "iron_plate", "重量感圧版 (重) ", 1, Material.IRON_PLATE, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "wood_door", "オークのドア", 1, Material.WOOD_DOOR, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "spruce_door_item", "マツのドア", 1, Material.SPRUCE_DOOR_ITEM, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "birch_door_item", "シラカバのドア", 1, Material.BIRCH_DOOR_ITEM, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "jungle_door_item", "ジャングルのドア", 1, Material.JUNGLE_DOOR_ITEM, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "acacia_door_item", "アカシアのドア", 1, Material.ACACIA_DOOR_ITEM, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "dark_oak_door_item", "ダークオークのドア", 1, Material.DARK_OAK_DOOR_ITEM, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "note_block", "音符ブロック", 1, Material.NOTE_BLOCK, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "redstone_lamp_off", "レッドストーンランプ", 1, Material.REDSTONE_LAMP_OFF, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "tripwire_hook", "トリップワイヤーフック", 1, Material.TRIPWIRE_HOOK, 0), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "redstone_torch_on", + "レッドストーントーチ", + 1, + Material.REDSTONE_TORCH_ON, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "trap_door", + "木のトラップドア", + 1, + Material.TRAP_DOOR, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "iron_trapdoor", + "鉄のトラップドア", + 1, + Material.IRON_TRAPDOOR, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "gold_plate", + "重量感圧版 (軽) ", + 1, + Material.GOLD_PLATE, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "iron_plate", + "重量感圧版 (重) ", + 1, + Material.IRON_PLATE, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "wood_door", + "オークのドア", + 1, + Material.WOOD_DOOR, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "spruce_door_item", + "マツのドア", + 1, + Material.SPRUCE_DOOR_ITEM, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "birch_door_item", + "シラカバのドア", + 1, + Material.BIRCH_DOOR_ITEM, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "jungle_door_item", + "ジャングルのドア", + 1, + Material.JUNGLE_DOOR_ITEM, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "acacia_door_item", + "アカシアのドア", + 1, + Material.ACACIA_DOOR_ITEM, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "dark_oak_door_item", + "ダークオークのドア", + 1, + Material.DARK_OAK_DOOR_ITEM, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "note_block", + "音符ブロック", + 1, + Material.NOTE_BLOCK, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "redstone_lamp_off", + "レッドストーンランプ", + 1, + Material.REDSTONE_LAMP_OFF, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "tripwire_hook", + "トリップワイヤーフック", + 1, + Material.TRIPWIRE_HOOK, + 0 + ), new MineStackObj(REDSTONE_AND_TRANSPORTATION, "dropper", "ドロッパー", 1, Material.DROPPER, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "piston_sticky_base", "粘着ピストン", 1, Material.PISTON_STICKY_BASE, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "piston_base", "ピストン", 1, Material.PISTON_BASE, 0), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "piston_sticky_base", + "粘着ピストン", + 1, + Material.PISTON_STICKY_BASE, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "piston_base", + "ピストン", + 1, + Material.PISTON_BASE, + 0 + ), new MineStackObj(REDSTONE_AND_TRANSPORTATION, "tnt", "TNT", 1, Material.TNT, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "trapped_chest", "トラップチェスト", 1, Material.TRAPPED_CHEST, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "daylight_detector", "日照センサー", 1, Material.DAYLIGHT_DETECTOR, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "iron_door", "鉄のドア", 1, Material.IRON_DOOR, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "diode", "レッドストーンリピーター", 1, Material.DIODE, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "dispenser", "ディスペンサー", 1, Material.DISPENSER, 0), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "trapped_chest", + "トラップチェスト", + 1, + Material.TRAPPED_CHEST, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "daylight_detector", + "日照センサー", + 1, + Material.DAYLIGHT_DETECTOR, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "iron_door", + "鉄のドア", + 1, + Material.IRON_DOOR, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "diode", + "レッドストーンリピーター", + 1, + Material.DIODE, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "dispenser", + "ディスペンサー", + 1, + Material.DISPENSER, + 0 + ), new MineStackObj(REDSTONE_AND_TRANSPORTATION, "hopper", "ホッパー", 1, Material.HOPPER, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "redstone_comparator", "レッドストーンコンパレーター", 1, Material.REDSTONE_COMPARATOR, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "powered_rail", "パワードレール", 1, Material.POWERED_RAIL, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "detector_rail", "ディテクターレール", 1, Material.DETECTOR_RAIL, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "activator_rail", "アクティベーターレール", 1, Material.ACTIVATOR_RAIL, 0), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "redstone_comparator", + "レッドストーンコンパレーター", + 1, + Material.REDSTONE_COMPARATOR, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "powered_rail", + "パワードレール", + 1, + Material.POWERED_RAIL, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "detector_rail", + "ディテクターレール", + 1, + Material.DETECTOR_RAIL, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "activator_rail", + "アクティベーターレール", + 1, + Material.ACTIVATOR_RAIL, + 0 + ), new MineStackObj(REDSTONE_AND_TRANSPORTATION, "boat", "オークのボート", 1, Material.BOAT, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "spruce_boat", "マツのボート", 1, Material.BOAT_SPRUCE, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "birch_boat", "シラカバのボート", 1, Material.BOAT_BIRCH, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "jungle_boat", "ジャングルのボート", 1, Material.BOAT_JUNGLE, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "acacia_boat", "アカシアのボート", 1, Material.BOAT_ACACIA, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "dark_oak_boat", "ダークオークのボート", 1, Material.BOAT_DARK_OAK, 0), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "spruce_boat", + "マツのボート", + 1, + Material.BOAT_SPRUCE, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "birch_boat", + "シラカバのボート", + 1, + Material.BOAT_BIRCH, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "jungle_boat", + "ジャングルのボート", + 1, + Material.BOAT_JUNGLE, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "acacia_boat", + "アカシアのボート", + 1, + Material.BOAT_ACACIA, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "dark_oak_boat", + "ダークオークのボート", + 1, + Material.BOAT_DARK_OAK, + 0 + ), new MineStackObj(REDSTONE_AND_TRANSPORTATION, "saddle", "サドル", 1, Material.SADDLE, 0), new MineStackObj(REDSTONE_AND_TRANSPORTATION, "minecart", "トロッコ", 1, Material.MINECART, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "chest_minecart", "チェスト付きトロッコ", 1, Material.STORAGE_MINECART, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "furnace_minecart", "かまど付きトロッコ", 1, Material.POWERED_MINECART, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "hopper_minecart", "ホッパー付きトロッコ", 1, Material.HOPPER_MINECART, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "iron_horse_armor", "鉄の馬鎧", 1, Material.IRON_BARDING, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "golden_horse_armor", "金の馬鎧", 1, Material.GOLD_BARDING, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "diamond_horse_armor", "ダイヤの馬鎧", 1, Material.DIAMOND_BARDING, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "record_13", "レコード", 1, Material.GOLD_RECORD, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "record_cat", "レコード", 1, Material.GREEN_RECORD, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "record_blocks", "レコード", 1, Material.RECORD_3, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "record_chirp", "レコード", 1, Material.RECORD_4, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "record_far", "レコード", 1, Material.RECORD_5, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "record_mall", "レコード", 1, Material.RECORD_6, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "record_mellohi", "レコード", 1, Material.RECORD_7, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "record_stal", "レコード", 1, Material.RECORD_8, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "record_strad", "レコード", 1, Material.RECORD_9, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "record_ward", "レコード", 1, Material.RECORD_10, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "record_11", "レコード", 1, Material.RECORD_11, 0), - new MineStackObj(REDSTONE_AND_TRANSPORTATION, "record_wait", "レコード", 1, Material.RECORD_12, 0) + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "chest_minecart", + "チェスト付きトロッコ", + 1, + Material.STORAGE_MINECART, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "furnace_minecart", + "かまど付きトロッコ", + 1, + Material.POWERED_MINECART, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "hopper_minecart", + "ホッパー付きトロッコ", + 1, + Material.HOPPER_MINECART, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "iron_horse_armor", + "鉄の馬鎧", + 1, + Material.IRON_BARDING, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "golden_horse_armor", + "金の馬鎧", + 1, + Material.GOLD_BARDING, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "diamond_horse_armor", + "ダイヤの馬鎧", + 1, + Material.DIAMOND_BARDING, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "record_13", + "レコード", + 1, + Material.GOLD_RECORD, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "record_cat", + "レコード", + 1, + Material.GREEN_RECORD, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "record_blocks", + "レコード", + 1, + Material.RECORD_3, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "record_chirp", + "レコード", + 1, + Material.RECORD_4, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "record_far", + "レコード", + 1, + Material.RECORD_5, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "record_mall", + "レコード", + 1, + Material.RECORD_6, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "record_mellohi", + "レコード", + 1, + Material.RECORD_7, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "record_stal", + "レコード", + 1, + Material.RECORD_8, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "record_strad", + "レコード", + 1, + Material.RECORD_9, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "record_ward", + "レコード", + 1, + Material.RECORD_10, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "record_11", + "レコード", + 1, + Material.RECORD_11, + 0 + ), + new MineStackObj( + REDSTONE_AND_TRANSPORTATION, + "record_wait", + "レコード", + 1, + Material.RECORD_12, + 0 + ) ) /** * デフォルトでガチャの内容に含まれている景品。 */ val minestackBuiltinGachaPrizes: List[MineStackObj] = List( - new MineStackObj("gachaimo", None, 1, StaticGachaPrizeFactory.getGachaRingo, true, -1, GACHA_PRIZES), - new MineStackObj("exp_bottle", Some("エンチャントの瓶"), 1, Material.EXP_BOTTLE, 0, false, -1, GACHA_PRIZES) + new MineStackObj( + "gachaimo", + None, + 1, + StaticGachaPrizeFactory.getGachaRingo, + true, + -1, + GACHA_PRIZES + ), + new MineStackObj( + "exp_bottle", + Some("エンチャントの瓶"), + 1, + Material.EXP_BOTTLE, + 0, + false, + -1, + GACHA_PRIZES + ) ) /** * マインスタックに格納できるガチャ景品。 */ // これは後に変更されるのでミュータブルでないといけない - val minestackGachaPrizes: mutable.ArrayBuffer[MineStackObj] = mutable.ArrayBuffer.from(minestackBuiltinGachaPrizes) + val minestackGachaPrizes: mutable.ArrayBuffer[MineStackObj] = + mutable.ArrayBuffer.from(minestackBuiltinGachaPrizes) // ランダムアクセスしないので val minestacklist: mutable.ArrayBuffer[MineStackObj] = mutable.ArrayBuffer() diff --git a/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala b/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala index d6df560b2d..e97b49f61f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala +++ b/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala @@ -11,7 +11,10 @@ import com.github.unchama.bungeesemaphoreresponder.domain.PlayerDataFinalizer import com.github.unchama.bungeesemaphoreresponder.{System => BungeeSemaphoreResponderSystem} import com.github.unchama.chatinterceptor.{ChatInterceptor, InterceptionScope} import com.github.unchama.concurrent.RepeatingRoutine -import com.github.unchama.datarepository.bukkit.player.{BukkitRepositoryControls, PlayerDataRepository} +import com.github.unchama.datarepository.bukkit.player.{ + BukkitRepositoryControls, + PlayerDataRepository +} import com.github.unchama.datarepository.definitions.SessionMutexRepositoryDefinition import com.github.unchama.datarepository.template.RepositoryDefinition import com.github.unchama.datarepository.template.finalization.RepositoryFinalization @@ -23,7 +26,10 @@ import com.github.unchama.generic.effect.unsafe.EffectEnvironment import com.github.unchama.menuinventory.MenuHandler import com.github.unchama.menuinventory.router.CanOpen import com.github.unchama.minecraft.actions.{GetConnectedPlayers, SendMinecraftMessage} -import com.github.unchama.minecraft.bukkit.actions.{GetConnectedBukkitPlayers, SendBukkitMessage} +import com.github.unchama.minecraft.bukkit.actions.{ + GetConnectedBukkitPlayers, + SendBukkitMessage +} import com.github.unchama.seichiassist.MaterialSets.BlockBreakableBySkill import com.github.unchama.seichiassist.SeichiAssist.seichiAssistConfig import com.github.unchama.seichiassist.bungee.BungeeReceiver @@ -34,7 +40,10 @@ import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.asyncS import com.github.unchama.seichiassist.data.player.PlayerData import com.github.unchama.seichiassist.data.{GachaPrize, MineStackGachaData, RankData} import com.github.unchama.seichiassist.database.DatabaseGateway -import com.github.unchama.seichiassist.domain.actions.{GetNetworkConnectionCount, UuidToLastSeenName} +import com.github.unchama.seichiassist.domain.actions.{ + GetNetworkConnectionCount, + UuidToLastSeenName +} import com.github.unchama.seichiassist.domain.configuration.RedisBungeeRedisConfiguration import com.github.unchama.seichiassist.infrastructure.akka.ConfiguredActorSystemProvider import com.github.unchama.seichiassist.infrastructure.logging.jul.NamedJULLogger @@ -45,12 +54,16 @@ import com.github.unchama.seichiassist.menus.{BuildMainMenu, TopLevelRouter} import com.github.unchama.seichiassist.meta.subsystem.Subsystem import com.github.unchama.seichiassist.minestack.{MineStackObj, MineStackObjectCategory} import com.github.unchama.seichiassist.subsystems._ +import com.github.unchama.seichiassist.subsystems.anywhereender.AnywhereEnderChestAPI import com.github.unchama.seichiassist.subsystems.breakcount.{BreakCountAPI, BreakCountReadAPI} import com.github.unchama.seichiassist.subsystems.breakcountbar.BreakCountBarAPI import com.github.unchama.seichiassist.subsystems.buildcount.BuildCountAPI import com.github.unchama.seichiassist.subsystems.discordnotification.DiscordNotificationAPI import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.application.Configuration -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.{FastDiggingEffectApi, FastDiggingSettingsApi} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.{ + FastDiggingEffectApi, + FastDiggingSettingsApi +} import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.FourDimensionalPocketApi import com.github.unchama.seichiassist.subsystems.gachapoint.GachaPointApi import com.github.unchama.seichiassist.subsystems.mana.{ManaApi, ManaReadApi} @@ -83,7 +96,7 @@ class SeichiAssist extends JavaPlugin() { private var hasBeenLoadedAlready = false - //region application infrastructure + // region application infrastructure /* * JDK14LoggerFactoryは `java.util.logging.Logger.getLogger` によりロガーを解決している。 @@ -102,26 +115,34 @@ class SeichiAssist extends JavaPlugin() { new JDK14LoggerFactory().getLogger(newLogger.getName) } - implicit val loggerF: io.chrisdavenport.log4cats.Logger[IO] = Slf4jLogger.getLoggerFromSlf4j(logger) + implicit val loggerF: io.chrisdavenport.log4cats.Logger[IO] = + Slf4jLogger.getLoggerFromSlf4j(logger) - //endregion + // endregion private var repeatedTaskFiber: Option[Fiber[IO, List[Nothing]]] = None - //region repositories - - private val activeSkillAvailabilityRepositoryControls: BukkitRepositoryControls[SyncIO, Ref[SyncIO, Boolean]] = - BukkitRepositoryControls.createHandles[SyncIO, Ref[SyncIO, Boolean]]( - RepositoryDefinition.Phased.SinglePhased.withoutTappingAction( - SinglePhasedRepositoryInitialization.withSupplier(Ref[SyncIO].of(true)), - RepositoryFinalization.trivial + // region repositories + + private val activeSkillAvailabilityRepositoryControls + : BukkitRepositoryControls[SyncIO, Ref[SyncIO, Boolean]] = + BukkitRepositoryControls + .createHandles[SyncIO, Ref[SyncIO, Boolean]]( + RepositoryDefinition + .Phased + .SinglePhased + .withoutTappingAction( + SinglePhasedRepositoryInitialization.withSupplier(Ref[SyncIO].of(true)), + RepositoryFinalization.trivial + ) ) - ).unsafeRunSync() + .unsafeRunSync() val activeSkillAvailability: PlayerDataRepository[Ref[SyncIO, Boolean]] = activeSkillAvailabilityRepositoryControls.repository - private val assaultSkillRoutinesRepositoryControls: BukkitRepositoryControls[SyncIO, SessionMutex[IO, SyncIO]] = { + private val assaultSkillRoutinesRepositoryControls + : BukkitRepositoryControls[SyncIO, SessionMutex[IO, SyncIO]] = { val definition = { import PluginExecutionContexts.asyncShift SessionMutexRepositoryDefinition.withRepositoryContext[IO, SyncIO, Player] @@ -139,9 +160,9 @@ class SeichiAssist extends JavaPlugin() { } } - //endregion + // endregion - //region resource scopes + // region resource scopes /** * スキル使用などで破壊されることが確定したブロック塊のスコープ @@ -162,9 +183,9 @@ class SeichiAssist extends JavaPlugin() { import PluginExecutionContexts.asyncShift ResourceScope.unsafeCreateSingletonScope } - //endregion + // endregion - //region subsystems + // region subsystems private lazy val expBottleStackSystem: subsystems.expbottlestack.System[IO, SyncIO, IO] = { import PluginExecutionContexts.asyncShift @@ -186,9 +207,10 @@ class SeichiAssist extends JavaPlugin() { implicit val effectEnvironment: DefaultEffectEnvironment.type = DefaultEffectEnvironment implicit val timer: Timer[IO] = IO.timer(cachedThreadPool) - val configuration = subsystems.managedfly.application.SystemConfiguration( - expConsumptionAmount = seichiAssistConfig.getFlyExp - ) + val configuration = subsystems + .managedfly + .application + .SystemConfiguration(expConsumptionAmount = seichiAssistConfig.getFlyExp) subsystems.managedfly.System.wired[IO, SyncIO](configuration).unsafeRunSync() } @@ -203,7 +225,6 @@ class SeichiAssist extends JavaPlugin() { } private lazy val buildCountSystem: subsystems.buildcount.System[IO, SyncIO] = { - import PluginExecutionContexts.timer implicit val configuration: subsystems.buildcount.application.Configuration = seichiAssistConfig.buildCountConfiguration @@ -255,24 +276,34 @@ class SeichiAssist extends JavaPlugin() { subsystems.ranking.System.wired[IO, IO].unsafeRunSync() } - private lazy val fourDimensionalPocketSystem: subsystems.fourdimensionalpocket.System[IO, Player] = { + private lazy val fourDimensionalPocketSystem + : subsystems.fourdimensionalpocket.System[IO, Player] = { import PluginExecutionContexts.{asyncShift, onMainThread} implicit val effectEnvironment: EffectEnvironment = DefaultEffectEnvironment implicit val concurrentEffect: ConcurrentEffect[IO] = IO.ioConcurrentEffect(asyncShift) - subsystems.fourdimensionalpocket.System.wired[IO, SyncIO](breakCountSystem.api).unsafeRunSync() + subsystems + .fourdimensionalpocket + .System + .wired[IO, SyncIO](breakCountSystem.api) + .unsafeRunSync() } - private lazy val fastDiggingEffectSystem: subsystems.fastdiggingeffect.System[IO, IO, Player] = { + private lazy val fastDiggingEffectSystem + : subsystems.fastdiggingeffect.System[IO, IO, Player] = { import PluginExecutionContexts.{asyncShift, onMainThread, timer} implicit val concurrentEffect: ConcurrentEffect[IO] = IO.ioConcurrentEffect(asyncShift) - implicit val configuration: Configuration = seichiAssistConfig.getFastDiggingEffectSystemConfiguration + implicit val configuration: Configuration = + seichiAssistConfig.getFastDiggingEffectSystemConfiguration implicit val breakCountApi: BreakCountAPI[IO, SyncIO, Player] = breakCountSystem.api - implicit val getConnectedPlayers: GetConnectedPlayers[IO, Player] = new GetConnectedBukkitPlayers[IO] - implicit val redisBungeeConfig: RedisBungeeRedisConfiguration = seichiAssistConfig.getRedisBungeeRedisConfiguration - implicit val networkConnectionCount: GetNetworkConnectionCount[IO] = new RedisBungeeNetworkConnectionCount[IO](asyncShift) + implicit val getConnectedPlayers: GetConnectedPlayers[IO, Player] = + new GetConnectedBukkitPlayers[IO] + implicit val redisBungeeConfig: RedisBungeeRedisConfiguration = + seichiAssistConfig.getRedisBungeeRedisConfiguration + implicit val networkConnectionCount: GetNetworkConnectionCount[IO] = + new RedisBungeeNetworkConnectionCount[IO](asyncShift) subsystems.fastdiggingeffect.System.wired[SyncIO, IO, SyncIO].unsafeRunSync() } @@ -281,7 +312,8 @@ class SeichiAssist extends JavaPlugin() { import PluginExecutionContexts.{asyncShift, onMainThread, timer} implicit val concurrentEffect: ConcurrentEffect[IO] = IO.ioConcurrentEffect(asyncShift) - implicit val getConnectedPlayers: GetConnectedPlayers[IO, Player] = new GetConnectedBukkitPlayers[IO] + implicit val getConnectedPlayers: GetConnectedPlayers[IO, Player] = + new GetConnectedBukkitPlayers[IO] subsystems.gachapoint.System.wired[IO, SyncIO](breakCountSystem.api).unsafeRunSync() } @@ -291,18 +323,23 @@ class SeichiAssist extends JavaPlugin() { implicit val effectEnvironment: EffectEnvironment = DefaultEffectEnvironment implicit val syncClock: Clock[SyncIO] = Clock.create[SyncIO] - implicit val syncSeasonalEventsSystemAPI: SeasonalEventsAPI[SyncIO] = seasonalEventsSystem.api[SyncIO] + implicit val syncSeasonalEventsSystemAPI: SeasonalEventsAPI[SyncIO] = + seasonalEventsSystem.api[SyncIO] subsystems.mebius.System.wired[IO, SyncIO].unsafeRunSync() } - private implicit lazy val discordNotificationSystem: subsystems.discordnotification.System[IO] = { + private implicit lazy val discordNotificationSystem + : subsystems.discordnotification.System[IO] = { import PluginExecutionContexts.asyncShift implicit val effectEnvironment: EffectEnvironment = DefaultEffectEnvironment implicit val concurrentEffect: ConcurrentEffect[IO] = IO.ioConcurrentEffect(asyncShift) - subsystems.discordnotification.System.wired[IO](seichiAssistConfig.discordNotificationConfiguration) + subsystems + .discordnotification + .System + .wired[IO](seichiAssistConfig.discordNotificationConfiguration) } lazy val subhomeSystem: subhome.System[IO] = { @@ -322,6 +359,16 @@ class SeichiAssist extends JavaPlugin() { subsystems.present.System.wired } + private lazy val anywhereEnderSystem: subsystems.anywhereender.System[IO] = { + import PluginExecutionContexts.onMainThread + + implicit val seichiAmountReadApi: BreakCountAPI[IO, SyncIO, Player] = breakCountSystem.api + subsystems + .anywhereender + .System + .wired[SyncIO, IO](seichiAssistConfig.getAnywhereEnderConfiguration) + } + private lazy val wiredSubsystems: List[Subsystem[IO]] = List( mebiusSystem, expBottleStackSystem, @@ -340,7 +387,8 @@ class SeichiAssist extends JavaPlugin() { gachaPointSystem, discordNotificationSystem, subhomeSystem, - presentSystem + presentSystem, + anywhereEnderSystem ) private lazy val buildAssist: BuildAssist = { @@ -353,16 +401,15 @@ class SeichiAssist extends JavaPlugin() { private lazy val bungeeSemaphoreResponderSystem: BungeeSemaphoreResponderSystem[IO] = { implicit val concurrentEffect: ConcurrentEffect[IO] = IO.ioConcurrentEffect(asyncShift) - implicit val systemConfiguration: com.github.unchama.bungeesemaphoreresponder.Configuration = + implicit val systemConfiguration + : com.github.unchama.bungeesemaphoreresponder.Configuration = seichiAssistConfig.getBungeeSemaphoreSystemConfiguration val savePlayerData: PlayerDataFinalizer[IO, Player] = { player => IO { import scala.util.chaining._ SeichiAssist.playermap.remove(player.getUniqueId).get.tap(_.updateOnQuit()) - } >>= (playerData => - PlayerDataSaveTask.savePlayerData[IO](player, playerData) - ) + } >>= (playerData => PlayerDataSaveTask.savePlayerData[IO](player, playerData)) } import PluginExecutionContexts.timer @@ -373,8 +420,7 @@ class SeichiAssist extends JavaPlugin() { savePlayerData, assaultSkillRoutinesRepositoryControls.finalizer.coerceContextTo[IO], activeSkillAvailabilityRepositoryControls.finalizer.coerceContextTo[IO] - ) - .appendedAll(wiredSubsystems.flatMap(_.managedFinalizers)) + ).appendedAll(wiredSubsystems.flatMap(_.managedFinalizers)) .appendedAll(wiredSubsystems.flatMap(_.managedRepositoryControls.map(_.finalizer))) .toList ), @@ -382,20 +428,21 @@ class SeichiAssist extends JavaPlugin() { ) } - //endregion + // endregion - private implicit val _akkaSystem: ActorSystem = ConfiguredActorSystemProvider("reference.conf").provide() + private implicit val _akkaSystem: ActorSystem = + ConfiguredActorSystemProvider("reference.conf").provide() /** * プラグインを初期化する。ここで例外が投げられるとBukkitがシャットダウンされる。 */ private def monitoredInitialization(): Unit = { + /** * Spigotサーバーが開始されるときにはまだPreLoginEventがcatchされない等色々な不都合があるので、 * SeichiAssistの初期化はプレーヤーが居ないことを前提として進めることとする。 * - * NOTE: - * PreLoginToQuitPlayerDataRepository に関してはJoinEventさえcatchできれば弾けるので、 + * NOTE: PreLoginToQuitPlayerDataRepository に関してはJoinEventさえcatchできれば弾けるので、 * 接続を試みているプレーヤーは弾かないで良さそう、と言うか弾く術がない */ kickAllPlayersDueToInitialization.unsafeRunSync() @@ -408,25 +455,26 @@ class SeichiAssist extends JavaPlugin() { implicit val syncClock: Clock[SyncIO] = Clock.create[SyncIO] - //チャンネルを追加 + // チャンネルを追加 Bukkit.getMessenger.registerOutgoingPluginChannel(this, "BungeeCord") // BungeeCordとのI/O - Bukkit.getMessenger.registerIncomingPluginChannel(this, "SeichiAssistBungee", new BungeeReceiver(this)) + Bukkit + .getMessenger + .registerIncomingPluginChannel(this, "SeichiAssistBungee", new BungeeReceiver(this)) Bukkit.getMessenger.registerOutgoingPluginChannel(this, "SeichiAssistBungee") - - //コンフィグ系の設定は全てConfig.javaに移動 + // コンフィグ系の設定は全てConfig.javaに移動 SeichiAssist.seichiAssistConfig = Config.loadFrom(this) if (SeichiAssist.seichiAssistConfig.getDebugMode == 1) { - //debugmode=1の時は最初からデバッグモードで鯖を起動 + // debugmode=1の時は最初からデバッグモードで鯖を起動 logger.info(s"${RED}SeichiAssistをデバッグモードで起動します") logger.info(s"${RED}コンソールから/seichi debugmode") logger.info(s"${RED}を実行するといつでもON/OFFを切り替えられます") SeichiAssist.DEBUG = true } else { - //debugmode=0の時は/seichi debugmodeによる変更コマンドも使えない + // debugmode=0の時は/seichi debugmodeによる変更コマンドも使えない logger.info(s"${GREEN}SeichiAssistを通常モードで起動します") logger.info(s"${GREEN}デバッグモードを使用する場合は") logger.info(s"${GREEN}config.ymlの設定値を書き換えて再起動してください") @@ -447,12 +495,16 @@ class SeichiAssist extends JavaPlugin() { */ ClassUtils.withThreadContextClassLoaderAs( classOf[SeichiAssist].getClassLoader, - () => Flyway.configure.dataSource(getURL, getID, getPW) - .baselineOnMigrate(true) - .locations("db/migration", "com/github/unchama/seichiassist/database/migrations") - .baselineVersion("1.0.0") - .schemas("flyway_managed_schema") - .load.migrate + () => + Flyway + .configure + .dataSource(getURL, getID, getPW) + .baselineOnMigrate(true) + .locations("db/migration", "com/github/unchama/seichiassist/database/migrations") + .baselineVersion("1.0.0") + .schemas("flyway_managed_schema") + .load + .migrate ) } @@ -460,16 +512,18 @@ class SeichiAssist extends JavaPlugin() { itemMigrationSystem.entryPoints.runWorldMigration.unsafeRunSync() SeichiAssist.databaseGateway = DatabaseGateway.createInitializedInstance( - SeichiAssist.seichiAssistConfig.getURL, SeichiAssist.seichiAssistConfig.getDB, - SeichiAssist.seichiAssistConfig.getID, SeichiAssist.seichiAssistConfig.getPW + SeichiAssist.seichiAssistConfig.getURL, + SeichiAssist.seichiAssistConfig.getDB, + SeichiAssist.seichiAssistConfig.getID, + SeichiAssist.seichiAssistConfig.getPW ) - //mysqlからガチャデータ読み込み + // mysqlからガチャデータ読み込み if (!SeichiAssist.databaseGateway.gachaDataManipulator.loadGachaData()) { throw new Exception("ガチャデータのロードに失敗しました。サーバーを停止します…") } - //mysqlからMineStack用ガチャデータ読み込み + // mysqlからMineStack用ガチャデータ読み込み if (!SeichiAssist.databaseGateway.mineStackGachaDataManipulator.loadMineStackGachaData()) { throw new Exception("MineStack用ガチャデータのロードに失敗しました。サーバーを停止します…") } @@ -477,13 +531,19 @@ class SeichiAssist extends JavaPlugin() { import PluginExecutionContexts._ implicit val breakCountApi: BreakCountAPI[IO, SyncIO, Player] = breakCountSystem.api implicit val breakCountBarApi: BreakCountBarAPI[SyncIO, Player] = breakCountBarSystem.api - implicit val fastDiggingEffectApi: FastDiggingEffectApi[IO, Player] = fastDiggingEffectSystem.effectApi - implicit val fastDiggingSettingsApi: FastDiggingSettingsApi[IO, Player] = fastDiggingEffectSystem.settingsApi - implicit val fourDimensionalPocketApi: FourDimensionalPocketApi[IO, Player] = fourDimensionalPocketSystem.api + implicit val fastDiggingEffectApi: FastDiggingEffectApi[IO, Player] = + fastDiggingEffectSystem.effectApi + implicit val fastDiggingSettingsApi: FastDiggingSettingsApi[IO, Player] = + fastDiggingEffectSystem.settingsApi + implicit val fourDimensionalPocketApi: FourDimensionalPocketApi[IO, Player] = + fourDimensionalPocketSystem.api implicit val gachaPointApi: GachaPointApi[IO, SyncIO, Player] = gachaPointSystem.api implicit val manaApi: ManaApi[IO, SyncIO, Player] = manaSystem.manaApi - implicit val globalNotification: DiscordNotificationAPI[IO] = discordNotificationSystem.globalNotification + implicit val globalNotification: DiscordNotificationAPI[IO] = + discordNotificationSystem.globalNotification implicit val subHomeReadApi: SubHomeReadAPI[IO] = subhomeSystem.api + implicit val everywhereEnderChestApi: AnywhereEnderChestAPI[IO] = + anywhereEnderSystem.accessApi val menuRouter = TopLevelRouter.apply import menuRouter.canOpenStickMenu @@ -506,7 +566,8 @@ class SeichiAssist extends JavaPlugin() { // 本来は曖昧さ回避のためにRouterのインスタンスを生成するべきではないが、生成を回避しようとすると // 巨大な変更が必要となる。そのため、Routerのインスタンスを新しく生成することで、それまでの間 // 機能を果たそうとするものである。 - implicit val canOpenBuildMainMenu: CanOpen[IO, BuildMainMenu.type] = BuildAssistMenuRouter.apply.canOpenBuildMainMenu + implicit val canOpenBuildMainMenu: CanOpen[IO, BuildMainMenu.type] = + BuildAssistMenuRouter.apply.canOpenBuildMainMenu // コマンドの登録 Map( "gacha" -> new GachaCommand(), @@ -525,14 +586,12 @@ class SeichiAssist extends JavaPlugin() { "x-transfer" -> RegionOwnerTransferCommand.executor, "stickmenu" -> StickMenuCommand.executor, "hat" -> HatCommand.executor - ) - .concat(wiredSubsystems.flatMap(_.commands)) - .foreach { - case (commandName, executor) => getCommand(commandName).setExecutor(executor) - } + ).concat(wiredSubsystems.flatMap(_.commands)).foreach { + case (commandName, executor) => getCommand(commandName).setExecutor(executor) + } import menuRouter.canOpenAchievementMenu - //リスナーの登録 + // リスナーの登録 val listeners = Seq( new PlayerJoinListener(), new PlayerClickListener(), @@ -547,9 +606,8 @@ class SeichiAssist extends JavaPlugin() { new ChatInterceptor(List(globalChatInterceptionScope)), new MenuHandler(), SpawnRegionProjectileInterceptor, - Y5DoubleSlabCanceller, - ) - .concat(bungeeSemaphoreResponderSystem.listenersToBeRegistered) + Y5DoubleSlabCanceller + ).concat(bungeeSemaphoreResponderSystem.listenersToBeRegistered) .concat { Seq( activeSkillAvailabilityRepositoryControls.initializer, @@ -563,7 +621,7 @@ class SeichiAssist extends JavaPlugin() { getServer.getPluginManager.registerEvents(_, this) } - //ランキングリストを最新情報に更新する + // ランキングリストを最新情報に更新する if (!SeichiAssist.databaseGateway.playerDataManipulator.successRankingUpdate()) { throw new RuntimeException("ランキングデータの作成に失敗しました。サーバーを停止します…") } @@ -622,7 +680,8 @@ class SeichiAssist extends JavaPlugin() { implicit val breakCountApi: BreakCountReadAPI[IO, SyncIO, Player] = breakCountSystem.api implicit val manaApi: ManaApi[IO, SyncIO, Player] = manaSystem.manaApi - implicit val fastDiggingEffectApi: FastDiggingEffectApi[IO, Player] = fastDiggingEffectSystem.effectApi + implicit val fastDiggingEffectApi: FastDiggingEffectApi[IO, Player] = + fastDiggingEffectSystem.effectApi implicit val ioConcurrent: ConcurrentEffect[IO] = IO.ioConcurrentEffect(asyncShift) implicit val sendMessages: SendMinecraftMessage[IO, Player] = new SendBukkitMessage[IO] @@ -630,7 +689,7 @@ class SeichiAssist extends JavaPlugin() { subsystems.dragonnighttime.System.backgroundProcess[IO, SyncIO, Player] val halfHourRankingRoutineOption: Option[IO[Nothing]] = - // 公共鯖(7)と建築鯖(8)なら整地量のランキングを表示する必要はない + // 公共鯖(7)と建築鯖(8)なら整地量のランキングを表示する必要はない Option.unless(Set(7, 8).contains(SeichiAssist.seichiAssistConfig.getServerNum)) { subsystems.halfhourranking.System.backgroundProcess[IO, SyncIO] } @@ -681,7 +740,9 @@ class SeichiAssist extends JavaPlugin() { // BungeeSemaphoreResponderの全ファイナライザを走らせる getServer - .getOnlinePlayers.asScala.toList + .getOnlinePlayers + .asScala + .toList .traverse(bungeeSemaphoreResponderSystem.finalizer.onQuitOf) .unsafeRunSync() @@ -700,7 +761,7 @@ class SeichiAssist extends JavaPlugin() { private def cancelRepeatedJobs(): Unit = { repeatedTaskFiber match { case Some(x) => x.cancel.unsafeRunSync() - case None => + case None => } } } @@ -708,27 +769,27 @@ class SeichiAssist extends JavaPlugin() { object SeichiAssist { val SEICHIWORLDNAME = "world_sw" val DEBUGWORLDNAME = "world" - //Gachadataに依存するデータリスト + // Gachadataに依存するデータリスト val gachadatalist: mutable.ArrayBuffer[GachaPrize] = mutable.ArrayBuffer() - //Playerdataに依存するデータリスト + // Playerdataに依存するデータリスト val playermap: mutable.HashMap[UUID, PlayerData] = mutable.HashMap() - //プレイ時間ランキング表示用データリスト + // プレイ時間ランキング表示用データリスト val ranklist_playtick: mutable.ArrayBuffer[RankData] = mutable.ArrayBuffer() - //投票ポイント表示用データリスト + // 投票ポイント表示用データリスト val ranklist_p_vote: mutable.ArrayBuffer[RankData] = mutable.ArrayBuffer() - //マナ妖精表示用のデータリスト + // マナ妖精表示用のデータリスト val ranklist_p_apple: mutable.ArrayBuffer[RankData] = mutable.ArrayBuffer() var instance: SeichiAssist = _ - //デバッグフラグ(デバッグモード使用時はここで変更するのではなくconfig.ymlの設定値を変更すること!) + // デバッグフラグ(デバッグモード使用時はここで変更するのではなくconfig.ymlの設定値を変更すること!) // TODO deprecate this var DEBUG = false - //ガチャシステムのメンテナンスフラグ + // ガチャシステムのメンテナンスフラグ var gachamente = false // TODO staticであるべきではない var databaseGateway: DatabaseGateway = _ var seichiAssistConfig: Config = _ - //(minestackに格納する)Gachadataに依存するデータリスト + // (minestackに格納する)Gachadataに依存するデータリスト val msgachadatalist: mutable.ArrayBuffer[MineStackGachaData] = mutable.ArrayBuffer() var allplayergiveapplelong = 0L @@ -744,8 +805,17 @@ object SeichiAssist { msgachadatalist .toList .zipWithIndex - .filter(_._1.itemStack.getType != Material.EXP_BOTTLE) //経験値瓶だけはすでにリストにあるので除外 - .map { case (g, i) => - new MineStackObj(g.objName, None, g.level, g.itemStack, true, i, MineStackObjectCategory.GACHA_PRIZES) + .filter(_._1.itemStack.getType != Material.EXP_BOTTLE) // 経験値瓶だけはすでにリストにあるので除外 + .map { + case (g, i) => + new MineStackObj( + g.objName, + None, + g.level, + g.itemStack, + true, + i, + MineStackObjectCategory.GACHA_PRIZES + ) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/achievement/AchievementCondition.scala b/src/main/scala/com/github/unchama/seichiassist/achievement/AchievementCondition.scala index bd65f2879e..d56c6251b8 100644 --- a/src/main/scala/com/github/unchama/seichiassist/achievement/AchievementCondition.scala +++ b/src/main/scala/com/github/unchama/seichiassist/achievement/AchievementCondition.scala @@ -6,15 +6,18 @@ object WithPlaceholder { implicit val unitIsWithPlaceholder: WithPlaceholder[Unit] = WithPlaceholder(()) } -case class AchievementCondition[P](shouldUnlock: PlayerPredicate, - conditionTemplate: ParameterizedText[P], - parameter: P) { +case class AchievementCondition[P]( + shouldUnlock: PlayerPredicate, + conditionTemplate: ParameterizedText[P], + parameter: P +) { val parameterizedDescription: String = conditionTemplate(parameter) } -case class HiddenAchievementCondition[P: WithPlaceholder](shouldDisplayToUI: PlayerPredicate, - underlying: AchievementCondition[P]) { +case class HiddenAchievementCondition[P: WithPlaceholder]( + shouldDisplayToUI: PlayerPredicate, + underlying: AchievementCondition[P] +) { val maskedDescription: String = underlying.conditionTemplate(implicitly[WithPlaceholder[P]].placeholder) } - diff --git a/src/main/scala/com/github/unchama/seichiassist/achievement/AchievementConditions.scala b/src/main/scala/com/github/unchama/seichiassist/achievement/AchievementConditions.scala index d1f0fc2253..e4883cbb83 100644 --- a/src/main/scala/com/github/unchama/seichiassist/achievement/AchievementConditions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/achievement/AchievementConditions.scala @@ -4,7 +4,6 @@ import cats.effect.IO import com.github.unchama.buildassist.BuildAssist import com.github.unchama.seichiassist.SeichiAssist import com.github.unchama.seichiassist.data.player.PlayerData -import com.github.unchama.seichiassist.achievement.NamedHoliday._ import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.SeichiExpAmount import org.bukkit.Material import org.bukkit.entity.Player @@ -14,7 +13,6 @@ import org.bukkit.inventory.meta.SkullMeta import java.time.temporal.TemporalAdjusters import java.time.{DayOfWeek, LocalDate, LocalTime, Month} import scala.concurrent.duration.FiniteDuration -import scala.math.floor object AchievementConditions { def playerDataPredicate(predicate: PlayerData => IO[Boolean]): PlayerPredicate = { player => @@ -23,20 +21,27 @@ object AchievementConditions { }.flatMap(predicate) } - def hasUnlocked(id: Int): PlayerPredicate = playerDataPredicate(d => IO { - d.TitleFlags.contains(id) - }) + def hasUnlocked(id: Int): PlayerPredicate = playerDataPredicate(d => + IO { + d.TitleFlags.contains(id) + } + ) - def dependsOn[A: WithPlaceholder](id: Int, condition: AchievementCondition[A]): HiddenAchievementCondition[A] = { + def dependsOn[A: WithPlaceholder]( + id: Int, + condition: AchievementCondition[A] + ): HiddenAchievementCondition[A] = { HiddenAchievementCondition(hasUnlocked(id), condition) } def brokenBlockRankingPosition_<=(n: Int): AchievementCondition[Int] = { val predicate: PlayerPredicate = { player: Player => - SeichiAssist.instance + SeichiAssist + .instance .rankingSystemApi .seichiAmountRanking - .ranking.read + .ranking + .read .map(_.positionOf(player.getName)) .map(_.exists(_ <= n)) } @@ -44,10 +49,15 @@ object AchievementConditions { AchievementCondition(predicate, "「整地神ランキング」" + _ + "位達成", n) } - def placedBlockAmount_>=(amount: BigDecimal, localizedAmount: String): AchievementCondition[String] = { + def placedBlockAmount_>=( + amount: BigDecimal, + localizedAmount: String + ): AchievementCondition[String] = { val predicate: PlayerPredicate = { player: Player => - BuildAssist.instance - .buildAmountDataRepository(player).read + BuildAssist + .instance + .buildAmountDataRepository(player) + .read .map(_.expAmount.amount >= amount) .toIO } @@ -56,24 +66,34 @@ object AchievementConditions { } def brokenBlockAmountPredicate(f: SeichiExpAmount => Boolean): PlayerPredicate = { player => - SeichiAssist.instance - .breakCountSystem.api + SeichiAssist + .instance + .breakCountSystem + .api .seichiAmountDataRepository(player) - .read.map(amount => f(amount.expAmount)) + .read + .map(amount => f(amount.expAmount)) .toIO } - def brokenBlockAmount_>=(amount: Long, localizedAmount: String): AchievementCondition[String] = { + def brokenBlockAmount_>=( + amount: Long, + localizedAmount: String + ): AchievementCondition[String] = { import cats.implicits._ val predicate = brokenBlockAmountPredicate(_ >= SeichiExpAmount.ofNonNegative(amount)) AchievementCondition(predicate, "整地量が " + _ + "を超える", localizedAmount) } - def totalPlayTime_>=(duration: FiniteDuration, localizedDuration: String): AchievementCondition[String] = { + def totalPlayTime_>=( + duration: FiniteDuration, + localizedDuration: String + ): AchievementCondition[String] = { import com.github.unchama.concurrent.syntax._ - val predicate = playerDataPredicate(d => IO { d.playTick.ticks.toMillis >= duration.toMillis }) + val predicate = + playerDataPredicate(d => IO { d.playTick.ticks.toMillis >= duration.toMillis }) AchievementCondition(predicate, "参加時間が " + _ + " を超える", localizedDuration) } @@ -102,23 +122,33 @@ object AchievementConditions { AchievementCondition(predicate, _ + "月にプレイ", month.getValue.toString) } - def playedOn(month: Month, dayOfMonth: Int, dateSpecification: String): AchievementCondition[String] = { + def playedOn( + month: Month, + dayOfMonth: Int, + dateSpecification: String + ): AchievementCondition[String] = { val predicate: PlayerPredicate = _ => IO { LocalDate.now().getMonth == month && - LocalDate.now().getDayOfMonth == dayOfMonth + LocalDate.now().getDayOfMonth == dayOfMonth } AchievementCondition(predicate, _ + "にプレイ", dateSpecification) } - def playedOn(month: Month, weekOfMonth: Int, dayOfWeek: DayOfWeek, dateSpecification: String): AchievementCondition[String] = { + def playedOn( + month: Month, + weekOfMonth: Int, + dayOfWeek: DayOfWeek, + dateSpecification: String + ): AchievementCondition[String] = { val predicate: PlayerPredicate = _ => IO { val now = LocalDate.now() // 現在の月の第[[weekOfMonth]][[dayOfWeek]]曜日 - val dayOfWeekOnWeekOfTheMonth = now.`with`(TemporalAdjusters.dayOfWeekInMonth(weekOfMonth, dayOfWeek)) + val dayOfWeekOnWeekOfTheMonth = + now.`with`(TemporalAdjusters.dayOfWeekInMonth(weekOfMonth, dayOfWeek)) now.getMonth == month && now == dayOfWeekOnWeekOfTheMonth } @@ -128,7 +158,7 @@ object AchievementConditions { def playedOn(holiday: NamedHoliday): AchievementCondition[String] = { val predicate: PlayerPredicate = _ => - IO{ + IO { val now = LocalDate.now() val target = holiday.dateOn(now.getYear) @@ -152,10 +182,11 @@ object AchievementConditions { import scala.util.chaining._ stack != null && - stack.getType == Material.SKULL_ITEM && - stack.getItemMeta.asInstanceOf[SkullMeta].pipe(meta => - meta.hasOwner && meta.getOwningPlayer.getName == "unchama" - ) + stack.getType == Material.SKULL_ITEM && + stack + .getItemMeta + .asInstanceOf[SkullMeta] + .pipe(meta => meta.hasOwner && meta.getOwningPlayer.getName == "unchama") } import com.github.unchama.menuinventory.syntax._ @@ -165,35 +196,50 @@ object AchievementConditions { } } - HiddenAchievementCondition(shouldDisplay, AchievementCondition(shouldUnlock, _ => "器を満たす奇跡の少女", ())) + HiddenAchievementCondition( + shouldDisplay, + AchievementCondition(shouldUnlock, _ => "器を満たす奇跡の少女", ()) + ) } val conditionFor8002: HiddenAchievementCondition[Unit] = { val shouldDisplay: PlayerPredicate = - brokenBlockAmountPredicate { case SeichiExpAmount(amount) => - amount % 1000000L == 0L && amount != 0L + brokenBlockAmountPredicate { + case SeichiExpAmount(amount) => + amount % 1000000L == 0L && amount != 0L } val unlockCondition: PlayerPredicate = - brokenBlockAmountPredicate { case SeichiExpAmount(amount) => - amount % 1000000L == 777777L + brokenBlockAmountPredicate { + case SeichiExpAmount(amount) => + amount % 1000000L == 777777L } - HiddenAchievementCondition(shouldDisplay, AchievementCondition(unlockCondition, _ => "[[[[[[LuckyNumber]]]]]]", ())) + HiddenAchievementCondition( + shouldDisplay, + AchievementCondition(unlockCondition, _ => "[[[[[[LuckyNumber]]]]]]", ()) + ) } val unlockConditionFor8003: PlayerPredicate = - playerDataPredicate(p => IO { - p.playTick % (20 * 60 * 60 * 8) <= (20 * 60) - }) + playerDataPredicate(p => + IO { + p.playTick % (20 * 60 * 60 * 8) <= (20 * 60) + } + ) val conditionFor8003: HiddenAchievementCondition[Unit] = { val shouldDisplay: PlayerPredicate = - playerDataPredicate(p => IO { - p.playTick % (20 * 60 * 60) >= 0 && p.playTick % (20 * 60 * 60) <= (20 * 60) - }) + playerDataPredicate(p => + IO { + p.playTick % (20 * 60 * 60) >= 0 && p.playTick % (20 * 60 * 60) <= (20 * 60) + } + ) - HiddenAchievementCondition(shouldDisplay, AchievementCondition(_ => IO.pure(false), _ => "定時分働いたら記録を確認!", ())) + HiddenAchievementCondition( + shouldDisplay, + AchievementCondition(_ => IO.pure(false), _ => "定時分働いたら記録を確認!", ()) + ) } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/achievement/NamedHoliday.scala b/src/main/scala/com/github/unchama/seichiassist/achievement/NamedHoliday.scala index aa5caba30f..568720eebb 100644 --- a/src/main/scala/com/github/unchama/seichiassist/achievement/NamedHoliday.scala +++ b/src/main/scala/com/github/unchama/seichiassist/achievement/NamedHoliday.scala @@ -1,9 +1,8 @@ package com.github.unchama.seichiassist.achievement -import java.time.{LocalDate, Month} - import enumeratum._ +import java.time.{LocalDate, Month} import scala.math.floor /** diff --git a/src/main/scala/com/github/unchama/seichiassist/achievement/NicknameMapping.scala b/src/main/scala/com/github/unchama/seichiassist/achievement/NicknameMapping.scala index a52873635d..64837e46d4 100644 --- a/src/main/scala/com/github/unchama/seichiassist/achievement/NicknameMapping.scala +++ b/src/main/scala/com/github/unchama/seichiassist/achievement/NicknameMapping.scala @@ -5,9 +5,11 @@ import com.github.unchama.seichiassist.achievement.SeichiAchievement._ object NicknameMapping { - case class NicknameCombination(first: Option[AchievementId], - second: Option[AchievementId] = None, - third: Option[AchievementId] = None) + case class NicknameCombination( + first: Option[AchievementId], + second: Option[AchievementId] = None, + third: Option[AchievementId] = None + ) val getNicknameCombinationFor: SeichiAchievement => NicknameCombination = CachedFunction { case No_1001 => NicknameCombination(Some(1001)) @@ -38,7 +40,6 @@ object NicknameMapping { case No_2013 => NicknameCombination(Some(2013), Some(9905), Some(2013)) case No_2014 => NicknameCombination(Some(2014), None, Some(2014)) - case No_3001 => NicknameCombination(Some(3001)) case No_3002 => NicknameCombination(Some(3002), Some(9905), Some(3002)) case No_3003 => NicknameCombination(Some(3003)) diff --git a/src/main/scala/com/github/unchama/seichiassist/achievement/Nicknames.scala b/src/main/scala/com/github/unchama/seichiassist/achievement/Nicknames.scala index 665f47567c..9f63619fce 100644 --- a/src/main/scala/com/github/unchama/seichiassist/achievement/Nicknames.scala +++ b/src/main/scala/com/github/unchama/seichiassist/achievement/Nicknames.scala @@ -15,7 +15,7 @@ object Nicknames { 1010 -> HeadTail(s"百傑", s"百傑"), 1011 -> HeadTail(s"超人", s"挑む"), 1012 -> HeadTail(s"継続", s"力なり"), - //建築量実績 + // 建築量実績 2001 -> HeadTail(s"建築", s"極地"), 2002 -> HeadTail(s"全て", s"創造者"), 2003 -> HeadTail(s"常識", s"覆す"), @@ -106,11 +106,11 @@ object Nicknames { 5118 -> HeadTail(s"昇給", s"まだですか"), 5119 -> HeadTail(s"歴史", s"生き証人"), 5120 -> FullSet(s"石の上", s"にも", s"三年"), - 5121 -> HeadTail(s"いつも",s"ログイン"), - 5122 -> HeadTail(s"長老",s"風格"), - 5123 -> FullSet(s"めぐ",s"り",s"巡る"), - 5124 -> FullSet(s"仙人",s"すら",s"凌ぐ"), - 5125 -> HeadTail(s"四つ葉",s"クローバー"),//の + 5121 -> HeadTail(s"いつも", s"ログイン"), + 5122 -> HeadTail(s"長老", s"風格"), + 5123 -> FullSet(s"めぐ", s"り", s"巡る"), + 5124 -> FullSet(s"仙人", s"すら", s"凌ぐ"), + 5125 -> HeadTail(s"四つ葉", s"クローバー"), // の // 投票数実績 6001 -> HeadTail(s"狂信者", s"狂信者"), 6002 -> HeadTail(s"全身", s"全霊"), @@ -284,38 +284,46 @@ object Nicknames { val getNicknameFor: AchievementId => Option[NicknamesToBeUnlocked] = Nicknames.map.get - def getHeadPartFor(achievementId: AchievementId): Option[PartialNickname] = getNicknameFor(achievementId).flatMap(_.head()) + def getHeadPartFor(achievementId: AchievementId): Option[PartialNickname] = + getNicknameFor(achievementId).flatMap(_.head()) - def getMiddlePartFor(achievementId: AchievementId): Option[PartialNickname] = getNicknameFor(achievementId).flatMap(_.middle()) + def getMiddlePartFor(achievementId: AchievementId): Option[PartialNickname] = + getNicknameFor(achievementId).flatMap(_.middle()) - def getTailPartFor(achievementId: AchievementId): Option[PartialNickname] = getNicknameFor(achievementId).flatMap(_.tail()) + def getTailPartFor(achievementId: AchievementId): Option[PartialNickname] = + getNicknameFor(achievementId).flatMap(_.tail()) /** - * @deprecated use [[getCombinedNicknameFor]] where possible + * @deprecated + * use [[getCombinedNicknameFor]] where possible */ - @deprecated def getTitleFor(headAchievementId: AchievementId, middleAchievementId: AchievementId, tailAchievementId: AchievementId): String = { + @deprecated def getTitleFor( + headAchievementId: AchievementId, + middleAchievementId: AchievementId, + tailAchievementId: AchievementId + ): String = { getHeadPartFor(headAchievementId).getOrElse("") + getMiddlePartFor(middleAchievementId).getOrElse("") + getTailPartFor(tailAchievementId).getOrElse("") } - def getCombinedNicknameFor(head: AchievementId, middle: AchievementId, tail: AchievementId): Option[Nickname] = { - val definedParts = List( - getHeadPartFor(head), - getMiddlePartFor(middle), - getTailPartFor(tail) - ).flatten + def getCombinedNicknameFor( + head: AchievementId, + middle: AchievementId, + tail: AchievementId + ): Option[Nickname] = { + val definedParts = + List(getHeadPartFor(head), getMiddlePartFor(middle), getTailPartFor(tail)).flatten definedParts match { - case nonEmptyParts@ ::(_, _) => Some(nonEmptyParts.mkString("")) - case Nil => None + case nonEmptyParts @ ::(_, _) => Some(nonEmptyParts.mkString("")) + case Nil => None } } } /** - * この構造で持たれる二つ名は、対応する実績を解除、または対応する二つ名を購入していれば、 - * 二つ名組み合わせ設定で使用できるようになる。 + * この構造で持たれる二つ名は、対応する実績を解除、または対応する二つ名を購入していれば、 二つ名組み合わせ設定で使用できるようになる。 */ sealed trait NicknamesToBeUnlocked { def head(): Option[String] @@ -325,7 +333,8 @@ sealed trait NicknamesToBeUnlocked { def tail(): Option[String] } -case class HeadTail(private val _head: String, private val _tail: String) extends NicknamesToBeUnlocked { +case class HeadTail(private val _head: String, private val _tail: String) + extends NicknamesToBeUnlocked { val head: Option[String] = Some(_head) val middle: Option[String] = None val tail: Option[String] = Some(_tail) @@ -343,13 +352,18 @@ case class HeadOnly(private val _head: String) extends NicknamesToBeUnlocked { val tail: Option[String] = None } -case class FullSet(private val _head: String, private val _middle: String, private val _tail: String) extends NicknamesToBeUnlocked { +case class FullSet( + private val _head: String, + private val _middle: String, + private val _tail: String +) extends NicknamesToBeUnlocked { val head: Option[String] = Some(_head) val middle: Option[String] = Some(_middle) val tail: Option[String] = Some(_tail) } -case class HeadMiddle(private val _head: String, private val _middle: String) extends NicknamesToBeUnlocked { +case class HeadMiddle(private val _head: String, private val _middle: String) + extends NicknamesToBeUnlocked { val head: Option[String] = Some(_head) val middle: Option[String] = Some(_middle) val tail: Option[String] = None diff --git a/src/main/scala/com/github/unchama/seichiassist/achievement/SeichiAchievement.scala b/src/main/scala/com/github/unchama/seichiassist/achievement/SeichiAchievement.scala index 5aefaca137..cae298c42d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/achievement/SeichiAchievement.scala +++ b/src/main/scala/com/github/unchama/seichiassist/achievement/SeichiAchievement.scala @@ -26,12 +26,32 @@ object SeichiAchievement extends Enum[SeichiAchievement] { val condition: HiddenAchievementCondition[P] } - sealed abstract class GrantedByConsole(override val id: Int, val condition: String, val explanation: Option[List[String]]) extends SeichiAchievement + sealed abstract class GrantedByConsole( + override val id: Int, + val condition: String, + val explanation: Option[List[String]] + ) extends SeichiAchievement - sealed abstract class NormalAuto[P](override val id: Int, override val condition: AchievementCondition[P]) extends Normal[P] with AutoUnlocked - sealed abstract class NormalManual[P](override val id: Int, override val condition: AchievementCondition[P]) extends Normal[P] with ManuallyUnlocked - sealed abstract class HiddenAuto[P](override val id: Int, override val condition: HiddenAchievementCondition[P]) extends Hidden[P] with AutoUnlocked - sealed abstract class HiddenManual[P](override val id: Int, override val condition: HiddenAchievementCondition[P]) extends Hidden[P] with ManuallyUnlocked + sealed abstract class NormalAuto[P]( + override val id: Int, + override val condition: AchievementCondition[P] + ) extends Normal[P] + with AutoUnlocked + sealed abstract class NormalManual[P]( + override val id: Int, + override val condition: AchievementCondition[P] + ) extends Normal[P] + with ManuallyUnlocked + sealed abstract class HiddenAuto[P]( + override val id: Int, + override val condition: HiddenAchievementCondition[P] + ) extends Hidden[P] + with AutoUnlocked + sealed abstract class HiddenManual[P]( + override val id: Int, + override val condition: HiddenAchievementCondition[P] + ) extends Hidden[P] + with ManuallyUnlocked import AchievementConditions.SecretAchievementConditions._ import AchievementConditions._ @@ -52,23 +72,31 @@ object SeichiAchievement extends Enum[SeichiAchievement] { case object No_1012 extends NormalAuto(1012, brokenBlockRankingPosition_<=(500)) // 建築量 - case object No_2001 extends NormalAuto(2001, placedBlockAmount_>=(1000000,"100万")) - case object No_2002 extends NormalAuto(2002, placedBlockAmount_>=(800000,"80万")) - case object No_2003 extends NormalAuto(2003, placedBlockAmount_>=(650000,"65万")) - case object No_2004 extends NormalAuto(2004, placedBlockAmount_>=(500000,"50万")) - case object No_2005 extends NormalAuto(2005, placedBlockAmount_>=(400000,"40万")) - case object No_2006 extends NormalAuto(2006, placedBlockAmount_>=(300000,"30万")) - case object No_2007 extends NormalAuto(2007, placedBlockAmount_>=(200000,"20万")) - case object No_2008 extends NormalAuto(2008, placedBlockAmount_>=(100000,"10万")) - case object No_2009 extends NormalAuto(2009, placedBlockAmount_>=(50000,"5万")) - case object No_2010 extends NormalAuto(2010, placedBlockAmount_>=(10000,"1万")) - case object No_2011 extends HiddenAuto(2011, dependsOn(2001,placedBlockAmount_>=(2000000,"200万"))) - case object No_2012 extends HiddenAuto(2012, dependsOn(2011,placedBlockAmount_>=(3000000,"300万"))) - case object No_2013 extends HiddenAuto(2013, dependsOn(2012,placedBlockAmount_>=(4000000,"400万"))) - case object No_2014 extends HiddenAuto(2014, dependsOn(2013,placedBlockAmount_>=(5000000,"500万"))) + case object No_2001 extends NormalAuto(2001, placedBlockAmount_>=(1000000, "100万")) + case object No_2002 extends NormalAuto(2002, placedBlockAmount_>=(800000, "80万")) + case object No_2003 extends NormalAuto(2003, placedBlockAmount_>=(650000, "65万")) + case object No_2004 extends NormalAuto(2004, placedBlockAmount_>=(500000, "50万")) + case object No_2005 extends NormalAuto(2005, placedBlockAmount_>=(400000, "40万")) + case object No_2006 extends NormalAuto(2006, placedBlockAmount_>=(300000, "30万")) + case object No_2007 extends NormalAuto(2007, placedBlockAmount_>=(200000, "20万")) + case object No_2008 extends NormalAuto(2008, placedBlockAmount_>=(100000, "10万")) + case object No_2009 extends NormalAuto(2009, placedBlockAmount_>=(50000, "5万")) + case object No_2010 extends NormalAuto(2010, placedBlockAmount_>=(10000, "1万")) + case object No_2011 + extends HiddenAuto(2011, dependsOn(2001, placedBlockAmount_>=(2000000, "200万"))) + case object No_2012 + extends HiddenAuto(2012, dependsOn(2011, placedBlockAmount_>=(3000000, "300万"))) + case object No_2013 + extends HiddenAuto(2013, dependsOn(2012, placedBlockAmount_>=(4000000, "400万"))) + case object No_2014 + extends HiddenAuto(2014, dependsOn(2013, placedBlockAmount_>=(5000000, "500万"))) // 整地量 - case object No_3001 extends HiddenAuto(3001, dependsOn(3002, brokenBlockAmount_>=(Int.MaxValue.toLong, "int型の壁"))) + case object No_3001 + extends HiddenAuto( + 3001, + dependsOn(3002, brokenBlockAmount_>=(Int.MaxValue.toLong, "int型の壁")) + ) case object No_3002 extends NormalAuto(3002, brokenBlockAmount_>=(1000000000L, "10億")) case object No_3003 extends NormalAuto(3003, brokenBlockAmount_>=(500000000L, "5億")) case object No_3004 extends NormalAuto(3004, brokenBlockAmount_>=(100000000L, "1億")) @@ -79,19 +107,28 @@ object SeichiAchievement extends Enum[SeichiAchievement] { case object No_3009 extends NormalAuto(3009, brokenBlockAmount_>=(500000L, "50万")) case object No_3010 extends NormalAuto(3010, brokenBlockAmount_>=(100000L, "10万")) case object No_3011 extends NormalAuto(3011, brokenBlockAmount_>=(10000L, "1万")) - case object No_3012 extends HiddenAuto(3012, dependsOn(3001, brokenBlockAmount_>=(3000000000L, "30億"))) - case object No_3013 extends HiddenAuto(3013, dependsOn(3001, brokenBlockAmount_>=(4000000000L, "40億"))) - case object No_3014 extends HiddenAuto(3014, dependsOn(3001, brokenBlockAmount_>=(5000000000L, "50億"))) - case object No_3015 extends HiddenAuto(3015, dependsOn(3014, brokenBlockAmount_>=(6000000000L, "60億"))) - case object No_3016 extends HiddenAuto(3016, dependsOn(3015, brokenBlockAmount_>=(7000000000L, "70億"))) - case object No_3017 extends HiddenAuto(3017, dependsOn(3016, brokenBlockAmount_>=(8000000000L, "80億"))) - case object No_3018 extends HiddenAuto(3018, dependsOn(3017, brokenBlockAmount_>=(9000000000L, "90億"))) - case object No_3019 extends HiddenAuto(3019, dependsOn(3018, brokenBlockAmount_>=(10000000000L, "100億"))) + case object No_3012 + extends HiddenAuto(3012, dependsOn(3001, brokenBlockAmount_>=(3000000000L, "30億"))) + case object No_3013 + extends HiddenAuto(3013, dependsOn(3001, brokenBlockAmount_>=(4000000000L, "40億"))) + case object No_3014 + extends HiddenAuto(3014, dependsOn(3001, brokenBlockAmount_>=(5000000000L, "50億"))) + case object No_3015 + extends HiddenAuto(3015, dependsOn(3014, brokenBlockAmount_>=(6000000000L, "60億"))) + case object No_3016 + extends HiddenAuto(3016, dependsOn(3015, brokenBlockAmount_>=(7000000000L, "70億"))) + case object No_3017 + extends HiddenAuto(3017, dependsOn(3016, brokenBlockAmount_>=(8000000000L, "80億"))) + case object No_3018 + extends HiddenAuto(3018, dependsOn(3017, brokenBlockAmount_>=(9000000000L, "90億"))) + case object No_3019 + extends HiddenAuto(3019, dependsOn(3018, brokenBlockAmount_>=(10000000000L, "100億"))) import scala.concurrent.duration._ // 参加時間 - case object No_4001 extends HiddenAuto(4001, dependsOn(4002, totalPlayTime_>=(2000.hours, "2000時間"))) + case object No_4001 + extends HiddenAuto(4001, dependsOn(4002, totalPlayTime_>=(2000.hours, "2000時間"))) case object No_4002 extends NormalAuto(4002, totalPlayTime_>=(1000.hours, "1000時間")) case object No_4003 extends NormalAuto(4003, totalPlayTime_>=(500.hours, "500時間")) case object No_4004 extends NormalAuto(4004, totalPlayTime_>=(250.hours, "250時間")) @@ -101,19 +138,32 @@ object SeichiAchievement extends Enum[SeichiAchievement] { case object No_4008 extends NormalAuto(4008, totalPlayTime_>=(15.hours, "15時間")) case object No_4009 extends NormalAuto(4009, totalPlayTime_>=(5.hours, "5時間")) case object No_4010 extends NormalAuto(4010, totalPlayTime_>=(1.hour, "1時間")) - case object No_4011 extends HiddenAuto(4011, dependsOn(4002, totalPlayTime_>=(3000.hours, "3000時間"))) - case object No_4012 extends HiddenAuto(4012, dependsOn(4002, totalPlayTime_>=(4000.hours, "4000時間"))) - case object No_4013 extends HiddenAuto(4013, dependsOn(4002, totalPlayTime_>=(5000.hours, "5000時間"))) - case object No_4014 extends HiddenAuto(4014, dependsOn(4013, totalPlayTime_>=(6000.hours, "6000時間"))) - case object No_4015 extends HiddenAuto(4015, dependsOn(4013, totalPlayTime_>=(7000.hours, "7000時間"))) - case object No_4016 extends HiddenAuto(4016, dependsOn(4013, totalPlayTime_>=(8000.hours, "8000時間"))) - case object No_4017 extends HiddenAuto(4017, dependsOn(4013, totalPlayTime_>=(9000.hours, "9000時間"))) - case object No_4018 extends HiddenAuto(4018, dependsOn(4013, totalPlayTime_>=(10000.hours, "10000時間"))) - case object No_4019 extends HiddenAuto(4019, dependsOn(4018, totalPlayTime_>=(12000.hours, "12000時間"))) - case object No_4020 extends HiddenAuto(4020, dependsOn(4019, totalPlayTime_>=(14000.hours, "14000時間"))) - case object No_4021 extends HiddenAuto(4021, dependsOn(4020, totalPlayTime_>=(16000.hours, "16000時間"))) - case object No_4022 extends HiddenAuto(4022, dependsOn(4021, totalPlayTime_>=(18000.hours, "18000時間"))) - case object No_4023 extends HiddenAuto(4023, dependsOn(4022, totalPlayTime_>=(20000.hours, "20000時間"))) + case object No_4011 + extends HiddenAuto(4011, dependsOn(4002, totalPlayTime_>=(3000.hours, "3000時間"))) + case object No_4012 + extends HiddenAuto(4012, dependsOn(4002, totalPlayTime_>=(4000.hours, "4000時間"))) + case object No_4013 + extends HiddenAuto(4013, dependsOn(4002, totalPlayTime_>=(5000.hours, "5000時間"))) + case object No_4014 + extends HiddenAuto(4014, dependsOn(4013, totalPlayTime_>=(6000.hours, "6000時間"))) + case object No_4015 + extends HiddenAuto(4015, dependsOn(4013, totalPlayTime_>=(7000.hours, "7000時間"))) + case object No_4016 + extends HiddenAuto(4016, dependsOn(4013, totalPlayTime_>=(8000.hours, "8000時間"))) + case object No_4017 + extends HiddenAuto(4017, dependsOn(4013, totalPlayTime_>=(9000.hours, "9000時間"))) + case object No_4018 + extends HiddenAuto(4018, dependsOn(4013, totalPlayTime_>=(10000.hours, "10000時間"))) + case object No_4019 + extends HiddenAuto(4019, dependsOn(4018, totalPlayTime_>=(12000.hours, "12000時間"))) + case object No_4020 + extends HiddenAuto(4020, dependsOn(4019, totalPlayTime_>=(14000.hours, "14000時間"))) + case object No_4021 + extends HiddenAuto(4021, dependsOn(4020, totalPlayTime_>=(16000.hours, "16000時間"))) + case object No_4022 + extends HiddenAuto(4022, dependsOn(4021, totalPlayTime_>=(18000.hours, "18000時間"))) + case object No_4023 + extends HiddenAuto(4023, dependsOn(4022, totalPlayTime_>=(20000.hours, "20000時間"))) // 連続ログイン case object No_5001 extends NormalAuto(5001, consecutiveLoginDays_>=(100)) @@ -168,27 +218,57 @@ object SeichiAchievement extends Enum[SeichiAchievement] { case object No_7003 extends GrantedByConsole(7003, "公式イベント「建築コンペ」で最優秀賞獲得", None) case object No_7004 extends GrantedByConsole(7004, "公式イベント「建築コンペ」で優秀賞獲得", None) case object No_7005 extends GrantedByConsole(7005, "公式イベント「建築コンペ」で佳作賞獲得", None) - case object No_7006 extends GrantedByConsole(7006, "公式イベント「第一回建築コンペ」で配布", Some(List("開催テーマは「桜」でした。"))) - case object No_7007 extends GrantedByConsole(7007, "公式イベント「第二回建築コンペ」で配布", Some(List("開催テーマは「アスレチック」でした。"))) + case object No_7006 + extends GrantedByConsole(7006, "公式イベント「第一回建築コンペ」で配布", Some(List("開催テーマは「桜」でした。"))) + case object No_7007 + extends GrantedByConsole(7007, "公式イベント「第二回建築コンペ」で配布", Some(List("開催テーマは「アスレチック」でした。"))) case object No_7008 extends GrantedByConsole(7008, "公式イベント「GTテクスチャコンペ」で採用", None) - case object No_7010 extends GrantedByConsole(7010, "公式イベント「第三回建築コンペ」で配布", Some(List("開催テーマAは「氷像(夏)」でした。"))) - case object No_7011 extends GrantedByConsole(7011, "公式イベント「第三回建築コンペ」で配布", Some(List("開催テーマBは「海岸建築(夏)」でした。"))) - case object No_7012 extends GrantedByConsole(7012, "公式イベント「第三回建築コンペ」で配布", Some(List("開催テーマCは「海上建築(夏)」でした。"))) - case object No_7013 extends GrantedByConsole(7013, "公式イベント「第四回建築コンペ」で配布", Some(List("開催テーマAは「和風建築」でした。"))) - case object No_7014 extends GrantedByConsole(7014, "公式イベント「第四回建築コンペ」で配布", Some(List("開催テーマBは「洋風建築」でした。"))) - case object No_7015 extends GrantedByConsole(7015, "公式イベント「第四回建築コンペ」で配布", Some(List("開催テーマCは「モダン建築」でした。"))) - case object No_7016 extends GrantedByConsole(7016, "公式イベント「第四回建築コンペ」で配布", Some(List("開催テーマDは「ファンタジー」でした。"))) + case object No_7010 + extends GrantedByConsole(7010, "公式イベント「第三回建築コンペ」で配布", Some(List("開催テーマAは「氷像(夏)」でした。"))) + case object No_7011 + extends GrantedByConsole(7011, "公式イベント「第三回建築コンペ」で配布", Some(List("開催テーマBは「海岸建築(夏)」でした。"))) + case object No_7012 + extends GrantedByConsole(7012, "公式イベント「第三回建築コンペ」で配布", Some(List("開催テーマCは「海上建築(夏)」でした。"))) + case object No_7013 + extends GrantedByConsole(7013, "公式イベント「第四回建築コンペ」で配布", Some(List("開催テーマAは「和風建築」でした。"))) + case object No_7014 + extends GrantedByConsole(7014, "公式イベント「第四回建築コンペ」で配布", Some(List("開催テーマBは「洋風建築」でした。"))) + case object No_7015 + extends GrantedByConsole(7015, "公式イベント「第四回建築コンペ」で配布", Some(List("開催テーマCは「モダン建築」でした。"))) + case object No_7016 + extends GrantedByConsole(7016, "公式イベント「第四回建築コンペ」で配布", Some(List("開催テーマDは「ファンタジー」でした。"))) case object No_7017 extends GrantedByConsole(7017, "公式イベント「イラストコンテスト」でグランプリ獲得", None) case object No_7018 extends GrantedByConsole(7018, "公式イベント「イラストコンテスト」に参加する", None) - case object No_7019 extends GrantedByConsole(7019, "公式イベント「イラストコンテスト」で配布", Some(List("条件:審査員賞(Yukki_XD)獲得"))) - case object No_7020 extends GrantedByConsole(7020, "公式イベント「イラストコンテスト」で配布", Some(List("条件:審査員賞(rukure2017)獲得"))) - case object No_7021 extends GrantedByConsole(7021, "公式イベント「イラストコンテスト」で配布", Some(List("条件:審査員賞(Lucky3028)獲得"))) - case object No_7022 extends GrantedByConsole(7022, "公式イベント「イラストコンテスト」で配布", Some(List("条件:審査員賞(tokuzi_)獲得"))) - case object No_7023 extends GrantedByConsole(7023, "公式イベント「イラストコンテスト」で配布", Some(List("条件:審査員賞(kamikami46)獲得"))) - case object No_7024 extends GrantedByConsole(7024, "公式イベント「イラストコンテスト」で配布", Some(List("条件:審査員賞(CourageousLeaf)獲得"))) - case object No_7025 extends GrantedByConsole(7025, "公式イベント「イラストコンテスト」で配布", Some(List("条件:審査員賞(nubasu)獲得"))) - case object No_7026 extends GrantedByConsole(7026, "公式イベント「イラストコンテスト」で配布", Some(List("条件:審査員賞(tetsusan)獲得"))) - case object No_7027 extends GrantedByConsole(7027, "公式イベント「イラストコンテスト」で配布", Some(List("条件:審査員賞(tar0ss)獲得"))) + case object No_7019 + extends GrantedByConsole(7019, "公式イベント「イラストコンテスト」で配布", Some(List("条件:審査員賞(Yukki_XD)獲得"))) + case object No_7020 + extends GrantedByConsole( + 7020, + "公式イベント「イラストコンテスト」で配布", + Some(List("条件:審査員賞(rukure2017)獲得")) + ) + case object No_7021 + extends GrantedByConsole(7021, "公式イベント「イラストコンテスト」で配布", Some(List("条件:審査員賞(Lucky3028)獲得"))) + case object No_7022 + extends GrantedByConsole(7022, "公式イベント「イラストコンテスト」で配布", Some(List("条件:審査員賞(tokuzi_)獲得"))) + case object No_7023 + extends GrantedByConsole( + 7023, + "公式イベント「イラストコンテスト」で配布", + Some(List("条件:審査員賞(kamikami46)獲得")) + ) + case object No_7024 + extends GrantedByConsole( + 7024, + "公式イベント「イラストコンテスト」で配布", + Some(List("条件:審査員賞(CourageousLeaf)獲得")) + ) + case object No_7025 + extends GrantedByConsole(7025, "公式イベント「イラストコンテスト」で配布", Some(List("条件:審査員賞(nubasu)獲得"))) + case object No_7026 + extends GrantedByConsole(7026, "公式イベント「イラストコンテスト」で配布", Some(List("条件:審査員賞(tetsusan)獲得"))) + case object No_7027 + extends GrantedByConsole(7027, "公式イベント「イラストコンテスト」で配布", Some(List("条件:審査員賞(tar0ss)獲得"))) case object No_7901 extends GrantedByConsole(7901, "超会議2018で配布", None) case object No_7902 extends GrantedByConsole(7902, "超会議2018で配布", None) case object No_7903 extends GrantedByConsole(7903, "超会議2018で配布", None) @@ -221,10 +301,12 @@ object SeichiAchievement extends Enum[SeichiAchievement] { case object No_9017 extends NormalManual(9017, playedIn(Month.MAY)) case object No_9018 extends NormalManual(9018, playedOn(Month.MAY, 5, "とある子供の日")) case object No_9019 extends NormalManual(9019, playedOn(Month.MAY, 5, "端午の節句")) - case object No_9020 extends NormalManual(9020, playedOn(Month.MAY, 2, DayOfWeek.SUNDAY, "母の日")) + case object No_9020 + extends NormalManual(9020, playedOn(Month.MAY, 2, DayOfWeek.SUNDAY, "母の日")) case object No_9021 extends NormalManual(9021, playedIn(Month.JUNE)) case object No_9022 extends NormalManual(9022, playedOn(Month.JUNE, 12, "とある日記の日")) - case object No_9023 extends NormalManual(9023, playedOn(Month.JUNE, 3, DayOfWeek.SUNDAY, "父の日")) + case object No_9023 + extends NormalManual(9023, playedOn(Month.JUNE, 3, DayOfWeek.SUNDAY, "父の日")) case object No_9024 extends NormalManual(9024, playedOn(Month.JUNE, 29, "とある生誕の日")) case object No_9025 extends NormalManual(9025, playedIn(Month.JULY)) case object No_9026 extends NormalManual(9026, playedOn(Month.JULY, 7, "七夕")) @@ -239,7 +321,8 @@ object SeichiAchievement extends Enum[SeichiAchievement] { case object No_9035 extends NormalManual(9035, playedOn(Month.SEPTEMBER, 12, "とあるマラソンの日")) case object No_9036 extends NormalManual(9036, playedOn(Month.SEPTEMBER, 29, "とあるふぐの日")) case object No_9037 extends NormalManual(9037, playedOn(Month.SEPTEMBER, 21, "とある中秋の日")) - case object No_9038 extends NormalManual(9038, playedOn(Month.SEPTEMBER, 21, "とあるファッションショーの日")) + case object No_9038 + extends NormalManual(9038, playedOn(Month.SEPTEMBER, 21, "とあるファッションショーの日")) case object No_9039 extends NormalManual(9039, playedOn(Month.SEPTEMBER, 15, "とある月見の日")) case object No_9040 extends NormalManual(9040, playedIn(Month.OCTOBER)) case object No_9041 extends NormalManual(9041, playedOn(Month.OCTOBER, 10, "とあるスポーツの日")) @@ -255,7 +338,7 @@ object SeichiAchievement extends Enum[SeichiAchievement] { val autoUnlockedAchievements: IndexedSeq[SeichiAchievement with AutoUnlocked] = values.flatMap { case u: AutoUnlocked => Some(u) - case _ => None + case _ => None } implicit class AutoUnlockedOps(autoUnlocked: AutoUnlocked) { diff --git a/src/main/scala/com/github/unchama/seichiassist/achievement/hierarchy/AchievementGroup.scala b/src/main/scala/com/github/unchama/seichiassist/achievement/hierarchy/AchievementGroup.scala index ed564be9ab..31cf66ec16 100644 --- a/src/main/scala/com/github/unchama/seichiassist/achievement/hierarchy/AchievementGroup.scala +++ b/src/main/scala/com/github/unchama/seichiassist/achievement/hierarchy/AchievementGroup.scala @@ -5,42 +5,27 @@ import com.github.unchama.seichiassist.achievement.hierarchy.AchievementCategory sealed abstract class AchievementGroup(val name: String, val parent: AchievementCategory) object AchievementGroup { - case object BrokenBlockAmount - extends AchievementGroup("整地量", BrokenBlock) + case object BrokenBlockAmount extends AchievementGroup("整地量", BrokenBlock) - case object BrokenBlockRanking - extends AchievementGroup("整地神ランキング", BrokenBlock) + case object BrokenBlockRanking extends AchievementGroup("整地神ランキング", BrokenBlock) - case object PlacedBlockAmount - extends AchievementGroup("建築量", Building) + case object PlacedBlockAmount extends AchievementGroup("建築量", Building) + case object PlayTime extends AchievementGroup("参加時間", Login) - case object PlayTime - extends AchievementGroup("参加時間", Login) + case object TotalLogins extends AchievementGroup("通算ログイン", Login) - case object TotalLogins - extends AchievementGroup("通算ログイン", Login) + case object ConsecutiveLogins extends AchievementGroup("連続ログイン", Login) - case object ConsecutiveLogins - extends AchievementGroup("連続ログイン", Login) + case object Anniversaries extends AchievementGroup("記念日", Login) - case object Anniversaries - extends AchievementGroup("記念日", Login) + case object MebiusBreeder extends AchievementGroup("MEBIUSブリーダー", Challenges) + case object StarLevel extends AchievementGroup("スターレベル", Challenges) - case object MebiusBreeder - extends AchievementGroup("MEBIUSブリーダー", Challenges) + case object OfficialEvent extends AchievementGroup("公式イベント", Specials) - case object StarLevel - extends AchievementGroup("スターレベル", Challenges) + case object VoteCounts extends AchievementGroup("JMS投票数", Specials) - - case object OfficialEvent - extends AchievementGroup("公式イベント", Specials) - - case object VoteCounts - extends AchievementGroup("JMS投票数", Specials) - - case object Secrets - extends AchievementGroup("極秘任務", Specials) + case object Secrets extends AchievementGroup("極秘任務", Specials) } diff --git a/src/main/scala/com/github/unchama/seichiassist/bungee/BungeeReceiver.scala b/src/main/scala/com/github/unchama/seichiassist/bungee/BungeeReceiver.scala index 368de8495a..daeef69d50 100644 --- a/src/main/scala/com/github/unchama/seichiassist/bungee/BungeeReceiver.scala +++ b/src/main/scala/com/github/unchama/seichiassist/bungee/BungeeReceiver.scala @@ -10,7 +10,11 @@ import java.util.UUID class BungeeReceiver(private val plugin: SeichiAssist) extends PluginMessageListener { - override def onPluginMessageReceived(channel: String, player: Player, message: Array[Byte]): Unit = synchronized { + override def onPluginMessageReceived( + channel: String, + player: Player, + message: Array[Byte] + ): Unit = synchronized { // ストリームの準備 val stream = new ByteArrayInputStream(message) val in = new DataInputStream(stream) @@ -26,7 +30,8 @@ class BungeeReceiver(private val plugin: SeichiAssist) extends PluginMessageList private def getLocation(servername: String, uuid: String, wanter: String): Unit = { val player = Bukkit.getServer.getPlayer(UUID.fromString(uuid)) - val seichiAmountData = plugin.breakCountSystem.api.seichiAmountDataRepository(player).read.unsafeRunSync() + val seichiAmountData = + plugin.breakCountSystem.api.seichiAmountDataRepository(player).read.unsafeRunSync() val level = seichiAmountData.levelCorrespondingToExp.level val totalBreakAmount = seichiAmountData.expAmount.amount diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/EffectCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/EffectCommand.scala index 1ebd63e5b1..7303aacda6 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/EffectCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/EffectCommand.scala @@ -32,42 +32,39 @@ class EffectCommand[F[_]](api: FastDiggingSettingsWriteApi[IO, Player]) { private val toggleExecutor = playerCommandBuilder .withEffectAsExecution { - api - .toggleEffectSuppression - .flatMap { newState => - MessageEffect { - newState match { - case FastDiggingEffectSuppressionState.EnabledWithoutLimit => - s"${GREEN}採掘速度上昇効果:ON(無制限)" - case limit: FastDiggingEffectSuppressionState.EnabledWithLimit => - s"${GREEN}採掘速度上昇効果:ON(${limit.limit}制限)" - case FastDiggingEffectSuppressionState.Disabled => - s"${RED}採掘速度上昇効果:OFF" - } + api.toggleEffectSuppression.flatMap { newState => + MessageEffect { + newState match { + case FastDiggingEffectSuppressionState.EnabledWithoutLimit => + s"${GREEN}採掘速度上昇効果:ON(無制限)" + case limit: FastDiggingEffectSuppressionState.EnabledWithLimit => + s"${GREEN}採掘速度上昇効果:ON(${limit.limit}制限)" + case FastDiggingEffectSuppressionState.Disabled => + s"${RED}採掘速度上昇効果:OFF" } - } >> MessageEffect("再度 /ef コマンドを実行することでトグルします。") + } + } >> MessageEffect("再度 /ef コマンドを実行することでトグルします。") } .build() private val messageFlagToggleExecutor = playerCommandBuilder .withEffectAsExecution { - api - .toggleStatsSettings - .flatMap { newSettings => - MessageEffect { - newSettings match { - case FastDiggingEffectStatsSettings.AlwaysReceiveDetails => - s"${GREEN}内訳表示:ON(OFFに戻したい時は再度コマンドを実行します。)" - case FastDiggingEffectStatsSettings.ReceiveTotalAmplifierOnUpdate => - s"${GREEN}内訳表示:OFF" - } + api.toggleStatsSettings.flatMap { newSettings => + MessageEffect { + newSettings match { + case FastDiggingEffectStatsSettings.AlwaysReceiveDetails => + s"${GREEN}内訳表示:ON(OFFに戻したい時は再度コマンドを実行します。)" + case FastDiggingEffectStatsSettings.ReceiveTotalAmplifierOnUpdate => + s"${GREEN}内訳表示:OFF" } } + } } .build() val executor: TabExecutor = BranchedExecutor( Map("smart" -> messageFlagToggleExecutor), - whenArgInsufficient = Some(toggleExecutor), whenBranchNotFound = Some(printUsageExecutor) + whenArgInsufficient = Some(toggleExecutor), + whenBranchNotFound = Some(printUsageExecutor) ).asNonBlockingTabExecutor() } diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/GiganticFeverCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/GiganticFeverCommand.scala index 417f87d3d0..8453b1caab 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/GiganticFeverCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/GiganticFeverCommand.scala @@ -16,17 +16,21 @@ import scala.concurrent.duration.FiniteDuration object GiganticFeverCommand { private val worldsToToggleDifficulty = ManagedWorld.seichiWorlds.map(_.alphabetName).toList - val executor: TabExecutor = ContextualExecutorBuilder.beginConfiguration() + val executor: TabExecutor = ContextualExecutorBuilder + .beginConfiguration() .execution { _ => val config = SeichiAssist.seichiAssistConfig Util.sendMessageToEveryoneIgnoringPreference(s"${AQUA}フィーバー!この時間MOBたちは踊りに出かけてるぞ!今が整地時だ!") - Util.sendMessageToEveryoneIgnoringPreference(s"$AQUA(${config.getGiganticFeverDisplayTime}間)") + Util.sendMessageToEveryoneIgnoringPreference( + s"$AQUA(${config.getGiganticFeverDisplayTime}間)" + ) Util.setDifficulty(worldsToToggleDifficulty, Difficulty.PEACEFUL) - IO.sleep(FiniteDuration(config.getGiganticFeverMinutes * 60, - scala.concurrent.duration.MINUTES))(IO.timer(ExecutionContext.global)) + IO.sleep( + FiniteDuration(config.getGiganticFeverMinutes * 60, scala.concurrent.duration.MINUTES) + )(IO.timer(ExecutionContext.global)) Util.setDifficulty(worldsToToggleDifficulty, Difficulty.HARD) Util.sendMessageToEveryoneIgnoringPreference(s"${AQUA}フィーバー終了!MOBたちは戻ってきたぞ!") diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/HalfBlockProtectCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/HalfBlockProtectCommand.scala index c71fdef105..55ed08d607 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/HalfBlockProtectCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/HalfBlockProtectCommand.scala @@ -13,4 +13,4 @@ object HalfBlockProtectCommand { } .build() .asNonBlockingTabExecutor() -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/HatCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/HatCommand.scala index 5523ba8a54..5cd43faf1d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/HatCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/HatCommand.scala @@ -7,7 +7,8 @@ import com.github.unchama.targetedeffect.{SequentialEffect, TargetedEffect} import org.bukkit.command.TabExecutor object HatCommand { - val executor: TabExecutor = BuilderTemplates.playerCommandBuilder + val executor: TabExecutor = BuilderTemplates + .playerCommandBuilder .argumentsParsers(List()) .execution { context => val player = context.sender diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/LastQuitCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/LastQuitCommand.scala index 86e44347cd..fe14a8ce27 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/LastQuitCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/LastQuitCommand.scala @@ -5,7 +5,8 @@ import com.github.unchama.seichiassist.SeichiAssist import org.bukkit.command.TabExecutor object LastQuitCommand { - val executor: TabExecutor = ContextualExecutorBuilder.beginConfiguration() + val executor: TabExecutor = ContextualExecutorBuilder + .beginConfiguration() .argumentsParsers(List(Parsers.identity)) .execution { context => val playerName = context.args.parsed.head.asInstanceOf[String] @@ -14,4 +15,4 @@ object LastQuitCommand { } .build() .asNonBlockingTabExecutor() -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/MapCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/MapCommand.scala index b5bb06cd54..7594df4b86 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/MapCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/MapCommand.scala @@ -8,7 +8,8 @@ import org.bukkit.ChatColor._ import org.bukkit.command.TabExecutor object MapCommand { - val executor: TabExecutor = BuilderTemplates.playerCommandBuilder + val executor: TabExecutor = BuilderTemplates + .playerCommandBuilder .execution { context => IO { val location = context.sender.getLocation diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/MineHeadCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/MineHeadCommand.scala index 887520cbeb..345acad815 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/MineHeadCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/MineHeadCommand.scala @@ -19,8 +19,6 @@ class MineHeadCommand(implicit ioOnMainThread: OnMinecraftServerThread[IO]) { MessageEffect(s"${GREEN}専用アイテムを付与しました。") ) - val executor: TabExecutor = playerCommandBuilder - .execution { _ => IO.pure(effect) } - .build() - .asNonBlockingTabExecutor() + val executor: TabExecutor = + playerCommandBuilder.execution { _ => IO.pure(effect) }.build().asNonBlockingTabExecutor() } diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/MineStackCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/MineStackCommand.scala index 1781fd8a5e..10cc12e26f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/MineStackCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/MineStackCommand.scala @@ -12,11 +12,14 @@ object MineStackCommand { val executor: TabExecutor = playerCommandBuilder .argumentsParsers( List( - Parsers.fromOptionParser({ - case "on" => Some(true) - case "off" => Some(false) - case _ => None - }, MessageEffect("/minestack … MineStackの対象アイテムを自動収集するか切り替えます")) + Parsers.fromOptionParser( + { + case "on" => Some(true) + case "off" => Some(false) + case _ => None + }, + MessageEffect("/minestack … MineStackの対象アイテムを自動収集するか切り替えます") + ) ) ) .execution { context => diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/RegionOwnerTransferCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/RegionOwnerTransferCommand.scala index fb6b440939..96c5659c44 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/RegionOwnerTransferCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/RegionOwnerTransferCommand.scala @@ -16,22 +16,25 @@ object RegionOwnerTransferCommand { import com.github.unchama.contextualexecutor.builder.ParserResponse._ val executor: TabExecutor = playerCommandBuilder - .argumentsParsers(List( - Parsers.identity, - recipientName => { - Bukkit.getPlayer(recipientName) match { - case recipient: Player => succeedWith(recipient) - case _ => failWith(s"${recipientName}というプレイヤーはサーバーに参加したことがありません。") + .argumentsParsers( + List( + Parsers.identity, + recipientName => { + Bukkit.getPlayer(recipientName) match { + case recipient: Player => succeedWith(recipient) + case _ => failWith(s"${recipientName}というプレイヤーはサーバーに参加したことがありません。") + } } - } - )) + ) + ) .execution { context => val regionName = context.args.parsed.head.asInstanceOf[String] val newOwner = context.args.parsed(1).asInstanceOf[Player] val sender = context.sender - val region = WorldGuardPlugin.inst().getRegionManager(sender.getWorld).getRegion(regionName) + val region = + WorldGuardPlugin.inst().getRegionManager(sender.getWorld).getRegion(regionName) if (region == null) { IO(MessageEffect(s"${regionName}という名前の保護は存在しません。")) } @@ -41,7 +44,11 @@ object RegionOwnerTransferCommand { .build() .asNonBlockingTabExecutor() - private def attemptRegionTransfer(donner: Player, recipient: Player, region: ProtectedRegion): IO[TargetedEffect[Player]] = IO { + private def attemptRegionTransfer( + donner: Player, + recipient: Player, + region: ProtectedRegion + ): IO[TargetedEffect[Player]] = IO { val owners = region.getOwners val regionWorld = donner.getWorld diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/RmpCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/RmpCommand.scala index cc5afe7ef0..b2ac950f93 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/RmpCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/RmpCommand.scala @@ -2,7 +2,11 @@ package com.github.unchama.seichiassist.commands import cats.effect.IO import com.github.unchama.contextualexecutor.builder.Parsers._ -import com.github.unchama.contextualexecutor.builder.{ContextualExecutorBuilder, ParserResponse, ResponseEffectOrResult} +import com.github.unchama.contextualexecutor.builder.{ + ContextualExecutorBuilder, + ParserResponse, + ResponseEffectOrResult +} import com.github.unchama.contextualexecutor.executors.{BranchedExecutor, EchoExecutor} import com.github.unchama.seichiassist.{ManagedWorld, SeichiAssist} import com.github.unchama.targetedeffect @@ -19,31 +23,33 @@ import scala.jdk.CollectionConverters._ object RmpCommand { import ParserResponse._ - private val printDescriptionExecutor = new EchoExecutor( - MessageEffect { - List( - s"$RED/rmp remove [world名] [日数]", - "全Ownerが[日数]間ログインしていないRegionを削除します(整地ワールドのみ)", - "", - s"$RED/rmp removeAll [world名]", - "原則全てのRegionを削除します(整地ワールドのみ)", - "", - s"$RED/rmp list [world名] [日数]", - "全Ownerが[日数]間ログインしていないRegionを表示します" - ) - } - ) - private val argsAndSenderConfiguredBuilder = ContextualExecutorBuilder.beginConfiguration() + private val printDescriptionExecutor = new EchoExecutor(MessageEffect { + List( + s"$RED/rmp remove [world名] [日数]", + "全Ownerが[日数]間ログインしていないRegionを削除します(整地ワールドのみ)", + "", + s"$RED/rmp removeAll [world名]", + "原則全てのRegionを削除します(整地ワールドのみ)", + "", + s"$RED/rmp list [world名] [日数]", + "全Ownerが[日数]間ログインしていないRegionを表示します" + ) + }) + private val argsAndSenderConfiguredBuilder = ContextualExecutorBuilder + .beginConfiguration() .refineSenderWithError[ConsoleCommandSender](s"${GREEN}このコマンドはコンソールから実行してください") - .argumentsParsers(List( - arg => { - Bukkit.getWorld(arg) match { - case world: World => succeedWith(world) - case _ => failWith(s"存在しないワールドです: $arg") - } - }, - nonNegativeInteger(MessageEffect(s"$RED[日数]には非負整数を入力してください")) - ), onMissingArguments = printDescriptionExecutor) + .argumentsParsers( + List( + arg => { + Bukkit.getWorld(arg) match { + case world: World => succeedWith(world) + case _ => failWith(s"存在しないワールドです: $arg") + } + }, + nonNegativeInteger(MessageEffect(s"$RED[日数]には非負整数を入力してください")) + ), + onMissingArguments = printDescriptionExecutor + ) private val removeExecutor = argsAndSenderConfiguredBuilder .execution { context => val world = context.args.parsed.head.asInstanceOf[World] @@ -73,11 +79,9 @@ object RmpCommand { if (removalTargets.isEmpty) { MessageEffect(s"${GREEN}該当Regionは存在しません") } else { - targetedeffect.SequentialEffect( - removalTargets.map { target => - MessageEffect(s"$GREEN[rmp] List Region => ${world.getName}.${target.getId}") - } - ) + targetedeffect.SequentialEffect(removalTargets.map { target => + MessageEffect(s"$GREEN[rmp] List Region => ${world.getName}.${target.getId}") + }) } }.merge } @@ -85,7 +89,8 @@ object RmpCommand { .build() private def removeRegions(world: World, days: Int): IO[TargetedEffect[CommandSender]] = IO { - val isSeichiWorldWithWGRegionsOption = ManagedWorld.fromBukkitWorld(world).map(_.isSeichiWorldWithWGRegions) + val isSeichiWorldWithWGRegionsOption = + ManagedWorld.fromBukkitWorld(world).map(_.isSeichiWorldWithWGRegions) val commandName = if (days == -1) "removeAll" else "remove" @@ -94,24 +99,29 @@ object RmpCommand { case Some(true) => getOldRegionsIn(world, days).map { removalTargets => removalTargets.foreach { target => - ExternalPlugins.getWorldGuard.getRegionContainer.get(world).removeRegion(target.getId) + ExternalPlugins + .getWorldGuard + .getRegionContainer + .get(world) + .removeRegion(target.getId) } // メッセージ生成 if (removalTargets.isEmpty) { MessageEffect(s"${GREEN}該当Regionは存在しません") } else { - targetedeffect.SequentialEffect( - removalTargets.map { target => - MessageEffect(s"$YELLOW[rmp] Deleted Region => ${world.getName}.${target.getId}") - } - ) + targetedeffect.SequentialEffect(removalTargets.map { target => + MessageEffect(s"$YELLOW[rmp] Deleted Region => ${world.getName}.${target.getId}") + }) } }.merge } } - private def getOldRegionsIn(world: World, daysThreshold: Int): ResponseEffectOrResult[CommandSender, List[ProtectedRegion]] = { + private def getOldRegionsIn( + world: World, + daysThreshold: Int + ): ResponseEffectOrResult[CommandSender, List[ProtectedRegion]] = { val databaseGateway = SeichiAssist.databaseGateway val leavers = databaseGateway.playerDataManipulator.selectLeaversUUIDs(daysThreshold) @@ -121,21 +131,20 @@ object RmpCommand { val regions = ExternalPlugins.getWorldGuard.getRegionContainer.get(world).getRegions.asScala - val oldRegions = regions.values.filter { region => - region.getId != "__global__" && region.getId != "spawn" && + val oldRegions = regions + .values + .filter { region => + region.getId != "__global__" && region.getId != "spawn" && region.getOwners.getUniqueIds.asScala.forall(leavers.contains(_)) - }.toList + } + .toList Right(oldRegions) } val executor: TabExecutor = BranchedExecutor( - Map( - "remove" -> removeExecutor, - "removeAll" -> removeAllExecutor, - "list" -> listExecutor - ), + Map("remove" -> removeExecutor, "removeAll" -> removeAllExecutor, "list" -> listExecutor), whenArgInsufficient = Some(printDescriptionExecutor), whenBranchNotFound = Some(printDescriptionExecutor) ).asNonBlockingTabExecutor() diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/SeichiAssistCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/SeichiAssistCommand.scala index c758d63707..8639c0e21f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/SeichiAssistCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/SeichiAssistCommand.scala @@ -9,18 +9,23 @@ import org.bukkit.ChatColor._ import org.bukkit.command.{ConsoleCommandSender, TabExecutor} object SeichiAssistCommand { - private val descriptionExecutor = new EchoExecutor(MessageEffect(List( - s"$YELLOW$BOLD[コマンドリファレンス]", - s"$RED/seichiassist reload-config", - "config.ymlの設定値を再読み込みします", - s"$RED/seichiassist toggle-debug", - "デバッグモードのON,OFFを切り替えます", - "config.ymlのdebugmodeの値が1の場合のみ、コンソールから使用可能", - s"$RED/seichiassist set-anniversary-flag", - "n周年記念フラグを立てる(コンソール限定コマンド)" - ))) + private val descriptionExecutor = new EchoExecutor( + MessageEffect( + List( + s"$YELLOW$BOLD[コマンドリファレンス]", + s"$RED/seichiassist reload-config", + "config.ymlの設定値を再読み込みします", + s"$RED/seichiassist toggle-debug", + "デバッグモードのON,OFFを切り替えます", + "config.ymlのdebugmodeの値が1の場合のみ、コンソールから使用可能", + s"$RED/seichiassist set-anniversary-flag", + "n周年記念フラグを立てる(コンソール限定コマンド)" + ) + ) + ) - private val reloadConfigExecutor = ContextualExecutorBuilder.beginConfiguration() + private val reloadConfigExecutor = ContextualExecutorBuilder + .beginConfiguration() .execution { _ => IO { SeichiAssist.seichiAssistConfig = Config.loadFrom(SeichiAssist.instance) @@ -29,12 +34,13 @@ object SeichiAssistCommand { } .build() - private val toggleDebugExecutor = ContextualExecutorBuilder.beginConfiguration() + private val toggleDebugExecutor = ContextualExecutorBuilder + .beginConfiguration() .execution { _ => IO { - //debugフラグ反転処理 + // debugフラグ反転処理 if (SeichiAssist.seichiAssistConfig.getDebugMode == 1) { - //メッセージフラグを反転 + // メッセージフラグを反転 SeichiAssist.DEBUG = !SeichiAssist.DEBUG SeichiAssist.instance.restartRepeatedJobs() @@ -57,11 +63,15 @@ object SeichiAssistCommand { } .build() - private val setAnniversaryFlagExecutor = ContextualExecutorBuilder.beginConfiguration() + private val setAnniversaryFlagExecutor = ContextualExecutorBuilder + .beginConfiguration() .refineSenderWithError[ConsoleCommandSender]("コンソール専用コマンドです") .execution { _ => IO { - SeichiAssist.databaseGateway.playerDataManipulator.setAnniversary(anniversary = true, null) + SeichiAssist + .databaseGateway + .playerDataManipulator + .setAnniversary(anniversary = true, null) MessageEffect("Anniversaryアイテムの配布を開始しました。") } @@ -74,6 +84,7 @@ object SeichiAssistCommand { "toggle-debug" -> toggleDebugExecutor, "set-anniversary-flag" -> setAnniversaryFlagExecutor ), - whenArgInsufficient = Some(descriptionExecutor), whenBranchNotFound = Some(descriptionExecutor) + whenArgInsufficient = Some(descriptionExecutor), + whenBranchNotFound = Some(descriptionExecutor) ).asNonBlockingTabExecutor() } diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/ShareInvCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/ShareInvCommand.scala index fc29c12ebb..e2690787b1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/ShareInvCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/ShareInvCommand.scala @@ -16,7 +16,7 @@ import org.bukkit.{Bukkit, Material} object ShareInvCommand { -import scala.jdk.CollectionConverters._ + import scala.jdk.CollectionConverters._ val executor: TabExecutor = playerCommandBuilder .execution { context => @@ -38,31 +38,37 @@ import scala.jdk.CollectionConverters._ { for { serial <- EitherT(databaseGateway.playerDataManipulator.loadShareInv(player)) - _ <- EitherT.cond[IO](serial != "", (), MessageEffect(s"$RESET$RED${BOLD}収納アイテムが存在しません。")) + _ <- EitherT + .cond[IO](serial != "", (), MessageEffect(s"$RESET$RED${BOLD}収納アイテムが存在しません。")) _ <- EitherT(databaseGateway.playerDataManipulator.clearShareInv(player, playerData)) playerInventory = player.getInventory _ <- EitherT.right { IO { // アイテムを取り出す. 手持ちはドロップさせる - playerInventory.getContents + playerInventory + .getContents .filterNot(_ == null) .filterNot(_.getType == Material.AIR) .foreach(stack => dropIfNotEmpty(Some(stack), player)) - playerInventory.setContents(ItemListSerialization.deserializeFromBase64(serial).asScala.toArray) + playerInventory.setContents( + ItemListSerialization.deserializeFromBase64(serial).asScala.toArray + ) playerData.contentsPresentInSharedInventory = false Bukkit.getLogger.info(s"${player.getName}がアイテム取り出しを実施(DB書き換え成功)") } } - successful <- EitherT.rightT[IO, TargetedEffect[Player]](MessageEffect(s"${GREEN}アイテムを取得しました。手持ちにあったアイテムはドロップしました。")) + successful <- EitherT.rightT[IO, TargetedEffect[Player]]( + MessageEffect(s"${GREEN}アイテムを取得しました。手持ちにあったアイテムはドロップしました。") + ) } yield successful - }.merge + }.merge } def dropIfNotEmpty(itemStackOption: Option[ItemStack], to: Player): Unit = { itemStackOption match { case Some(itemStack) => Util.dropItem(to, itemStack) - case None => + case None => } } @@ -72,18 +78,22 @@ import scala.jdk.CollectionConverters._ val playerInventory = player.getInventory - def takeIfNotNull[App[_] : Applicative, E, A](a: A, fail: E): EitherT[App, E, A] = + def takeIfNotNull[App[_]: Applicative, E, A](a: A, fail: E): EitherT[App, E, A] = EitherT.cond[App](a != null, a, fail) { for { - inventory <- EitherT.rightT[IO, TargetedEffect[Player]](playerInventory.getContents.toList.asJava) + inventory <- EitherT.rightT[IO, TargetedEffect[Player]]( + playerInventory.getContents.toList.asJava + ) serializedInventory <- takeIfNotNull[IO, TargetedEffect[Player], String]( ItemListSerialization.serializeToBase64(inventory), MessageEffect(s"$RESET$RED${BOLD}収納アイテムの変換に失敗しました。") ) - _ <- EitherT(databaseGateway.playerDataManipulator.saveSharedInventory(player, serializedInventory)) + _ <- EitherT( + databaseGateway.playerDataManipulator.saveSharedInventory(player, serializedInventory) + ) successEffect <- EitherT.right[TargetedEffect[Player]] { IO { // 現所持アイテムを全て削除 @@ -98,6 +108,6 @@ import scala.jdk.CollectionConverters._ } } } yield successEffect - }.merge + }.merge } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/StickCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/StickCommand.scala index d663fe561a..cf86c5f3da 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/StickCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/StickCommand.scala @@ -25,4 +25,4 @@ object StickCommand { } .build() .asNonBlockingTabExecutor() -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/StickMenuCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/StickMenuCommand.scala index 30ea499959..0c6803bab7 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/StickMenuCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/StickMenuCommand.scala @@ -10,25 +10,32 @@ import com.github.unchama.seichiassist.menus.stickmenu.{FirstPage, StickMenu} import org.bukkit.command.TabExecutor /** -* 棒メニューを開くコマンド -* @author KisaragiEffective -*/ + * 棒メニューを開くコマンド + * @author + * KisaragiEffective + */ object StickMenuCommand { - def executorA(implicit ioCanOpenStickMenuFirstPage: IO CanOpen FirstPage.type): ContextualExecutor = + def executorA( + implicit ioCanOpenStickMenuFirstPage: IO CanOpen FirstPage.type + ): ContextualExecutor = playerCommandBuilder .execution { _ => IO.pure(ioCanOpenStickMenuFirstPage.open(StickMenu.firstPage)) } .build() - def executorB(implicit ioCanOpenBuildMainMenu: IO CanOpen BuildMainMenu.type): ContextualExecutor = + def executorB( + implicit ioCanOpenBuildMainMenu: IO CanOpen BuildMainMenu.type + ): ContextualExecutor = playerCommandBuilder .execution { _ => IO.pure(ioCanOpenBuildMainMenu.open(BuildMainMenu)) } .build() - def executor(implicit ioCanOpenStickMenuFirstPage: IO CanOpen FirstPage.type, - ioCanOpenBuildMainMenu: IO CanOpen BuildMainMenu.type): TabExecutor = + def executor( + implicit ioCanOpenStickMenuFirstPage: IO CanOpen FirstPage.type, + ioCanOpenBuildMainMenu: IO CanOpen BuildMainMenu.type + ): TabExecutor = BranchedExecutor( Map("b" -> executorB), - whenArgInsufficient = Some(executorA), whenBranchNotFound = Some(executorA) - ) - .asNonBlockingTabExecutor() + whenArgInsufficient = Some(executorA), + whenBranchNotFound = Some(executorA) + ).asNonBlockingTabExecutor() } diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/VoteCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/VoteCommand.scala index 80cd1ef976..4d9eddb4dc 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/VoteCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/VoteCommand.scala @@ -1,59 +1,45 @@ package com.github.unchama.seichiassist.commands -import cats.data.Kleisli -import cats.effect.IO -import com.github.unchama.contextualexecutor.builder.Parsers +import com.github.unchama.contextualexecutor.builder.ContextualExecutorBuilder +import com.github.unchama.contextualexecutor.executors.{BranchedExecutor, EchoExecutor} import com.github.unchama.seichiassist.SeichiAssist -import com.github.unchama.seichiassist.commands.contextual.builder.BuilderTemplates.playerCommandBuilder -import com.github.unchama.seichiassist.util.Util -import com.github.unchama.targetedeffect.{TargetedEffect, UnfocusedEffect} import com.github.unchama.targetedeffect.commandsender.MessageEffect -import org.bukkit.ChatColor.{GREEN, RED, YELLOW} -import org.bukkit.command.{CommandSender, TabExecutor} +import com.github.unchama.targetedeffect.{SequentialEffect, UnfocusedEffect} +import org.bukkit.ChatColor.{RED, YELLOW} +import org.bukkit.command.TabExecutor object VoteCommand { - sealed trait Operation - case object Record extends Operation + private val usageEchoExecutor: EchoExecutor = EchoExecutor( + MessageEffect(List(s"$RED/vote record <プレイヤー名>", "投票特典配布用コマンドです")) + ) - val usageEchoEcexutor: TargetedEffect[CommandSender] = MessageEffect(List( - s"$RED/vote record <プレイヤー名>", - "投票特典配布用コマンドです" - )) - val executor: TabExecutor = playerCommandBuilder - .argumentsParsers( - List( - Parsers.fromOptionParser({ - case "record" => Some(Record) - case _ => None - }, usageEchoEcexutor), - Parsers.identity - ) - ) - .execution(context => { - val args = context.args.parsed - val command: Operation = args.head.asInstanceOf - val name: String = args(1).asInstanceOf - command match { - case Record => { - //引数が2つの時の処理 - val lowerCasePlayerName = Util.getName(name) - //プレイヤーオンライン、オフラインにかかわらずsqlに送信(マルチ鯖におけるコンフリクト防止の為) - IO { - for { - _ <- MessageEffect(s"$YELLOW${lowerCasePlayerName}の投票特典配布処理開始…") - _ <- UnfocusedEffect { - SeichiAssist.databaseGateway.playerDataManipulator.incrementVotePoint(lowerCasePlayerName) - } - k = if (SeichiAssist.databaseGateway.playerDataManipulator.addChainVote(lowerCasePlayerName)) { - MessageEffect(s"${GREEN}連続投票数の記録に成功") - } else { - MessageEffect(s"${RED}連続投票数の記録に失敗") - } - } yield k + private val recordExecutor = + ContextualExecutorBuilder + .beginConfiguration() + .executionCSEffect(context => { + val playerName: String = context.args.yetToBeParsed.head + val lowerCasePlayerName = playerName.toLowerCase + + SequentialEffect( + MessageEffect(s"$YELLOW${lowerCasePlayerName}の投票特典配布処理開始…"), + UnfocusedEffect { + SeichiAssist + .databaseGateway + .playerDataManipulator + .incrementVotePoint(lowerCasePlayerName) + }, + UnfocusedEffect { + SeichiAssist.databaseGateway.playerDataManipulator.addChainVote(lowerCasePlayerName) } - } - } - }) - .build() - .asNonBlockingTabExecutor() + ) + }) + .build() + + val executor: TabExecutor = { + BranchedExecutor( + Map("record" -> recordExecutor), + whenBranchNotFound = Some(usageEchoExecutor), + whenArgInsufficient = Some(usageEchoExecutor) + ).asNonBlockingTabExecutor() + } } diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/contextual/builder/BuilderTemplates.scala b/src/main/scala/com/github/unchama/seichiassist/commands/contextual/builder/BuilderTemplates.scala index 6b7e3f3acf..ddd71fd963 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/contextual/builder/BuilderTemplates.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/contextual/builder/BuilderTemplates.scala @@ -7,7 +7,8 @@ import org.bukkit.entity.Player object BuilderTemplates { val playerCommandBuilder: ContextualExecutorBuilder[Player] = - ContextualExecutorBuilder.beginConfiguration() + ContextualExecutorBuilder + .beginConfiguration() .refineSenderWithError[Player](s"${GREEN}このコマンドはゲーム内から実行してください。") -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/commands/legacy/DonationCommand.scala b/src/main/scala/com/github/unchama/seichiassist/commands/legacy/DonationCommand.scala index 3cdb7090a1..347df9b4cf 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/legacy/DonationCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/legacy/DonationCommand.scala @@ -8,12 +8,16 @@ import org.bukkit.ChatColor._ import org.bukkit.command.{Command, CommandExecutor, CommandSender} class DonationCommand extends CommandExecutor { - override def onCommand(sender: CommandSender, command: Command, label: String, args: Array[String]): Boolean = { + override def onCommand( + sender: CommandSender, + command: Command, + label: String, + args: Array[String] + ): Boolean = { def printRecordCommandUsage(): Unit = { - sender.sendMessage(Array( - s"$RED/donation record <プレイヤー名> <ポイント数>", - "寄付者用プレミアムエフェクトポイント配布コマンドです(マルチ鯖対応済)" - )) + sender.sendMessage( + Array(s"$RED/donation record <プレイヤー名> <ポイント数>", "寄付者用プレミアムエフェクトポイント配布コマンドです(マルチ鯖対応済)") + ) } def printHelp(): Unit = { @@ -26,14 +30,14 @@ class DonationCommand extends CommandExecutor { printHelp() } else if (args(0).equalsIgnoreCase("record")) { if (args.length != 3) { - //引数が3でない時の処理 + // 引数が3でない時の処理 printRecordCommandUsage() } else { - //引数が3の時の処理 + // 引数が3の時の処理 - //プレイヤー名を取得(小文字にする) + // プレイヤー名を取得(小文字にする) val name = Util.getName(args(1)) - //配布ポイント数取得 + // 配布ポイント数取得 val num = args(2).toInt if (databaseGateway.donateDataManipulator.addDonate(name, num) eq Fail) diff --git a/src/main/scala/com/github/unchama/seichiassist/concurrent/PluginExecutionContexts.scala b/src/main/scala/com/github/unchama/seichiassist/concurrent/PluginExecutionContexts.scala index 99fd5217bb..a225ebcc9b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/concurrent/PluginExecutionContexts.scala +++ b/src/main/scala/com/github/unchama/seichiassist/concurrent/PluginExecutionContexts.scala @@ -18,16 +18,23 @@ object PluginExecutionContexts { implicit val pluginInstance: JavaPlugin = SeichiAssist.instance - val cachedThreadPool: ExecutionContext = ExecutionContext.fromExecutor(Executors.newCachedThreadPool()) + val cachedThreadPool: ExecutionContext = + ExecutionContext.fromExecutor(Executors.newCachedThreadPool()) implicit val timer: Timer[IO] = IO.timer(cachedThreadPool) implicit val asyncShift: NonServerThreadContextShift[IO] = { - tag.apply[NonServerThreadContextShiftTag][ContextShift[IO]](IO.contextShift(cachedThreadPool)) + tag.apply[NonServerThreadContextShiftTag][ContextShift[IO]]( + IO.contextShift(cachedThreadPool) + ) } implicit val onMainThread: OnMinecraftServerThread[IO] = { - new OnBukkitServerThread[IO]()(pluginInstance, asyncShift, IO.ioConcurrentEffect(asyncShift)) + new OnBukkitServerThread[IO]()( + pluginInstance, + asyncShift, + IO.ioConcurrentEffect(asyncShift) + ) } implicit val layoutPreparationContext: LayoutPreparationContext = diff --git a/src/main/scala/com/github/unchama/seichiassist/data/GachaPrize.scala b/src/main/scala/com/github/unchama/seichiassist/data/GachaPrize.scala index 69f10edd48..dfcd187ea1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/data/GachaPrize.scala +++ b/src/main/scala/com/github/unchama/seichiassist/data/GachaPrize.scala @@ -6,7 +6,7 @@ import org.bukkit.ChatColor._ import org.bukkit.inventory.ItemStack class GachaPrize(_itemStack: ItemStack, var probability: Double) { - //アイテムデータ格納 + // アイテムデータ格納 val itemStack: ItemStack = _itemStack.clone() @Deprecated @@ -15,29 +15,36 @@ class GachaPrize(_itemStack: ItemStack, var probability: Double) { import scala.jdk.CollectionConverters._ /** - * @deprecated ここをなんのデータクラスだと思っているんだ + * @deprecated + * ここをなんのデータクラスだと思っているんだ */ @Deprecated() def compare(m: ItemStack, name: String): Boolean = { val mlore: List[String] = m.getItemMeta.getLore.asScala.toList val lore: List[String] = this.itemStack.getItemMeta.getLore.asScala.toList - if (lore.forall(line => mlore.contains(line)) && this.itemStack.getItemMeta.getDisplayName == m.getItemMeta.getDisplayName) { + if ( + lore + .forall(line => mlore.contains(line)) && this.itemStack.getItemMeta.getDisplayName == m + .getItemMeta + .getDisplayName + ) { val index = Util.loreIndexOf(mlore, "所有者") if (index >= 0) { - //保有者であれば交換 - //保有者でなければ交換できない + // 保有者であれば交換 + // 保有者でなければ交換できない mlore(index).toLowerCase().contains(name) } else { - //所有者の記載がなければ交換できる。 + // 所有者の記載がなければ交換できる。 true } } else false } /** - * @deprecated ここをなんのデータクラスだと思っているんだ + * @deprecated + * ここをなんのデータクラスだと思っているんだ */ @Deprecated() def appendOwnerLore(name: String): Unit = { @@ -55,7 +62,7 @@ object GachaPrize { var sum = 1.0 val rand = Math.random() - for {gachadata <- SeichiAssist.gachadatalist} { + for { gachadata <- SeichiAssist.gachadatalist } { sum -= gachadata.probability if (sum <= rand) { return gachadata.copy() diff --git a/src/main/scala/com/github/unchama/seichiassist/data/GachaSkullData.scala b/src/main/scala/com/github/unchama/seichiassist/data/GachaSkullData.scala index e36ec40d23..a292189c20 100644 --- a/src/main/scala/com/github/unchama/seichiassist/data/GachaSkullData.scala +++ b/src/main/scala/com/github/unchama/seichiassist/data/GachaSkullData.scala @@ -22,9 +22,7 @@ object GachaSkullData { import meta._ setDisplayName(s"$YELLOW${BOLD}ガチャ券") setLore { - List( - s"$RESET${GREEN}右クリックで使えます" - ).asJava + List(s"$RESET${GREEN}右クリックで使えます").asJava } setOwner("unchama") } @@ -43,10 +41,7 @@ object GachaSkullData { import meta._ setDisplayName(s"$YELLOW${BOLD}ガチャ券") setLore { - List( - s"$RESET${GREEN}右クリックで使えます", - s"$RESET${LIGHT_PURPLE}投票ありがとナス♡" - ).asJava + List(s"$RESET${GREEN}右クリックで使えます", s"$RESET${LIGHT_PURPLE}投票ありがとナス♡").asJava } setOwner("unchama") } @@ -65,10 +60,7 @@ object GachaSkullData { import meta._ setDisplayName(s"$YELLOW${BOLD}ガチャ券") setLore { - List( - s"$RESET${GREEN}右クリックで使えます", - s"$RESET${GRAY}ガチャ景品と交換しました。" - ).asJava + List(s"$RESET${GREEN}右クリックで使えます", s"$RESET${GRAY}ガチャ景品と交換しました。").asJava } setOwner("unchama") } diff --git a/src/main/scala/com/github/unchama/seichiassist/data/MineStackGachaData.scala b/src/main/scala/com/github/unchama/seichiassist/data/MineStackGachaData.scala index 7fdc9b8a7c..ac019448d9 100644 --- a/src/main/scala/com/github/unchama/seichiassist/data/MineStackGachaData.scala +++ b/src/main/scala/com/github/unchama/seichiassist/data/MineStackGachaData.scala @@ -6,22 +6,26 @@ import org.bukkit.block.Banner import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.BlockStateMeta -class MineStackGachaData(val objName: String, - _itemStack: ItemStack, - val probability: Double, - val level: Int) extends Cloneable { +class MineStackGachaData( + val objName: String, + _itemStack: ItemStack, + val probability: Double, + val level: Int +) extends Cloneable { val itemStack: ItemStack = _itemStack.clone() import scala.jdk.CollectionConverters._ /** - * @deprecated use itemStack.amount + * @deprecated + * use itemStack.amount */ @Deprecated() val amount: Int = itemStack.getAmount /** - * @deprecated ここをなんのデータクラスだと思っているんだ + * @deprecated + * ここをなんのデータクラスだと思っているんだ */ @Deprecated() def itemStackEquals(another: ItemStack): Boolean = { @@ -30,11 +34,21 @@ class MineStackGachaData(val objName: String, val lore = crt.getLore val anotherLore = ant.getLore - if (anotherLore.containsAll(lore) && (crt.getDisplayName.contains(another.getItemMeta.getDisplayName) || ant.getDisplayName.contains(this.itemStack.getItemMeta.getDisplayName))) { - //この時点で名前と内容が一致 - //盾、バナー用の模様判定 + if ( + anotherLore.containsAll(lore) && (crt + .getDisplayName + .contains(another.getItemMeta.getDisplayName) || ant + .getDisplayName + .contains(this.itemStack.getItemMeta.getDisplayName)) + ) { + // この時点で名前と内容が一致 + // 盾、バナー用の模様判定 val otherType = another.getType - if ((otherType == Material.SHIELD || otherType == Material.BANNER) && this.itemStack.getType == otherType) { + if ( + (otherType == Material.SHIELD || otherType == Material.BANNER) && this + .itemStack + .getType == otherType + ) { val bs0 = ant.asInstanceOf[BlockStateMeta] val b0 = bs0.getBlockState.asInstanceOf[Banner] val p0 = b0.getPatterns @@ -51,7 +65,8 @@ class MineStackGachaData(val objName: String, } /** - * @deprecated ここをなんのデータクラスだと思っているんだ + * @deprecated + * ここをなんのデータクラスだと思っているんだ */ @Deprecated() def appendOwnerLore(name: String): Unit = { @@ -60,5 +75,6 @@ class MineStackGachaData(val objName: String, itemStack.getItemMeta.setLore(lore.:+(s"$RESET${DARK_GREEN}所有者:$name").asJava) } - def copy(): MineStackGachaData = new MineStackGachaData(objName, itemStack.clone(), probability, level) + def copy(): MineStackGachaData = + new MineStackGachaData(objName, itemStack.clone(), probability, level) } diff --git a/src/main/scala/com/github/unchama/seichiassist/data/descrptions/PlayerStatsLoreGenerator.scala b/src/main/scala/com/github/unchama/seichiassist/data/descrptions/PlayerStatsLoreGenerator.scala index a097bd7549..11829d1ab8 100644 --- a/src/main/scala/com/github/unchama/seichiassist/data/descrptions/PlayerStatsLoreGenerator.scala +++ b/src/main/scala/com/github/unchama/seichiassist/data/descrptions/PlayerStatsLoreGenerator.scala @@ -3,9 +3,15 @@ package com.github.unchama.seichiassist.data.descrptions import cats.effect.IO import com.github.unchama.seichiassist.data.player.PlayerData import com.github.unchama.seichiassist.subsystems.breakcount.domain.SeichiAmountData -import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.{SeichiExpAmount, SeichiStarLevel} +import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.{ + SeichiExpAmount, + SeichiStarLevel +} import com.github.unchama.seichiassist.subsystems.breakcountbar.domain.BreakCountBarVisibility -import com.github.unchama.seichiassist.subsystems.ranking.domain.Ranking +import com.github.unchama.seichiassist.subsystems.ranking.domain.{ + Ranking, + RankingRecordWithPosition +} import com.github.unchama.seichiassist.text.WarningsGenerator import com.github.unchama.seichiassist.util.TypeConverter import org.bukkit.Bukkit @@ -15,10 +21,12 @@ import org.bukkit.entity.Player /** * Created by karayuu on 2019/05/05 */ -class PlayerStatsLoreGenerator(playerData: PlayerData, - seichiRanking: Ranking[SeichiAmountData], - seichiAmountData: SeichiAmountData, - expBarVisibility: BreakCountBarVisibility) { +class PlayerStatsLoreGenerator( + playerData: PlayerData, + seichiRanking: Ranking[SeichiAmountData], + seichiAmountData: SeichiAmountData, + expBarVisibility: BreakCountBarVisibility +) { private val targetPlayer: Player = Bukkit.getPlayer(playerData.uuid) /** @@ -36,26 +44,20 @@ class PlayerStatsLoreGenerator(playerData: PlayerData, passiveSkillDescription(), List(totalBreakAmountDescription()), rankingDescription().toList, - rankingDiffDescription().toList, + rankingDiffDescription(), List( totalLoginTimeDescrpition(), totalLoginDaysDescrption(), totalChainLoginDaysDescription() ), totalChainVoteDaysDescription(), - List( - s"$DARK_GRAY※1分毎に更新", - s"${GREEN}統計データは", - s"${GREEN}各サバイバルサーバー間で", - s"${GREEN}共有されます" - ), + List(s"$DARK_GRAY※1分毎に更新", s"${GREEN}統計データは", s"${GREEN}各サバイバルサーバー間で", s"${GREEN}共有されます"), expBarDescription() ).flatten } /** - * 木の棒メニュー等で用いられる整地Lvの説明文 - * スターレベルを保持していたら,スターレベルも同時に表示します. + * 木の棒メニュー等で用いられる整地Lvの説明文 スターレベルを保持していたら,スターレベルも同時に表示します. */ private def seichiLevelDescription(): String = { val starLevel = seichiAmountData.starLevelCorrespondingToExp @@ -94,39 +96,52 @@ class PlayerStatsLoreGenerator(playerData: PlayerData, /** * 総整地量の説明文 */ - private def totalBreakAmountDescription(): String = s"${AQUA}総整地量:${seichiAmountData.expAmount.formatted}" + private def totalBreakAmountDescription(): String = + s"${AQUA}総整地量:${seichiAmountData.expAmount.formatted}" /** * ランキングの順位の説明文 */ private def rankingDescription(): Option[String] = - seichiRanking - .positionOf(targetPlayer.getName) - .map { rank => - s"${GOLD}ランキング:${rank}位$GRAY(${seichiRanking.recordCount}人中)" - } + seichiRanking.positionOf(targetPlayer.getName).map { rank => + s"${GOLD}ランキング:${rank}位$GRAY(${seichiRanking.recordCount}人中)" + } /** * 一つ前のランキングのプレイヤーとの整地量の差を表す説明文を返します. */ - private def rankingDiffDescription(): Option[String] = - seichiRanking - .positionAndRecordOf(targetPlayer.getName) - .flatMap { case (position, record) => - if (position > 1) { - val positionOneAbove = position - 1 - val recordOneAbove = seichiRanking.recordsWithPositions(positionOneAbove - 1)._2 - val difference = - SeichiExpAmount.orderedMonus.subtractTruncate( - recordOneAbove.value.expAmount, - record.value.expAmount - ) - Some( - s"$AQUA${positionOneAbove}位(${recordOneAbove.playerName})との差:${difference.formatted}" - ) - } else - None - } + private def rankingDiffDescription(): List[String] = + seichiRanking.positionAndRecordOf(targetPlayer.getName).toList.flatMap { + case RankingRecordWithPosition(record, _) => + import SeichiExpAmount._ + import com.github.unchama.generic.algebra.typeclasses.OrderedMonus._ + + val above = + seichiRanking.worstRecordAbove(targetPlayer.getName).map { worstRecordAbovePlayer => + val difference = + worstRecordAbovePlayer.record.value.expAmount |-| record.value.expAmount + + // noinspection DuplicatedCode + val aboveRecordPosition = worstRecordAbovePlayer.positionInRanking + val aboveRecordPlayerName = worstRecordAbovePlayer.record.playerName + + s"$AQUA${aboveRecordPosition}位($aboveRecordPlayerName)との差:${difference.formatted}" + } + + val below = + seichiRanking.bestRecordBelow(targetPlayer.getName).map { bestRecordBelowPlayer => + val difference = + record.value.expAmount |-| bestRecordBelowPlayer.record.value.expAmount + + // noinspection DuplicatedCode + val belowRecordPosition = bestRecordBelowPlayer.positionInRanking + val belowRecordPlayerName = bestRecordBelowPlayer.record.playerName + + s"$AQUA${belowRecordPosition}位($belowRecordPlayerName)との差:${difference.formatted}" + } + + above.toList ++ below.toList + } /** * 総ログイン時間の説明文 @@ -137,12 +152,14 @@ class PlayerStatsLoreGenerator(playerData: PlayerData, /** * 通算ログイン日数の説明文 */ - private def totalLoginDaysDescrption(): String = s"${GRAY}通算ログイン日数:${playerData.loginStatus.totalLoginDay}日" + private def totalLoginDaysDescrption(): String = + s"${GRAY}通算ログイン日数:${playerData.loginStatus.totalLoginDay}日" /** * 連続ログイン日数の説明文 */ - private def totalChainLoginDaysDescription(): String = s"${GRAY}連続ログイン日数:${playerData.loginStatus.consecutiveLoginDays}日" + private def totalChainLoginDaysDescription(): String = + s"${GRAY}連続ログイン日数:${playerData.loginStatus.consecutiveLoginDays}日" /** * 連続投票日数の説明文. @@ -159,15 +176,9 @@ class PlayerStatsLoreGenerator(playerData: PlayerData, private def expBarDescription(): List[String] = { expBarVisibility match { case BreakCountBarVisibility.Shown => - List( - s"${GREEN}整地量バーを表示", - s"$DARK_RED${UNDERLINE}クリックで非表示" - ) + List(s"${GREEN}整地量バーを表示", s"$DARK_RED${UNDERLINE}クリックで非表示") case BreakCountBarVisibility.Hidden => - List( - s"${RED}整地量バーを非表示", - s"$DARK_GREEN${UNDERLINE}クリックで表示" - ) + List(s"${RED}整地量バーを非表示", s"$DARK_GREEN${UNDERLINE}クリックで表示") } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/data/player/AchievementPoint.scala b/src/main/scala/com/github/unchama/seichiassist/data/player/AchievementPoint.scala index ca5928d804..952e8c2f1a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/data/player/AchievementPoint.scala +++ b/src/main/scala/com/github/unchama/seichiassist/data/player/AchievementPoint.scala @@ -1,7 +1,11 @@ package com.github.unchama.seichiassist.data.player -case class AchievementPoint(fromUnlockedAchievements: Int = 0, used: Int = 0, conversionCount: Int = 0) { +case class AchievementPoint( + fromUnlockedAchievements: Int = 0, + used: Int = 0, + conversionCount: Int = 0 +) { val cumulativeTotal: Int = fromUnlockedAchievements + conversionCount * 3 val left: Int = cumulativeTotal - used -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/data/player/ClaimUnit.scala b/src/main/scala/com/github/unchama/seichiassist/data/player/ClaimUnit.scala index 64e026a93c..41e084f539 100644 --- a/src/main/scala/com/github/unchama/seichiassist/data/player/ClaimUnit.scala +++ b/src/main/scala/com/github/unchama/seichiassist/data/player/ClaimUnit.scala @@ -1,3 +1,3 @@ package com.github.unchama.seichiassist.data.player -case class ClaimUnit(ahead: Int, behind: Int, right: Int, left: Int) \ No newline at end of file +case class ClaimUnit(ahead: Int, behind: Int, right: Int, left: Int) diff --git a/src/main/scala/com/github/unchama/seichiassist/data/player/GiganticBerserk.scala b/src/main/scala/com/github/unchama/seichiassist/data/player/GiganticBerserk.scala index a7c8500b6c..823d126560 100644 --- a/src/main/scala/com/github/unchama/seichiassist/data/player/GiganticBerserk.scala +++ b/src/main/scala/com/github/unchama/seichiassist/data/player/GiganticBerserk.scala @@ -3,7 +3,13 @@ package com.github.unchama.seichiassist.data.player import com.github.unchama.seichiassist.LevelThresholds import org.bukkit.Material -case class GiganticBerserk(level: Int = 0, exp: Int = 0, stage: Int = 0, canEvolve: Boolean = false, cd: Int = 0) { +case class GiganticBerserk( + level: Int = 0, + exp: Int = 0, + stage: Int = 0, + canEvolve: Boolean = false, + cd: Int = 0 +) { def reachedLimit(): Boolean = stage == 5 && level == 9 @@ -31,7 +37,8 @@ case class GiganticBerserk(level: Int = 0, exp: Int = 0, stage: Int = 0, canEvol /** * 現在の `level` と `stage` において、次の `level` までに倒す必要がある敵の数(= `exp`)を返します. - * @return 次の `level` までに倒す必要がある敵の数(= `exp`) + * @return + * 次の `level` までに倒す必要がある敵の数(= `exp`) */ def requiredExpToNextLevel(): Int = { val current = stage * 10 + level diff --git a/src/main/scala/com/github/unchama/seichiassist/data/player/LoginStatus.scala b/src/main/scala/com/github/unchama/seichiassist/data/player/LoginStatus.scala index ce8c5f3e77..03c689f5c3 100644 --- a/src/main/scala/com/github/unchama/seichiassist/data/player/LoginStatus.scala +++ b/src/main/scala/com/github/unchama/seichiassist/data/player/LoginStatus.scala @@ -2,4 +2,8 @@ package com.github.unchama.seichiassist.data.player import java.time.LocalDate -case class LoginStatus(lastLoginDate: Option[LocalDate], totalLoginDay: Int = 0, consecutiveLoginDays: Int = 0) \ No newline at end of file +case class LoginStatus( + lastLoginDate: Option[LocalDate], + totalLoginDay: Int = 0, + consecutiveLoginDays: Int = 0 +) diff --git a/src/main/scala/com/github/unchama/seichiassist/data/player/MineStack.scala b/src/main/scala/com/github/unchama/seichiassist/data/player/MineStack.scala index 644e19b2a4..fa71b136f6 100644 --- a/src/main/scala/com/github/unchama/seichiassist/data/player/MineStack.scala +++ b/src/main/scala/com/github/unchama/seichiassist/data/player/MineStack.scala @@ -20,7 +20,8 @@ class MineStack(_objectCountMap: collection.Map[MineStackObj, Long] = collection def addStackedAmountOf(mineStackObj: MineStackObj, by: Long): Unit = setStackedAmountOf(mineStackObj, getStackedAmountOf(mineStackObj) + by) - def getStackedAmountOf(mineStackObj: MineStackObj): Long = objectCountMap.getOrElse(mineStackObj, 0) + def getStackedAmountOf(mineStackObj: MineStackObj): Long = + objectCountMap.getOrElse(mineStackObj, 0) private def setStackedAmountOf(mineStackObj: MineStackObj, to: Long): Unit = { objectCountMap.put(mineStackObj, to) diff --git a/src/main/scala/com/github/unchama/seichiassist/data/player/PlayerData.scala b/src/main/scala/com/github/unchama/seichiassist/data/player/PlayerData.scala index a6a0acb4ef..0633863457 100644 --- a/src/main/scala/com/github/unchama/seichiassist/data/player/PlayerData.scala +++ b/src/main/scala/com/github/unchama/seichiassist/data/player/PlayerData.scala @@ -24,35 +24,33 @@ import java.util.{GregorianCalendar, NoSuchElementException, UUID} import scala.collection.mutable /** - * @deprecated PlayerDataはuuidに依存するべきではない + * @deprecated + * PlayerDataはuuidに依存するべきではない */ -class PlayerData( - @Deprecated() val uuid: UUID, - val name: String - ) { +class PlayerData(@Deprecated() val uuid: UUID, val name: String) { import com.github.unchama.targetedeffect._ - //region session-specific data + // region session-specific data // TODO many properties here might not be right to belong here - //MineStackの履歴 + // MineStackの履歴 val hisotryData: MineStackUsageHistory = new MineStackUsageHistory() - //現在座標 + // 現在座標 var loc: Option[Location] = None - //放置時間 + // 放置時間 var idleMinute = 0 - //経験値マネージャ + // 経験値マネージャ lazy private val expmanager: IExperienceManager = new ExperienceManager(player) val settings = new PlayerSettings() - //プレイヤー名 + // プレイヤー名 val lowercaseName: String = name.toLowerCase() private val dummyDate = new GregorianCalendar(2100, 1, 1, 0, 0, 0) - //チェスト破壊トグル + // チェスト破壊トグル var chestflag = true /** @@ -64,22 +62,23 @@ class PlayerData( var canCreateRegion = true var unitPerClick = 1 - //投票受け取りボタン連打防止用 + // 投票受け取りボタン連打防止用 var votecooldownflag = true - //ガチャボタン連打防止用 + // ガチャボタン連打防止用 var gachacooldownflag = true - //インベントリ共有ボタン連打防止用 + // インベントリ共有ボタン連打防止用 var shareinvcooldownflag = true - var samepageflag = false //実績ショップ用 + var samepageflag = false // 実績ショップ用 - //endregion + // endregion - //共有インベントリにアイテムが入っているかどうか + // 共有インベントリにアイテムが入っているかどうか var contentsPresentInSharedInventory = false - //詫び券をあげる数 + // 詫び券をあげる数 var unclaimedApologyItems = 0 - //ワールドガード保護自動設定用 + // ワールドガード保護自動設定用 var regionCount = 0 + /** * 保護申請の番号を更新させる[UnfocusedEffect] */ @@ -88,40 +87,40 @@ class PlayerData( } var minestack = new MineStack() - //プレイ時間 + // プレイ時間 var playTick = 0 - //合計経験値 + // 合計経験値 var totalexp = 0L - //特典受け取り済み投票数 + // 特典受け取り済み投票数 var p_givenvote = 0 - //連続・通算ログイン用 + // 連続・通算ログイン用 // ロード時に初期化される var lastcheckdate: String = _ var loginStatus: LoginStatus = LoginStatus(null) - //期間限定ログイン用 + // 期間限定ログイン用 var LimitedLoginCount = 0 var ChainVote = 0 - //region スキル関連のデータ + // region スキル関連のデータ val skillState: Ref[IO, PlayerSkillState] = Ref.unsafe(PlayerSkillState.initial) var skillEffectState: PlayerSkillEffectState = PlayerSkillEffectState.initial var effectPoint: Int = 0 - //endregion + // endregion - //二つ名解禁フラグ保存用 + // 二つ名解禁フラグ保存用 var TitleFlags: mutable.BitSet = new mutable.BitSet(10001) - //二つ名関連用にp_vote(投票数)を引っ張る。(予期せぬエラー回避のため名前を複雑化) + // 二つ名関連用にp_vote(投票数)を引っ張る。(予期せぬエラー回避のため名前を複雑化) var p_vote_forT = 0 - //二つ名配布予約NOの保存 + // 二つ名配布予約NOの保存 var giveachvNo = 0 - //実績ポイント用 + // 実績ポイント用 var achievePoint: AchievementPoint = AchievementPoint() // n周年記念 var anniversary = false var templateMap: mutable.Map[Int, GridTemplate] = mutable.HashMap() - //投票妖精関連 + // 投票妖精関連 var usingVotingFairy = false var voteFairyPeriod = new ClosedRange(dummyDate, dummyDate) var hasVotingFairyMana = 0 @@ -131,19 +130,20 @@ class PlayerData( var p_apple: Long = 0 var toggleVFSound = true var giganticBerserk: GiganticBerserk = GiganticBerserk() - //ハーフブロック破壊抑制用 + // ハーフブロック破壊抑制用 private val allowBreakingHalfBlocks = false - //プレイ時間差分計算用int + // プレイ時間差分計算用int private var totalPlayTick: Option[Int] = None - //region calculated + // region calculated // TODO many properties here may be inlined and deleted - //グリッド式保護関連 + // グリッド式保護関連 private var claimUnit = ClaimUnit(0, 0, 0, 0) - def gridChunkAmount: Int = (claimUnit.ahead + claimUnit.behind + 1) * (claimUnit.right + claimUnit.left + 1) + def gridChunkAmount: Int = + (claimUnit.ahead + claimUnit.behind + 1) * (claimUnit.right + claimUnit.left + 1) - //オフラインかどうか + // オフラインかどうか def isOffline: Boolean = SeichiAssist.instance.getServer.getPlayer(uuid) == null def GBexp: Int = giganticBerserk.exp @@ -165,11 +165,13 @@ class PlayerData( giganticBerserk = giganticBerserk.copy(cd = value) } - //join時とonenable時、プレイヤーデータを最新の状態に更新 + // join時とonenable時、プレイヤーデータを最新の状態に更新 def updateOnJoin(): Unit = { if (unclaimedApologyItems > 0) { player.playSound(player.getLocation, Sound.BLOCK_ANVIL_PLACE, 1f, 1f) - player.sendMessage(s"${GREEN}運営チームから${unclaimedApologyItems}枚の${GOLD}ガチャ券${WHITE}が届いています!\n木の棒メニューから受け取ってください") + player.sendMessage( + s"${GREEN}運営チームから${unclaimedApologyItems}枚の${GOLD}ガチャ券${WHITE}が届いています!\n木の棒メニューから受け取ってください" + ) } synchronizeDisplayNameToLevelState() @@ -178,25 +180,28 @@ class PlayerData( isVotingFairy() } - //レベルを更新 + // レベルを更新 def synchronizeDisplayNameToLevelState(): Unit = { setDisplayName() } - //表示される名前に整地Lvor二つ名を追加 + // 表示される名前に整地Lvor二つ名を追加 def setDisplayName(): Unit = { val playerName = player.getName - //放置時に色を変える + // 放置時に色を変える val idleColor: String = if (idleMinute >= 10) s"$DARK_GRAY" else if (idleMinute >= 3) s"$GRAY" else "" val amountData = - SeichiAssist.instance - .breakCountSystem.api - .seichiAmountDataRepository(player).read + SeichiAssist + .instance + .breakCountSystem + .api + .seichiAmountDataRepository(player) + .read .unsafeRunSync() val level = amountData.levelCorrespondingToExp.level @@ -205,9 +210,15 @@ class PlayerData( val newDisplayName = idleColor + { val nicknameSettings = settings.nickname val currentNickname = - Option.unless(nicknameSettings.style == NicknameStyle.Level)( - Nicknames.getCombinedNicknameFor(nicknameSettings.id1, nicknameSettings.id2, nicknameSettings.id3) - ).flatten + Option + .unless(nicknameSettings.style == NicknameStyle.Level)( + Nicknames.getCombinedNicknameFor( + nicknameSettings.id1, + nicknameSettings.id2, + nicknameSettings.id3 + ) + ) + .flatten currentNickname.fold { val levelPart = @@ -217,9 +228,7 @@ class PlayerData( s"[ Lv$level ]" s"$levelPart$playerName$WHITE" - } { nickname => - s"[$nickname]$playerName$WHITE" - } + } { nickname => s"[$nickname]$playerName$WHITE" } } player.setDisplayName(newDisplayName) @@ -227,38 +236,44 @@ class PlayerData( } /** - * キャッシュされた [[Player]] のインスタンス。 - * プレーヤーの参加前や退出後は `Bukkit.getPlayer(uuid)` にてインスタンスが取得できないので、 + * キャッシュされた [[Player]] のインスタンス。 プレーヤーの参加前や退出後は `Bukkit.getPlayer(uuid)` にてインスタンスが取得できないので、 * 暫定的にこう実装している。 * - * @deprecated PlayerDataはPlayerに依存するべきではない。 + * @deprecated + * PlayerDataはPlayerに依存するべきではない。 */ @Deprecated() private var cachedPlayer: Option[Player] = None /** - * @deprecated PlayerDataはPlayerに依存するべきではない。 + * @deprecated + * PlayerDataはPlayerに依存するべきではない。 */ @Deprecated() def player: Player = { cachedPlayer = cachedPlayer.orElse { Bukkit.getPlayer(uuid) match { case null => throw new NoSuchElementException("プレーヤーがオンラインではありません") - case p => Some(p) + case p => Some(p) } } cachedPlayer.get } - //サーバー保管経験値をクライアントに読み込み + // サーバー保管経験値をクライアントに読み込み private def loadTotalExp(): Unit = { expmanager.setExp(totalexp) } private def isVotingFairy(): Unit = { - //効果は継続しているか - if (this.usingVotingFairy && !Util.isVotingFairyPeriod(this.votingFairyStartTime, this.votingFairyEndTime)) { + // 効果は継続しているか + if ( + this.usingVotingFairy && !Util.isVotingFairyPeriod( + this.votingFairyStartTime, + this.votingFairyEndTime + ) + ) { this.usingVotingFairy = false player.sendMessage(s"$LIGHT_PURPLE${BOLD}妖精は何処かへ行ってしまったようだ...") } else if (this.usingVotingFairy) { @@ -272,23 +287,25 @@ class PlayerData( voteFairyPeriod = new ClosedRange(voteFairyPeriod.start, value) } - def updateNickname(id1: Int = settings.nickname.id1, - id2: Int = settings.nickname.id2, - id3: Int = settings.nickname.id3, - style: NicknameStyle = NicknameStyle.TitleCombination): Unit = { + def updateNickname( + id1: Int = settings.nickname.id1, + id2: Int = settings.nickname.id2, + id3: Int = settings.nickname.id3, + style: NicknameStyle = NicknameStyle.TitleCombination + ): Unit = { settings.nickname = settings.nickname.copy(id1 = id1, id2 = id2, id3 = id3, style = style) } - //quit時とondisable時、プレイヤーデータを最新の状態に更新 + // quit時とondisable時、プレイヤーデータを最新の状態に更新 def updateOnQuit(): Unit = { - //総プレイ時間更新 + // 総プレイ時間更新 updatePlayTick() - //クライアント経験値をサーバー保管 + // クライアント経験値をサーバー保管 saveTotalExp() } - //総プレイ時間を更新する + // 総プレイ時間を更新する def updatePlayTick(): Unit = { // WARN: 1分毎にupdatePlayTickが呼び出されるというコンテクストに依存している. val nowTotalPlayTick = player.getStatistic(Statistic.PLAY_ONE_TICK) @@ -304,11 +321,14 @@ class PlayerData( def giganticBerserkLevelUp(): Unit = { val currentLevel = giganticBerserk.level - giganticBerserk = if (currentLevel >= 10) giganticBerserk else giganticBerserk.copy(level = currentLevel + 1, exp = 0) + giganticBerserk = + if (currentLevel >= 10) giganticBerserk + else giganticBerserk.copy(level = currentLevel + 1, exp = 0) } def recalculateAchievePoint(): Unit = { - val max = TitleFlags.toList + val max = TitleFlags + .toList .filter(index => (1000 to 9799) contains index) .count(_ => true) * 10 /* Safe Conversation: BitSet indexes => Int */ @@ -325,7 +345,7 @@ class PlayerData( } def calcPlayerApple(): Int = { - //ランク用関数 + // ランク用関数 var i = 0 val t = p_apple @@ -333,7 +353,7 @@ class PlayerData( var rankdata = SeichiAssist.ranklist_p_apple(i) - //ランクが上がらなくなるまで処理 + // ランクが上がらなくなるまで処理 while (rankdata.p_apple > t) { i += 1 rankdata = SeichiAssist.ranklist_p_apple(i) @@ -342,14 +362,18 @@ class PlayerData( i + 1 } - //パッシブスキルの獲得量表示 + // パッシブスキルの獲得量表示 def getPassiveExp: Double = { val level = - SeichiAssist.instance - .breakCountSystem.api - .seichiAmountDataRepository(player).read + SeichiAssist + .instance + .breakCountSystem + .api + .seichiAmountDataRepository(player) + .read .unsafeRunSync() - .levelCorrespondingToExp.level + .levelCorrespondingToExp + .level if (level < 8) 0.0 else if (level < 18) SeichiAssist.seichiAssistConfig.getDropExplevel(1) @@ -370,21 +394,21 @@ class PlayerData( val limit = SeichiAssist.seichiAssistConfig.getGridLimitPerWorld(world) val chunkMap = unitMap - //チャンクを拡大すると仮定する + // チャンクを拡大すると仮定する val assumedAmoont = chunkMap(directionType) + this.unitPerClick - //一応すべての拡張値を出しておく + // 一応すべての拡張値を出しておく val ahead = chunkMap(DirectionType.AHEAD) val behind = chunkMap(DirectionType.BEHIND) val right = chunkMap(DirectionType.RIGHT) val left = chunkMap(DirectionType.LEFT) - //合計チャンク再計算値 + // 合計チャンク再計算値 val assumedUnitAmount = directionType match { - case DirectionType.AHEAD => (assumedAmoont + 1 + behind) * (right + 1 + left) + case DirectionType.AHEAD => (assumedAmoont + 1 + behind) * (right + 1 + left) case DirectionType.BEHIND => (ahead + 1 + assumedAmoont) * (right + 1 + left) - case DirectionType.RIGHT => (ahead + 1 + behind) * (assumedAmoont + 1 + left) - case DirectionType.LEFT => (ahead + 1 + behind) * (right + 1 + assumedAmoont) + case DirectionType.RIGHT => (ahead + 1 + behind) * (assumedAmoont + 1 + left) + case DirectionType.LEFT => (ahead + 1 + behind) * (right + 1 + assumedAmoont) } assumedUnitAmount <= limit @@ -404,7 +428,7 @@ class PlayerData( def canGridReduce(directionType: DirectionType): Boolean = { val chunkMap = unitMap - //減らしたと仮定する + // 減らしたと仮定する val sizeAfterShrink = chunkMap(directionType) - unitPerClick sizeAfterShrink >= 0 @@ -412,10 +436,10 @@ class PlayerData( def setUnitAmount(directionType: DirectionType, amount: Int): Unit = { this.claimUnit = directionType match { - case DirectionType.AHEAD => this.claimUnit.copy(ahead = amount) + case DirectionType.AHEAD => this.claimUnit.copy(ahead = amount) case DirectionType.BEHIND => this.claimUnit.copy(behind = amount) - case DirectionType.RIGHT => this.claimUnit.copy(right = amount) - case DirectionType.LEFT => this.claimUnit.copy(left = amount) + case DirectionType.RIGHT => this.claimUnit.copy(right = amount) + case DirectionType.LEFT => this.claimUnit.copy(left = amount) } } @@ -423,17 +447,21 @@ class PlayerData( def addUnitAmount(directionType: DirectionType, amount: Int): Unit = { directionType match { - case DirectionType.AHEAD => this.claimUnit = this.claimUnit.copy(ahead = this.claimUnit.ahead + amount) - case DirectionType.BEHIND => this.claimUnit = this.claimUnit.copy(behind = this.claimUnit.behind + amount) - case DirectionType.RIGHT => this.claimUnit = this.claimUnit.copy(right = this.claimUnit.right + amount) - case DirectionType.LEFT => this.claimUnit = this.claimUnit.copy(left = this.claimUnit.left + amount) + case DirectionType.AHEAD => + this.claimUnit = this.claimUnit.copy(ahead = this.claimUnit.ahead + amount) + case DirectionType.BEHIND => + this.claimUnit = this.claimUnit.copy(behind = this.claimUnit.behind + amount) + case DirectionType.RIGHT => + this.claimUnit = this.claimUnit.copy(right = this.claimUnit.right + amount) + case DirectionType.LEFT => + this.claimUnit = this.claimUnit.copy(left = this.claimUnit.left + amount) } } def toggleUnitPerGrid(): Unit = { this.unitPerClick = this.unitPerClick match { - case 1 => 10 - case 10 => 100 + case 1 => 10 + case 10 => 100 case 100 => 1 } } @@ -443,10 +471,10 @@ class PlayerData( val cal = this.votingFairyStartTime if (votingFairyStartTime == dummyDate) { - //設定されてない場合 + // 設定されてない場合 ",,,,," } else { - //設定されてる場合 + // 設定されてる場合 val date = cal.getTime val format = new SimpleDateFormat("yyyy,MM,dd,HH,mm,") format.format(date) @@ -466,7 +494,13 @@ class PlayerData( val year = s(0).toInt val month = s(1).toInt - 1 val dayOfMonth = s(2).toInt - val starts = new GregorianCalendar(year, month, dayOfMonth, Integer.parseInt(s(3)), Integer.parseInt(s(4))) + val starts = new GregorianCalendar( + year, + month, + dayOfMonth, + Integer.parseInt(s(3)), + Integer.parseInt(s(4)) + ) var min = Integer.parseInt(s(4)) + 1 var hour = Integer.parseInt(s(3)) @@ -474,8 +508,8 @@ class PlayerData( min = if (this.toggleVotingFairy % 2 != 0) min + 30 else min hour = this.toggleVotingFairy match { case 2 | 3 => hour + 1 - case 4 => hour + 2 - case _ => hour + case 4 => hour + 2 + case _ => hour } val ends = new GregorianCalendar(year, month, dayOfMonth, hour, min) @@ -489,39 +523,45 @@ class PlayerData( * 運営権限により強制的に実績を解除することを試みる。 * 解除に成功し、このインスタンスが指す[Player]がオンラインであるならばその[Player]に解除の旨がチャットにて通知される。 * - * @param number 解除対象の実績番号 - * @return この作用の実行者に向け操作の結果を記述する[MessageToSender] + * @param number + * 解除対象の実績番号 + * @return + * この作用の実行者に向け操作の結果を記述する[MessageToSender] */ - def tryForcefullyUnlockAchievement(number: Int): TargetedEffect[CommandSender] = DeferredEffect(IO { - if (!TitleFlags(number)) { - TitleFlags.addOne(number) - player.sendMessage(s"運営チームよりNo${number}の実績が配布されました。") - - MessageEffect(s"$lowercaseName に実績No. $number を${GREEN}付与${RESET}しました。") - } else { - MessageEffect(s"$GRAY$lowercaseName は既に実績No. $number を獲得しています。") - } - }) + def tryForcefullyUnlockAchievement(number: Int): TargetedEffect[CommandSender] = + DeferredEffect(IO { + if (!TitleFlags(number)) { + TitleFlags.addOne(number) + player.sendMessage(s"運営チームよりNo${number}の実績が配布されました。") + + MessageEffect(s"$lowercaseName に実績No. $number を${GREEN}付与${RESET}しました。") + } else { + MessageEffect(s"$GRAY$lowercaseName は既に実績No. $number を獲得しています。") + } + }) /** - * 運営権限により強制的に実績を剥奪することを試みる。 - * 実績剥奪の通知はプレーヤーには行われない。 + * 運営権限により強制的に実績を剥奪することを試みる。 実績剥奪の通知はプレーヤーには行われない。 * - * @param number 解除対象の実績番号 - * @return この作用の実行者に向け操作の結果を記述する[TargetedEffect] + * @param number + * 解除対象の実績番号 + * @return + * この作用の実行者に向け操作の結果を記述する[TargetedEffect] */ - def forcefullyDepriveAchievement(number: Int): TargetedEffect[CommandSender] = DeferredEffect(IO { - if (TitleFlags(number)) { - TitleFlags(number) = false - - MessageEffect(s"$lowercaseName から実績No. $number を${RED}剥奪${GREEN}しました。") - } else { - MessageEffect(s"$GRAY$lowercaseName は実績No. $number を獲得していません。") + def forcefullyDepriveAchievement(number: Int): TargetedEffect[CommandSender] = DeferredEffect( + IO { + if (TitleFlags(number)) { + TitleFlags(number) = false + + MessageEffect(s"$lowercaseName から実績No. $number を${RED}剥奪${GREEN}しました。") + } else { + MessageEffect(s"$GRAY$lowercaseName は実績No. $number を獲得していません。") + } } - }) + ) } object PlayerData { - //TODO:もちろんここにあるべきではない + // TODO:もちろんここにあるべきではない val passiveSkillProbability = 10 } diff --git a/src/main/scala/com/github/unchama/seichiassist/data/player/PlayerNickName.scala b/src/main/scala/com/github/unchama/seichiassist/data/player/PlayerNickName.scala index 8e1b618423..5e879b06f7 100644 --- a/src/main/scala/com/github/unchama/seichiassist/data/player/PlayerNickName.scala +++ b/src/main/scala/com/github/unchama/seichiassist/data/player/PlayerNickName.scala @@ -8,5 +8,9 @@ object NicknameStyle { def marshal(isLevel: Boolean): NicknameStyle = if (isLevel) Level else TitleCombination } -case class PlayerNickname(style: NicknameStyle = NicknameStyle.TitleCombination, - id1: Int = 0, id2: Int = 0, id3: Int = 0) +case class PlayerNickname( + style: NicknameStyle = NicknameStyle.TitleCombination, + id1: Int = 0, + id2: Int = 0, + id3: Int = 0 +) diff --git a/src/main/scala/com/github/unchama/seichiassist/data/player/PlayerSkillState.scala b/src/main/scala/com/github/unchama/seichiassist/data/player/PlayerSkillState.scala index 367a1fa8ee..fb6933295a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/data/player/PlayerSkillState.scala +++ b/src/main/scala/com/github/unchama/seichiassist/data/player/PlayerSkillState.scala @@ -3,20 +3,27 @@ package com.github.unchama.seichiassist.data.player import com.github.unchama.seichiassist.seichiskill.SeichiSkillUsageMode.Disabled import com.github.unchama.seichiassist.seichiskill._ import com.github.unchama.seichiassist.seichiskill.effect.ActiveSkillEffect.NoEffect -import com.github.unchama.seichiassist.seichiskill.effect.{ActiveSkillEffect, UnlockableActiveSkillEffect} +import com.github.unchama.seichiassist.seichiskill.effect.{ + ActiveSkillEffect, + UnlockableActiveSkillEffect +} -case class PlayerSkillEffectState(obtainedEffects: Set[UnlockableActiveSkillEffect], - selection: ActiveSkillEffect) +case class PlayerSkillEffectState( + obtainedEffects: Set[UnlockableActiveSkillEffect], + selection: ActiveSkillEffect +) object PlayerSkillEffectState { val initial: PlayerSkillEffectState = PlayerSkillEffectState(Set(), NoEffect) } -case class PlayerSkillState(obtainedSkills: Set[SeichiSkill], - usageMode: SeichiSkillUsageMode, - activeSkill: Option[ActiveSkill], - assaultSkill: Option[AssaultSkill]) { +case class PlayerSkillState( + obtainedSkills: Set[SeichiSkill], + usageMode: SeichiSkillUsageMode, + activeSkill: Option[ActiveSkill], + assaultSkill: Option[AssaultSkill] +) { lazy val consumedActiveSkillPoint: Int = obtainedSkills.toList.map(_.requiredActiveSkillPoint).sum @@ -46,8 +53,7 @@ case class PlayerSkillState(obtainedSkills: Set[SeichiSkill], SkillDependency.prerequisites(skill).find(p => !obtainedSkills.contains(p)) /** - * `skill`が選択されていた場合、その選択を解除した状態を、 - * そうでなければこの状態を返す。 + * `skill`が選択されていた場合、その選択を解除した状態を、 そうでなければこの状態を返す。 */ def deselect(skill: SeichiSkill): PlayerSkillState = skill match { @@ -75,10 +81,12 @@ object PlayerSkillState { assaultSkill = None ) - def fromUnsafeConfiguration(obtainedSkills: Set[SeichiSkill], - usageMode: SeichiSkillUsageMode, - activeSkill: Option[ActiveSkill], - assaultSkill: Option[AssaultSkill]): PlayerSkillState = { + def fromUnsafeConfiguration( + obtainedSkills: Set[SeichiSkill], + usageMode: SeichiSkillUsageMode, + activeSkill: Option[ActiveSkill], + assaultSkill: Option[AssaultSkill] + ): PlayerSkillState = { def notObtained(skill: SeichiSkill): Boolean = !obtainedSkills.contains(skill) val selections = Seq(activeSkill, assaultSkill).flatten diff --git a/src/main/scala/com/github/unchama/seichiassist/data/player/settings/BroadcastMutingSettings.scala b/src/main/scala/com/github/unchama/seichiassist/data/player/settings/BroadcastMutingSettings.scala index 0c74b007a1..8922b14168 100644 --- a/src/main/scala/com/github/unchama/seichiassist/data/player/settings/BroadcastMutingSettings.scala +++ b/src/main/scala/com/github/unchama/seichiassist/data/player/settings/BroadcastMutingSettings.scala @@ -5,25 +5,33 @@ package com.github.unchama.seichiassist.data.player.settings */ object BroadcastMutingSettings { - def fromBooleanSettings(shouldMuteMessage: Boolean, shouldMuteSounds: Boolean): BroadcastMutingSettings = { + def fromBooleanSettings( + shouldMuteMessage: Boolean, + shouldMuteSounds: Boolean + ): BroadcastMutingSettings = { (shouldMuteMessage, shouldMuteSounds) match { case (true, true) => MuteMessageAndSound - case (_, true) => ReceiveMessageOnly - case _ => ReceiveMessageAndSound + case (_, true) => ReceiveMessageOnly + case _ => ReceiveMessageAndSound } } - case object ReceiveMessageAndSound extends BroadcastMutingSettings(ReceiveMessageOnly, false, false) + case object ReceiveMessageAndSound + extends BroadcastMutingSettings(ReceiveMessageOnly, false, false) - case object ReceiveMessageOnly extends BroadcastMutingSettings(MuteMessageAndSound, false, true) + case object ReceiveMessageOnly + extends BroadcastMutingSettings(MuteMessageAndSound, false, true) - case object MuteMessageAndSound extends BroadcastMutingSettings(ReceiveMessageAndSound, true, true) + case object MuteMessageAndSound + extends BroadcastMutingSettings(ReceiveMessageAndSound, true, true) } -sealed abstract class BroadcastMutingSettings(nextThunk: => BroadcastMutingSettings, - val shouldMuteMessages: Boolean, - val shouldMuteSounds: Boolean) { +sealed abstract class BroadcastMutingSettings( + nextThunk: => BroadcastMutingSettings, + val shouldMuteMessages: Boolean, + val shouldMuteSounds: Boolean +) { // case objectの循環参照のため lazy val next: BroadcastMutingSettings = nextThunk } diff --git a/src/main/scala/com/github/unchama/seichiassist/data/player/settings/PlayerSettings.scala b/src/main/scala/com/github/unchama/seichiassist/data/player/settings/PlayerSettings.scala index d3c74e6316..d955014d3a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/data/player/settings/PlayerSettings.scala +++ b/src/main/scala/com/github/unchama/seichiassist/data/player/settings/PlayerSettings.scala @@ -11,19 +11,22 @@ class PlayerSettings { import com.github.unchama.targetedeffect._ var autoMineStack = true - //キルログ表示トグル + // キルログ表示トグル var shouldDisplayDeathMessages = false - //ワールドガード保護ログ表示トグル + // ワールドガード保護ログ表示トグル var shouldDisplayWorldGuardLogs = true - //region accessors and modifiers - var broadcastMutingSettings: BroadcastMutingSettings = BroadcastMutingSettings.MuteMessageAndSound - //複数種類破壊トグル - var multipleidbreakflag = false - //PvPトグル + // region accessors and modifiers + var broadcastMutingSettings: BroadcastMutingSettings = + BroadcastMutingSettings.MuteMessageAndSound + + // 複数種類破壊トグル + var performMultipleIDBlockBreakWhenOutsideSeichiWorld: Boolean = false + + // PvPトグル var pvpflag = false var nickname: PlayerNickname = PlayerNickname(NicknameStyle.Level) - //ハーフブロック破壊抑制用 + // ハーフブロック破壊抑制用 private var allowBreakingHalfBlocks = false val toggleAutoMineStack: TargetedEffect[Any] = @@ -41,14 +44,12 @@ class PlayerSettings { val getBroadcastMutingSettings: IO[BroadcastMutingSettings] = IO { broadcastMutingSettings } - val toggleBroadcastMutingSettings: TargetedEffect[Any] = Kleisli.liftF( - for { - currentSettings <- getBroadcastMutingSettings - nextSettings = currentSettings.next - } yield { - broadcastMutingSettings = nextSettings - } - ) + val toggleBroadcastMutingSettings: TargetedEffect[Any] = Kleisli.liftF(for { + currentSettings <- getBroadcastMutingSettings + nextSettings = currentSettings.next + } yield { + broadcastMutingSettings = nextSettings + }) val toggleHalfBreakFlag: TargetedEffect[Player] = DeferredEffect(IO { allowBreakingHalfBlocks = !allowBreakingHalfBlocks @@ -62,6 +63,7 @@ class PlayerSettings { * 複数ブロック同時破壊のON/OFFを切り替える[UnforcedEffect] */ val toggleMultipleIdBreakFlag: TargetedEffect[Player] = UnfocusedEffect { - multipleidbreakflag = !multipleidbreakflag + performMultipleIDBlockBreakWhenOutsideSeichiWorld = + !performMultipleIDBlockBreakWhenOutsideSeichiWorld } } diff --git a/src/main/scala/com/github/unchama/seichiassist/data/syntax/XYZTupleSyntax.scala b/src/main/scala/com/github/unchama/seichiassist/data/syntax/XYZTupleSyntax.scala index cd1f681d7c..6834bfa58f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/data/syntax/XYZTupleSyntax.scala +++ b/src/main/scala/com/github/unchama/seichiassist/data/syntax/XYZTupleSyntax.scala @@ -11,13 +11,15 @@ trait XYZTupleSyntax { implicit class XYZTupleOps(a: XYZTuple) { def mapEachComponent(f: Int => Int): XYZTuple = XYZTuple(f(a.x), f(a.y), f(a.z)) - def +(another: XYZTuple): XYZTuple = XYZTuple(a.x + another.x, a.y + another.y, a.z + another.z) + def +(another: XYZTuple): XYZTuple = + XYZTuple(a.x + another.x, a.y + another.y, a.z + another.z) def negative: XYZTuple = (-1) * a def -(another: XYZTuple): XYZTuple = a + another.negative - def toLocation(world: World): Location = new Location(world, a.x.toDouble, a.y.toDouble, a.z.toDouble) + def toLocation(world: World): Location = + new Location(world, a.x.toDouble, a.y.toDouble, a.z.toDouble) def /(k: Double): XYZTuple = mapEachComponent(c => (c / k).toInt) @@ -29,4 +31,4 @@ trait XYZTupleSyntax { } } -object XYZTupleSyntax extends XYZTupleSyntax \ No newline at end of file +object XYZTupleSyntax extends XYZTupleSyntax diff --git a/src/main/scala/com/github/unchama/seichiassist/data/syntax/package.scala b/src/main/scala/com/github/unchama/seichiassist/data/syntax/package.scala index 6d2db0bffe..abc22212b1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/data/syntax/package.scala +++ b/src/main/scala/com/github/unchama/seichiassist/data/syntax/package.scala @@ -1,5 +1,3 @@ package com.github.unchama.seichiassist.data -package object syntax - extends AxisAlignedCuboidSyntax - with XYZTupleSyntax +package object syntax extends AxisAlignedCuboidSyntax with XYZTupleSyntax diff --git a/src/main/scala/com/github/unchama/seichiassist/database/manipulators/DonateDataManipulator.scala b/src/main/scala/com/github/unchama/seichiassist/database/manipulators/DonateDataManipulator.scala index 9febe1b1db..6e94762d78 100644 --- a/src/main/scala/com/github/unchama/seichiassist/database/manipulators/DonateDataManipulator.scala +++ b/src/main/scala/com/github/unchama/seichiassist/database/manipulators/DonateDataManipulator.scala @@ -2,7 +2,11 @@ package com.github.unchama.seichiassist.database.manipulators import cats.effect.IO import com.github.unchama.generic.effect.SyncExtra -import com.github.unchama.seichiassist.database.manipulators.DonateDataManipulator.{Obtained, PremiumPointTransaction, Used} +import com.github.unchama.seichiassist.database.manipulators.DonateDataManipulator.{ + Obtained, + PremiumPointTransaction, + Used +} import com.github.unchama.seichiassist.database.{DatabaseConstants, DatabaseGateway} import com.github.unchama.seichiassist.seichiskill.effect.ActiveSkillPremiumEffect import com.github.unchama.util.ActionStatus @@ -12,9 +16,13 @@ class DonateDataManipulator(private val gateway: DatabaseGateway) { import com.github.unchama.util.syntax.ResultSetSyntax._ - private def tableReference: String = s"${gateway.databaseName}.${DatabaseConstants.DONATEDATA_TABLENAME}" + private def tableReference: String = + s"${gateway.databaseName}.${DatabaseConstants.DONATEDATA_TABLENAME}" - def recordPremiumEffectPurchase(player: Player, effect: ActiveSkillPremiumEffect): IO[ActionStatus] = { + def recordPremiumEffectPurchase( + player: Player, + effect: ActiveSkillPremiumEffect + ): IO[ActionStatus] = { val command = s"insert into $tableReference (playername,playeruuid,effectname,usepoint,date) " + s"value('${player.getName}','${player.getUniqueId.toString}','${effect.entryName}',${effect.usePoint},cast(now() as datetime))" @@ -41,7 +49,7 @@ class DonateDataManipulator(private val gateway: DatabaseGateway) { List(), IO { gateway.executeQuery(command).recordIteration { lrs => - //ポイント購入の処理 + // ポイント購入の処理 val getPoint = lrs.getInt("getpoint") val usePoint = lrs.getInt("usepoint") val date = lrs.getString("date") @@ -50,7 +58,8 @@ class DonateDataManipulator(private val gateway: DatabaseGateway) { Obtained(getPoint, date) } else if (usePoint > 0) { val effectName = lrs.getString("effectname") - val nameOrEffect = ActiveSkillPremiumEffect.withNameOption(effectName).toRight(effectName) + val nameOrEffect = + ActiveSkillPremiumEffect.withNameOption(effectName).toRight(effectName) Used(usePoint, date, nameOrEffect) } else { throw new IllegalStateException("usepointまたはgetpointが正である必要があります") @@ -64,7 +73,7 @@ class DonateDataManipulator(private val gateway: DatabaseGateway) { loadTransactionHistoryFor(player).map { history => history.map { case Obtained(p, _) => p - case Used(p, _, _) => -p + case Used(p, _, _) => -p }.sum } } @@ -73,5 +82,9 @@ class DonateDataManipulator(private val gateway: DatabaseGateway) { object DonateDataManipulator { sealed trait PremiumPointTransaction case class Obtained(amount: Int, date: String) extends PremiumPointTransaction - case class Used(amount: Int, date: String, forPurchaseOf: Either[String, ActiveSkillPremiumEffect]) extends PremiumPointTransaction -} \ No newline at end of file + case class Used( + amount: Int, + date: String, + forPurchaseOf: Either[String, ActiveSkillPremiumEffect] + ) extends PremiumPointTransaction +} diff --git a/src/main/scala/com/github/unchama/seichiassist/database/manipulators/GachaDataManipulator.scala b/src/main/scala/com/github/unchama/seichiassist/database/manipulators/GachaDataManipulator.scala index ba6ba2925f..10fe9aae78 100644 --- a/src/main/scala/com/github/unchama/seichiassist/database/manipulators/GachaDataManipulator.scala +++ b/src/main/scala/com/github/unchama/seichiassist/database/manipulators/GachaDataManipulator.scala @@ -1,8 +1,5 @@ package com.github.unchama.seichiassist.database.manipulators -import java.io.IOException -import java.sql.SQLException - import com.github.unchama.seichiassist.SeichiAssist import com.github.unchama.seichiassist.data.GachaPrize import com.github.unchama.seichiassist.database.{DatabaseConstants, DatabaseGateway} @@ -10,15 +7,18 @@ import com.github.unchama.seichiassist.util.BukkitSerialization import com.github.unchama.util.ActionStatus import org.bukkit.Bukkit +import java.io.IOException +import java.sql.SQLException import scala.collection.mutable.ArrayBuffer class GachaDataManipulator(private val gateway: DatabaseGateway) { import com.github.unchama.util.syntax.ResultSetSyntax._ - private val tableReference: String = gateway.databaseName + "." + DatabaseConstants.GACHADATA_TABLENAME + private val tableReference: String = + gateway.databaseName + "." + DatabaseConstants.GACHADATA_TABLENAME - //ガチャデータロード + // ガチャデータロード def loadGachaData(): Boolean = { val prizes = ArrayBuffer[GachaPrize]() @@ -33,7 +33,7 @@ class GachaDataManipulator(private val gateway: DatabaseGateway) { prizes.append(prize) } } catch { - case e@(_: SQLException | _: IOException) => + case e @ (_: SQLException | _: IOException) => println("sqlクエリの実行に失敗しました。以下にエラーを表示します") e.printStackTrace() return false @@ -44,18 +44,18 @@ class GachaDataManipulator(private val gateway: DatabaseGateway) { true } - //ガチャデータセーブ + // ガチャデータセーブ def saveGachaData(): Boolean = { - //まずmysqlのガチャテーブルを初期化(中身全削除) + // まずmysqlのガチャテーブルを初期化(中身全削除) var command = s"truncate table $tableReference" if (gateway.executeUpdate(command) == ActionStatus.Fail) { return false } - //次に現在のgachadatalistでmysqlを更新 - for {gachadata <- SeichiAssist.gachadatalist} { - //Inventory作ってガチャのitemstackに突っ込む + // 次に現在のgachadatalistでmysqlを更新 + for { gachadata <- SeichiAssist.gachadatalist } { + // Inventory作ってガチャのitemstackに突っ込む val inventory = Bukkit.getServer.createInventory(null, 9 * 1) inventory.setItem(0, gachadata.itemStack) @@ -70,5 +70,4 @@ class GachaDataManipulator(private val gateway: DatabaseGateway) { true } - } diff --git a/src/main/scala/com/github/unchama/seichiassist/database/manipulators/MineStackGachaDataManipulator.scala b/src/main/scala/com/github/unchama/seichiassist/database/manipulators/MineStackGachaDataManipulator.scala index 7108a07ac3..ae242475bf 100644 --- a/src/main/scala/com/github/unchama/seichiassist/database/manipulators/MineStackGachaDataManipulator.scala +++ b/src/main/scala/com/github/unchama/seichiassist/database/manipulators/MineStackGachaDataManipulator.scala @@ -1,8 +1,5 @@ package com.github.unchama.seichiassist.database.manipulators -import java.io.IOException -import java.sql.SQLException - import com.github.unchama.seichiassist.SeichiAssist import com.github.unchama.seichiassist.data.MineStackGachaData import com.github.unchama.seichiassist.database.{DatabaseConstants, DatabaseGateway} @@ -10,15 +7,18 @@ import com.github.unchama.seichiassist.util.BukkitSerialization import com.github.unchama.util.ActionStatus import org.bukkit.Bukkit +import java.io.IOException +import java.sql.SQLException import scala.collection.mutable.ArrayBuffer class MineStackGachaDataManipulator(private val gateway: DatabaseGateway) { import com.github.unchama.util.syntax.ResultSetSyntax._ - private val tableReference: String = gateway.databaseName + "." + DatabaseConstants.MINESTACK_GACHADATA_TABLENAME + private val tableReference: String = + gateway.databaseName + "." + DatabaseConstants.MINESTACK_GACHADATA_TABLENAME - //MineStack用ガチャデータロード + // MineStack用ガチャデータロード def loadMineStackGachaData(): Boolean = { val gachadatalist = ArrayBuffer[MineStackGachaData]() @@ -29,13 +29,16 @@ class MineStackGachaDataManipulator(private val gateway: DatabaseGateway) { val itemStack = savedInventory.getItem(0) val gachaData = new MineStackGachaData( - lrs.getString("obj_name"), itemStack, lrs.getDouble("probability"), lrs.getInt("level") + lrs.getString("obj_name"), + itemStack, + lrs.getDouble("probability"), + lrs.getInt("level") ) gachadatalist += gachaData } } catch { - case e@(_: SQLException | _: IOException) => + case e @ (_: SQLException | _: IOException) => println("sqlクエリの実行に失敗しました。以下にエラーを表示します") e.printStackTrace() return false @@ -46,19 +49,18 @@ class MineStackGachaDataManipulator(private val gateway: DatabaseGateway) { true } - //MineStack用ガチャデータセーブ + // MineStack用ガチャデータセーブ def saveMineStackGachaData(): Boolean = { - - //まずmysqlのガチャテーブルを初期化(中身全削除) + // まずmysqlのガチャテーブルを初期化(中身全削除) var command = s"truncate table $tableReference" if (gateway.executeUpdate(command) == ActionStatus.Fail) { return false } - //次に現在のgachadatalistでmysqlを更新 - for {gachadata <- SeichiAssist.msgachadatalist} { - //Inventory作ってガチャのitemstackに突っ込む + // 次に現在のgachadatalistでmysqlを更新 + for { gachadata <- SeichiAssist.msgachadatalist } { + // Inventory作ってガチャのitemstackに突っ込む val inventory = Bukkit.getServer.createInventory(null, 9 * 1) inventory.setItem(0, gachadata.itemStack) diff --git a/src/main/scala/com/github/unchama/seichiassist/database/manipulators/PlayerDataManipulator.scala b/src/main/scala/com/github/unchama/seichiassist/database/manipulators/PlayerDataManipulator.scala index 6c2b6d0f3a..a711bb143a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/database/manipulators/PlayerDataManipulator.scala +++ b/src/main/scala/com/github/unchama/seichiassist/database/manipulators/PlayerDataManipulator.scala @@ -30,9 +30,10 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { private val plugin = SeichiAssist.instance - private val tableReference: String = s"${gateway.databaseName}.${DatabaseConstants.PLAYERDATA_TABLENAME}" + private val tableReference: String = + s"${gateway.databaseName}.${DatabaseConstants.PLAYERDATA_TABLENAME}" - //投票特典配布時の処理(p_givenvoteの値の更新もココ) + // 投票特典配布時の処理(p_givenvoteの値の更新もココ) def compareVotePoint(player: Player, playerdata: PlayerData): Int = { ifCoolDownDoneThenGet(player, playerdata) { val struuid = playerdata.uuid.toString @@ -54,7 +55,7 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { return 0 } - //比較して差があればその差の値を返す(同時にp_givenvoteも更新しておく) + // 比較して差があればその差の値を返す(同時にp_givenvoteも更新しておく) if (p_vote > p_givenvote) { command = ("update " + tableReference + " set p_givenvote = " + p_vote @@ -71,21 +72,20 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { } } - //最新のnumofsorryforbug値を返してmysqlのnumofsorrybug値を初期化する処理 + // 最新のnumofsorryforbug値を返してmysqlのnumofsorrybug値を初期化する処理 def givePlayerBug(player: Player): Int = { val uuid = player.getUniqueId.toString val numberToGrant = { val command = s"select numofsorryforbug from $tableReference where uuid = '$uuid'" val rawMaximum = try { - gateway.executeQuery(command) - .recordIteration { _.getInt("numofsorryforbug") } - .head - } catch { case e: Exception => - println("sqlクエリの実行に失敗しました。以下にエラーを表示します") - e.printStackTrace() - player.sendMessage(RED.toString + "ガチャ券の受け取りに失敗しました") - return 0 + gateway.executeQuery(command).recordIteration { _.getInt("numofsorryforbug") }.head + } catch { + case e: Exception => + println("sqlクエリの実行に失敗しました。以下にエラーを表示します") + e.printStackTrace() + player.sendMessage(RED.toString + "ガチャ券の受け取りに失敗しました") + return 0 } Math.min(rawMaximum, 576) @@ -105,8 +105,10 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { numberToGrant } - @inline private def ifCoolDownDoneThenGet(player: Player, playerdata: PlayerData)(supplier: => Int): Int = { - //連打による負荷防止の為クールダウン処理 + @inline private def ifCoolDownDoneThenGet(player: Player, playerdata: PlayerData)( + supplier: => Int + ): Int = { + // 連打による負荷防止の為クールダウン処理 if (!playerdata.votecooldownflag) { player.sendMessage(RED.toString + "しばらく待ってからやり直してください") return 0 @@ -119,17 +121,16 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { /** * 投票ポイントをインクリメントするメソッド。 * - * @param playerName プレーヤー名 + * @param playerName + * プレーヤー名 */ def incrementVotePoint(playerName: String): Unit = { DB.localTx { implicit session => - sql"update playerdata set p_vote = p_vote + 1 where name = $playerName" - .update() - .apply() + sql"update playerdata set p_vote = p_vote + 1 where name = $playerName".update().apply() } } - //指定されたプレイヤーにガチャ券を送信する + // 指定されたプレイヤーにガチャ券を送信する def addPlayerBug(playerName: String, num: Int): ActionStatus = { val command = ("update " + tableReference + " set numofsorryforbug = numofsorryforbug + " + num @@ -138,7 +139,7 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { gateway.executeUpdate(command) } - def addChainVote(name: String): Boolean = + def addChainVote(name: String): Unit = DB.localTx { implicit session => val calendar = Calendar.getInstance() val dateFormat = new SimpleDateFormat("yyyy/MM/dd") @@ -149,7 +150,7 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { .map(_.string("lastvote")) .headOption() .apply() - .getOrElse(return false) + .get if (readLastVote == null || readLastVote == "") dateFormat.format(calendar.getTime) @@ -158,7 +159,8 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { } sql"UPDATE playerdata SET lastvote = ${dateFormat.format(calendar.getTime)} WHERE name = $name" - .update().apply() + .update() + .apply() val TodayDate = dateFormat.parse(dateFormat.format(calendar.getTime)) val LastDate = dateFormat.parse(lastVote) @@ -171,13 +173,13 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { val newCount = if (shouldIncrementChainVote) { sql"""select chainvote from playerdata where name = $name""" .map(_.int("chainvote")) - .first().apply() + .first() + .apply() .get + 1 } else 1 sql"""update playerdata set chainvote = $newCount where name = $name""" - true - } + } // anniversary変更 def setAnniversary(anniversary: Boolean, uuid: Option[UUID]): Boolean = { @@ -191,7 +193,10 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { true } - def saveSharedInventory(player: Player, serializedInventory: String): IO[ResponseEffectOrResult[Player, Unit]] = { + def saveSharedInventory( + player: Player, + serializedInventory: String + ): IO[ResponseEffectOrResult[Player, Unit]] = { val assertSharedInventoryBeEmpty: EitherT[IO, TargetedEffect[CommandSender], Unit] = for { sharedInventorySerialized <- EitherT(loadShareInv(player)) @@ -205,7 +210,8 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { val writeInventoryData = IO { // シリアル化されたインベントリデータを書き込む - val updateCommand = s"UPDATE $tableReference SET shareinv = '$serializedInventory' WHERE uuid = '${player.getUniqueId}'" + val updateCommand = + s"UPDATE $tableReference SET shareinv = '$serializedInventory' WHERE uuid = '${player.getUniqueId}'" if (gateway.executeUpdate(updateCommand) == ActionStatus.Fail) { Bukkit.getLogger.warning(s"${player.getName} database failure.") @@ -220,38 +226,46 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { _ <- assertSharedInventoryBeEmpty _ <- EitherT(writeInventoryData) } yield () - }.value + }.value def loadShareInv(player: Player): IO[ResponseEffectOrResult[CommandSender, String]] = { - val loadInventoryData: IO[Either[Nothing, String]] = EitherT.right(IO { - val command = s"SELECT shareinv FROM $tableReference WHERE uuid = '${player.getUniqueId}'" + val loadInventoryData: IO[Either[Nothing, String]] = EitherT + .right(IO { + val command = + s"SELECT shareinv FROM $tableReference WHERE uuid = '${player.getUniqueId}'" - gateway.executeQuery(command).recordIteration(_.getString("shareinv")).headOption.get - }).value + gateway.executeQuery(command).recordIteration(_.getString("shareinv")).headOption.get + }) + .value for { _ <- EitherT(checkInventoryOperationCoolDown(player)) serializedInventory <- EitherT(catchingDatabaseErrors(player.getName, loadInventoryData)) } yield serializedInventory - }.value + }.value - private def catchingDatabaseErrors[R](targetName: String, - program: IO[Either[TargetedEffect[CommandSender], R]]): IO[Either[TargetedEffect[CommandSender], R]] = { + private def catchingDatabaseErrors[R]( + targetName: String, + program: IO[Either[TargetedEffect[CommandSender], R]] + ): IO[Either[TargetedEffect[CommandSender], R]] = { program.attempt.flatMap { - case Left(error) => IO { - Bukkit.getLogger.warning(s"database failure for $targetName.") - error.printStackTrace() + case Left(error) => + IO { + Bukkit.getLogger.warning(s"database failure for $targetName.") + error.printStackTrace() - Left(MessageEffect(s"${RED}データベースアクセスに失敗しました。")) - } + Left(MessageEffect(s"${RED}データベースアクセスに失敗しました。")) + } case Right(result) => IO.pure(result) } } - private def checkInventoryOperationCoolDown(player: Player): IO[Either[TargetedEffect[CommandSender], Unit]] = { + private def checkInventoryOperationCoolDown( + player: Player + ): IO[Either[TargetedEffect[CommandSender], Unit]] = { val playerData = SeichiAssist.playermap(player.getUniqueId) IO { - //連打による負荷防止 + // 連打による負荷防止 if (!playerData.shareinvcooldownflag) Left(MessageEffect(s"${RED}しばらく待ってからやり直してください")) else { @@ -261,7 +275,10 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { } } - def clearShareInv(player: Player, playerdata: PlayerData): IO[ResponseEffectOrResult[CommandSender, Unit]] = IO { + def clearShareInv( + player: Player, + playerdata: PlayerData + ): IO[ResponseEffectOrResult[CommandSender, Unit]] = IO { val command = s"UPDATE $tableReference SET shareinv = '' WHERE uuid = '${playerdata.uuid}'" if (gateway.executeUpdate(command) == ActionStatus.Fail) { @@ -299,8 +316,8 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { /** * 全ランキングリストの更新処理 * - * @return 成否…true: 成功、false: 失敗 - * TODO この処理はDB上と通信を行う為非同期にすべき + * @return + * 成否…true: 成功、false: 失敗 TODO この処理はDB上と通信を行う為非同期にすべき */ def successRankingUpdate(): Boolean = { if (!successPlayTickRankingUpdate()) return false @@ -308,7 +325,7 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { successAppleNumberRankingUpdate() } - //ランキング表示用にプレイ時間のカラムだけ全員分引っ張る + // ランキング表示用にプレイ時間のカラムだけ全員分引っ張る private def successPlayTickRankingUpdate(): Boolean = { val ranklist = mutable.ArrayBuffer[RankData]() val command = ("select name,playtick from " + tableReference @@ -332,7 +349,7 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { true } - //ランキング表示用に投票数のカラムだけ全員分引っ張る + // ランキング表示用に投票数のカラムだけ全員分引っ張る private def successVoteRankingUpdate(): Boolean = { val ranklist = mutable.ArrayBuffer[RankData]() val command = ("select name,p_vote from " + tableReference @@ -356,7 +373,7 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { true } - //ランキング表示用に上げたりんご数のカラムだけ全員分引っ張る + // ランキング表示用に上げたりんご数のカラムだけ全員分引っ張る private def successAppleNumberRankingUpdate(): Boolean = { val ranklist = mutable.ArrayBuffer[RankData]() SeichiAssist.allplayergiveapplelong = 0 @@ -381,32 +398,35 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { true } - //全員に詫びガチャの配布 + // 全員に詫びガチャの配布 def addAllPlayerBug(amount: Int): ActionStatus = { val command = s"update $tableReference set numofsorryforbug = numofsorryforbug + $amount" gateway.executeUpdate(command) } - def selectPocketInventoryOf(uuid: UUID): IO[ResponseEffectOrResult[CommandSender, Inventory]] = { + def selectPocketInventoryOf( + uuid: UUID + ): IO[ResponseEffectOrResult[CommandSender, Inventory]] = { val command = s"select inventory from $tableReference where uuid = '$uuid'" val executeQuery = IO { - gateway.executeQuery(command).recordIteration { lrs => - BukkitSerialization.fromBase64(lrs.getString("inventory")) - }.head + gateway + .executeQuery(command) + .recordIteration { lrs => BukkitSerialization.fromBase64(lrs.getString("inventory")) } + .head } catchingDatabaseErrors(uuid.toString, EitherT.right(executeQuery).value) } def inquireLastQuitOf(playerName: String): IO[TargetedEffect[CommandSender]] = { - val fetchLastQuitData: IO[ResponseEffectOrResult[CommandSender, String]] = EitherT.right(IO { - val command = s"select lastquit from $tableReference where name = '$playerName'" + val fetchLastQuitData: IO[ResponseEffectOrResult[CommandSender, String]] = EitherT + .right(IO { + val command = s"select lastquit from $tableReference where name = '$playerName'" - gateway.executeQuery(command) - .recordIteration(_.getString("lastquit")) - .head - }).value + gateway.executeQuery(command).recordIteration(_.getString("lastquit")).head + }) + .value catchingDatabaseErrors(playerName, fetchLastQuitData).map { case Left(errorEffect) => @@ -429,15 +449,15 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { val table = DatabaseConstants.PLAYERDATA_TABLENAME val db = SeichiAssist.seichiAssistConfig.getDB - //sqlコネクションチェック + // sqlコネクションチェック databaseGateway.ensureConnection() - //同ステートメントだとmysqlの処理がバッティングした時に止まってしまうので別ステートメントを作成する + // 同ステートメントだとmysqlの処理がバッティングした時に止まってしまうので別ステートメントを作成する val stmt = databaseGateway.con.createStatement() val stringUuid: String = playerUUID.toString.toLowerCase() - //uuidがsqlデータ内に存在するか検索 + // uuidがsqlデータ内に存在するか検索 val count = { val command = s"select count(*) as count from $db.$table where uuid = '$stringUuid'" @@ -446,11 +466,12 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { count match { case Some(0) => - //uuidが存在しない時 + // uuidが存在しない時 SeichiAssist.instance.getLogger.info(s"$YELLOW${playerName}は完全初見です。プレイヤーデータを作成します") - //新しくuuidとnameを設定し行を作成 - val command = s"insert into $db.$table (name,uuid,loginflag) values('$playerName','$stringUuid','1')" + // 新しくuuidとnameを設定し行を作成 + val command = + s"insert into $db.$table (name,uuid,loginflag) values('$playerName','$stringUuid','1')" stmt.executeUpdate(command) new PlayerData(playerUUID, playerName) diff --git a/src/main/scala/com/github/unchama/seichiassist/domain/actions/UuidToLastSeenName.scala b/src/main/scala/com/github/unchama/seichiassist/domain/actions/UuidToLastSeenName.scala index 3cd9bc2e1b..8a2946aace 100644 --- a/src/main/scala/com/github/unchama/seichiassist/domain/actions/UuidToLastSeenName.scala +++ b/src/main/scala/com/github/unchama/seichiassist/domain/actions/UuidToLastSeenName.scala @@ -5,12 +5,11 @@ import java.util.UUID trait UuidToLastSeenName[F[_]] { /** - * サーバーに参加したことがあるプレイヤーのデータが格納されているストレージを全探索し、 - * UUIDとそれに対応したプレイヤーの名前を列挙してMapとして返す。 + * サーバーに参加したことがあるプレイヤーのデータが格納されているストレージを全探索し、 UUIDとそれに対応したプレイヤーの名前を列挙してMapとして返す。 * - * この時のプレイヤーの名前は、そのプレイヤーの名前の集合の内、 - * 最も新しく、かつその名前がサーバーにとって既知であるという性質を持つ。 - * @return プレイヤーのUUIDとそれに対応する名前が組になったMapを計算する作用 + * この時のプレイヤーの名前は、そのプレイヤーの名前の集合の内、 最も新しく、かつその名前がサーバーにとって既知であるという性質を持つ。 + * @return + * プレイヤーのUUIDとそれに対応する名前が組になったMapを計算する作用 */ def entries: F[Map[UUID, String]] @@ -20,4 +19,4 @@ object UuidToLastSeenName { def apply[F[_]: UuidToLastSeenName]: UuidToLastSeenName[F] = implicitly[UuidToLastSeenName[F]] -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/domain/explevel/ExpLevelConversion.scala b/src/main/scala/com/github/unchama/seichiassist/domain/explevel/ExpLevelConversion.scala index 5305239025..2741b52b47 100644 --- a/src/main/scala/com/github/unchama/seichiassist/domain/explevel/ExpLevelConversion.scala +++ b/src/main/scala/com/github/unchama/seichiassist/domain/explevel/ExpLevelConversion.scala @@ -5,8 +5,8 @@ package com.github.unchama.seichiassist.domain.explevel * * `succ` をレベルをインクリメントする操作、 `l: Level`、 `e: ExpAmount` を任意に取るとき、 * - * - `levelAt(expAt(l)) = l` - * - `expAt(levelAt(e)) <= e < expAt(succ(levelAt(e)))` + * - `levelAt(expAt(l)) = l` + * - `expAt(levelAt(e)) <= e < expAt(succ(levelAt(e)))` * * を満たす。 */ diff --git a/src/main/scala/com/github/unchama/seichiassist/domain/explevel/FiniteExpLevelTable.scala b/src/main/scala/com/github/unchama/seichiassist/domain/explevel/FiniteExpLevelTable.scala index 21c4e91e69..ca722bada6 100644 --- a/src/main/scala/com/github/unchama/seichiassist/domain/explevel/FiniteExpLevelTable.scala +++ b/src/main/scala/com/github/unchama/seichiassist/domain/explevel/FiniteExpLevelTable.scala @@ -6,45 +6,49 @@ import com.github.unchama.generic.algebra.typeclasses.PositiveInt import scala.collection.Searching /** - * 経験値量とレベルを相互変換する有限のテーブル。 - * [[ExpLevelConversion]] と似ているが、こちらはレベルに有限性を仮定していることに注意。 + * 経験値量とレベルを相互変換する有限のテーブル。 [[ExpLevelConversion]] と似ているが、こちらはレベルに有限性を仮定していることに注意。 * - * @param internalTable 経験値の遷移を記述するSeq。 - * i番目の要素に、レベルi+1になるのに必要な経験値量が入る。 - * この列は単調増加であることが要求される。 + * @param internalTable + * 経験値の遷移を記述するSeq。 i番目の要素に、レベルi+1になるのに必要な経験値量が入る。 この列は単調増加であることが要求される。 */ -class FiniteExpLevelTable[ - L: PositiveInt, - ExpAmount: Order : LowerBounded -](private val internalTable: Vector[ExpAmount]) { +class FiniteExpLevelTable[L: PositiveInt, ExpAmount: Order: LowerBounded]( + private val internalTable: Vector[ExpAmount] +) { import cats.implicits._ - require({ - internalTable.sliding(2).forall { case Seq(x1, x2) => - x1 <= x2 - } - }, "internalTable must be sorted") + require( + { + internalTable.sliding(2).forall { + case Seq(x1, x2) => + x1 <= x2 + } + }, + "internalTable must be sorted" + ) require(internalTable.nonEmpty, "table must be nonempty") - require({ - internalTable.head == LowerBounded[ExpAmount].minBound - }, "first element of the table must be the minimum amount") + require( + { + internalTable.head == LowerBounded[ExpAmount].minBound + }, + "first element of the table must be the minimum amount" + ) /** * 与えられた経験値量 `expAmount` で到達できるレベルを計算する。 */ def levelAt(expAmount: ExpAmount): L = PositiveInt[L].wrapPositive { internalTable.search(expAmount) match { - case Searching.Found(foundIndex) => foundIndex + 1 + case Searching.Found(foundIndex) => foundIndex + 1 case Searching.InsertionPoint(insertionPoint) => insertionPoint } } /** - * 与えられたレベル `level` に上がるのに必要な経験値量を計算する。 - * `level` が `maxLevel` よりも真に大きい場合、 `maxLevel` に到達するのに必要な経験値量が返される。 + * 与えられたレベル `level` に上がるのに必要な経験値量を計算する。 `level` が `maxLevel` よりも真に大きい場合、 `maxLevel` + * に到達するのに必要な経験値量が返される。 */ def expAt(level: L): ExpAmount = { val rawLevel = PositiveInt[L].asInt(level) @@ -71,12 +75,15 @@ class FiniteExpLevelTable[ def extendToLevel(level: L): ExtensionBuilder = ExtensionBuilder(level) case class ExtensionBuilder(extensionTarget: L) { + /** * [[extensionTarget]] まで、レベルを1延長するごとに必要な経験値量を `exp` 増やすよう延長したテーブルを返す。 */ - def withLinearIncreaseOf(exp: ExpAmount) - (implicit addition: Monoid[ExpAmount]): FiniteExpLevelTable[L, ExpAmount] = { - val lengthToFill = (PositiveInt[L].asInt(extensionTarget) - PositiveInt[L].asInt(maxLevel)) max 0 + def withLinearIncreaseOf( + exp: ExpAmount + )(implicit addition: Monoid[ExpAmount]): FiniteExpLevelTable[L, ExpAmount] = { + val lengthToFill = + (PositiveInt[L].asInt(extensionTarget) - PositiveInt[L].asInt(maxLevel)) max 0 val lastThreshold = internalTable.last val extensionHead = addition.combine(lastThreshold, exp) val extension = Vector.iterate(extensionHead, lengthToFill)(addition.combine(_, exp)) diff --git a/src/main/scala/com/github/unchama/seichiassist/infrastructure/akka/ConfiguredActorSystemProvider.scala b/src/main/scala/com/github/unchama/seichiassist/infrastructure/akka/ConfiguredActorSystemProvider.scala index ccf81c1d7b..c89e776733 100644 --- a/src/main/scala/com/github/unchama/seichiassist/infrastructure/akka/ConfiguredActorSystemProvider.scala +++ b/src/main/scala/com/github/unchama/seichiassist/infrastructure/akka/ConfiguredActorSystemProvider.scala @@ -8,10 +8,7 @@ case class ConfiguredActorSystemProvider(configurationPath: String) { lazy val classLoader: ClassLoader = getClass.getClassLoader def provide(): ActorSystem = { - akka.actor.ActorSystem( - name = "default", - classLoader = Some(classLoader) - ) + akka.actor.ActorSystem(name = "default", classLoader = Some(classLoader)) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/infrastructure/redisbungee/RedisBungeeNetworkConnectionCount.scala b/src/main/scala/com/github/unchama/seichiassist/infrastructure/redisbungee/RedisBungeeNetworkConnectionCount.scala index 510436b72e..cc9428a5f9 100644 --- a/src/main/scala/com/github/unchama/seichiassist/infrastructure/redisbungee/RedisBungeeNetworkConnectionCount.scala +++ b/src/main/scala/com/github/unchama/seichiassist/infrastructure/redisbungee/RedisBungeeNetworkConnectionCount.scala @@ -9,12 +9,10 @@ import redis.RedisClient import redis.api.scripting.RedisScript import redis.protocol.{Bulk, MultiBulk} -class RedisBungeeNetworkConnectionCount[ - F[_] : Effect : ErrorLogger -](connectionContextShift: ContextShift[IO]) - (implicit - configuration: RedisBungeeRedisConfiguration, - actorSystem: ActorSystem) extends GetNetworkConnectionCount[F] { +class RedisBungeeNetworkConnectionCount[F[_]: Effect: ErrorLogger]( + connectionContextShift: ContextShift[IO] +)(implicit configuration: RedisBungeeRedisConfiguration, actorSystem: ActorSystem) + extends GetNetworkConnectionCount[F] { import cats.implicits._ @@ -28,12 +26,11 @@ class RedisBungeeNetworkConnectionCount[ /** * RedisBungee 0.5.1の仕様として、 - * - proxy:{serverName}:usersOnline に serverName でオンラインであるプレーヤーのUUIDがsetで置かれており - * - player:{uuid} の server フィールドに uuid を持つプレーヤーが参加しているサーバーの名前が置かれている - * ので、プレーヤーが参加しているサーバー名の重複ありのリストを返すluaスクリプトを投げればよい + * - proxy:{serverName}:usersOnline に serverName でオンラインであるプレーヤーのUUIDがsetで置かれており + * - player:{uuid} の server フィールドに uuid を持つプレーヤーが参加しているサーバーの名前が置かれている + * ので、プレーヤーが参加しているサーバー名の重複ありのリストを返すluaスクリプトを投げればよい */ - private val script = RedisScript( - """ + private val script = RedisScript(""" local proxy_keys = redis.call('keys', 'proxy:*:usersOnline') local player_keys = {} @@ -51,22 +48,23 @@ for i, player_key in ipairs(player_keys) do end return result""") - private val seichiServers = Set( - "s1", "s2", "s3", "s5", "s7" - ) + private val seichiServers = Set("s1", "s2", "s3", "s5", "s7") override val now: F[Int] = - Effect[F].liftIO { - IO.fromFuture(IO(redisClient.evalshaOrEval(script))).flatMap { - case MultiBulk(Some(vector)) => IO.pure { - vector.count { - case Bulk(Some(byteString)) => seichiServers.contains(byteString.utf8String) - case _ => false - } + Effect[F] + .liftIO { + IO.fromFuture(IO(redisClient.evalshaOrEval(script))).flatMap { + case MultiBulk(Some(vector)) => + IO.pure { + vector.count { + case Bulk(Some(byteString)) => seichiServers.contains(byteString.utf8String) + case _ => false + } + } + case _ => IO.raiseError(new RuntimeException("Expected MultiBulk response")) } - case _ => IO.raiseError(new RuntimeException("Expected MultiBulk response")) } - }.handleErrorWith { error => - ErrorLogger[F].error(error)("RedisBungee関連のデータを取得するのに失敗しました").as(0) - } + .handleErrorWith { error => + ErrorLogger[F].error(error)("RedisBungee関連のデータを取得するのに失敗しました").as(0) + } } diff --git a/src/main/scala/com/github/unchama/seichiassist/infrastructure/scalikejdbc/ScalikeJDBCConfiguration.scala b/src/main/scala/com/github/unchama/seichiassist/infrastructure/scalikejdbc/ScalikeJDBCConfiguration.scala index 3063bcf1c8..435c87e318 100644 --- a/src/main/scala/com/github/unchama/seichiassist/infrastructure/scalikejdbc/ScalikeJDBCConfiguration.scala +++ b/src/main/scala/com/github/unchama/seichiassist/infrastructure/scalikejdbc/ScalikeJDBCConfiguration.scala @@ -1,14 +1,16 @@ package com.github.unchama.seichiassist.infrastructure.scalikejdbc -import scalikejdbc.{ConnectionPool, ConnectionPoolSettings, GlobalSettings, LoggingSQLAndTimeSettings} +import scalikejdbc.{ + ConnectionPool, + ConnectionPoolSettings, + GlobalSettings, + LoggingSQLAndTimeSettings +} object ScalikeJDBCConfiguration { - val connectionPoolSettings: ConnectionPoolSettings = ConnectionPoolSettings( - initialSize = 5, - maxSize = 20, - connectionTimeoutMillis = 100000L - ) + val connectionPoolSettings: ConnectionPoolSettings = + ConnectionPoolSettings(initialSize = 5, maxSize = 20, connectionTimeoutMillis = 100000L) val loggingSettings: LoggingSQLAndTimeSettings = LoggingSQLAndTimeSettings( enabled = true, diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/BuildMainMenuOpener.scala b/src/main/scala/com/github/unchama/seichiassist/listener/BuildMainMenuOpener.scala index 8a992fc3e4..3cd8534404 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/BuildMainMenuOpener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/BuildMainMenuOpener.scala @@ -11,8 +11,10 @@ import org.bukkit.event.player.PlayerInteractEvent import org.bukkit.event.{EventHandler, Listener} import org.bukkit.inventory.EquipmentSlot -class BuildMainMenuOpener(implicit effectEnvironment: EffectEnvironment, - ioCanOpenBuildMainMenu: IO CanOpen BuildMainMenu.type) extends Listener { +class BuildMainMenuOpener( + implicit effectEnvironment: EffectEnvironment, + ioCanOpenBuildMainMenu: IO CanOpen BuildMainMenu.type +) extends Listener { import com.github.unchama.targetedeffect._ @@ -22,7 +24,7 @@ class BuildMainMenuOpener(implicit effectEnvironment: EffectEnvironment, event.getAction match { case Action.LEFT_CLICK_AIR | Action.LEFT_CLICK_BLOCK => - case _ => return + case _ => return } { diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/EntityListener.scala b/src/main/scala/com/github/unchama/seichiassist/listener/EntityListener.scala index b1dd94eca3..77a8019327 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/EntityListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/EntityListener.scala @@ -3,9 +3,9 @@ package com.github.unchama.seichiassist.listener import cats.effect.{ConcurrentEffect, IO, SyncIO} import com.github.unchama.generic.effect.unsafe.EffectEnvironment import com.github.unchama.minecraft.actions.OnMinecraftServerThread +import com.github.unchama.seichiassist.ManagedWorld._ import com.github.unchama.seichiassist.MaterialSets.{BlockBreakableBySkill, BreakTool} import com.github.unchama.seichiassist._ -import com.github.unchama.seichiassist.ManagedWorld._ import com.github.unchama.seichiassist.seichiskill.{BlockSearching, BreakArea} import com.github.unchama.seichiassist.subsystems.discordnotification.DiscordNotificationAPI import com.github.unchama.seichiassist.subsystems.mana.ManaApi @@ -18,65 +18,84 @@ import org.bukkit.entity.{Player, Projectile} import org.bukkit.event.entity._ import org.bukkit.event.{EventHandler, Listener} -class EntityListener(implicit effectEnvironment: EffectEnvironment, - ioOnMainThread: OnMinecraftServerThread[IO], - manaApi: ManaApi[IO, SyncIO, Player], - globalNotification: DiscordNotificationAPI[IO]) extends Listener { +class EntityListener( + implicit effectEnvironment: EffectEnvironment, + ioOnMainThread: OnMinecraftServerThread[IO], + manaApi: ManaApi[IO, SyncIO, Player], + globalNotification: DiscordNotificationAPI[IO] +) extends Listener { private val playermap = SeichiAssist.playermap - @EventHandler def onPlayerActiveSkillEvent(event: ProjectileHitEvent): Unit = { //矢を取得する + @EventHandler def onPlayerActiveSkillEvent(event: ProjectileHitEvent): Unit = { // 矢を取得する val projectile = event.getEntity - if (!SeichiAssist.instance.arrowSkillProjectileScope.isTracked(projectile).unsafeRunSync()) return + if (!SeichiAssist.instance.arrowSkillProjectileScope.isTracked(projectile).unsafeRunSync()) + return SeichiAssist.instance.arrowSkillProjectileScope.getReleaseAction(projectile).unsafeRunSync() val player = projectile.getShooter match { case p: Player => p - case _ => return + case _ => return } - //もしサバイバルでなければ処理を終了 - //もしフライ中なら終了 + // もしサバイバルでなければ処理を終了 + // もしフライ中なら終了 if ((player.getGameMode ne GameMode.SURVIVAL) || player.isFlying) return - //壊されるブロックを取得 + // 壊されるブロックを取得 val block = - MaterialSets.refineBlock( - player.getWorld.getBlockAt(projectile.getLocation.add(projectile.getVelocity.normalize)), - MaterialSets.materials - ).getOrElse(return) + MaterialSets + .refineBlock( + player + .getWorld + .getBlockAt(projectile.getLocation.add(projectile.getVelocity.normalize)), + MaterialSets.materials + ) + .getOrElse( + return + ) - //整地ワールドでは重力値によるキャンセル判定を行う(スキル判定より先に判定させること) + // 整地ワールドでは重力値によるキャンセル判定を行う(スキル判定より先に判定させること) if (BreakUtil.getGravity(player, block, isAssault = false) > 3) { - player.playSound(player.getLocation, Sound.BLOCK_ANVIL_FALL, 0.0F, -1.0F) + player.playSound(player.getLocation, Sound.BLOCK_ANVIL_FALL, 0.0f, -1.0f) player.sendMessage(ChatColor.RED + "整地ワールドでは必ず上から掘ってください。") return } - //スキル発動条件がそろってなければ終了 + // スキル発動条件がそろってなければ終了 if (!player.getWorld.isSeichiSkillAllowed) return - //破壊不可能な場合は処理を終了 + // 破壊不可能な場合は処理を終了 if (!BreakUtil.canBreakWithSkill(player, block)) return - //実際に使用するツール - val tool = MaterialSets.refineItemStack( - player.getInventory.getItemInMainHand, - MaterialSets.breakToolMaterials - ).getOrElse(return) + // 実際に使用するツール + val tool = MaterialSets + .refineItemStack(player.getInventory.getItemInMainHand, MaterialSets.breakToolMaterials) + .getOrElse( + return + ) - //耐久値がマイナスかつ耐久無限ツールでない時処理を終了 - if (tool.getDurability > tool.getType.getMaxDurability && !tool.getItemMeta.isUnbreakable) return + // 耐久値がマイナスかつ耐久無限ツールでない時処理を終了 + if (tool.getDurability > tool.getType.getMaxDurability && !tool.getItemMeta.isUnbreakable) + return runArrowSkillOfHitBlock(player, block, tool) } - private def runArrowSkillOfHitBlock(player: Player, hitBlock: BlockBreakableBySkill, tool: BreakTool): Unit = { + private def runArrowSkillOfHitBlock( + player: Player, + hitBlock: BlockBreakableBySkill, + tool: BreakTool + ): Unit = { val playerData = playermap(player.getUniqueId) val skillState = playerData.skillState.get.unsafeRunSync() - val selectedSkill = skillState.activeSkill.getOrElse(return) + val selectedSkill = skillState + .activeSkill + .getOrElse( + return + ) val activeSkillArea = BreakArea(selectedSkill, skillState.usageMode) val breakArea = activeSkillArea.makeBreakArea(player).unsafeRunSync().head @@ -87,7 +106,8 @@ class EntityListener(implicit effectEnvironment: EffectEnvironment, breakLength.x * breakLength.y * breakLength.z } - val isMultiTypeBreakingSkillEnabled = BreakUtil.multiplyBreakValidlyEnabled(player).unsafeRunSync() + val isMultiTypeBreakingSkillEnabled = + BreakUtil.performsMultipleIDBlockBreakWhenUsingSkills(player).unsafeRunSync() import com.github.unchama.seichiassist.data.syntax._ val BlockSearching.Result(breakBlocks, _, lavaBlocks) = @@ -95,18 +115,20 @@ class EntityListener(implicit effectEnvironment: EffectEnvironment, .searchForBlocksBreakableWithSkill(player, breakArea.gridPoints(), hitBlock) .unsafeRunSync() .filterSolids(targetBlock => - isMultiTypeBreakingSkillEnabled || BlockSearching.multiTypeBreakingFilterPredicate(hitBlock)(targetBlock) + isMultiTypeBreakingSkillEnabled || BlockSearching + .multiTypeBreakingFilterPredicate(hitBlock)(targetBlock) ) - //重力値計算 + // 重力値計算 val gravity = BreakUtil.getGravity(player, hitBlock, isAssault = false) - //減る経験値計算 - //実際に破壊するブロック数 * 全てのブロックを破壊したときの消費経験値 ÷ すべての破壊するブロック数 * 重力 - val manaConsumption = breakBlocks.size.toDouble * (gravity + 1) * selectedSkill.manaCost / breakAreaVolume + // 減る経験値計算 + // 実際に破壊するブロック数 * 全てのブロックを破壊したときの消費経験値 ÷ すべての破壊するブロック数 * 重力 + val manaConsumption = + breakBlocks.size.toDouble * (gravity + 1) * selectedSkill.manaCost / breakAreaVolume - //セットする耐久値の計算 - //1マス溶岩を破壊するのにはブロック10個分の耐久が必要 + // セットする耐久値の計算 + // 1マス溶岩を破壊するのにはブロック10個分の耐久が必要 val nextDurability = { val durabilityEnchantment = tool.getEnchantmentLevel(Enchantment.DURABILITY) @@ -115,7 +137,7 @@ class EntityListener(implicit effectEnvironment: EffectEnvironment, BreakUtil.calcDurability(durabilityEnchantment, 10 * lavaBlocks.size) }.toShort - //重力値の判定 + // 重力値の判定 if (gravity > 15) { player.sendMessage(ChatColor.RED + "スキルを使用するには上から掘ってください。") return @@ -127,23 +149,34 @@ class EntityListener(implicit effectEnvironment: EffectEnvironment, } // マナを減らす - if (manaApi.manaAmount(player).tryAcquire(ManaAmount(manaConsumption)).unsafeRunSync().isEmpty) + if ( + manaApi.manaAmount(player).tryAcquire(ManaAmount(manaConsumption)).unsafeRunSync().isEmpty + ) return - //耐久値を減らす + // 耐久値を減らす if (!tool.getItemMeta.isUnbreakable) tool.setDurability(nextDurability) - //以降破壊する処理 - //溶岩を破壊する処理 + // 以降破壊する処理 + // 溶岩を破壊する処理 lavaBlocks.foreach(_.setType(Material.AIR)) - //元ブロックの真ん中の位置 + // 元ブロックの真ん中の位置 val centerOfBlock = hitBlock.getLocation.add(0.5, 0.5, 0.5) effectEnvironment.unsafeRunEffectAsync( "破壊エフェクトを再生する", - playerData.skillEffectState.selection - .runBreakEffect(player, selectedSkill, tool, breakBlocks.toSet, breakArea, centerOfBlock) + playerData + .skillEffectState + .selection + .runBreakEffect( + player, + selectedSkill, + tool, + breakBlocks.toSet, + breakArea, + centerOfBlock + ) ) } @@ -178,15 +211,15 @@ class EntityListener(implicit effectEnvironment: EffectEnvironment, import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.asyncShift implicit val ioCE: ConcurrentEffect[IO] = IO.ioConcurrentEffect /*GiganticBerserk用*/ - //死んだMOBがGiganticBerserkの対象MOBでなければ終了 + // 死んだMOBがGiganticBerserkの対象MOBでなければ終了 val entity = event.getEntity if (!Util.isEnemy(entity.getType)) return val player = entity.getKiller - //MOBを倒したプレイヤーがいなければ終了 + // MOBを倒したプレイヤーがいなければ終了 if (player == null) return - //プレイヤーが整地ワールドに居ない場合終了 + // プレイヤーが整地ワールドに居ない場合終了 if (!player.getWorld.isSeichi) return val GBTR = new GiganticBerserkTask GBTR.PlayerKillEnemy(player) } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/GachaItemListener.scala b/src/main/scala/com/github/unchama/seichiassist/listener/GachaItemListener.scala index f4a293333a..4c72682d8b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/GachaItemListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/GachaItemListener.scala @@ -25,27 +25,27 @@ class GachaItemListener(implicit manaApi: ManaApi[IO, SyncIO, Player]) extends L if (Util.loreIndexOf(lore, "マナ完全回復") > 0) { manaApi.manaAmount(player).restoreCompletely.unsafeRunSync() - player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0F, 1.2F) + player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0f, 1.2f) } if (Util.loreIndexOf(lore, "マナ回復(小)") > 0) { manaApi.manaAmount(player).restoreAbsolute(ManaAmount(300)).unsafeRunSync() - player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0F, 1.2F) + player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0f, 1.2f) } if (Util.loreIndexOf(lore, "マナ回復(中)") > 0) { manaApi.manaAmount(player).restoreAbsolute(ManaAmount(1500)).unsafeRunSync() - player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0F, 1.2F) + player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0f, 1.2f) } if (Util.loreIndexOf(lore, "マナ回復(大)") > 0) { manaApi.manaAmount(player).restoreAbsolute(ManaAmount(10000)).unsafeRunSync() - player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0F, 1.2F) + player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0f, 1.2f) } if (Util.loreIndexOf(lore, "マナ回復(極)") > 0) { manaApi.manaAmount(player).restoreAbsolute(ManaAmount(100000)).unsafeRunSync() - player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0F, 1.2F) + player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0f, 1.2f) } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerBlockBreakListener.scala b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerBlockBreakListener.scala index aba3fac5a9..cb47b3a893 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerBlockBreakListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerBlockBreakListener.scala @@ -12,7 +12,7 @@ import com.github.unchama.seichiassist.seichiskill.{BlockSearching, BreakArea} import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.SeichiExpAmount import com.github.unchama.seichiassist.subsystems.mana.ManaApi import com.github.unchama.seichiassist.subsystems.mana.domain.ManaAmount -import com.github.unchama.seichiassist.util.{BreakUtil, Util} +import com.github.unchama.seichiassist.util.BreakUtil import com.github.unchama.seichiassist.{MaterialSets, SeichiAssist} import com.github.unchama.targetedeffect.player.FocusedSoundEffect import com.github.unchama.util.effect.BukkitResources @@ -29,31 +29,36 @@ import org.bukkit.inventory.ItemStack import scala.collection.mutable.ArrayBuffer import scala.util.control.Breaks -class PlayerBlockBreakListener(implicit effectEnvironment: EffectEnvironment, - ioOnMainThread: OnMinecraftServerThread[IO], - manaApi: ManaApi[IO, SyncIO, Player]) extends Listener { +class PlayerBlockBreakListener( + implicit effectEnvironment: EffectEnvironment, + ioOnMainThread: OnMinecraftServerThread[IO], + manaApi: ManaApi[IO, SyncIO, Player] +) extends Listener { private val plugin = SeichiAssist.instance import cats.implicits._ import plugin.activeSkillAvailability - //アクティブスキルの実行 + // アクティブスキルの実行 @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) def onPlayerActiveSkillEvent(event: BlockBreakEvent): Unit = { val player = event.getPlayer - val block = MaterialSets.refineBlock( - event.getBlock, - MaterialSets.materials - ).getOrElse(return) + val block = MaterialSets + .refineBlock(event.getBlock, MaterialSets.materials) + .getOrElse( + return + ) - //重力値によるキャンセル判定(スキル判定より先に判定させること) + // 重力値によるキャンセル判定(スキル判定より先に判定させること) val gravity = BreakUtil.getGravity(player, block, isAssault = false) - if (!MaterialSets.gravityMaterials.contains(block.getType) && - !MaterialSets.cancelledMaterials.contains(block.getType) && gravity > 15) { + if ( + !MaterialSets.gravityMaterials.contains(block.getType) && + !MaterialSets.cancelledMaterials.contains(block.getType) && gravity > 15 + ) { - player.playSound(player.getLocation, Sound.BLOCK_ANVIL_FALL, 0.0F, -1.0F) + player.playSound(player.getLocation, Sound.BLOCK_ANVIL_FALL, 0.0f, -1.0f) player.sendMessage(s"${RED}整地ワールドでは必ず上から掘ってください。") event.setCancelled(true) return @@ -61,68 +66,72 @@ class PlayerBlockBreakListener(implicit effectEnvironment: EffectEnvironment, if (!player.getWorld.isSeichiSkillAllowed) return - //破壊不可能ブロックの時処理を終了 + // 破壊不可能ブロックの時処理を終了 if (!BreakUtil.canBreak(player, block)) { event.setCancelled(true) return } - //実際に使用するツール - val tool: BreakTool = MaterialSets.refineItemStack( - player.getInventory.getItemInMainHand, - MaterialSets.breakToolMaterials - ).getOrElse(return) + // 実際に使用するツール + val tool: BreakTool = MaterialSets + .refineItemStack(player.getInventory.getItemInMainHand, MaterialSets.breakToolMaterials) + .getOrElse( + return + ) // 耐久値がマイナスかつ耐久無限ツールでない時処理を終了 - if (tool.getDurability > tool.getType.getMaxDurability && !tool.getItemMeta.isUnbreakable) return + if (tool.getDurability > tool.getType.getMaxDurability && !tool.getItemMeta.isUnbreakable) + return // もしサバイバルでなければ、またはフライ中なら終了 if (player.getGameMode != GameMode.SURVIVAL || player.isFlying) return val playerData = SeichiAssist.playermap(player.getUniqueId) val skillState = playerData.skillState.get.unsafeRunSync() - val playerLevel = SeichiAssist.instance - .breakCountSystem.api - .seichiAmountDataRepository(player).read - .unsafeRunSync() - .levelCorrespondingToExp.level if (!player.getWorld.isSeichiSkillAllowed) return - //クールダウンタイム中は処理を終了 + // クールダウンタイム中は処理を終了 if (!activeSkillAvailability(player).get.unsafeRunSync()) { - //SEを再生 + // SEを再生 player.playSound(player.getLocation, Sound.BLOCK_DISPENSER_FAIL, 0.5f, 1) return } // 追加マナ獲得 - manaApi.manaAmount(player).restoreAbsolute(ManaAmount(BreakUtil.calcManaDrop(player))).unsafeRunSync() + manaApi + .manaAmount(player) + .restoreAbsolute(ManaAmount(BreakUtil.calcManaDrop(player))) + .unsafeRunSync() - val selectedSkill = skillState.activeSkill.getOrElse(return) + val selectedSkill = skillState + .activeSkill + .getOrElse( + return + ) if (!selectedSkill.range.isInstanceOf[MultiArea] || skillState.usageMode == Disabled) return event.setCancelled(true) { - //プレイヤーの足のy座標を取得 + // プレイヤーの足のy座標を取得 val playerLocY = player.getLocation.getBlockY - 1 - val centerOfBlock = block.getLocation.add(0.5, 0.5, 0.5) val skillArea = BreakArea(selectedSkill, skillState.usageMode) val breakAreaList = skillArea.makeBreakArea(player).unsafeRunSync() - val isMultiTypeBreakingSkillEnabled = BreakUtil.multiplyBreakValidlyEnabled(player).unsafeRunSync() + val isMultiTypeBreakingSkillEnabled = + BreakUtil.performsMultipleIDBlockBreakWhenUsingSkills(player).unsafeRunSync() val totalBreakRangeVolume = { val breakLength = skillArea.breakLength breakLength.x * breakLength.y * breakLength.z * skillArea.breakNum } - //エフェクト用に壊されるブロック全てのリストデータ + // エフェクト用に壊されるブロック全てのリストデータ val multiBreakList = new ArrayBuffer[Set[BlockBreakableBySkill]] - //壊される溶岩の全てのリストデータ + // 壊される溶岩の全てのリストデータ val multiLavaList = new ArrayBuffer[Set[Block]] // 全ての耐久消費量 var toolDamageToSet = tool.getDurability.toInt @@ -130,7 +139,7 @@ class PlayerBlockBreakListener(implicit effectEnvironment: EffectEnvironment, // 消費が予約されたマナ val reservedMana = new ArrayBuffer[ManaAmount] - //繰り返し回数だけ繰り返す + // 繰り返し回数だけ繰り返す val b = new Breaks b.breakable { breakAreaList.foreach { breakArea => @@ -141,10 +150,13 @@ class PlayerBlockBreakListener(implicit effectEnvironment: EffectEnvironment, .searchForBlocksBreakableWithSkill(player, breakArea.gridPoints(), block) .unsafeRunSync() .filterSolids(targetBlock => - isMultiTypeBreakingSkillEnabled || BlockSearching.multiTypeBreakingFilterPredicate(block)(targetBlock) + isMultiTypeBreakingSkillEnabled || BlockSearching + .multiTypeBreakingFilterPredicate(block)(targetBlock) ) .filterAll(targetBlock => - player.isSneaking || targetBlock.getLocation.getBlockY > playerLocY || targetBlock == block + player.isSneaking || targetBlock + .getLocation + .getBlockY > playerLocY || targetBlock == block ) // このチャンクで消費されるマナ @@ -153,18 +165,24 @@ class PlayerBlockBreakListener(implicit effectEnvironment: EffectEnvironment, } // マナを消費する - manaApi.manaAmount(player).tryAcquire(manaToConsumeOnThisChunk).unsafeRunSync() match { + manaApi + .manaAmount(player) + .tryAcquire(manaToConsumeOnThisChunk) + .unsafeRunSync() match { case Some(value) => reservedMana.addOne(value) - case None => b.break() + case None => b.break() } - //減る耐久値の計算(1マス溶岩を破壊するのにはブロック10個分の耐久が必要) + // 減る耐久値の計算(1マス溶岩を破壊するのにはブロック10個分の耐久が必要) toolDamageToSet += BreakUtil.calcDurability( - tool.getEnchantmentLevel(Enchantment.DURABILITY), breakBlocks.size + 10 * lavaBlocks.size + tool.getEnchantmentLevel(Enchantment.DURABILITY), + breakBlocks.size + 10 * lavaBlocks.size ) - //実際に耐久値を減らせるか判定 - if (tool.getType.getMaxDurability <= toolDamageToSet && !tool.getItemMeta.isUnbreakable) + // 実際に耐久値を減らせるか判定 + if ( + tool.getType.getMaxDurability <= toolDamageToSet && !tool.getItemMeta.isUnbreakable + ) b.break() multiBreakList.addOne(breakBlocks.toSet) @@ -181,40 +199,59 @@ class PlayerBlockBreakListener(implicit effectEnvironment: EffectEnvironment, import cats.implicits._ import com.github.unchama.concurrent.syntax._ import com.github.unchama.generic.ContextCoercion._ - import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.{asyncShift, cachedThreadPool} + import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.{ + asyncShift, + cachedThreadPool + } val effectPrograms = for { ((blocks, lavas), chunkIndex) <- multiBreakList.zip(multiLavaList).zipWithIndex - blockChunk = BukkitResources.vanishingBlockSetResource[IO, BlockBreakableBySkill](blocks) + blockChunk = BukkitResources.vanishingBlockSetResource[IO, BlockBreakableBySkill]( + blocks + ) } yield { - SeichiAssist.instance.lockedBlockChunkScope.useTracked(blockChunk) { blocks => - for { - _ <- IO.sleep((chunkIndex * 4).ticks)(IO.timer(cachedThreadPool)) - _ <- ioOnMainThread.runAction(SyncIO { - lavas.foreach(_.setType(Material.AIR)) - }) - _ <- playerData.skillEffectState.selection.runBreakEffect( - player, selectedSkill, tool, blocks, - breakAreaList(chunkIndex), block.getLocation.add(0.5, 0.5, 0.5) - ) - } yield () - }.start(asyncShift) + SeichiAssist + .instance + .lockedBlockChunkScope + .useTracked(blockChunk) { blocks => + for { + _ <- IO.sleep((chunkIndex * 4).ticks)(IO.timer(cachedThreadPool)) + _ <- ioOnMainThread.runAction(SyncIO { + lavas.foreach(_.setType(Material.AIR)) + }) + _ <- playerData + .skillEffectState + .selection + .runBreakEffect( + player, + selectedSkill, + tool, + blocks, + breakAreaList(chunkIndex), + block.getLocation.add(0.5, 0.5, 0.5) + ) + } yield () + } + .start(asyncShift) } - //壊したブロック数に応じてクールダウンを発生させる + // 壊したブロック数に応じてクールダウンを発生させる val availabilityFlagManipulation = { val brokenBlockNum = multiBreakList.map(_.size).sum val coolDownTicks = - (selectedSkill.maxCoolDownTicks.getOrElse(0).toDouble * brokenBlockNum / totalBreakRangeVolume) - .ceil - .toInt + (selectedSkill + .maxCoolDownTicks + .getOrElse(0) + .toDouble * brokenBlockNum / totalBreakRangeVolume).ceil.toInt val reference = SeichiAssist.instance.activeSkillAvailability(player) if (coolDownTicks != 0) { for { _ <- reference.set(false).coerceTo[IO] - _ <- IO.timer(PluginExecutionContexts.sleepAndRoutineContext).sleep(coolDownTicks.ticks) + _ <- IO + .timer(PluginExecutionContexts.sleepAndRoutineContext) + .sleep(coolDownTicks.ticks) _ <- reference.set(true).coerceTo[IO] _ <- FocusedSoundEffect(Sound.ENTITY_ARROW_HIT_PLAYER, 0.5f, 0.1f).run(player) } yield () @@ -244,24 +281,25 @@ class PlayerBlockBreakListener(implicit effectEnvironment: EffectEnvironment, def onPlayerBreakBlockFinally(event: BlockBreakEvent): Unit = { val player = event.getPlayer val block = event.getBlock - val amount = - SeichiExpAmount.ofNonNegative { - BreakUtil.blockCountWeight(event.getPlayer.getWorld) * BreakUtil.totalBreakCount(Seq(block.getType)) - } + import PluginExecutionContexts.timer + val amount = SeichiExpAmount.ofNonNegative { + BreakUtil + .blockCountWeight[IO](event.getPlayer.getWorld) + .map(multiplier => BreakUtil.totalBreakCount(Seq(block.getType)) * multiplier) + .unsafeRunSync() + } effectEnvironment.unsafeRunEffectAsync( "通常破壊されたブロックを整地量に計上する", - SeichiAssist.instance - .breakCountSystem.api - .incrementSeichiExp.of(player, amount) - .toIO + SeichiAssist.instance.breakCountSystem.api.incrementSeichiExp.of(player, amount).toIO ) } /** * y5ハーフブロック破壊抑制 * - * @param event BlockBreakEvent + * @param event + * BlockBreakEvent */ @EventHandler(priority = EventPriority.LOWEST) @SuppressWarnings(Array("deprecation")) @@ -270,7 +308,7 @@ class PlayerBlockBreakListener(implicit effectEnvironment: EffectEnvironment, val b = event.getBlock val world = p.getWorld val data = SeichiAssist.playermap.apply(p.getUniqueId) - //そもそも自分の保護じゃなきゃ処理かけない + // そもそも自分の保護じゃなきゃ処理かけない if (!ExternalPlugins.getWorldGuard.canBuild(p, b.getLocation)) return if ((b.getType eq Material.DOUBLE_STEP) && b.getData == 0) { b.setType(Material.STEP) diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerClickListener.scala b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerClickListener.scala index fb26df2a40..818684ccb2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerClickListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerClickListener.scala @@ -15,7 +15,7 @@ import com.github.unchama.seichiassist.seichiskill.assault.AssaultRoutine import com.github.unchama.seichiassist.subsystems.mana.ManaApi import com.github.unchama.seichiassist.task.CoolDownTask import com.github.unchama.seichiassist.util.{BreakUtil, Util} -import com.github.unchama.seichiassist.{SeichiAssist, _} +import com.github.unchama.seichiassist._ import com.github.unchama.targetedeffect.player.FocusedSoundEffect import com.github.unchama.util.bukkit.ItemStackUtil import com.github.unchama.util.external.ExternalPlugins @@ -32,10 +32,12 @@ import org.bukkit.{GameMode, Material, Sound} import scala.collection.mutable -class PlayerClickListener(implicit effectEnvironment: EffectEnvironment, - manaApi: ManaApi[IO, SyncIO, Player], - ioCanOpenStickMenu: IO CanOpen FirstPage.type, - ioOnMainThread: OnMinecraftServerThread[IO]) extends Listener { +class PlayerClickListener( + implicit effectEnvironment: EffectEnvironment, + manaApi: ManaApi[IO, SyncIO, Player], + ioCanOpenStickMenu: IO CanOpen FirstPage.type, + ioOnMainThread: OnMinecraftServerThread[IO] +) extends Listener { import com.github.unchama.generic.ContextCoercion._ import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.{asyncShift, timer} @@ -51,7 +53,7 @@ class PlayerClickListener(implicit effectEnvironment: EffectEnvironment, private val playerMap = SeichiAssist.playermap private val gachaDataList = SeichiAssist.gachadatalist - //アクティブスキル処理 + // アクティブスキル処理 @EventHandler def onPlayerActiveSkillEvent(event: PlayerInteractEvent): Unit = { val player = event.getPlayer @@ -77,9 +79,9 @@ class PlayerClickListener(implicit effectEnvironment: EffectEnvironment, case Action.PHYSICAL => return } - //クールダウンタイム中は処理を終了 + // クールダウンタイム中は処理を終了 if (!activeSkillAvailability(player).get.unsafeRunSync()) { - //SEを再生 + // SEを再生 player.playSound(player.getLocation, Sound.BLOCK_DISPENSER_FAIL, 0.5f, 1f) return } @@ -90,7 +92,7 @@ class PlayerClickListener(implicit effectEnvironment: EffectEnvironment, import cats.implicits._ import com.github.unchama.concurrent.syntax._ - //クールダウン処理 + // クールダウン処理 val coolDownTicks = coolDownOption.getOrElse(0) val soundEffectAfterCoolDown = if (coolDownTicks > 5) @@ -107,46 +109,49 @@ class PlayerClickListener(implicit effectEnvironment: EffectEnvironment, val arrowEffect = playerData.skillEffectState.selection.arrowEffect effectEnvironment.unsafeRunEffectAsync("スキルのクールダウンの状態を戻す", controlSkillAvailability) - effectEnvironment.unsafeRunEffectAsync("ArrowEffectを非同期で実行する", arrowEffect.run(player)) + effectEnvironment.unsafeRunEffectAsync( + "ArrowEffectを非同期で実行する", + arrowEffect.run(player) + ) case _ => } } } - //プレイヤーが右クリックした時に実行(ガチャを引く部分の処理) + // プレイヤーが右クリックした時に実行(ガチャを引く部分の処理) @EventHandler def onPlayerRightClickGachaEvent(event: PlayerInteractEvent): Unit = { val player = event.getPlayer val playerData = playerMap(player.getUniqueId) - //もしサバイバルでなければ処理を終了 + // もしサバイバルでなければ処理を終了 if (player.getGameMode != GameMode.SURVIVAL) return val clickedItemStack = event.getItem.ifNull { return } - //ガチャ用の頭でなければ終了 + // ガチャ用の頭でなければ終了 if (!Util.isGachaTicket(clickedItemStack)) return event.setCancelled(true) - //連打防止クールダウン処理 + // 連打防止クールダウン処理 if (!playerData.gachacooldownflag) return - //連打による負荷防止の為クールダウン処理 + // 連打による負荷防止の為クールダウン処理 new CoolDownTask(player, false, true).runTaskLater(plugin, 4) - //オフハンドから実行された時処理を終了 + // オフハンドから実行された時処理を終了 if (event.getHand == EquipmentSlot.OFF_HAND) return - //ガチャシステムメンテナンス中は処理を終了 + // ガチャシステムメンテナンス中は処理を終了 if (SeichiAssist.gachamente) { player.sendMessage("現在ガチャシステムはメンテナンス中です。\nしばらく経ってからもう一度お試しください") return } - //ガチャデータが設定されていない場合 + // ガチャデータが設定されていない場合 if (gachaDataList.isEmpty) { player.sendMessage("ガチャが設定されていません") return @@ -162,32 +167,39 @@ class PlayerClickListener(implicit effectEnvironment: EffectEnvironment, 参照:https://github.com/GiganticMinecraft/SeichiAssist/issues/770 */ if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) return - if (action == Action.RIGHT_CLICK_BLOCK && (clickedBlock.getType == Material.CHEST || clickedBlock.getType == Material.TRAPPED_CHEST)) return + if ( + action == Action.RIGHT_CLICK_BLOCK && (clickedBlock.getType == Material.CHEST || clickedBlock.getType == Material.TRAPPED_CHEST) + ) return val count = if (player.isSneaking) { val amount = clickedItemStack.getAmount player.sendMessage(s"$AQUA${amount}回ガチャを回しました。") amount - } - else 1 + } else 1 if (!Util.removeItemfromPlayerInventory(player.getInventory, clickedItemStack, count)) { player.sendMessage(RED.toString + "ガチャ券の数が不正です。") return } - //各自当たった個数を記録するための変数 + // 各自当たった個数を記録するための変数 var gachaBigWin = 0 var gachaWin = 0 var gachaGTWin = 0 - val playerLevel = SeichiAssist.instance - .breakCountSystem.api.seichiAmountDataRepository(player) - .read.unsafeRunSync().levelCorrespondingToExp.level + val playerLevel = SeichiAssist + .instance + .breakCountSystem + .api + .seichiAmountDataRepository(player) + .read + .unsafeRunSync() + .levelCorrespondingToExp + .level (1 to count).foreach { _ => - //プレゼント用ガチャデータ作成 + // プレゼント用ガチャデータ作成 val present = GachaPrize.runGacha() val probabilityOfItem = present.probability @@ -198,14 +210,19 @@ class PlayerClickListener(implicit effectEnvironment: EffectEnvironment, else base } - //メッセージ設定 + // メッセージ設定 val additionalMessage = if (!Util.isPlayerInventoryFull(player)) { Util.addItem(player, givenItem) "" } else { - //アイテムがスタックでき、かつ整地Lvがマインスタックの開放レベルに足りているとき... - if (BreakUtil.tryAddItemIntoMineStack(player, present.itemStack) && playerLevel >= SeichiAssist.seichiAssistConfig.getMineStacklevel(1)) { + // アイテムがスタックでき、かつ整地Lvがマインスタックの開放レベルに足りているとき... + if ( + BreakUtil.tryAddItemIntoMineStack( + player, + present.itemStack + ) && playerLevel >= SeichiAssist.seichiAssistConfig.getMineStacklevel(1) + ) { // ...格納した! s"${AQUA}景品をマインスタックに収納しました。" } else { @@ -216,12 +233,14 @@ class PlayerClickListener(implicit effectEnvironment: EffectEnvironment, } } - //確率に応じてメッセージを送信 + // 確率に応じてメッセージを送信 if (probabilityOfItem < 0.001) { Util.sendEverySoundWithoutIgnore(Sound.ENTITY_ENDERDRAGON_DEATH, 0.5f, 2f) { - playerData.settings.getBroadcastMutingSettings + playerData + .settings + .getBroadcastMutingSettings .flatMap(settings => IO { if (!settings.shouldMuteMessages) { @@ -231,15 +250,14 @@ class PlayerClickListener(implicit effectEnvironment: EffectEnvironment, ) }.unsafeRunSync() - val loreWithoutOwnerName = givenItem.getItemMeta.getLore.asScala.toList - .filterNot { - _ == s"§r§2所有者:${player.getName}" - } + val loreWithoutOwnerName = givenItem.getItemMeta.getLore.asScala.toList.filterNot { + _ == s"§r§2所有者:${player.getName}" + } - val localizedEnchantmentList = givenItem.getItemMeta.getEnchants.asScala.toSeq - .map { case (enchantment, level) => + val localizedEnchantmentList = givenItem.getItemMeta.getEnchants.asScala.toSeq.map { + case (enchantment, level) => s"$GRAY${Util.getEnchantName(enchantment.getName, level)}" - } + } import scala.util.chaining._ val message = @@ -287,19 +305,17 @@ class PlayerClickListener(implicit effectEnvironment: EffectEnvironment, if (gachaBigWin > 0) rewardDetailTexts += s"${GOLD}大当たりが${gachaBigWin}個" if (gachaGTWin > 0) rewardDetailTexts += s"${RED}Gigantic☆大当たりが${gachaGTWin}個" if (count != 1) { - player.sendMessage( - if (rewardDetailTexts.isEmpty) { - s"${WHITE}はずれ!また遊んでね!" - } else { - s"${rewardDetailTexts.mkString(s"$GRAY,")}${GOLD}出ました!" - } - ) + player.sendMessage(if (rewardDetailTexts.isEmpty) { + s"${WHITE}はずれ!また遊んでね!" + } else { + s"${rewardDetailTexts.mkString(s"$GRAY,")}${GOLD}出ました!" + }) } player.playSound(player.getLocation, Sound.ENTITY_ARROW_HIT_PLAYER, 1f, 0.1f) } - //スキル切り替えのイベント + // スキル切り替えのイベント @EventHandler def onPlayerActiveSkillToggleEvent(event: PlayerInteractEvent): Unit = { val player = event.getPlayer @@ -311,9 +327,15 @@ class PlayerClickListener(implicit effectEnvironment: EffectEnvironment, if (currentItem == Material.STICK || currentItem == Material.SKULL_ITEM) return val playerData = playerMap(player.getUniqueId) - val playerLevel = SeichiAssist.instance - .breakCountSystem.api.seichiAmountDataRepository(player) - .read.unsafeRunSync().levelCorrespondingToExp.level + val playerLevel = SeichiAssist + .instance + .breakCountSystem + .api + .seichiAmountDataRepository(player) + .read + .unsafeRunSync() + .levelCorrespondingToExp + .level if (playerLevel < SeichiAssist.seichiAssistConfig.getDualBreaklevel) return if (!Util.seichiSkillsAllowedIn(player.getWorld)) return @@ -322,17 +344,19 @@ class PlayerClickListener(implicit effectEnvironment: EffectEnvironment, if (action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK) { val hasToolInMainHand = MaterialSets.breakToolMaterials.contains(currentItem) - if (action == Action.RIGHT_CLICK_BLOCK && - MaterialSets.cancelledMaterials.contains(event.getClickedBlock.getType)) return + if ( + action == Action.RIGHT_CLICK_BLOCK && + MaterialSets.cancelledMaterials.contains(event.getClickedBlock.getType) + ) return val skillState = playerData.skillState.get.unsafeRunSync() if (equipmentSlot == EquipmentSlot.HAND && hasToolInMainHand) { - //メインハンドで指定ツールを持っていた時の処理 - //スニークしていないかつアサルトタイプが選択されていない時処理を終了 + // メインハンドで指定ツールを持っていた時の処理 + // スニークしていないかつアサルトタイプが選択されていない時処理を終了 if (!player.isSneaking && skillState.assaultSkill.isEmpty) return - //設置をキャンセル + // 設置をキャンセル event.setCancelled(true) skillState.activeSkill match { @@ -347,13 +371,14 @@ class PlayerClickListener(implicit effectEnvironment: EffectEnvironment, } else if (equipmentSlot == EquipmentSlot.OFF_HAND) { skillState.assaultSkill match { case Some(skill) => - //オフハンドで指定ツールを持っていた時の処理 + // オフハンドで指定ツールを持っていた時の処理 event.setCancelled(true) import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.sleepAndRoutineContext - SeichiAssist.instance + SeichiAssist + .instance .assaultSkillRoutines(player) .flipState(TryableFiber.start(AssaultRoutine.tryStart(player, skill))) .as(()) @@ -374,16 +399,20 @@ class PlayerClickListener(implicit effectEnvironment: EffectEnvironment, if (!(action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK)) return - if (inventory.getItemInMainHand.getType == Material.STICK && inventory.getItemInOffHand != null) + if ( + inventory + .getItemInMainHand + .getType == Material.STICK && inventory.getItemInOffHand != null + ) event.setCancelled(true) } - //棒メニューを開くイベント + // 棒メニューを開くイベント @EventHandler def onPlayerMenuEvent(event: PlayerInteractEvent): Unit = { - //プレイヤーを取得 + // プレイヤーを取得 val player = event.getPlayer - //プレイヤーが起こしたアクションを取得 + // プレイヤーが起こしたアクションを取得 val action = event.getAction if (player.getInventory.getItemInMainHand.getType != Material.STICK) return @@ -402,51 +431,56 @@ class PlayerClickListener(implicit effectEnvironment: EffectEnvironment, ) } - //頭の即時回収 + // 頭の即時回収 @EventHandler def onPlayerRightClickMineHeadEvent(e: PlayerInteractEvent): Unit = { val p = e.getPlayer val useItem = p.getInventory.getItemInMainHand - //専用アイテムを持っていない場合無視 + // 専用アイテムを持っていない場合無視 if (!Util.isMineHeadItem(useItem)) { return } val action = e.getAction - //ブロックの左クリックじゃない場合無視 + // ブロックの左クリックじゃない場合無視 if (action != Action.LEFT_CLICK_BLOCK) { return } val targetBlock = e.getClickedBlock - //頭じゃない場合無視 + // 頭じゃない場合無視 if (targetBlock.getType != Material.SKULL) { return } - //壊せない場合無視 + // 壊せない場合無視 if (!BreakUtil.canBreak(p, targetBlock)) { return } - //インベントリに空がない場合無視 + // インベントリに空がない場合無視 if (Util.isPlayerInventoryFull(p)) { p.sendMessage(RED.toString + "インベントリがいっぱいです") return } - //頭を付与 + // 頭を付与 Util.getSkullDataFromBlock(targetBlock) match { case Some(itemStack) => p.getInventory.addItem(itemStack) - case None => + case None => } if (!ExternalPlugins.getCoreProtectWrapper.queueBlockRemoval(p, targetBlock)) { - SeichiAssist.instance.getLogger.warning(s"Logging in skull break: Failed Location: ${targetBlock.getLocation}, Player:$p") + SeichiAssist + .instance + .getLogger + .warning( + s"Logging in skull break: Failed Location: ${targetBlock.getLocation}, Player:$p" + ) } - //ブロックを空気で置き換える + // ブロックを空気で置き換える targetBlock.setType(Material.AIR) - //音を鳴らしておく + // 音を鳴らしておく p.playSound(p.getLocation, Sound.ENTITY_ITEM_PICKUP, 2.0f, 1.0f) } @@ -460,7 +494,9 @@ class PlayerClickListener(implicit effectEnvironment: EffectEnvironment, if (!isRegionOwner(event.getPlayer, clickedBlock.getLocation)) return if (event.getHand == EquipmentSlot.OFF_HAND) return - if (event.getAction != Action.RIGHT_CLICK_BLOCK || clickedBlock.getType != Material.IRON_TRAPDOOR) return + if ( + event.getAction != Action.RIGHT_CLICK_BLOCK || clickedBlock.getType != Material.IRON_TRAPDOOR + ) return // TODO: 手に何も持っていない場合は機能するが、ブロックなどを持っている場合は機能しない(手に持っているものが設置できるもののときや弓矢は反応する) val blockState = clickedBlock.getState diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerInventoryListener.scala b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerInventoryListener.scala index 37f8c8cb33..a08f01f707 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerInventoryListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerInventoryListener.scala @@ -26,14 +26,14 @@ import org.bukkit.inventory.{ItemFlag, ItemStack} import org.bukkit.{Bukkit, Material, Sound} import scala.collection.mutable.ArrayBuffer -import scala.jdk.CollectionConverters._ -import scala.util.chaining._ -class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, - manaApi: ManaApi[IO, SyncIO, Player], - ioCanOpenFirstPage: IO CanOpen FirstPage.type, - ioCanOpenAchievementMenu: IO CanOpen AchievementMenu.type, - ioOnMainThread: OnMinecraftServerThread[IO]) extends Listener { +class PlayerInventoryListener( + implicit effectEnvironment: EffectEnvironment, + manaApi: ManaApi[IO, SyncIO, Player], + ioCanOpenFirstPage: IO CanOpen FirstPage.type, + ioCanOpenAchievementMenu: IO CanOpen AchievementMenu.type, + ioOnMainThread: OnMinecraftServerThread[IO] +) extends Listener { import com.github.unchama.targetedeffect._ import com.github.unchama.util.InventoryUtil._ @@ -43,7 +43,7 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, private val gachaDataList = SeichiAssist.gachadatalist private val databaseGateway = SeichiAssist.databaseGateway - //ガチャ交換システム + // ガチャ交換システム @EventHandler def onGachaTradeEvent(event: InventoryCloseEvent): Unit = { val player = event.getPlayer.asInstanceOf[Player] @@ -51,73 +51,80 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, val playerdata = playerMap(uuid).ifNull { return } - //エラー分岐 + // エラー分岐 val name = playerdata.lowercaseName val inventory = event.getInventory - //インベントリサイズが36でない時終了 + // インベントリサイズが36でない時終了 if (inventory.row != 4) { return } if (inventory.getTitle == s"${LIGHT_PURPLE.toString}${BOLD}交換したい景品を入れてください") { var givegacha = 0 /* - * step1 for文でinventory内に対象商品がないか検索 - * あったらdurabilityに応じてgivegachaを増やし、非対象商品は返却boxへ - */ - //ガチャ景品交換インベントリの中身を取得 + * step1 for文でinventory内に対象商品がないか検索 + * あったらdurabilityに応じてgivegachaを増やし、非対象商品は返却boxへ + */ + // ガチャ景品交換インベントリの中身を取得 val items = inventory.getContents - //ドロップ用アイテムリスト(返却box)作成 + // ドロップ用アイテムリスト(返却box)作成 val dropitem = ArrayBuffer[ItemStack]() - //カウント用 + // カウント用 var big = 0 var reg = 0 - //for文で1個ずつ対象アイテムか見る - //ガチャ景品交換インベントリを一個ずつ見ていくfor文 + // for文で1個ずつ対象アイテムか見る + // ガチャ景品交換インベントリを一個ずつ見ていくfor文 items.foreach { case null => - case item if - // TODO: gachamenteフラグがtrueのときはすべてのアイテムが返却されるんだからearly-returnするべき - SeichiAssist.gachamente || - !item.hasItemMeta || - !item.getItemMeta.hasLore || - item.getType == Material.SKULL_ITEM => + case item + if + // TODO: gachamenteフラグがtrueのときはすべてのアイテムが返却されるんだからearly-returnするべき + SeichiAssist.gachamente || + !item.hasItemMeta || + !item.getItemMeta.hasLore || + item.getType == Material.SKULL_ITEM => dropitem += item case item => - //ガチャ景品リスト上を線形探索する + // ガチャ景品リスト上を線形探索する val matchingGachaData = gachaDataList.find { gachadata => - //ガチャ景品リストにある商品の場合(Lore=説明文と表示名で判別),無い場合はアイテム返却 - if (gachadata.itemStack.hasItemMeta && gachadata.itemStack.getItemMeta.hasLore && gachadata.compare(item, name)) { - if (SeichiAssist.DEBUG) player.sendMessage(gachadata.itemStack.getItemMeta.getDisplayName) + // ガチャ景品リストにある商品の場合(Lore=説明文と表示名で判別),無い場合はアイテム返却 + if ( + gachadata.itemStack.hasItemMeta && gachadata + .itemStack + .getItemMeta + .hasLore && gachadata.compare(item, name) + ) { + if (SeichiAssist.DEBUG) + player.sendMessage(gachadata.itemStack.getItemMeta.getDisplayName) val amount = item.getAmount if (gachadata.probability < 0.001) { - //ギガンティック大当たりの部分 - //ガチャ券に交換せずそのままアイテムを返す + // ギガンティック大当たりの部分 + // ガチャ券に交換せずそのままアイテムを返す dropitem += item } else if (gachadata.probability < 0.01) { - //大当たりの部分 + // 大当たりの部分 givegacha += 12 * amount big += amount } else if (gachadata.probability < 0.1) { - //当たりの部分 + // 当たりの部分 givegacha += 3 * amount reg += amount } else { - //それ以外アイテム返却(経験値ポーションとかがここにくるはず) + // それ以外アイテム返却(経験値ポーションとかがここにくるはず) dropitem += item } true } else false } matchingGachaData match { - //ガチャ景品リストに対象アイテムが無かった場合 + // ガチャ景品リストに対象アイテムが無かった場合 case None => dropitem += item - case _ => + case _ => } } - //ガチャシステムメンテナンス中は全て返却する + // ガチャシステムメンテナンス中は全て返却する if (SeichiAssist.gachamente) { player.sendMessage(s"${RED}ガチャシステムメンテナンス中の為全てのアイテムを返却します") } else if (big <= 0 && reg <= 0) { @@ -126,8 +133,8 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, player.sendMessage(s"${GREEN}大当たり景品を${big}個、当たり景品を${reg}個認識しました") } /* - * step2 非対象商品をインベントリに戻す - */ + * step2 非対象商品をインベントリに戻す + */ for (m <- dropitem) { if (!Util.isPlayerInventoryFull(player)) { Util.addItem(player, m) @@ -136,8 +143,8 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, } } /* - * step3 ガチャ券をインベントリへ - */ + * step3 ガチャ券をインベントリへ + */ val skull = GachaSkullData.gachaForExchanging var count = 0 while (givegacha > 0) { @@ -151,27 +158,29 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, } if (count > 0) { player.playSound(player.getLocation, Sound.BLOCK_ANVIL_PLACE, 1f, 1f) - player.sendMessage(GREEN.toString + "" + count + "枚の" + GOLD + "ガチャ券" + WHITE + "を受け取りました") + player.sendMessage( + GREEN.toString + "" + count + "枚の" + GOLD + "ガチャ券" + WHITE + "を受け取りました" + ) } } } - //実績メニューの処理 + // 実績メニューの処理 @EventHandler def onPlayerClickTitleMenuEvent(event: InventoryClickEvent): Unit = { OnClickTitleMenu.onPlayerClickTitleMenuEvent(event) } - //鉱石・交換券変換システム + // 鉱石・交換券変換システム @EventHandler def onOreTradeEvent(event: InventoryCloseEvent): Unit = { val player = event.getPlayer.asInstanceOf[Player] - //エラー分岐 + // エラー分岐 val inventory = event.getInventory - //インベントリサイズが36でない時終了 + // インベントリサイズが36でない時終了 if (inventory.row != 4) return if (inventory.getTitle != s"$LIGHT_PURPLE${BOLD}交換したい鉱石を入れてください") return @@ -195,19 +204,17 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, val inventoryContents = inventory.getContents.filter(_ != null) val (itemsToExchange, rejectedItems) = - inventoryContents - .partition { stack => requiredAmountPerTicket.contains(stack.getType) } + inventoryContents.partition { stack => requiredAmountPerTicket.contains(stack.getType) } - val exchangingAmount = itemsToExchange - .groupBy(_.getType) - .toList - .map { case (key, stacks) => key -> stacks.map(_.getAmount).sum } + val exchangingAmount = itemsToExchange.groupBy(_.getType).toList.map { + case (key, stacks) => key -> stacks.map(_.getAmount).sum + } - val ticketAmount = exchangingAmount - .map { case (material, amount) => amount / requiredAmountPerTicket(material) } - .sum + val ticketAmount = exchangingAmount.map { + case (material, amount) => amount / requiredAmountPerTicket(material) + }.sum - //プレイヤー通知 + // プレイヤー通知 if (ticketAmount == 0) { player.sendMessage(s"${YELLOW}鉱石を認識しなかったか数が不足しています。全てのアイテムを返却します") } else { @@ -249,23 +256,25 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, */ val itemStacksToReturn = exchangingAmount - .flatMap { case (exchangedMaterial, exchangedAmount) => - val returningAmount = exchangedAmount % requiredAmountPerTicket(exchangedMaterial) - import scala.util.chaining._ - if (returningAmount != 0) - Some(new ItemStack(exchangedMaterial).tap(_.setAmount(returningAmount))) - else - None - }.++(rejectedItems) - - //返却処理 + .flatMap { + case (exchangedMaterial, exchangedAmount) => + val returningAmount = exchangedAmount % requiredAmountPerTicket(exchangedMaterial) + import scala.util.chaining._ + if (returningAmount != 0) + Some(new ItemStack(exchangedMaterial).tap(_.setAmount(returningAmount))) + else + None + } + .++(rejectedItems) + + // 返却処理 effectEnvironment.unsafeRunAsyncTargetedEffect(player)( Util.grantItemStacksEffect(itemStacksToReturn: _*), "鉱石交換でのアイテム返却を行う" ) } - //ギガンティック→椎名林檎交換システム + // ギガンティック→椎名林檎交換システム @EventHandler def onGachaRingoEvent(event: InventoryCloseEvent): Unit = { val player = event.getPlayer.asInstanceOf[Player] @@ -273,61 +282,66 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, val playerdata = playerMap(uuid).ifNull { return } - //エラー分岐 + // エラー分岐 val name = playerdata.lowercaseName val inventory = event.getInventory - //インベントリサイズが4列でない時終了 + // インベントリサイズが4列でない時終了 if (inventory.row != 4) { return } if (inventory.getTitle == GOLD.toString + "" + BOLD + "椎名林檎と交換したい景品を入れてネ") { var giveringo = 0 /* - * step1 for文でinventory内に対象商品がないか検索 - * あったらdurabilityに応じてgivegachaを増やし、非対象商品は返却boxへ - */ - //ガチャ景品交換インベントリの中身を取得 + * step1 for文でinventory内に対象商品がないか検索 + * あったらdurabilityに応じてgivegachaを増やし、非対象商品は返却boxへ + */ + // ガチャ景品交換インベントリの中身を取得 val item = inventory.getContents - //ドロップ用アイテムリスト(返却box)作成 + // ドロップ用アイテムリスト(返却box)作成 val dropitem = ArrayBuffer[ItemStack]() - //カウント用 + // カウント用 var giga = 0 - //for文で1個ずつ対象アイテムか見る - //ガチャ景品交換インベントリを一個ずつ見ていくfor文 + // for文で1個ずつ対象アイテムか見る + // ガチャ景品交換インベントリを一個ずつ見ていくfor文 item.foreach { case null => - case m if - SeichiAssist.gachamente || - !m.hasItemMeta || - !m.getItemMeta.hasLore || - m.getType == Material.SKULL_ITEM => + case m + if SeichiAssist.gachamente || + !m.hasItemMeta || + !m.getItemMeta.hasLore || + m.getType == Material.SKULL_ITEM => dropitem.addOne(m) case m => - //ガチャ景品リストを一個ずつ見ていくfor文 + // ガチャ景品リストを一個ずつ見ていくfor文 gachaDataList.find { gachadata => - if (gachadata.itemStack.hasItemMeta && gachadata.itemStack.getItemMeta.hasLore && gachadata.compare(m, name)) { + if ( + gachadata.itemStack.hasItemMeta && gachadata + .itemStack + .getItemMeta + .hasLore && gachadata.compare(m, name) + ) { if (SeichiAssist.DEBUG) { player.sendMessage(gachadata.itemStack.getItemMeta.getDisplayName) } val amount = m.getAmount if (gachadata.probability < 0.001) { - //ギガンティック大当たりの部分 - //1個につき椎名林檎n個と交換する + // ギガンティック大当たりの部分 + // 1個につき椎名林檎n個と交換する giveringo += SeichiAssist.seichiAssistConfig.rateGiganticToRingo * amount giga += 1 } else { - //それ以外アイテム返却 + // それ以外アイテム返却 dropitem.addOne(m) } true } else false } match { case None => dropitem.addOne(m) - case _ => + case _ => } } - //ガチャシステムメンテナンス中は全て返却する + // ガチャシステムメンテナンス中は全て返却する if (SeichiAssist.gachamente) { player.sendMessage(RED.toString + "ガチャシステムメンテナンス中の為全てのアイテムを返却します") } else if (giga <= 0) { @@ -336,8 +350,8 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, player.sendMessage(GREEN.toString + "ギガンティック大当り景品を" + giga + "個認識しました") } /* - * step2 非対象商品をインベントリに戻す - */ + * step2 非対象商品をインベントリに戻す + */ for (m <- dropitem) { if (!Util.isPlayerInventoryFull(player)) { Util.addItem(player, m) @@ -346,8 +360,8 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, } } /* - * step3 椎名林檎をインベントリへ - */ + * step3 椎名林檎をインベントリへ + */ val ringo = StaticGachaPrizeFactory.getMaxRingo(player.getName) var count = 0 while (giveringo > 0) { @@ -361,16 +375,18 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, } if (count > 0) { player.playSound(player.getLocation, Sound.BLOCK_ANVIL_PLACE, 1f, 1f) - player.sendMessage(GREEN.toString + "" + count + "個の" + GOLD + "椎名林檎" + WHITE + "を受け取りました") + player.sendMessage( + GREEN.toString + "" + count + "個の" + GOLD + "椎名林檎" + WHITE + "を受け取りました" + ) } } } - //投票ptメニュー + // 投票ptメニュー @EventHandler def onVotingMenuEvent(event: InventoryClickEvent): Unit = { - //外枠のクリック処理なら終了 + // 外枠のクリック処理なら終了 if (event.getClickedInventory == null) { return } @@ -378,7 +394,7 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, val itemstackcurrent = event.getCurrentItem val view = event.getView val he = view.getPlayer - //インベントリを開けたのがプレイヤーではない時終了 + // インベントリを開けたのがプレイヤーではない時終了 if (he.getType != EntityType.PLAYER) { return } @@ -386,8 +402,8 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, val topinventory = view.getTopInventory.ifNull { return } - //インベントリが存在しない時終了 - //インベントリサイズが4列でない時終了 + // インベントリが存在しない時終了 + // インベントリサイズが4列でない時終了 if (topinventory.row != 4) { return } @@ -395,11 +411,17 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, val uuid = player.getUniqueId val playerdata = playerMap(uuid) - val playerLevel = SeichiAssist.instance - .breakCountSystem.api.seichiAmountDataRepository(player) - .read.unsafeRunSync().levelCorrespondingToExp.level - - //インベントリ名が以下の時処理 + val playerLevel = SeichiAssist + .instance + .breakCountSystem + .api + .seichiAmountDataRepository(player) + .read + .unsafeRunSync() + .levelCorrespondingToExp + .level + + // インベントリ名が以下の時処理 if (topinventory.getTitle == DARK_PURPLE.toString + "" + BOLD + "投票ptメニュー") { event.setCancelled(true) @@ -410,27 +432,27 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, val isSkull = itemstackcurrent.getType == Material.SKULL_ITEM /* - * クリックしたボタンに応じた各処理内容の記述ここから - */ + * クリックしたボタンに応じた各処理内容の記述ここから + */ - //投票pt受取 + // 投票pt受取 if (itemstackcurrent.getType == Material.DIAMOND) { - //nは特典をまだ受け取ってない投票分 + // nは特典をまだ受け取ってない投票分 var n = databaseGateway.playerDataManipulator.compareVotePoint(player, playerdata) - //投票数に変化が無ければ処理終了 + // 投票数に変化が無ければ処理終了 if (n == 0) { return } - //先にp_voteの値を更新しておく + // 先にp_voteの値を更新しておく playerdata.p_givenvote = playerdata.p_givenvote + n var count = 0 while (n > 0) { - //ここに投票1回につきプレゼントする特典の処理を書く + // ここに投票1回につきプレゼントする特典の処理を書く - //ガチャ券プレゼント処理 + // ガチャ券プレゼント処理 val skull = GachaSkullData.gachaForVoting - for {_ <- 0 to 9} { + for { _ <- 0 to 9 } { if (player.getInventory.contains(skull) || !Util.isPlayerInventoryFull(player)) { Util.addItem(player, skull) } else { @@ -438,7 +460,7 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, } } - //ピッケルプレゼント処理(レベル50になるまで) + // ピッケルプレゼント処理(レベル50になるまで) if (playerLevel < 50) { val pickaxe = ItemData.getSuperPickaxe(1) if (Util.isPlayerInventoryFull(player)) { @@ -448,7 +470,7 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, } } - //投票ギフト処理(レベル50から) + // 投票ギフト処理(レベル50から) if (playerLevel >= 50) { val gift = ItemData.getVotingGift(1) if (Util.isPlayerInventoryFull(player)) { @@ -457,7 +479,7 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, Util.addItem(player, gift) } } - //エフェクトポイント加算処理 + // エフェクトポイント加算処理 playerdata.effectPoint += 10 n -= 1 @@ -472,11 +494,20 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, player.openInventory(MenuInventoryData.getVotingMenuData(player)) } else if (itemstackcurrent.getType == Material.BOOK_AND_QUILL) { // 投票リンク表示 - player.sendMessage(RED.toString + "" + UNDERLINE + "https://minecraft.jp/servers/54d3529e4ddda180780041a7/vote") - player.sendMessage(RED.toString + "" + UNDERLINE + "https://monocraft.net/servers/Cf3BffNIRMERDNbAfWQm") + player.sendMessage( + RED.toString + "" + UNDERLINE + "https://minecraft.jp/servers/54d3529e4ddda180780041a7/vote" + ) + player.sendMessage( + RED.toString + "" + UNDERLINE + "https://monocraft.net/servers/Cf3BffNIRMERDNbAfWQm" + ) player.playSound(player.getLocation, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) player.closeInventory() - } else if (isSkull && itemstackcurrent.getItemMeta.asInstanceOf[SkullMeta].getOwner == "MHF_ArrowLeft") { + } else if ( + isSkull && itemstackcurrent + .getItemMeta + .asInstanceOf[SkullMeta] + .getOwner == "MHF_ArrowLeft" + ) { effectEnvironment.unsafeRunAsyncTargetedEffect(player)( SequentialEffect( @@ -502,21 +533,21 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, } else if (itemstackcurrent.getType == Material.GHAST_TEAR) { player.closeInventory() - //プレイヤーレベルが10に達していないとき + // プレイヤーレベルが10に達していないとき if (playerLevel < 10) { player.sendMessage(GOLD.toString + "プレイヤーレベルが足りません") player.playSound(player.getLocation, Sound.BLOCK_GLASS_PLACE, 1f, 0.1f) return } - //既に妖精召喚している場合終了 + // 既に妖精召喚している場合終了 if (playerdata.usingVotingFairy) { player.sendMessage(GOLD.toString + "既に妖精を召喚しています") player.playSound(player.getLocation, Sound.BLOCK_GLASS_PLACE, 1f, 0.1f) return } - //投票ptが足りない場合終了 + // 投票ptが足りない場合終了 if (playerdata.effectPoint < playerdata.toggleVotingFairy * 2) { player.sendMessage(GOLD.toString + "投票ptが足りません") player.playSound(player.getLocation, Sound.BLOCK_GLASS_PLACE, 1f, 0.1f) @@ -526,20 +557,24 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, VotingFairyListener.summon(player) player.closeInventory() } else if (itemstackcurrent.getType == Material.COMPASS) { - VotingFairyTask.speak(player, "僕は" + Util.showHour(playerdata.votingFairyEndTime) + "には帰るよー。", playerdata.toggleVFSound) + VotingFairyTask.speak( + player, + "僕は" + Util.showHour(playerdata.votingFairyEndTime) + "には帰るよー。", + playerdata.toggleVFSound + ) player.closeInventory() - } //妖精召喚 - //妖精音トグル - //妖精リンゴトグル - //妖精時間トグル - //棒メニューに戻る + } // 妖精召喚 + // 妖精音トグル + // 妖精リンゴトグル + // 妖精時間トグル + // 棒メニューに戻る } } @EventHandler def onGiganticBerserkMenuEvent(event: InventoryClickEvent): Unit = { - //外枠のクリック処理なら終了 + // 外枠のクリック処理なら終了 if (event.getClickedInventory == null) { return } @@ -547,17 +582,17 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, val itemstackcurrent = event.getCurrentItem val view = event.getView val he = view.getPlayer - //インベントリを開けたのがプレイヤーではない時終了 + // インベントリを開けたのがプレイヤーではない時終了 if (he.getType != EntityType.PLAYER) { return } - //インベントリが存在しない時終了 + // インベントリが存在しない時終了 val topinventory = view.getTopInventory.ifNull { return } - //インベントリが6列でない時終了 + // インベントリが6列でない時終了 if (topinventory.row != 6) { return } @@ -579,5 +614,3 @@ class PlayerInventoryListener(implicit effectEnvironment: EffectEnvironment, } } - - diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerJoinListener.scala b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerJoinListener.scala index ff1d1d202e..721dd2b84d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerJoinListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerJoinListener.scala @@ -1,20 +1,27 @@ package com.github.unchama.seichiassist.listener import cats.effect.IO +import com.github.unchama.seichiassist.ManagedWorld._ +import com.github.unchama.seichiassist.SeichiAssist import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.onMainThread import com.github.unchama.seichiassist.data.player.PlayerData import com.github.unchama.seichiassist.seichiskill.SeichiSkillUsageMode.Disabled import com.github.unchama.seichiassist.subsystems.mebius.bukkit.codec.BukkitMebiusItemStackCodec -import com.github.unchama.seichiassist.subsystems.mebius.domain.property.{MebiusProperty, NormalMebius} +import com.github.unchama.seichiassist.subsystems.mebius.domain.property.{ + MebiusProperty, + NormalMebius +} import com.github.unchama.seichiassist.util.Util -import com.github.unchama.seichiassist.SeichiAssist -import com.github.unchama.seichiassist.ManagedWorld._ import com.github.unchama.targetedeffect.player.FocusedSoundEffect import net.coreprotect.model.Config import org.bukkit.ChatColor._ import org.bukkit.enchantments.Enchantment import org.bukkit.entity.Player -import org.bukkit.event.player.{AsyncPlayerPreLoginEvent, PlayerChangedWorldEvent, PlayerJoinEvent} +import org.bukkit.event.player.{ + AsyncPlayerPreLoginEvent, + PlayerChangedWorldEvent, + PlayerJoinEvent +} import org.bukkit.event.{EventHandler, Listener} import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.BookMeta @@ -83,14 +90,18 @@ class PlayerJoinListener extends Listener { } } - //join時とonenable時、プレイヤーデータを最新の状態に更新 + // join時とonenable時、プレイヤーデータを最新の状態に更新 playerMap(player.getUniqueId).updateOnJoin() // 初見さんへの処理 if (!player.hasPlayedBefore) { - //初見さんであることを全体告知 - Util.sendMessageToEveryoneIgnoringPreference(s"$LIGHT_PURPLE$BOLD${player.getName}さんはこのサーバーに初めてログインしました!") - Util.sendMessageToEveryoneIgnoringPreference(s"${WHITE}webサイトはもう読みましたか?→$YELLOW${UNDERLINE}https://www.seichi.network/gigantic") + // 初見さんであることを全体告知 + Util.sendMessageToEveryoneIgnoringPreference( + s"$LIGHT_PURPLE$BOLD${player.getName}さんはこのサーバーに初めてログインしました!" + ) + Util.sendMessageToEveryoneIgnoringPreference( + s"${WHITE}webサイトはもう読みましたか?→$YELLOW${UNDERLINE}https://www.seichi.network/gigantic" + ) Util.sendEverySound(Sound.ENTITY_PLAYER_LEVELUP, 1f, 1f) // ルール熟読をタイトル・メッセージ で迫る// @@ -100,7 +111,7 @@ class PlayerJoinListener extends Listener { player.sendTitle(s"${YELLOW}ルールは確認されましたか?", s"${LIGHT_PURPLE}公式サイトで確認できます。", 10, 10, 10) player.sendMessage(s"${YELLOW}ルール→ $YELLOW${UNDERLINE}https://www.seichi.network/rule") - //初見プレイヤーに木の棒、エリトラ、ピッケルを配布 + // 初見プレイヤーに木の棒、エリトラ、ピッケルを配布 val inv = player.getInventory inv.addItem(new ItemStack(Material.STICK)) inv.addItem(new ItemStack(Material.ELYTRA)) @@ -112,10 +123,12 @@ class PlayerJoinListener extends Listener { inv.addItem(pickaxe) inv.addItem(new ItemStack(Material.DIAMOND_SPADE)) - inv.addItem(new ItemStack(Material.LOG, 64, 0.toShort), + inv.addItem( + new ItemStack(Material.LOG, 64, 0.toShort), new ItemStack(Material.LOG, 64, 0.toShort), new ItemStack(Material.LOG, 64, 2.toShort), - new ItemStack(Material.LOG_2, 64, 1.toShort)) + new ItemStack(Material.LOG_2, 64, 1.toShort) + ) inv.addItem(new ItemStack(Material.BAKED_POTATO, 64)) @@ -146,7 +159,7 @@ class PlayerJoinListener extends Listener { """この他細かなルールや処罰の具体的な内容はHPをご確認ください。 |ルールはサーバー内の情勢に応じて予告なく更新されることがあります。 |その際プレイヤーに個別通知することは致しませんので、ご利用の際にはお手数ですが随時最新のルールをご確認ください。 - |""".stripMargin.linesIterator.mkString("\n"), + |""".stripMargin.linesIterator.mkString("\n") ) contents.foreach(meta.addPage(_)) meta.setTitle("サーバーに初参加された方にお読みいただきたい本(v1)") @@ -155,19 +168,22 @@ class PlayerJoinListener extends Listener { is.setItemMeta(meta) }) - //メビウスおひとつどうぞ - inv.setHelmet(BukkitMebiusItemStackCodec.materialize( - // **getDisplayNameは二つ名も含むのでMCIDにはgetNameが適切** - MebiusProperty.initialProperty(NormalMebius, player.getName, player.getUniqueId.toString), - damageValue = 0.toShort - )) + // メビウスおひとつどうぞ + inv.setHelmet( + BukkitMebiusItemStackCodec.materialize( + // **getDisplayNameは二つ名も含むのでMCIDにはgetNameが適切** + MebiusProperty + .initialProperty(NormalMebius, player.getName, player.getUniqueId.toString), + damageValue = 0.toShort + ) + ) /* 期間限定ダイヤ配布.期間終了したので64→32に変更して恒久継続 */ inv.addItem(new ItemStack(Material.DIAMOND, 32)) player.sendMessage("初期装備を配布しました。Eキーで確認してネ") - //初見さんにメッセージを送信 + // 初見さんにメッセージを送信 player.sendMessage { "整地鯖では整地をするとレベルが上がり、様々な恩恵が受けられます。\n初めての方は整地ワールドで掘ってレベルを上げてみましょう!\n木の棒を右クリックしてメニューを開き右上のビーコンボタンをクリック!" } @@ -177,7 +193,11 @@ class PlayerJoinListener extends Listener { if (SeichiAssist.seichiAssistConfig.getServerNum == 5) player.sendTitle( s"${WHITE}ここは$BLUE${UNDERLINE}上級者向けのサーバー${WHITE}", - s"${WHITE}始めたては他がおすすめ", 10, 70, 20) + s"${WHITE}始めたては他がおすすめ", + 10, + 70, + 20 + ) } // プレイヤーがワールドを移動したとき @@ -200,12 +220,17 @@ class PlayerJoinListener extends Listener { // アサルトスキルを切る val skillState = pd.skillState.get.unsafeRunSync() if (skillState.usageMode != Disabled) { - SeichiAssist.instance.assaultSkillRoutines(p).stopAnyFiber.flatMap(stopped => - if (stopped) - FocusedSoundEffect(Sound.BLOCK_LEVER_CLICK, 1f, 1f).run(p) - else - IO.unit - ).unsafeRunSync() + SeichiAssist + .instance + .assaultSkillRoutines(p) + .stopAnyFiber + .flatMap(stopped => + if (stopped) + FocusedSoundEffect(Sound.BLOCK_LEVER_CLICK, 1f, 1f).run(p) + else + IO.unit + ) + .unsafeRunSync() } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerPickupItemListener.scala b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerPickupItemListener.scala index ec6d31792c..a0595b2c13 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerPickupItemListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerPickupItemListener.scala @@ -18,10 +18,18 @@ class PlayerPickupItemListener extends Listener { if (player.getGameMode != GameMode.SURVIVAL) return - val playerData = playerMap(player.getUniqueId).ifNull(return) - val playerLevel = SeichiAssist.instance - .breakCountSystem.api.seichiAmountDataRepository(player) - .read.unsafeRunSync().levelCorrespondingToExp.level + val playerData = playerMap(player.getUniqueId).ifNull( + return + ) + val playerLevel = SeichiAssist + .instance + .breakCountSystem + .api + .seichiAmountDataRepository(player) + .read + .unsafeRunSync() + .levelCorrespondingToExp + .level if (playerLevel < config.getMineStacklevel(1)) return diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/RegionInventoryListener.scala b/src/main/scala/com/github/unchama/seichiassist/listener/RegionInventoryListener.scala index d9fe0e632e..367f445df7 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/RegionInventoryListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/RegionInventoryListener.scala @@ -1,7 +1,5 @@ package com.github.unchama.seichiassist.listener -import java.util.UUID - import com.github.unchama.seichiassist.data.player.PlayerData import com.github.unchama.seichiassist.data.{GridTemplate, RegionMenuData} import com.github.unchama.seichiassist.util.Util @@ -21,13 +19,14 @@ import org.bukkit.event.inventory.{InventoryClickEvent, InventoryType} import org.bukkit.event.{EventHandler, Listener} import org.bukkit.{Location, Material, Sound} +import java.util.UUID import scala.collection.mutable /** * 保護関連メニューのListenerクラス * - * @author karayuu - * 2017/09/02 + * @author + * karayuu 2017/09/02 */ class RegionInventoryListener extends Listener { val playermap: mutable.HashMap[UUID, PlayerData] = SeichiAssist.playermap @@ -37,15 +36,16 @@ class RegionInventoryListener extends Listener { /** * グリッド式保護メニューInventoryClickListener * - * @param event InventoryClickEvent + * @param event + * InventoryClickEvent */ @EventHandler def onPlayerClickGridMenu(event: InventoryClickEvent): Unit = { - //外枠のクリック処理なら終了 + // 外枠のクリック処理なら終了 if (event.getClickedInventory == null) { return } - //クリックしたところにアイテムがない場合終了 + // クリックしたところにアイテムがない場合終了 if (event.getCurrentItem == null) { return } @@ -53,55 +53,81 @@ class RegionInventoryListener extends Listener { val itemstackcurrent = event.getCurrentItem val view = event.getView val he = view.getPlayer - //インベントリを開けたのがプレイヤーではない時終了 + // インベントリを開けたのがプレイヤーではない時終了 if (he.getType != EntityType.PLAYER) { return } val topinventory = view.getTopInventory.ifNull { return } - //インベントリが存在しない時終了 - //インベントリタイプがディスペンサーでない時終了 + // インベントリが存在しない時終了 + // インベントリタイプがディスペンサーでない時終了 if (topinventory.getType != InventoryType.DISPENSER) { return } - //インベントリ名が以下の時処理 + // インベントリ名が以下の時処理 if (topinventory.getTitle == LIGHT_PURPLE.toString + "グリッド式保護設定メニュー") { event.setCancelled(true) - //プレイヤーインベントリのクリックの場合終了 + // プレイヤーインベントリのクリックの場合終了 if (event.getClickedInventory.getType == InventoryType.PLAYER) { return } /* - * クリックしたボタンに応じた各処理内容の記述ここから - */ + * クリックしたボタンに応じた各処理内容の記述ここから + */ val player = view.getPlayer.asInstanceOf[Player] val uuid = player.getUniqueId val playerData = playermap(uuid) - //チャンク延長 - if (itemstackcurrent.getType == Material.STAINED_GLASS_PANE && itemstackcurrent.getDurability.toInt == 14) { + // チャンク延長 + if ( + itemstackcurrent.getType == Material.STAINED_GLASS_PANE && itemstackcurrent + .getDurability + .toInt == 14 + ) { gridChangeFunction(player, DirectionType.AHEAD, event) - } else if (itemstackcurrent.getType == Material.STAINED_GLASS_PANE && itemstackcurrent.getDurability.toInt == 10) { + } else if ( + itemstackcurrent.getType == Material.STAINED_GLASS_PANE && itemstackcurrent + .getDurability + .toInt == 10 + ) { gridChangeFunction(player, DirectionType.LEFT, event) - } else if (itemstackcurrent.getType == Material.STAINED_GLASS_PANE && itemstackcurrent.getDurability.toInt == 5) { + } else if ( + itemstackcurrent.getType == Material.STAINED_GLASS_PANE && itemstackcurrent + .getDurability + .toInt == 5 + ) { gridChangeFunction(player, DirectionType.RIGHT, event) - } else if (itemstackcurrent.getType == Material.STAINED_GLASS_PANE && itemstackcurrent.getDurability.toInt == 13) { + } else if ( + itemstackcurrent.getType == Material.STAINED_GLASS_PANE && itemstackcurrent + .getDurability + .toInt == 13 + ) { gridChangeFunction(player, DirectionType.BEHIND, event) - } else if (itemstackcurrent.getType == Material.WOOL && itemstackcurrent.getDurability.toInt == 11) { + } else if ( + itemstackcurrent.getType == Material.WOOL && itemstackcurrent.getDurability.toInt == 11 + ) { player.chat("//expand vert") createRegion(player) playerData.regionCount = playerData.regionCount + 1 player.playSound(player.getLocation, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f, 1.0f) player.closeInventory() - } else if (itemstackcurrent.getType == Material.STAINED_GLASS_PANE && itemstackcurrent.getDurability.toInt == 4) { + } else if ( + itemstackcurrent.getType == Material.STAINED_GLASS_PANE && itemstackcurrent + .getDurability + .toInt == 4 + ) { gridResetFunction(player) player.playSound(player.getLocation, Sound.BLOCK_ANVIL_DESTROY, 0.5f, 1.0f) player.openInventory(RegionMenuData.getGridWorldGuardMenu(player)) - } else if (itemstackcurrent.getType == Material.STAINED_GLASS_PANE && itemstackcurrent.getDurability.toInt == 0) { + } else if ( + itemstackcurrent.getType == Material.STAINED_GLASS_PANE && itemstackcurrent + .getDurability + .toInt == 0 + ) { playerData.toggleUnitPerGrid() player.playSound(player.getLocation, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f, 1.0f) player.openInventory(RegionMenuData.getGridWorldGuardMenu(player)) @@ -116,8 +142,11 @@ class RegionInventoryListener extends Listener { val playerData = SeichiAssist.playermap(player.getUniqueId) val selection = We.getSelection(player) - val region = new ProtectedCuboidRegion(player.getName + "_" + playerData.regionCount, - selection.getNativeMinimumPoint.toBlockVector, selection.getNativeMaximumPoint.toBlockVector) + val region = new ProtectedCuboidRegion( + player.getName + "_" + playerData.regionCount, + selection.getNativeMinimumPoint.toBlockVector, + selection.getNativeMaximumPoint.toBlockVector + ) val manager = Wg.getRegionManager(player.getWorld) val task = new RegionAdder(Wg, manager, region) @@ -125,17 +154,20 @@ class RegionInventoryListener extends Listener { task.setOwnersInput(Array(player.getName)) val future = Wg.getExecutorService.submit(task) - AsyncCommandHelper.wrap(future, Wg, player).formatUsing(player.getName + "_" + playerData.regionCount) - .registerWithSupervisor("保護申請中").thenRespondWith("保護申請完了。保護名: '%s'", "保護作成失敗") + AsyncCommandHelper + .wrap(future, Wg, player) + .formatUsing(player.getName + "_" + playerData.regionCount) + .registerWithSupervisor("保護申請中") + .thenRespondWith("保護申請完了。保護名: '%s'", "保護作成失敗") } @EventHandler def onPlayerClickRegionTemplateMenu(event: InventoryClickEvent): Unit = { - //外枠のクリック処理なら終了 + // 外枠のクリック処理なら終了 if (event.getClickedInventory == null) { return } - //クリックしたところにアイテムがない場合終了 + // クリックしたところにアイテムがない場合終了 if (event.getCurrentItem == null) { return } @@ -143,32 +175,32 @@ class RegionInventoryListener extends Listener { val itemstackcurrent = event.getCurrentItem val view = event.getView val he = view.getPlayer - //インベントリを開けたのがプレイヤーではない時終了 + // インベントリを開けたのがプレイヤーではない時終了 if (he.getType != EntityType.PLAYER) { return } val topinventory = view.getTopInventory.ifNull { return } - //インベントリが存在しない時終了 + // インベントリが存在しない時終了 - //インベントリ名が以下の時処理 + // インベントリ名が以下の時処理 if (topinventory.getTitle == LIGHT_PURPLE.toString + "グリッド式保護・設定保存") { event.setCancelled(true) - //プレイヤーインベントリのクリックの場合終了 + // プレイヤーインベントリのクリックの場合終了 if (event.getClickedInventory.getType == InventoryType.PLAYER) { return } /* - * クリックしたボタンに応じた各処理内容の記述ここから - */ + * クリックしたボタンに応じた各処理内容の記述ここから + */ val player = view.getPlayer.asInstanceOf[Player] val uuid = player.getUniqueId val playerData = playermap(uuid) - //戻るボタン + // 戻るボタン if (itemstackcurrent.getType == Material.BARRIER) { player.playSound(player.getLocation, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) player.openInventory(RegionMenuData.getGridWorldGuardMenu(player)) @@ -178,9 +210,9 @@ class RegionInventoryListener extends Listener { val template = playerData.templateMap(slot) if (template == null) { - //何も登録されてないとき + // 何も登録されてないとき if (event.isLeftClick) { - //左クリックの時は新規登録処理 + // 左クリックの時は新規登録処理 playerGridTemplateSave(player, slot) player.openInventory(RegionMenuData.getGridTemplateInventory(player)) } @@ -200,7 +232,7 @@ class RegionInventoryListener extends Listener { } if (event.isRightClick) { - //新規登録処理 + // 新規登録処理 playerGridTemplateSave(player, slot) player.openInventory(RegionMenuData.getGridTemplateInventory(player)) } @@ -220,11 +252,11 @@ object RegionInventoryListener { playerData.setUnitAmount(DirectionType.BEHIND, 0) playerData.setUnitAmount(DirectionType.RIGHT, 0) playerData.setUnitAmount(DirectionType.LEFT, 0) - //始点座標Map(最短) + // 始点座標Map(最短) val start = getNearlyUnitStart(player) - //終点座標Map(最短) + // 終点座標Map(最短) val end = getNearlyUnitEnd(player) - //範囲選択 + // 範囲選択 wgSelect( new Location(player.getWorld, start("x"), 0.0, start("z")), new Location(player.getWorld, end("x"), 256.0, end("z")), @@ -234,7 +266,11 @@ object RegionInventoryListener { canCreateRegion(player) } - private def gridChangeFunction(player: Player, directionType: DirectionType, event: InventoryClickEvent): Unit = { + private def gridChangeFunction( + player: Player, + directionType: DirectionType, + event: InventoryClickEvent + ): Unit = { val playerData = SeichiAssist.playermap(player.getUniqueId) if (event.isLeftClick) { if (playerData.canGridExtend(directionType, player.getWorld.getName)) { @@ -266,7 +302,7 @@ object RegionInventoryListener { val rightsideUnitAmount = unitMap(DirectionType.RIGHT) val behindUnitAmount = unitMap(DirectionType.BEHIND) - //0ユニット指定の始点/終点のx,z座標 + // 0ユニット指定の始点/終点のx,z座標 val start_x = getNearlyUnitStart(player)("x") val start_z = getNearlyUnitStart(player)("z") val end_x = getNearlyUnitEnd(player)("x") @@ -275,29 +311,69 @@ object RegionInventoryListener { val (start_loc, end_loc) = direction match { case Util.Direction.NORTH => ( - new Location(world, start_x - 15 * leftsideUnitAmount, 0.0, start_z - 15 * aheadUnitAmount), - new Location(world, end_x + 15 * rightsideUnitAmount, 256.0, end_z + 15 * behindUnitAmount) + new Location( + world, + start_x - 15 * leftsideUnitAmount, + 0.0, + start_z - 15 * aheadUnitAmount + ), + new Location( + world, + end_x + 15 * rightsideUnitAmount, + 256.0, + end_z + 15 * behindUnitAmount + ) ) case Util.Direction.EAST => ( - new Location(world, start_x - 15 * behindUnitAmount, 0.0, start_z + 15 * leftsideUnitAmount), - new Location(world, end_x - 15 * aheadUnitAmount, 256.0, end_z + 15 * rightsideUnitAmount) + new Location( + world, + start_x - 15 * behindUnitAmount, + 0.0, + start_z + 15 * leftsideUnitAmount + ), + new Location( + world, + end_x - 15 * aheadUnitAmount, + 256.0, + end_z + 15 * rightsideUnitAmount + ) ) case Util.Direction.SOUTH => ( - new Location(world, start_x - 15 * rightsideUnitAmount, 0.0, start_z - 15 * behindUnitAmount), - new Location(world, end_x + 15 * leftsideUnitAmount, 256.0, end_z + 15 * aheadUnitAmount) + new Location( + world, + start_x - 15 * rightsideUnitAmount, + 0.0, + start_z - 15 * behindUnitAmount + ), + new Location( + world, + end_x + 15 * leftsideUnitAmount, + 256.0, + end_z + 15 * aheadUnitAmount + ) ) case Util.Direction.WEST => ( - new Location(world, start_x - 15 * aheadUnitAmount, 0.0, start_z - 15 * rightsideUnitAmount), - new Location(world, end_x + 15 * behindUnitAmount, 256.0, end_z + 15 * leftsideUnitAmount) + new Location( + world, + start_x - 15 * aheadUnitAmount, + 0.0, + start_z - 15 * rightsideUnitAmount + ), + new Location( + world, + end_x + 15 * behindUnitAmount, + 256.0, + end_z + 15 * leftsideUnitAmount + ) ) case _ => (null, null) - } //わざと何もしない。 + } // わざと何もしない。 wgSelect(start_loc, end_loc, player) } @@ -310,23 +386,24 @@ object RegionInventoryListener { /** * ユニット単位における最短の終点(始点から対角になる)のx,z座標を取得します。 * - * @param player 該当プレイヤー - * @return x,z座標のMap + * @param player + * 該当プレイヤー + * @return + * x,z座標のMap */ def getNearlyUnitEnd(player: Player): Map[String, Double] = { val startCoordinate = getNearlyUnitStart(player) - Map( - "x" -> (startCoordinate("x") + 14.0), - "z" -> (startCoordinate("z") + 14.0) - ) + Map("x" -> (startCoordinate("x") + 14.0), "z" -> (startCoordinate("z") + 14.0)) } /** * ユニット単位における最短の始点のx,z座標を取得します。 * - * @param player 該当プレイヤー - * @return x,z座標のMap + * @param player + * 該当プレイヤー + * @return + * x,z座標のMap */ def getNearlyUnitStart(player: Player): Map[String, Double] = { def getNearestUnitStart(component: Int) = (component / 15) * 15 @@ -349,8 +426,11 @@ object RegionInventoryListener { playerData.canCreateRegion = false } - val region = new ProtectedCuboidRegion(player.getName + "_" + playerData.regionCount, - selection.getNativeMinimumPoint.toBlockVector, selection.getNativeMaximumPoint.toBlockVector) + val region = new ProtectedCuboidRegion( + player.getName + "_" + playerData.regionCount, + selection.getNativeMinimumPoint.toBlockVector, + selection.getNativeMaximumPoint.toBlockVector + ) val regions = manager.getApplicableRegions(region) if (regions.size() != 0) { @@ -359,7 +439,11 @@ object RegionInventoryListener { } val maxRegionCount = wcfg.getMaxRegionCount(player) - if (maxRegionCount >= 0 && manager.getRegionCountOfPlayer(Wg.wrapPlayer(player)) >= maxRegionCount) { + if ( + maxRegionCount >= 0 && manager.getRegionCountOfPlayer( + Wg.wrapPlayer(player) + ) >= maxRegionCount + ) { playerData.canCreateRegion = false return } @@ -373,8 +457,12 @@ object RegionInventoryListener { player.sendMessage(GREEN.toString + "グリッド式保護の現在の設定を保存しました。") player.playSound(player.getLocation, Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1f, 1f) - val template = new GridTemplate(unitMap(DirectionType.AHEAD), unitMap(DirectionType.BEHIND), - unitMap(DirectionType.RIGHT), unitMap(DirectionType.LEFT)) + val template = new GridTemplate( + unitMap(DirectionType.AHEAD), + unitMap(DirectionType.BEHIND), + unitMap(DirectionType.RIGHT), + unitMap(DirectionType.LEFT) + ) playerData.templateMap(i) = template } } diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/SpawnRegionProjectileInterceptor.scala b/src/main/scala/com/github/unchama/seichiassist/listener/SpawnRegionProjectileInterceptor.scala index a66c49618f..6f04c7ae26 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/SpawnRegionProjectileInterceptor.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/SpawnRegionProjectileInterceptor.scala @@ -18,7 +18,14 @@ object SpawnRegionProjectileInterceptor extends Listener { "world-spawn" ) val projectiles = Set( - BOW, EGG, LINGERING_POTION, SPLASH_POTION, ENDER_PEARL, EYE_OF_ENDER, SNOW_BALL, EXP_BOTTLE + BOW, + EGG, + LINGERING_POTION, + SPLASH_POTION, + ENDER_PEARL, + EYE_OF_ENDER, + SNOW_BALL, + EXP_BOTTLE ) @EventHandler @@ -27,10 +34,12 @@ object SpawnRegionProjectileInterceptor extends Listener { val action = event.getAction // Projectileを持った状態で右クリックし、playerがいる保護がspawn保護の中であった場合はイベントをキャンセルする - if (event.hasItem + if ( + event.hasItem && projectiles.contains(event.getItem.getType) && (action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK) - && getRegions(player.getLocation).asScala.map(_.getId).exists(spawnRegions.contains)) { + && getRegions(player.getLocation).asScala.map(_.getId).exists(spawnRegions.contains) + ) { event.setCancelled(true) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/VotingFairyListener.scala b/src/main/scala/com/github/unchama/seichiassist/listener/VotingFairyListener.scala index 6849ac5a4c..782bfdf609 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/VotingFairyListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/VotingFairyListener.scala @@ -20,39 +20,39 @@ object VotingFairyListener { val uuid = p.getUniqueId val playerdata = playermap.apply(uuid) - //召喚した時間を取り出す - playerdata.votingFairyStartTime = - new GregorianCalendar( - Calendar.getInstance.get(Calendar.YEAR), - Calendar.getInstance.get(Calendar.MONTH), - Calendar.getInstance.get(Calendar.DATE), - Calendar.getInstance.get(Calendar.HOUR_OF_DAY), - Calendar.getInstance.get(Calendar.MINUTE) - ) + // 召喚した時間を取り出す + playerdata.votingFairyStartTime = new GregorianCalendar( + Calendar.getInstance.get(Calendar.YEAR), + Calendar.getInstance.get(Calendar.MONTH), + Calendar.getInstance.get(Calendar.DATE), + Calendar.getInstance.get(Calendar.HOUR_OF_DAY), + Calendar.getInstance.get(Calendar.MINUTE) + ) var min = Calendar.getInstance.get(Calendar.MINUTE) + 1 var hour = Calendar.getInstance.get(Calendar.HOUR_OF_DAY) min = if ((playerdata.toggleVotingFairy % 2) != 0) min + 30 else min - hour = if (playerdata.toggleVotingFairy == 2) hour + 1 - else if (playerdata.toggleVotingFairy == 3) hour + 1 - else if (playerdata.toggleVotingFairy == 4) hour + 2 - else hour - - playerdata.votingFairyEndTime = - new GregorianCalendar( - Calendar.getInstance.get(Calendar.YEAR), - Calendar.getInstance.get(Calendar.MONTH), - Calendar.getInstance.get(Calendar.DATE), - hour, min - ) + hour = + if (playerdata.toggleVotingFairy == 2) hour + 1 + else if (playerdata.toggleVotingFairy == 3) hour + 1 + else if (playerdata.toggleVotingFairy == 4) hour + 2 + else hour + + playerdata.votingFairyEndTime = new GregorianCalendar( + Calendar.getInstance.get(Calendar.YEAR), + Calendar.getInstance.get(Calendar.MONTH), + Calendar.getInstance.get(Calendar.DATE), + hour, + min + ) - //投票ptを減らす + // 投票ptを減らす playerdata.effectPoint_$eq(playerdata.effectPoint - playerdata.toggleVotingFairy * 2) - //フラグ + // フラグ playerdata.usingVotingFairy = true - //マナ回復量最大値の決定 + // マナ回復量最大値の決定 val n = manaApi.readManaAmount(p).unsafeRunSync().cap.value playerdata.VotingFairyRecoveryValue = ((n / 10 - n / 30 + new Random().nextInt((n / 20).toInt)) / 2.9).toInt + 200 @@ -61,19 +61,28 @@ object VotingFairyListener { p.sendMessage(s"$RESET$YELLOW${BOLD}この子は1分間に約${playerdata.VotingFairyRecoveryValue}マナ") p.sendMessage(s"$RESET$YELLOW${BOLD}回復させる力を持っているようです。") - //メッセージ + // メッセージ val morning = List( - "おはよ![str1]", "ヤッホー[str1]!", "ふわぁ。。。[str1]の朝は早いね。", - "うーん、今日も一日頑張ろ!", "今日は整地日和だね![str1]!" + "おはよ![str1]", + "ヤッホー[str1]!", + "ふわぁ。。。[str1]の朝は早いね。", + "うーん、今日も一日頑張ろ!", + "今日は整地日和だね![str1]!" ) val day = List( - "やあ![str1]", "ヤッホー[str1]!", "あっ、[str1]じゃん。丁度お腹空いてたんだ!", - "この匂い…[str1]ってがちゃりんごいっぱい持ってる…?", "今日のおやつはがちゃりんごいっぱいだ!" + "やあ![str1]", + "ヤッホー[str1]!", + "あっ、[str1]じゃん。丁度お腹空いてたんだ!", + "この匂い…[str1]ってがちゃりんごいっぱい持ってる…?", + "今日のおやつはがちゃりんごいっぱいだ!" ) val night = List( - "やあ![str1]", "ヤッホー[str1]!", "ふわぁ。。。[str1]は夜も元気だね。", - "もう寝ようと思ってたのにー。[str1]はしょうがないなぁ", "こんな時間に呼ぶなんて…りんごははずんでもらうよ?" + "やあ![str1]", + "ヤッホー[str1]!", + "ふわぁ。。。[str1]は夜も元気だね。", + "もう寝ようと思ってたのにー。[str1]はしょうがないなぁ", + "こんな時間に呼ぶなんて…りんごははずんでもらうよ?" ) if (Util.getTimeZone(playerdata.votingFairyStartTime) == "morning") { @@ -97,31 +106,36 @@ object VotingFairyListener { val oldManaAmount = manaApi.readManaAmount(player).unsafeRunSync() if (oldManaAmount.isFull) { - //マナが最大だった場合はメッセージを送信して終わり + // マナが最大だった場合はメッセージを送信して終わり val msg = List( - "整地しないのー?", "たくさん働いて、たくさんりんごを食べようね!", - "僕はいつか大きながちゃりんごを食べ尽して見せるっ!", "ちょっと食べ疲れちゃった", + "整地しないのー?", + "たくさん働いて、たくさんりんごを食べようね!", + "僕はいつか大きながちゃりんごを食べ尽して見せるっ!", + "ちょっと食べ疲れちゃった", "[str1]はどのりんごが好き?僕はがちゃりんご!", "動いてお腹を空かしていっぱい食べるぞー!" ) VotingFairyTask.speak(player, getMessage(msg, player.getName), playerdata.toggleVFSound) } else { val playerLevel = - SeichiAssist.instance - .breakCountSystem.api + SeichiAssist + .instance + .breakCountSystem + .api .seichiAmountDataRepository(player) - .read.unsafeRunSync() + .read + .unsafeRunSync() .levelCorrespondingToExp - var n = playerdata.VotingFairyRecoveryValue //実際のマナ回復量 - var m = getGiveAppleValue(playerLevel) //りんご消費量 + var n = playerdata.VotingFairyRecoveryValue // 実際のマナ回復量 + var m = getGiveAppleValue(playerLevel) // りんご消費量 - //連続投票によってりんご消費量を抑える + // 連続投票によってりんご消費量を抑える if (playerdata.ChainVote >= 30) m /= 2 else if (playerdata.ChainVote >= 10) m = (m / 1.5).toInt else if (playerdata.ChainVote >= 3) m = (m / 1.25).toInt - //トグルで数値変更 + // トグルで数値変更 if (playerdata.toggleGiveApple == 2) if (oldManaAmount.ratioToCap.exists(_ >= 0.75)) { n /= 2 @@ -137,18 +151,19 @@ object VotingFairyListener { n /= 4 m = 0 } else { - //ちょっとつまみ食いする + // ちょっとつまみ食いする if (m >= 10) m += new Random().nextInt(m / 10) } - //りんご所持数で値変更 + // りんご所持数で値変更 val gachaimoObject = Util.findMineStackObjectByName("gachaimo").get val l = playerdata.minestack.getStackedAmountOf(gachaimoObject) if (m > l) { if (l == 0) { n /= 2 if (playerdata.toggleGiveApple == 1) n /= 2 - if (playerdata.toggleGiveApple == 2 && oldManaAmount.ratioToCap.exists(_ < 0.75)) n /= 2 + if (playerdata.toggleGiveApple == 2 && oldManaAmount.ratioToCap.exists(_ < 0.75)) + n /= 2 player.sendMessage(s"$RESET$YELLOW${BOLD}MineStackにがちゃりんごがないようです。。。") } else { val M = m @@ -159,26 +174,27 @@ object VotingFairyListener { m = l.toInt } - //回復量に若干乱数をつける + // 回復量に若干乱数をつける n = (n - n / 100) + Random.nextInt(n / 50) - //マナ回復 + // マナ回復 manaApi.manaAmount(player).restoreAbsolute(ManaAmount(n)).unsafeRunSync() - //りんごを減らす - playerdata.minestack.subtractStackedAmountOf(Util.findMineStackObjectByName("gachaimo").get, m) + // りんごを減らす + playerdata + .minestack + .subtractStackedAmountOf(Util.findMineStackObjectByName("gachaimo").get, m) - //減ったりんごの数をplayerdataに加算 + // 減ったりんごの数をplayerdataに加算 playerdata.p_apple += m - val yes = List( - "(´~`)モグモグ…", "がちゃりんごって美味しいよね!", - "あぁ!幸せ!", "[str1]のりんごはおいしいなぁ", - "いつもりんごをありがとう!" - ) + val yes = + List("(´~`)モグモグ…", "がちゃりんごって美味しいよね!", "あぁ!幸せ!", "[str1]のりんごはおいしいなぁ", "いつもりんごをありがとう!") val no = List( - "お腹空いたなぁー。", "がちゃりんごがっ!食べたいっ!", - "(´;ω;`)ウゥゥ ヒモジイ...", "@うんちゃま [str1]が意地悪するんだっ!", + "お腹空いたなぁー。", + "がちゃりんごがっ!食べたいっ!", + "(´;ω;`)ウゥゥ ヒモジイ...", + "@うんちゃま [str1]が意地悪するんだっ!", "うわーん!お腹空いたよー!" ) @@ -202,4 +218,4 @@ object VotingFairyListener { } } -class VotingFairyListener extends Listener {} \ No newline at end of file +class VotingFairyListener extends Listener {} diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/Y5DoubleSlabCanceller.scala b/src/main/scala/com/github/unchama/seichiassist/listener/Y5DoubleSlabCanceller.scala index 4d9a6a493f..fd84ecbdfa 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/Y5DoubleSlabCanceller.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/Y5DoubleSlabCanceller.scala @@ -2,10 +2,11 @@ package com.github.unchama.seichiassist.listener import com.github.unchama.seichiassist.ManagedWorld._ import org.bukkit.Material -import org.bukkit.event.{EventHandler, Listener} import org.bukkit.event.block.BlockPlaceEvent +import org.bukkit.event.{EventHandler, Listener} object Y5DoubleSlabCanceller extends Listener { + /** * 以下の条件をすべて満たすときにブロックの設置をキャンセルし、その旨を示すメッセージを送出する * - プレイヤーが対象座標にブロックを設置できる @@ -13,8 +14,10 @@ object Y5DoubleSlabCanceller extends Listener { * - 対象座標の改変後ブロックが焼き石の二段重ねハーフブロックである * - 対象座標が整地ワールドを指している * - 対象ブロックのY座標が5である - * @see https://github.com/GiganticMinecraft/SeichiAssist/issues/775 - * @param event 対象イベント + * @see + * https://github.com/GiganticMinecraft/SeichiAssist/issues/775 + * @param event + * 対象イベント */ @EventHandler def onPlaceDoubleSlabAtY5(event: BlockPlaceEvent): Unit = { diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/invlistener/OnClickTitleMenu.scala b/src/main/scala/com/github/unchama/seichiassist/listener/invlistener/OnClickTitleMenu.scala index 8734835786..2a3482fe8e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/invlistener/OnClickTitleMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/invlistener/OnClickTitleMenu.scala @@ -26,18 +26,24 @@ object OnClickTitleMenu { private def isApplicableAsPrevPageButton(is: ItemStack): Boolean = is.getItemMeta.asInstanceOf[SkullMeta].getOwner == "MHF_ArrowLeft" - + private def isApplicableAsNextPageButton(is: ItemStack): Boolean = is.getItemMeta.asInstanceOf[SkullMeta].getOwner == "MHF_ArrowRight" - - def onPlayerClickTitleMenuEvent(event: InventoryClickEvent)(implicit effectEnvironment: EffectEnvironment, - ioCanOpenAchievementMenu: IO CanOpen AchievementMenu.type): Unit = { + + def onPlayerClickTitleMenuEvent(event: InventoryClickEvent)( + implicit effectEnvironment: EffectEnvironment, + ioCanOpenAchievementMenu: IO CanOpen AchievementMenu.type + ): Unit = { import com.github.unchama.util.syntax.Nullability.NullabilityExtensionReceiver - //外枠のクリック処理なら終了 - event.getClickedInventory.ifNull(return) + // 外枠のクリック処理なら終了 + event + .getClickedInventory + .ifNull( + return + ) - //インベントリを開けたのがプレイヤーではない時終了 + // インベントリを開けたのがプレイヤーではない時終了 val view = event.getView val he = view.getPlayer @@ -45,12 +51,16 @@ object OnClickTitleMenu { return } - //インベントリが存在しない時終了 - val topInventory = view.getTopInventory.ifNull(return) + // インベントリが存在しない時終了 + val topInventory = view + .getTopInventory + .ifNull( + return + ) import com.github.unchama.util.InventoryUtil._ - //インベントリサイズが4列でない時終了 + // インベントリサイズが4列でない時終了 if (topInventory.row != 4) { return } @@ -60,7 +70,7 @@ object OnClickTitleMenu { val pd = SeichiAssist.playermap(player.getUniqueId) if (event.getClickedInventory.getType == InventoryType.PLAYER) { - //プレイヤーインベントリのクリックの場合終了 + // プレイヤーインベントリのクリックの場合終了 return } @@ -71,11 +81,11 @@ object OnClickTitleMenu { event.setCancelled(true) // 二つ名組み合わせトップ mat match { - //実績ポイント最新化 + // 実績ポイント最新化 case Material.EMERALD_ORE => clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) - //エフェクトポイント→実績ポイント変換 + // エフェクトポイント→実績ポイント変換 case Material.EMERALD => clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) if (pd.effectPoint >= 10) { @@ -84,29 +94,27 @@ object OnClickTitleMenu { player.sendMessage("エフェクトポイントが不足しています。") } - //パーツショップ + // パーツショップ case Material.ITEM_FRAME => clickedSound(player, Sound.BLOCK_FENCE_GATE_OPEN, 0.1f) player.openInventory(MenuInventoryData.computePartsShopMenu(player)) - //前パーツ + // 前パーツ case Material.WATER_BUCKET => clickedSound(player, Sound.BLOCK_FENCE_GATE_OPEN, 0.1f) player.openInventory(MenuInventoryData.computeHeadPartCustomMenu(player)) - //中パーツ + // 中パーツ case Material.MILK_BUCKET => clickedSound(player, Sound.BLOCK_FENCE_GATE_OPEN, 0.1f) player.openInventory(MenuInventoryData.computeMiddlePartCustomMenu(player)) - //後パーツ + // 後パーツ case Material.LAVA_BUCKET => clickedSound(player, Sound.BLOCK_FENCE_GATE_OPEN, 0.1f) player.openInventory(MenuInventoryData.computeTailPartCustomMenu(player)) case _ if isSkull && isApplicableAsPrevPageButton(current) => - - effectEnvironment.unsafeRunAsyncTargetedEffect(player)( SequentialEffect( CommonSoundEffects.menuTransitionFenceSound, @@ -126,7 +134,7 @@ object OnClickTitleMenu { case _ => } - + case MenuType.HEAD.invName => event.setCancelled(true) mat match { @@ -134,13 +142,18 @@ object OnClickTitleMenu { clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) val id = current.getItemMeta.getDisplayName.toInt - val length = Nicknames.getTitleFor(id, - pd.settings.nickname.id2, pd.settings.nickname.id3).length + val length = Nicknames + .getTitleFor(id, pd.settings.nickname.id2, pd.settings.nickname.id3) + .length if (length > MAX_LENGTH) { player.sendMessage(LENGTH_LIMIT_EXCEEDED) } else { pd.updateNickname(id1 = id) - player.sendMessage("前パーツ「" + Nicknames.getHeadPartFor(pd.settings.nickname.id1).getOrElse("") + "」をセットしました。") + player.sendMessage( + "前パーツ「" + Nicknames + .getHeadPartFor(pd.settings.nickname.id1) + .getOrElse("") + "」をセットしました。" + ) } case Material.GRASS => @@ -158,7 +171,11 @@ object OnClickTitleMenu { clickedSound(player, Sound.BLOCK_FENCE_GATE_OPEN, 0.1f) val uuid = player.getUniqueId val menuType = MenuInventoryData.MenuType.HEAD - MenuInventoryData.setHeadingIndex(uuid, menuType, MenuInventoryData.getHeadingIndex(uuid, menuType).get + PER_PAGE) + MenuInventoryData.setHeadingIndex( + uuid, + menuType, + MenuInventoryData.getHeadingIndex(uuid, menuType).get + PER_PAGE + ) player.openInventory(MenuInventoryData.computeHeadPartCustomMenu(player)) case _ => @@ -171,13 +188,18 @@ object OnClickTitleMenu { clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) val id = current.getItemMeta.getDisplayName.toInt - val length = Nicknames.getTitleFor(pd.settings.nickname.id1, - id, pd.settings.nickname.id3).length + val length = Nicknames + .getTitleFor(pd.settings.nickname.id1, id, pd.settings.nickname.id3) + .length if (length > MAX_LENGTH) { player.sendMessage(LENGTH_LIMIT_EXCEEDED) } else { pd.updateNickname(id2 = id) - player.sendMessage("中パーツ「" + Nicknames.getMiddlePartFor(pd.settings.nickname.id2).getOrElse("") + "」をセットしました。") + player.sendMessage( + "中パーツ「" + Nicknames + .getMiddlePartFor(pd.settings.nickname.id2) + .getOrElse("") + "」をセットしました。" + ) } case Material.GRASS => @@ -193,7 +215,11 @@ object OnClickTitleMenu { clickedSound(player, Sound.BLOCK_FENCE_GATE_OPEN, 0.1f) val uuid = player.getUniqueId val menuType = MenuInventoryData.MenuType.MIDDLE - MenuInventoryData.setHeadingIndex(uuid, menuType, MenuInventoryData.getHeadingIndex(uuid, menuType).get + PER_PAGE) + MenuInventoryData.setHeadingIndex( + uuid, + menuType, + MenuInventoryData.getHeadingIndex(uuid, menuType).get + PER_PAGE + ) player.openInventory(MenuInventoryData.computeMiddlePartCustomMenu(player)) case _ => @@ -206,13 +232,18 @@ object OnClickTitleMenu { clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) val id = current.getItemMeta.getDisplayName.toInt - val length = Nicknames.getTitleFor(pd.settings.nickname.id1, - pd.settings.nickname.id2, id).length + val length = Nicknames + .getTitleFor(pd.settings.nickname.id1, pd.settings.nickname.id2, id) + .length if (length > MAX_LENGTH) { player.sendMessage(LENGTH_LIMIT_EXCEEDED) } else { pd.updateNickname(id3 = id) - player.sendMessage("後パーツ「" + Nicknames.getTailPartFor(pd.settings.nickname.id3).getOrElse("") + "」をセットしました。") + player.sendMessage( + "後パーツ「" + Nicknames + .getTailPartFor(pd.settings.nickname.id3) + .getOrElse("") + "」をセットしました。" + ) } case Material.GRASS => @@ -228,7 +259,11 @@ object OnClickTitleMenu { clickedSound(player, Sound.BLOCK_FENCE_GATE_OPEN, 0.1f) val uuid = player.getUniqueId val menuType = MenuInventoryData.MenuType.TAIL - MenuInventoryData.setHeadingIndex(uuid, menuType, MenuInventoryData.getHeadingIndex(uuid, menuType).get + PER_PAGE) + MenuInventoryData.setHeadingIndex( + uuid, + menuType, + MenuInventoryData.getHeadingIndex(uuid, menuType).get + PER_PAGE + ) player.openInventory(MenuInventoryData.computeTailPartCustomMenu(player)) case _ => @@ -237,7 +272,7 @@ object OnClickTitleMenu { case MenuType.SHOP.invName => event.setCancelled(true) mat match { - //実績ポイント最新化 + // 実績ポイント最新化 case Material.EMERALD_ORE => clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) pd.recalculateAchievePoint() @@ -251,11 +286,8 @@ object OnClickTitleMenu { val num = current.getItemMeta.getDisplayName.toInt val isHead = num < 9900 val required = if (isHead) 20 else 35 - val getPart = if (isHead) { - num => Nicknames.getHeadPartFor(num) - } else { - num => Nicknames.getMiddlePartFor(num) - } + val getPart = if (isHead) { num => Nicknames.getHeadPartFor(num) } + else { num => Nicknames.getMiddlePartFor(num) } if (pd.achievePoint.left >= required) { pd.TitleFlags.addOne(num) @@ -275,7 +307,11 @@ object OnClickTitleMenu { clickedSound(player, Sound.BLOCK_FENCE_GATE_OPEN, 0.1f) val uuid = player.getUniqueId val menuType = MenuInventoryData.MenuType.SHOP - MenuInventoryData.setHeadingIndex(uuid, menuType, MenuInventoryData.getHeadingIndex(uuid, menuType).get + PER_PAGE) + MenuInventoryData.setHeadingIndex( + uuid, + menuType, + MenuInventoryData.getHeadingIndex(uuid, menuType).get + PER_PAGE + ) player.openInventory(MenuInventoryData.computePartsShopMenu(player)) case _ => diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/BuildMainMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/BuildMainMenu.scala index d1db26eb43..2d1265705d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/BuildMainMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/BuildMainMenu.scala @@ -6,26 +6,41 @@ import com.github.unchama.buildassist.{BuildAssist, MenuInventoryData} import com.github.unchama.itemstackbuilder.{IconItemStackBuilder, SkullItemStackBuilder} import com.github.unchama.menuinventory import com.github.unchama.menuinventory.router.CanOpen -import com.github.unchama.menuinventory.slot.button.action.{ClickEventFilter, FilteredButtonEffect} +import com.github.unchama.menuinventory.slot.button.action.{ + ClickEventFilter, + FilteredButtonEffect +} import com.github.unchama.menuinventory.slot.button.{Button, RecomputedButton, action} import com.github.unchama.menuinventory.{Menu, MenuFrame, MenuSlotLayout} import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.SkullOwners import com.github.unchama.seichiassist.menus.BuildMainMenu.EMPHASIZE -import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.{BuildAssistExpTable, BuildLevel} import com.github.unchama.seichiassist.subsystems.managedfly.ManagedFlyApi -import com.github.unchama.seichiassist.subsystems.managedfly.domain.{Flying, NotFlying, RemainingFlyDuration} +import com.github.unchama.seichiassist.subsystems.managedfly.domain.{ + Flying, + NotFlying, + RemainingFlyDuration +} import com.github.unchama.targetedeffect.commandsender.MessageEffect -import com.github.unchama.targetedeffect.player.PlayerEffects.{closeInventoryEffect, openInventoryEffect} +import com.github.unchama.targetedeffect.player.PlayerEffects.{ + closeInventoryEffect, + openInventoryEffect +} import com.github.unchama.targetedeffect.player.{CommandEffect, FocusedSoundEffect} -import com.github.unchama.targetedeffect.{ComputedEffect, DeferredEffect, SequentialEffect, UnfocusedEffect} +import com.github.unchama.targetedeffect.{ + ComputedEffect, + DeferredEffect, + SequentialEffect, + UnfocusedEffect +} import org.bukkit.ChatColor._ import org.bukkit.entity.Player import org.bukkit.inventory.ItemFlag import org.bukkit.{Material, Sound} -private case class ButtonComputations(player: Player) - (implicit ioOnMainThread: OnMinecraftServerThread[IO]) { +private case class ButtonComputations(player: Player)( + implicit ioOnMainThread: OnMinecraftServerThread[IO] +) { import BuildMainMenu._ import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.layoutPreparationContext @@ -39,13 +54,13 @@ private case class ButtonComputations(player: Player) val lore = { val alwaysDisplayedInfo = List( s"$RESET${AQUA}建築Lv: $rawLevel", - s"$RESET${AQUA}総建築量: ${data.expAmount.toPlainString}", + s"$RESET${AQUA}総建築量: ${data.expAmount.toPlainString}" ) // 最大レベルに到達した後は”次のレベル”が存在しないため、表示しない - val nextLevelInfo: Option[String] = data.levelProgress.map(blp => - s"$RESET${AQUA}次のレベルまで: ${blp.expAmountToNextLevel.toPlainString}" - ) + val nextLevelInfo: Option[String] = data + .levelProgress + .map(blp => s"$RESET${AQUA}次のレベルまで: ${blp.expAmountToNextLevel.toPlainString}") alwaysDisplayedInfo ++ nextLevelInfo } @@ -60,7 +75,9 @@ private case class ButtonComputations(player: Player) } } - def computeButtonToShowStateOfFlying(implicit flyApi: ManagedFlyApi[SyncIO, Player]): IO[Button] = { + def computeButtonToShowStateOfFlying( + implicit flyApi: ManagedFlyApi[SyncIO, Player] + ): IO[Button] = { for { flyStatus <- flyApi.playerFlyDurations(player).read.toIO } yield { @@ -76,9 +93,7 @@ private case class ButtonComputations(player: Player) } ) case NotFlying => - List( - s"$RESET${AQUA}Fly 効果: OFF" - ) + List(s"$RESET${AQUA}Fly 効果: OFF") } val iconItemStack = new IconItemStackBuilder(Material.COOKED_CHICKEN) @@ -91,53 +106,65 @@ private case class ButtonComputations(player: Player) } def computeButtonToToggleRangedPlaceSkill(): IO[Button] = RecomputedButton( - BuildAssist.instance.buildAmountDataRepository(player).read.toIO.flatMap(amountData => - IO { - val openerData = BuildAssist.instance.temporaryData(getUniqueId) - val openerLevel = amountData.levelCorrespondingToExp.level - - val iconItemStack = new IconItemStackBuilder(Material.STONE) - .title(s"$GREEN$EMPHASIZE「範囲設置スキル」現在:${if (openerData.ZoneSetSkillFlag) "ON" else "OFF"}") - .lore( - s"$RESET$YELLOW「スニーク+左クリック」をすると、", - s"$RESET${YELLOW}オフハンドに持っているブロックと同じ物を", - s"$RESET${YELLOW}インベントリ内から消費し設置します。", - s"$RESET$LIGHT_PURPLE<クリックでON/OFF切り替え>" - ) - .build() - - Button( - iconItemStack, - FilteredButtonEffect(ClickEventFilter.ALWAYS_INVOKE) { _ => - SequentialEffect( - FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f), - DeferredEffect { - IO { - if (openerLevel < BuildAssist.config.getZoneSetSkillLevel) { - MessageEffect(s"${RED}建築Lvが足りません") - } else { - if (openerData.ZoneSetSkillFlag) SequentialEffect( - UnfocusedEffect { - openerData.ZoneSetSkillFlag = false - }, - MessageEffect(s"${RED}範囲設置スキルOFF") - ) else SequentialEffect( - UnfocusedEffect { - openerData.ZoneSetSkillFlag = true - }, - MessageEffect(s"${RED}範囲設置スキルON") - ) + BuildAssist + .instance + .buildAmountDataRepository(player) + .read + .toIO + .flatMap(amountData => + IO { + val openerData = BuildAssist.instance.temporaryData(getUniqueId) + val openerLevel = amountData.levelCorrespondingToExp.level + + val iconItemStack = new IconItemStackBuilder(Material.STONE) + .title( + s"$GREEN$EMPHASIZE「範囲設置スキル」現在:${if (openerData.ZoneSetSkillFlag) "ON" else "OFF"}" + ) + .lore( + s"$RESET$YELLOW「スニーク+左クリック」をすると、", + s"$RESET${YELLOW}オフハンドに持っているブロックと同じ物を", + s"$RESET${YELLOW}インベントリ内から消費し設置します。", + s"$RESET$LIGHT_PURPLE<クリックでON/OFF切り替え>" + ) + .build() + + Button( + iconItemStack, + FilteredButtonEffect(ClickEventFilter.ALWAYS_INVOKE) { _ => + SequentialEffect( + FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f), + DeferredEffect { + IO { + if (openerLevel < BuildAssist.config.getZoneSetSkillLevel) { + MessageEffect(s"${RED}建築Lvが足りません") + } else { + if (openerData.ZoneSetSkillFlag) + SequentialEffect( + UnfocusedEffect { + openerData.ZoneSetSkillFlag = false + }, + MessageEffect(s"${RED}範囲設置スキルOFF") + ) + else + SequentialEffect( + UnfocusedEffect { + openerData.ZoneSetSkillFlag = true + }, + MessageEffect(s"${RED}範囲設置スキルON") + ) + } } } - } - ) - } - ) - } - ) + ) + } + ) + } + ) ) - def computeButtonToOpenRangedPlaceSkillMenu(implicit canOpenBlockPlacementSkillMenu: CanOpen[IO, BlockPlacementSkillMenu.type]): IO[Button] = + def computeButtonToOpenRangedPlaceSkillMenu( + implicit canOpenBlockPlacementSkillMenu: CanOpen[IO, BlockPlacementSkillMenu.type] + ): IO[Button] = BuildAssist.instance.buildAmountDataRepository(player).read.toIO.flatMap { amountData => IO { val openerData = BuildAssist.instance.temporaryData(getUniqueId) @@ -151,13 +178,18 @@ private case class ButtonComputations(player: Player) ) .build() - Button(iconItemStack, + Button( + iconItemStack, action.FilteredButtonEffect(ClickEventFilter.ALWAYS_INVOKE) { _ => SequentialEffect( FocusedSoundEffect(Sound.BLOCK_FENCE_GATE_OPEN, 1f, 0.1f), DeferredEffect { IO { - if (amountData.levelCorrespondingToExp.level < BuildAssist.config.getblocklineuplevel) { + if ( + amountData + .levelCorrespondingToExp + .level < BuildAssist.config.getblocklineuplevel + ) { MessageEffect(s"${RED}建築Lvが足りません") } else { canOpenBlockPlacementSkillMenu.open(BlockPlacementSkillMenu) @@ -184,11 +216,16 @@ private case class ButtonComputations(player: Player) ) .build() - Button(iconItemStack, + Button( + iconItemStack, action.FilteredButtonEffect(ClickEventFilter.ALWAYS_INVOKE) { _ => DeferredEffect { IO { - if (amountData.levelCorrespondingToExp.level < BuildAssist.config.getblocklineuplevel) { + if ( + amountData + .levelCorrespondingToExp + .level < BuildAssist.config.getblocklineuplevel + ) { MessageEffect(s"${RED}建築Lvが足りません") } else { SequentialEffect( @@ -199,7 +236,9 @@ private case class ButtonComputations(player: Player) }, DeferredEffect { IO { - MessageEffect(s"${GREEN}直列設置: ${BuildAssist.line_up_str(openerData.line_up_flg)}") + MessageEffect( + s"${GREEN}直列設置: ${BuildAssist.line_up_str(openerData.line_up_flg)}" + ) } } ) @@ -226,7 +265,8 @@ private case class ButtonComputations(player: Player) ) .build() - Button(iconItemStack, + Button( + iconItemStack, FilteredButtonEffect(ClickEventFilter.ALWAYS_INVOKE) { _ => SequentialEffect( FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f), @@ -236,14 +276,16 @@ private case class ButtonComputations(player: Player) ) } - def computeButtonToOpenMenuToCraftItemsWhereMineStack(implicit - canOpenMassCraftMenu: CanOpen[IO, MineStackMassCraftMenu]): IO[Button] = IO { + def computeButtonToOpenMenuToCraftItemsWhereMineStack( + implicit canOpenMassCraftMenu: CanOpen[IO, MineStackMassCraftMenu] + ): IO[Button] = IO { val iconItemStackBuilder = new IconItemStackBuilder(Material.WORKBENCH) .title(s"$YELLOW${EMPHASIZE}MineStackブロック一括クラフト画面へ") .lore(s"$RESET$DARK_RED${UNDERLINE}クリックで移動") .build() - Button(iconItemStackBuilder, + Button( + iconItemStackBuilder, action.FilteredButtonEffect(ClickEventFilter.ALWAYS_INVOKE) { _ => SequentialEffect( FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f), @@ -331,10 +373,7 @@ private object ConstantButtons { val buttonToTerminateFlight: Button = { val iconItemStack = new IconItemStackBuilder(Material.CHAINMAIL_BOOTS) .title(s"$YELLOW${EMPHASIZE}Fly機能、OFF") - .lore( - s"$RESET${RED}クリックすると、残り時間にかかわらず", - s"$RESET${RED}Flyを終了します。" - ) + .lore(s"$RESET${RED}クリックすると、残り時間にかかわらず", s"$RESET${RED}Flyを終了します。") .flagged(ItemFlag.HIDE_ATTRIBUTES) .build() @@ -355,17 +394,20 @@ object BuildMainMenu extends Menu { import menuinventory.syntax._ - class Environment(implicit - val flyApi: ManagedFlyApi[SyncIO, Player], - val ioOnMainThread: OnMinecraftServerThread[IO], - val canOpenBlockPlacementSkillMenu: CanOpen[IO, BlockPlacementSkillMenu.type], - val canOpenMassCraftMenu: CanOpen[IO, MineStackMassCraftMenu]) + class Environment( + implicit val flyApi: ManagedFlyApi[SyncIO, Player], + val ioOnMainThread: OnMinecraftServerThread[IO], + val canOpenBlockPlacementSkillMenu: CanOpen[IO, BlockPlacementSkillMenu.type], + val canOpenMassCraftMenu: CanOpen[IO, MineStackMassCraftMenu] + ) val EMPHASIZE = s"$UNDERLINE$BOLD" override val frame: MenuFrame = MenuFrame(4.chestRows, s"${LIGHT_PURPLE}木の棒メニューB") - override def computeMenuLayout(player: Player)(implicit environment: Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { import ConstantButtons._ import environment._ @@ -379,7 +421,6 @@ object BuildMainMenu extends Menu { ) import cats.implicits._ - import environment._ val dynamicPartComputation: IO[List[(Int, Button)]] = List( @@ -390,9 +431,7 @@ object BuildMainMenu extends Menu { 27 -> computeButtonToLineUpBlocks(), 28 -> computeButtonToOpenLineUpBlocksMenu(), 35 -> computeButtonToOpenMenuToCraftItemsWhereMineStack - ) - .map(_.sequence) - .sequence + ).map(_.sequence).sequence for { dynamicPart <- dynamicPartComputation diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/CommonButtons.scala b/src/main/scala/com/github/unchama/seichiassist/menus/CommonButtons.scala index 9b1d0820c9..fddb7593ee 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/CommonButtons.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/CommonButtons.scala @@ -17,21 +17,19 @@ object CommonButtons { import com.github.unchama.targetedeffect._ - def transferButton[M <: Menu](partialBuilder: AbstractItemStackBuilder[Nothing], - transferDescription: String, - target: M, - actionDescription: String = "クリックで移動") - (implicit canOpenM: CanOpen[IO, M]): Button = + def transferButton[M <: Menu]( + partialBuilder: AbstractItemStackBuilder[Nothing], + transferDescription: String, + target: M, + actionDescription: String = "クリックで移動" + )(implicit canOpenM: CanOpen[IO, M]): Button = Button( partialBuilder .title(navigation(transferDescription)) .lore(List(clickResultDescription(actionDescription))) .build(), action.LeftClickButtonEffect( - SequentialEffect( - CommonSoundEffects.menuTransitionFenceSound, - canOpenM.open(target) - ) + SequentialEffect(CommonSoundEffects.menuTransitionFenceSound, canOpenM.open(target)) ) ) @@ -42,4 +40,4 @@ object CommonButtons { StickMenu.firstPage ) } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/HomeMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/HomeMenu.scala index 7b1208e566..471dbf7094 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/HomeMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/HomeMenu.scala @@ -29,8 +29,10 @@ object HomeMenu extends Menu { import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.onMainThread import eu.timepit.refined.auto._ - class Environment(implicit val ioCanOpenConfirmationMenu: IO CanOpen ConfirmationMenu, - val ioCanReadSubHome: SubHomeReadAPI[IO]) + class Environment( + implicit val ioCanOpenConfirmationMenu: IO CanOpen ConfirmationMenu, + val ioCanReadSubHome: SubHomeReadAPI[IO] + ) /** * メニューのサイズとタイトルに関する情報 @@ -38,9 +40,12 @@ object HomeMenu extends Menu { override val frame: MenuFrame = MenuFrame(3.chestRows, s"$DARK_PURPLE${BOLD}ホームメニュー") /** - * @return `player`からメニューの[[MenuSlotLayout]]を計算する[[IO]] + * @return + * `player`からメニューの[[MenuSlotLayout]]を計算する[[IO]] */ - override def computeMenuLayout(player: Player)(implicit environment: Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { import eu.timepit.refined._ import eu.timepit.refined.auto._ import eu.timepit.refined.numeric._ @@ -53,10 +58,11 @@ object HomeMenu extends Menu { } yield { val column = refineV[Interval.ClosedOpen[0, 9]](subHomeNumber - 1) column match { - case Right(value) => Map( - ChestSlotRef(0, value) -> ConstantButtons.warpToSubHomePointButton(subHomeNumber), - ChestSlotRef(2, value) -> ConstantButtons.setSubHomeButton(subHomeNumber) - ) + case Right(value) => + Map( + ChestSlotRef(0, value) -> ConstantButtons.warpToSubHomePointButton(subHomeNumber), + ChestSlotRef(2, value) -> ConstantButtons.setSubHomeButton(subHomeNumber) + ) case Left(_) => throw new RuntimeException("This branch should not be reached.") } } @@ -70,15 +76,13 @@ object HomeMenu extends Menu { implicit val ioCanReadSubHome: SubHomeReadAPI[IO] = environment.ioCanReadSubHome column match { case Right(value) => ChestSlotRef(1, value) -> setSubHomeNameButton[IO](subHomeNumber) - case Left(_) => throw new RuntimeException("This branch should not be reached.") + case Left(_) => throw new RuntimeException("This branch should not be reached.") } - }.sequence) - .toList - .sequence + }.sequence).toList.sequence for { dynamicPart <- dynamicPartComputation - } yield MenuSlotLayout(subHomePointPart.flatten ++ dynamicPart.toMap:_*) + } yield MenuSlotLayout(subHomePointPart.flatten ++ dynamicPart.toMap: _*) } private object ConstantButtons { @@ -86,11 +90,16 @@ object HomeMenu extends Menu { Button( new IconItemStackBuilder(Material.COMPASS) .title(s"$YELLOW$UNDERLINE${BOLD}サブホームポイント${subHomeNumber}にワープ") - .lore(List( - s"${GRAY}あらかじめ設定した", s"${GRAY}サブホームポイント${subHomeNumber}にワープします", - s"${DARK_GRAY}うまく機能しない時は", s"${DARK_GRAY}再接続してみてください", - s"$DARK_RED${UNDERLINE}クリックでワープ", s"${DARK_GRAY}command->[/subhome warp $subHomeNumber]" - )) + .lore( + List( + s"${GRAY}あらかじめ設定した", + s"${GRAY}サブホームポイント${subHomeNumber}にワープします", + s"${DARK_GRAY}うまく機能しない時は", + s"${DARK_GRAY}再接続してみてください", + s"$DARK_RED${UNDERLINE}クリックでワープ", + s"${DARK_GRAY}command->[/subhome warp $subHomeNumber]" + ) + ) .build(), LeftClickButtonEffect { SequentialEffect( @@ -104,13 +113,15 @@ object HomeMenu extends Menu { Button( new IconItemStackBuilder(Material.BED) .title(s"$YELLOW$UNDERLINE${BOLD}サブホームポイント${subHomeNumber}を設定") - .lore(List( - s"${GRAY}現在位置をサブホームポイント$subHomeNumber", - s"${GRAY}として設定します", - s"$DARK_GRAY※確認メニューが開きます", - s"$DARK_RED${UNDERLINE}クリックで設定", - s"${DARK_GRAY}command->[/subhome set $subHomeNumber]" - )) + .lore( + List( + s"${GRAY}現在位置をサブホームポイント$subHomeNumber", + s"${GRAY}として設定します", + s"$DARK_GRAY※確認メニューが開きます", + s"$DARK_RED${UNDERLINE}クリックで設定", + s"${DARK_GRAY}command->[/subhome set $subHomeNumber]" + ) + ) .build(), LeftClickButtonEffect { SequentialEffect( @@ -122,7 +133,9 @@ object HomeMenu extends Menu { } private case class ButtonComputations(player: Player) { - def setSubHomeNameButton[F[_] : SubHomeReadAPI : ConcurrentEffect](subHomeNumber: Int): IO[Button] = { + def setSubHomeNameButton[F[_]: SubHomeReadAPI: ConcurrentEffect]( + subHomeNumber: Int + ): IO[Button] = { import cats.implicits._ val subHomeId = SubHomeId(subHomeNumber) @@ -135,25 +148,20 @@ object HomeMenu extends Menu { case Some(SubHome(optionName, location)) => val worldName = { ManagedWorld - .fromName(location.worldName).map(_.japaneseName) + .fromName(location.worldName) + .map(_.japaneseName) .getOrElse(location.worldName) } val nameStatus = optionName match { - case Some(name) => List( - s"${GRAY}サブホームポイント${subHomeId}は", - s"$GRAY$name", - s"${GRAY}と名付けられています", - ) - case None => List( - s"${GRAY}サブホームポイント${subHomeId}は", - s"${GRAY}名前が未設定です", - ) + case Some(name) => + List(s"${GRAY}サブホームポイント${subHomeId}は", s"$GRAY$name", s"${GRAY}と名付けられています") + case None => List(s"${GRAY}サブホームポイント${subHomeId}は", s"${GRAY}名前が未設定です") } val commandInfo = List( s"$DARK_RED${UNDERLINE}クリックで名称変更", - s"${DARK_GRAY}command->[/subhome name $subHomeId]", + s"${DARK_GRAY}command->[/subhome name $subHomeId]" ) val coordinates = List( @@ -184,7 +192,8 @@ object HomeMenu extends Menu { } } - case class ConfirmationMenu(changeSubHomeNumber: Option[Int], subHomeName: String = "") extends Menu { + case class ConfirmationMenu(changeSubHomeNumber: Option[Int], subHomeName: String = "") + extends Menu { override type Environment = ConfirmationMenu.Environment /** @@ -193,30 +202,29 @@ object HomeMenu extends Menu { override val frame: MenuFrame = MenuFrame(3.chestRows, s"$RED${BOLD}ホームポイントを変更しますか") /** - * @return `player`からメニューの[[MenuSlotLayout]]を計算する[[IO]] + * @return + * `player`からメニューの[[MenuSlotLayout]]を計算する[[IO]] */ - override def computeMenuLayout(player: Player)(implicit environment: Environment): IO[MenuSlotLayout] = { - val baseSlotMap = Map( - ChestSlotRef(1, 2) -> changeButton, - ChestSlotRef(1, 6) -> cancelButton - ) + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { + val baseSlotMap = + Map(ChestSlotRef(1, 2) -> changeButton, ChestSlotRef(1, 6) -> cancelButton) val slotMap = changeSubHomeNumber match { case None => baseSlotMap - case _ => baseSlotMap ++ Map(ChestSlotRef(0, 4) -> informationButton) + case _ => baseSlotMap ++ Map(ChestSlotRef(0, 4) -> informationButton) } IO.pure(MenuSlotLayout(slotMap)) } val changeButton: Button = Button( - new IconItemStackBuilder(Material.WOOL, durability = 5) - .title(s"${GREEN}変更する") - .build(), + new IconItemStackBuilder(Material.WOOL, durability = 5).title(s"${GREEN}変更する").build(), LeftClickButtonEffect { SequentialEffect( FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f), changeSubHomeNumber match { - case None => CommandEffect("sethome") + case None => CommandEffect("sethome") case Some(homeNumber) => CommandEffect(s"subhome set $homeNumber") }, closeInventoryEffect @@ -226,9 +234,7 @@ object HomeMenu extends Menu { def cancelButton(implicit environment: Environment): Button = Button( - new IconItemStackBuilder(Material.WOOL, durability = 14) - .title(s"${RED}変更しない") - .build(), + new IconItemStackBuilder(Material.WOOL, durability = 14).title(s"${RED}変更しない").build(), LeftClickButtonEffect { FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) environment.ioCanOpenHomeMenu.open(HomeMenu) @@ -239,10 +245,9 @@ object HomeMenu extends Menu { Button( new IconItemStackBuilder(Material.PAPER) .title(s"${GREEN}設定するサブホームポイントの情報") - .lore(List( - s"${GRAY}No.${changeSubHomeNumber.getOrElse(0)}", - s"${GRAY}名称:$subHomeName" - )) + .lore( + List(s"${GRAY}No.${changeSubHomeNumber.getOrElse(0)}", s"${GRAY}名称:$subHomeName") + ) .build() ) } diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/RegionMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/RegionMenu.scala index d244752499..d531821fdb 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/RegionMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/RegionMenu.scala @@ -3,7 +3,10 @@ package com.github.unchama.seichiassist.menus import cats.effect.IO import com.github.unchama.itemstackbuilder.IconItemStackBuilder import com.github.unchama.menuinventory -import com.github.unchama.menuinventory.slot.button.action.{ClickEventFilter, FilteredButtonEffect} +import com.github.unchama.menuinventory.slot.button.action.{ + ClickEventFilter, + FilteredButtonEffect +} import com.github.unchama.menuinventory.slot.button.{Button, action} import com.github.unchama.menuinventory.{Menu, MenuFrame, MenuSlotLayout} import com.github.unchama.seichiassist.SeichiAssist @@ -20,13 +23,15 @@ object RegionMenu extends Menu { import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.onMainThread import com.github.unchama.targetedeffect._ - import com.github.unchama.targetedeffect.player.PlayerEffects.{closeInventoryEffect, _} + import com.github.unchama.targetedeffect.player.PlayerEffects._ override type Environment = Unit override val frame: MenuFrame = MenuFrame(Right(InventoryType.HOPPER), s"${BLACK}保護メニュー") - override def computeMenuLayout(player: Player)(implicit environment: Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { import ConstantButtons._ val computations = ButtonComputations(player) import computations._ @@ -65,24 +70,13 @@ object RegionMenu extends Menu { val lore = { if (!playerHasPermission) - Seq( - s"${RED}このワールドでは", - s"${RED}保護を作成できません" - ) + Seq(s"${RED}このワールドでは", s"${RED}保護を作成できません") else if (isSelectionNull) - Seq( - s"${RED}範囲指定されていません", - s"${RED}先に木の斧で2か所クリックしてネ" - ) + Seq(s"${RED}範囲指定されていません", s"${RED}先に木の斧で2か所クリックしてネ") else if (!selectionHasEnoughSpace) - Seq( - s"${RED}選択された範囲が狭すぎます", - s"${RED}一辺当たり最低10ブロック以上にしてネ" - ) - else Seq( - s"$DARK_GREEN${UNDERLINE}範囲指定されています", - s"$DARK_GREEN${UNDERLINE}クリックすると保護を作成します" - ) + Seq(s"${RED}選択された範囲が狭すぎます", s"${RED}一辺当たり最低10ブロック以上にしてネ") + else + Seq(s"$DARK_GREEN${UNDERLINE}範囲指定されています", s"$DARK_GREEN${UNDERLINE}クリックすると保護を作成します") } ++ { if (playerHasPermission) Seq( @@ -95,7 +89,8 @@ object RegionMenu extends Menu { s"$GRAY・別の保護と被っていないか", s"$GRAY・保護数上限に達していないか", s"${GRAY}確認してください" - ) else Seq() + ) + else Seq() } import scala.util.chaining._ @@ -152,7 +147,8 @@ object RegionMenu extends Menu { s"$DARK_GREEN$UNDERLINE※インベントリを空けておこう", s"${DARK_GRAY}command=>[//wand]" ) - ).build() + ) + .build() Button( iconItemStack, @@ -184,7 +180,8 @@ object RegionMenu extends Menu { s"$RED$UNDERLINE/rg removemember 保護名 プレイヤー名", s"${GRAY}該当保護の指定メンバーを削除", s"${DARK_GRAY}その他のコマンドはwikiを参照", - s"${DARK_GRAY}command=>[/rg list]") + s"${DARK_GRAY}command=>[/rg list]" + ) .build() Button( @@ -207,7 +204,8 @@ object RegionMenu extends Menu { s"${RED}保護の管理が超簡単に!", s"${YELLOW}自分の所有する保護内でクリックすると", s"${YELLOW}保護の各種設定や削除が行えます", - s"${DARK_GRAY}command=>[/land]") + s"${DARK_GRAY}command=>[/land]" + ) .build() Button( @@ -225,7 +223,8 @@ object RegionMenu extends Menu { s"${YELLOW}グリッド式保護とは...", s"${GRAY}保護をユニット単位で管理するシステムのこと", s"${AQUA}15ブロック=1ユニットとして", - s"${AQUA}保護が作成されます。") + s"${AQUA}保護が作成されます。" + ) .build() Button( diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/ServerSwitchMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/ServerSwitchMenu.scala index da0b85512f..f037604ab3 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/ServerSwitchMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/ServerSwitchMenu.scala @@ -26,25 +26,43 @@ object ServerSwitchMenu extends Menu { /** * UI上のサーバを表すオブジェクト. * - * @param uiLabel UI上で表示される際のサーバ名 - * @param identifier /server を使って移動する際に指定するサーバの識別子 - * @param chestSlotRef UI上でのButtonの表示位置 - * @param material UI上でのButtonのMaterial + * @param uiLabel + * UI上で表示される際のサーバ名 + * @param identifier + * /server を使って移動する際に指定するサーバの識別子 + * @param chestSlotRef + * UI上でのButtonの表示位置 + * @param material + * UI上でのButtonのMaterial */ - sealed abstract class Server(val uiLabel: String, val identifier: String, - val chestSlotRef: Int, val material: Material) extends EnumEntry + sealed abstract class Server( + val uiLabel: String, + val identifier: String, + val chestSlotRef: Int, + val material: Material + ) extends EnumEntry case object Server extends Enum[Server] { - case object ARCADIA extends Server(s"$YELLOW${BOLD}アルカディア", "s1", ChestSlotRef(0, 0), Material.DIAMOND_PICKAXE) + case object ARCADIA + extends Server( + s"$YELLOW${BOLD}アルカディア", + "s1", + ChestSlotRef(0, 0), + Material.DIAMOND_PICKAXE + ) - case object EDEN extends Server(s"$YELLOW${BOLD}エデン", "s2", ChestSlotRef(0, 1), Material.DIAMOND_SPADE) + case object EDEN + extends Server(s"$YELLOW${BOLD}エデン", "s2", ChestSlotRef(0, 1), Material.DIAMOND_SPADE) - case object VALHALLA extends Server(s"$YELLOW${BOLD}ヴァルハラ", "s3", ChestSlotRef(0, 2), Material.DIAMOND_AXE) + case object VALHALLA + extends Server(s"$YELLOW${BOLD}ヴァルハラ", "s3", ChestSlotRef(0, 2), Material.DIAMOND_AXE) - case object PUBLIC extends Server(s"$GREEN${BOLD}公共施設", "s7", ChestSlotRef(0, 8), Material.DIAMOND) + case object PUBLIC + extends Server(s"$GREEN${BOLD}公共施設", "s7", ChestSlotRef(0, 8), Material.DIAMOND) - case object SEICHI extends Server(s"$YELLOW${BOLD}整地専用", "s5", ChestSlotRef(0, 4), Material.IRON_PICKAXE) + case object SEICHI + extends Server(s"$YELLOW${BOLD}整地専用", "s5", ChestSlotRef(0, 4), Material.IRON_PICKAXE) val values: IndexedSeq[Server] = findValues @@ -61,36 +79,42 @@ object ServerSwitchMenu extends Menu { override val frame: MenuFrame = MenuFrame(2.chestRows, s"$DARK_RED${BOLD}サーバーを選択してください") val serverButtonLayout: MenuSlotLayout = { - val layoutMap = Server.values.map { server => - val slotIndex = server.chestSlotRef - val iconItemStack = new IconItemStackBuilder(server.material) - .title(server.uiLabel + "サーバー") - .lore { - // 整地専用サーバーの場合は上級者向けのサーバーである旨を通知 - if (server.identifier == "s5") - List("上級者向けのサーバー", "始めたての頃は他のサーバーがおすすめ").map { str => s"$RESET$BLUE$str" } - else Nil - } - .enchanted() - .build() - val button = Button( - iconItemStack, - LeftClickButtonEffect { - ComputedEffect(_ => { - connectToServerEffect(server.identifier) - }) - } - ) - slotIndex -> button - }.toMap + val layoutMap = Server + .values + .map { server => + val slotIndex = server.chestSlotRef + val iconItemStack = new IconItemStackBuilder(server.material) + .title(server.uiLabel + "サーバー") + .lore { + // 整地専用サーバーの場合は上級者向けのサーバーである旨を通知 + if (server.identifier == "s5") + List("上級者向けのサーバー", "始めたての頃は他のサーバーがおすすめ").map { str => s"$RESET$BLUE$str" } + else Nil + } + .enchanted() + .build() + val button = Button( + iconItemStack, + LeftClickButtonEffect { + ComputedEffect(_ => { + connectToServerEffect(server.identifier) + }) + } + ) + slotIndex -> button + } + .toMap MenuSlotLayout(layoutMap) } /** - * @return `player`からメニューの[[MenuSlotLayout]]を計算する[[IO]] + * @return + * `player`からメニューの[[MenuSlotLayout]]を計算する[[IO]] */ - override def computeMenuLayout(player: Player)(implicit environment: Environment): IO[MenuSlotLayout] = + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = IO.pure { import environment._ diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/TopLevelRouter.scala b/src/main/scala/com/github/unchama/seichiassist/menus/TopLevelRouter.scala index eb82503812..2576cce1ba 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/TopLevelRouter.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/TopLevelRouter.scala @@ -6,17 +6,32 @@ import com.github.unchama.menuinventory.router.CanOpen import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.menus.HomeMenu.ConfirmationMenu import com.github.unchama.seichiassist.menus.achievement.group.AchievementGroupMenu -import com.github.unchama.seichiassist.menus.achievement.{AchievementCategoryMenu, AchievementMenu} -import com.github.unchama.seichiassist.menus.minestack.{CategorizedMineStackMenu, MineStackMainMenu} +import com.github.unchama.seichiassist.menus.achievement.{ + AchievementCategoryMenu, + AchievementMenu +} +import com.github.unchama.seichiassist.menus.minestack.{ + CategorizedMineStackMenu, + MineStackMainMenu +} import com.github.unchama.seichiassist.menus.ranking.{RankingMenu, RankingRootMenu} -import com.github.unchama.seichiassist.menus.skill.{ActiveSkillEffectMenu, ActiveSkillMenu, PassiveSkillMenu, PremiumPointTransactionHistoryMenu} +import com.github.unchama.seichiassist.menus.skill.{ + ActiveSkillEffectMenu, + ActiveSkillMenu, + PassiveSkillMenu, + PremiumPointTransactionHistoryMenu +} import com.github.unchama.seichiassist.menus.stickmenu.{FirstPage, SecondPage} +import com.github.unchama.seichiassist.subsystems.anywhereender.AnywhereEnderChestAPI import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountAPI import com.github.unchama.seichiassist.subsystems.breakcount.domain.SeichiAmountData import com.github.unchama.seichiassist.subsystems.breakcountbar.BreakCountBarAPI import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountData import com.github.unchama.seichiassist.subsystems.discordnotification.DiscordNotificationAPI -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.{FastDiggingEffectApi, FastDiggingSettingsApi} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.{ + FastDiggingEffectApi, + FastDiggingSettingsApi +} import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.FourDimensionalPocketApi import com.github.unchama.seichiassist.subsystems.gachapoint.GachaPointApi import com.github.unchama.seichiassist.subsystems.mana.ManaApi @@ -36,52 +51,76 @@ trait TopLevelRouter[F[_]] { object TopLevelRouter { - def apply(implicit - javaTime: JavaTime[IO], - layoutPreparationContext: LayoutPreparationContext, - onMainThread: OnMinecraftServerThread[IO], - breakCountApi: BreakCountAPI[IO, SyncIO, Player], - breakCountBarAPI: BreakCountBarAPI[SyncIO, Player], - manaApi: ManaApi[IO, SyncIO, Player], - assortedRankingApi: AssortedRankingApi[IO], - gachaPointApi: GachaPointApi[IO, SyncIO, Player], - fastDiggingEffectApi: FastDiggingEffectApi[IO, Player], - fastDiggingSettingsApi: FastDiggingSettingsApi[IO, Player], - fourDimensionalPocketApi: FourDimensionalPocketApi[IO, Player], - globalNotification: DiscordNotificationAPI[IO], - subHomeReadApi: SubHomeReadAPI[IO]): TopLevelRouter[IO] = new TopLevelRouter[IO] { + def apply( + implicit javaTime: JavaTime[IO], + layoutPreparationContext: LayoutPreparationContext, + onMainThread: OnMinecraftServerThread[IO], + breakCountApi: BreakCountAPI[IO, SyncIO, Player], + breakCountBarAPI: BreakCountBarAPI[SyncIO, Player], + manaApi: ManaApi[IO, SyncIO, Player], + assortedRankingApi: AssortedRankingApi[IO], + gachaPointApi: GachaPointApi[IO, SyncIO, Player], + fastDiggingEffectApi: FastDiggingEffectApi[IO, Player], + fastDiggingSettingsApi: FastDiggingSettingsApi[IO, Player], + fourDimensionalPocketApi: FourDimensionalPocketApi[IO, Player], + globalNotification: DiscordNotificationAPI[IO], + subHomeReadApi: SubHomeReadAPI[IO], + enderChestAccessApi: AnywhereEnderChestAPI[IO] + ): TopLevelRouter[IO] = new TopLevelRouter[IO] { import assortedRankingApi._ implicit lazy val secondPageEnv: SecondPage.Environment = new SecondPage.Environment - implicit lazy val mineStackMainMenuEnv: MineStackMainMenu.Environment = new MineStackMainMenu.Environment - implicit lazy val categorizedMineStackMenuEnv: CategorizedMineStackMenu.Environment = new CategorizedMineStackMenu.Environment + implicit lazy val mineStackMainMenuEnv: MineStackMainMenu.Environment = + new MineStackMainMenu.Environment + implicit lazy val categorizedMineStackMenuEnv: CategorizedMineStackMenu.Environment = + new CategorizedMineStackMenu.Environment implicit lazy val regionMenuEnv: RegionMenu.Environment = () - implicit lazy val activeSkillMenuEnv: ActiveSkillMenu.Environment = new ActiveSkillMenu.Environment - implicit lazy val activeSkillEffectMenuEnv: ActiveSkillEffectMenu.Environment = new ActiveSkillEffectMenu.Environment - implicit lazy val premiumPointTransactionHistoryMenuEnv: PremiumPointTransactionHistoryMenu.Environment = new PremiumPointTransactionHistoryMenu.Environment - implicit lazy val serverSwitchMenuEnv: ServerSwitchMenu.Environment = new ServerSwitchMenu.Environment - implicit lazy val achievementMenuEnv: AchievementMenu.Environment = new AchievementMenu.Environment + implicit lazy val activeSkillMenuEnv: ActiveSkillMenu.Environment = + new ActiveSkillMenu.Environment + implicit lazy val activeSkillEffectMenuEnv: ActiveSkillEffectMenu.Environment = + new ActiveSkillEffectMenu.Environment + implicit lazy val premiumPointTransactionHistoryMenuEnv + : PremiumPointTransactionHistoryMenu.Environment = + new PremiumPointTransactionHistoryMenu.Environment + implicit lazy val serverSwitchMenuEnv: ServerSwitchMenu.Environment = + new ServerSwitchMenu.Environment + implicit lazy val achievementMenuEnv: AchievementMenu.Environment = + new AchievementMenu.Environment implicit lazy val homeMenuEnv: HomeMenu.Environment = new HomeMenu.Environment - implicit lazy val homeConfirmationMenuEnv: HomeMenu.ConfirmationMenu.Environment = new ConfirmationMenu.Environment - implicit lazy val achievementCategoryMenuEnv: AchievementCategoryMenu.Environment = new AchievementCategoryMenu.Environment - implicit lazy val achievementGroupMenuEnv: AchievementGroupMenu.Environment = new AchievementGroupMenu.Environment - implicit lazy val passiveSkillMenuEnv: PassiveSkillMenu.Environment = new PassiveSkillMenu.Environment + implicit lazy val homeConfirmationMenuEnv: HomeMenu.ConfirmationMenu.Environment = + new ConfirmationMenu.Environment + implicit lazy val achievementCategoryMenuEnv: AchievementCategoryMenu.Environment = + new AchievementCategoryMenu.Environment + implicit lazy val achievementGroupMenuEnv: AchievementGroupMenu.Environment = + new AchievementGroupMenu.Environment + implicit lazy val passiveSkillMenuEnv: PassiveSkillMenu.Environment = + new PassiveSkillMenu.Environment - implicit lazy val seichiRankingMenuEnv: RankingMenu[SeichiAmountData]#Environment = new RankingMenu.Environment - implicit lazy val buildRankingMenuEnv: RankingMenu[BuildAmountData]#Environment = new RankingMenu.Environment - implicit lazy val loginTimeRankingMenuEnv: RankingMenu[LoginTime]#Environment = new RankingMenu.Environment - implicit lazy val voteCountRankingMenuEnv: RankingMenu[VoteCount]#Environment = new RankingMenu.Environment + implicit lazy val seichiRankingMenuEnv: RankingMenu[SeichiAmountData]#Environment = + new RankingMenu.Environment + implicit lazy val buildRankingMenuEnv: RankingMenu[BuildAmountData]#Environment = + new RankingMenu.Environment + implicit lazy val loginTimeRankingMenuEnv: RankingMenu[LoginTime]#Environment = + new RankingMenu.Environment + implicit lazy val voteCountRankingMenuEnv: RankingMenu[VoteCount]#Environment = + new RankingMenu.Environment - implicit lazy val rankingRootMenuEnv: RankingRootMenu.Environment = new RankingRootMenu.Environment + implicit lazy val rankingRootMenuEnv: RankingRootMenu.Environment = + new RankingRootMenu.Environment implicit lazy val stickMenuEnv: FirstPage.Environment = new FirstPage.Environment implicit lazy val ioCanOpenAchievementGroupMenu: IO CanOpen AchievementGroupMenu = _.open - implicit lazy val ioCanOpenHomeConfirmationMenu: IO CanOpen HomeMenu.ConfirmationMenu = _.open - implicit lazy val ioCanOpenAchievementCategoryMenu: IO CanOpen AchievementCategoryMenu = _.open - implicit lazy val ioCanOpenPremiumPointTransactionHistoryMenu: IO CanOpen PremiumPointTransactionHistoryMenu = _.open - implicit lazy val ioCanOpenActiveSkillEffectMenu: IO CanOpen ActiveSkillEffectMenu.type = _.open - implicit lazy val ioCanOpenCategorizedMineStackMenu: IO CanOpen CategorizedMineStackMenu = _.open + implicit lazy val ioCanOpenHomeConfirmationMenu: IO CanOpen HomeMenu.ConfirmationMenu = + _.open + implicit lazy val ioCanOpenAchievementCategoryMenu: IO CanOpen AchievementCategoryMenu = + _.open + implicit lazy val ioCanOpenPremiumPointTransactionHistoryMenu + : IO CanOpen PremiumPointTransactionHistoryMenu = _.open + implicit lazy val ioCanOpenActiveSkillEffectMenu: IO CanOpen ActiveSkillEffectMenu.type = + _.open + implicit lazy val ioCanOpenCategorizedMineStackMenu: IO CanOpen CategorizedMineStackMenu = + _.open implicit lazy val ioCanOpenSecondPage: IO CanOpen SecondPage.type = _.open implicit lazy val ioCanOpenMineStackMenu: IO CanOpen MineStackMainMenu.type = _.open implicit lazy val ioCanOpenRegionMenu: IO CanOpen RegionMenu.type = _.open @@ -90,8 +129,10 @@ object TopLevelRouter { implicit lazy val ioCanOpenHomeMenu: IO CanOpen HomeMenu.type = _.open implicit lazy val ioCanOpenPassiveSkillMenu: IO CanOpen PassiveSkillMenu.type = _.open - implicit lazy val ioCanOpenSeichiRankingMenu: IO CanOpen RankingMenu[SeichiAmountData] = _.open - implicit lazy val ioCanOpenBuildRankingMenu: IO CanOpen RankingMenu[BuildAmountData] = _.open + implicit lazy val ioCanOpenSeichiRankingMenu: IO CanOpen RankingMenu[SeichiAmountData] = + _.open + implicit lazy val ioCanOpenBuildRankingMenu: IO CanOpen RankingMenu[BuildAmountData] = + _.open implicit lazy val ioCanOpenLoginTimeRankingMenu: IO CanOpen RankingMenu[LoginTime] = _.open implicit lazy val ioCanOpenVoteCountRankingMenu: IO CanOpen RankingMenu[VoteCount] = _.open implicit lazy val ioCanOpenRankingRootMenu: IO CanOpen RankingRootMenu.type = _.open diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/achievement/AchievementCategoryMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/achievement/AchievementCategoryMenu.scala index bb9263776a..587499d69b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/achievement/AchievementCategoryMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/achievement/AchievementCategoryMenu.scala @@ -9,9 +9,15 @@ import com.github.unchama.menuinventory.{ChestSlotRef, Menu, MenuFrame, MenuSlot import com.github.unchama.seichiassist.SkullOwners import com.github.unchama.seichiassist.achievement.hierarchy.AchievementCategory._ import com.github.unchama.seichiassist.achievement.hierarchy.AchievementGroup._ -import com.github.unchama.seichiassist.achievement.hierarchy.{AchievementCategory, AchievementGroup} +import com.github.unchama.seichiassist.achievement.hierarchy.{ + AchievementCategory, + AchievementGroup +} import com.github.unchama.seichiassist.effects.player.CommonSoundEffects -import com.github.unchama.seichiassist.menus.achievement.AchievementCategoryMenu.{buttonFor, groupsLayoutFor} +import com.github.unchama.seichiassist.menus.achievement.AchievementCategoryMenu.{ + buttonFor, + groupsLayoutFor +} import com.github.unchama.seichiassist.menus.achievement.group.AchievementGroupMenu import com.github.unchama.seichiassist.menus.{ColorScheme, CommonButtons} import org.bukkit.ChatColor._ @@ -31,9 +37,7 @@ object AchievementCategoryMenu { ChestSlotRef(1, 5) -> (BrokenBlockRanking, Material.DIAMOND_PICKAXE) ) case Building => - Map( - ChestSlotRef(1, 4) -> (PlacedBlockAmount, Material.BIRCH_WOOD_STAIRS) - ) + Map(ChestSlotRef(1, 4) -> (PlacedBlockAmount, Material.BIRCH_WOOD_STAIRS)) case Login => Map( ChestSlotRef(1, 1) -> (PlayTime, Material.COMPASS), @@ -50,39 +54,34 @@ object AchievementCategoryMenu { Map( ChestSlotRef(1, 2) -> (OfficialEvent, Material.BLAZE_POWDER), ChestSlotRef(1, 4) -> (VoteCounts, Material.YELLOW_FLOWER), - ChestSlotRef(1, 6) -> (Secrets, Material.DIAMOND_BARDING), + ChestSlotRef(1, 6) -> (Secrets, Material.DIAMOND_BARDING) ) } - def buttonFor(groupRepr: AchievementGroupRepr) - (implicit ioCanOpenGroupMenu: IO CanOpen AchievementGroupMenu): Button = { + def buttonFor( + groupRepr: AchievementGroupRepr + )(implicit ioCanOpenGroupMenu: IO CanOpen AchievementGroupMenu): Button = { val (group, material) = groupRepr val partialBuilder = - new IconItemStackBuilder(material) - .title(ColorScheme.navigation(s"実績「${group.name}」")) + new IconItemStackBuilder(material).title(ColorScheme.navigation(s"実績「${group.name}」")) if (AchievementGroupMenu.sequentialEntriesIn(group).nonEmpty) { Button( - partialBuilder - .lore(s"${RED}獲得状況を表示します。") - .build(), + partialBuilder.lore(s"${RED}獲得状況を表示します。").build(), LeftClickButtonEffect( CommonSoundEffects.menuTransitionFenceSound, ioCanOpenGroupMenu.open(AchievementGroupMenu(group)) ) ) } else { - Button( - partialBuilder - .lore(s"${RED}獲得状況を表示します。※未実装") - .build() - ) + Button(partialBuilder.lore(s"${RED}獲得状況を表示します。※未実装").build()) } } - class Environment(implicit - val ioCanOpenAchievementMainMenu: IO CanOpen AchievementMenu.type, - val ioCanOpenAchievementGroupMenu: IO CanOpen AchievementGroupMenu) + class Environment( + implicit val ioCanOpenAchievementMainMenu: IO CanOpen AchievementMenu.type, + val ioCanOpenAchievementGroupMenu: IO CanOpen AchievementGroupMenu + ) } @@ -94,28 +93,24 @@ case class AchievementCategoryMenu(category: AchievementCategory) extends Menu { val frame: MenuFrame = MenuFrame(4.chestRows, s"$DARK_PURPLE${BOLD}カテゴリ「${category.name}」") - override def computeMenuLayout(player: Player)(implicit environment: Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { import environment._ import eu.timepit.refined.auto._ val groupButtons = - groupsLayoutFor(category) - .view - .mapValues(repr => buttonFor(repr)).toMap + groupsLayoutFor(category).view.mapValues(repr => buttonFor(repr)).toMap val toMainMenuButton: Button = CommonButtons.transferButton( new SkullItemStackBuilder(SkullOwners.MHF_ArrowLeft), "実績・二つ名メニューへ", - AchievementMenu, + AchievementMenu ) IO.pure { - MenuSlotLayout( - groupButtons ++ Map( - ChestSlotRef(3, 0) -> toMainMenuButton - ) - ) + MenuSlotLayout(groupButtons ++ Map(ChestSlotRef(3, 0) -> toMainMenuButton)) } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/achievement/AchievementMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/achievement/AchievementMenu.scala index b5f8973d71..6bc773881a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/achievement/AchievementMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/achievement/AchievementMenu.scala @@ -25,10 +25,11 @@ object AchievementMenu extends Menu { import com.github.unchama.menuinventory.syntax._ import eu.timepit.refined.auto._ - class Environment(implicit - val ioCanOpenStickMenu: IO CanOpen FirstPage.type, - val ioCanOpenCategoryMenu: IO CanOpen AchievementCategoryMenu, - val ioOnMainThread: OnMinecraftServerThread[IO]) + class Environment( + implicit val ioCanOpenStickMenu: IO CanOpen FirstPage.type, + val ioCanOpenCategoryMenu: IO CanOpen AchievementCategoryMenu, + val ioOnMainThread: OnMinecraftServerThread[IO] + ) override val frame: MenuFrame = MenuFrame(4.chestRows, s"$DARK_PURPLE${BOLD}実績・二つ名システム") @@ -43,15 +44,17 @@ object AchievementMenu extends Menu { ChestSlotRef(2, 4) -> (Specials, Material.EYE_OF_ENDER) ) - def buttonFor(categoryRepr: AchievementCategoryRepr) - (implicit ioCanOpenCategoryMenu: IO CanOpen AchievementCategoryMenu): Button = + def buttonFor( + categoryRepr: AchievementCategoryRepr + )(implicit ioCanOpenCategoryMenu: IO CanOpen AchievementCategoryMenu): Button = categoryRepr match { case (category, material) => val includedGroups = AchievementCategoryMenu.groupsLayoutFor(category).values.map(_._1) val partialBuilder = - new IconItemStackBuilder(material).title(ColorScheme.navigation(s"カテゴリ「${category.name}」")) + new IconItemStackBuilder(material) + .title(ColorScheme.navigation(s"カテゴリ「${category.name}」")) if (includedGroups.nonEmpty) { Button( @@ -73,19 +76,24 @@ object AchievementMenu extends Menu { } } - override def computeMenuLayout(player: Player)(implicit environment: Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { import environment._ - val categoryButtonsSection = categoryLayout.view.mapValues(category => buttonFor(category)).toMap + val categoryButtonsSection = + categoryLayout.view.mapValues(category => buttonFor(category)).toMap val toggleTitleToPlayerLevelButton = Button( new IconItemStackBuilder(Material.REDSTONE_TORCH_ON) .title(ColorScheme.navigation("整地Lvを表示")) - .lore(List( - s"${RED}このボタンをクリックすると、", - s"$RED「整地Lv」に表示を切り替えます。", - s"$YELLOW※反映されるまで最大1分ほどかかります。" - )) + .lore( + List( + s"${RED}このボタンをクリックすると、", + s"$RED「整地Lv」に表示を切り替えます。", + s"$YELLOW※反映されるまで最大1分ほどかかります。" + ) + ) .build(), action.LeftClickButtonEffect( FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f), diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/achievement/group/AchievementGroupMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/achievement/group/AchievementGroupMenu.scala index bd75dea838..a9839f17b5 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/achievement/group/AchievementGroupMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/achievement/group/AchievementGroupMenu.scala @@ -18,8 +18,10 @@ import org.bukkit.entity.Player object AchievementGroupMenu { - class Environment(implicit val ioCanOpenGroupMenu: IO CanOpen AchievementGroupMenu, - val ioCanOpenCategoryMenu: IO CanOpen AchievementCategoryMenu) + class Environment( + implicit val ioCanOpenGroupMenu: IO CanOpen AchievementGroupMenu, + val ioCanOpenCategoryMenu: IO CanOpen AchievementCategoryMenu + ) val sequentialEntriesIn: AchievementGroup => List[GroupMenuEntry] = CachedFunction { @@ -68,7 +70,8 @@ case class AchievementGroupMenu(group: AchievementGroup, pageNumber: Int = 1) ex import com.github.unchama.menuinventory.syntax._ override type Environment = AchievementGroupMenu.Environment - override val frame: MenuFrame = MenuFrame(4.chestRows, ColorScheme.purpleBold(s"実績「${group.name}」")) + override val frame: MenuFrame = + MenuFrame(4.chestRows, ColorScheme.purpleBold(s"実績「${group.name}」")) private val groupEntries = sequentialEntriesIn(group) private val entriesToDisplay = { @@ -84,10 +87,11 @@ case class AchievementGroupMenu(group: AchievementGroup, pageNumber: Int = 1) ex private val groupAchievementsCount = groupEntries.size private val maxPageNumber = Math.ceil(groupAchievementsCount / 27.0).toInt - override def open(implicit - environment: AchievementGroupMenu.Environment, - ctx: LayoutPreparationContext, - onMainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = { + override def open( + implicit environment: AchievementGroupMenu.Environment, + ctx: LayoutPreparationContext, + onMainThread: OnMinecraftServerThread[IO] + ): TargetedEffect[Player] = { // redirect if (entriesToDisplay.isEmpty) { if (groupAchievementsCount == 0) { @@ -98,12 +102,17 @@ case class AchievementGroupMenu(group: AchievementGroup, pageNumber: Int = 1) ex } else super.open } - override def computeMenuLayout(player: Player)(implicit environment: AchievementGroupMenu.Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout( + player: Player + )(implicit environment: AchievementGroupMenu.Environment): IO[MenuSlotLayout] = { import cats.implicits._ import environment._ import eu.timepit.refined.auto._ - def buttonToTransferTo(newPageNumber: Int, skullOwnerReference: SkullOwnerReference): Button = + def buttonToTransferTo( + newPageNumber: Int, + skullOwnerReference: SkullOwnerReference + ): Button = CommonButtons.transferButton( new SkullItemStackBuilder(skullOwnerReference), s"${newPageNumber}ページ目へ", @@ -111,13 +120,9 @@ case class AchievementGroupMenu(group: AchievementGroup, pageNumber: Int = 1) ex ) /** - * 上位メニューはこのメニューを参照していて、 - * このセクションのボタンは上位メニューを参照するので、 - * 貪欲に計算すると計算が再帰する。 + * 上位メニューはこのメニューを参照していて、 このセクションのボタンは上位メニューを参照するので、 貪欲に計算すると計算が再帰する。 * - * `computeMenuLayout`にて初めて参照されるため、 - * lazyにすることで実際にプレーヤーがこのメニューを開くまで評価されず、 - * 再帰自体は回避される。 + * `computeMenuLayout`にて初めて参照されるため、 lazyにすることで実際にプレーヤーがこのメニューを開くまで評価されず、 再帰自体は回避される。 */ lazy val toCategoryMenuButtonSection = Map( ChestSlotRef(3, 0) -> CommonButtons.transferButton( @@ -137,7 +142,9 @@ case class AchievementGroupMenu(group: AchievementGroup, pageNumber: Int = 1) ex lazy val nextPageButtonSection = if (pageNumber < maxPageNumber) { - Map(ChestSlotRef(3, 8) -> buttonToTransferTo(pageNumber + 1, SkullOwners.MHF_ArrowRight)) + Map( + ChestSlotRef(3, 8) -> buttonToTransferTo(pageNumber + 1, SkullOwners.MHF_ArrowRight) + ) } else { Map() } @@ -150,10 +157,10 @@ case class AchievementGroupMenu(group: AchievementGroup, pageNumber: Int = 1) ex for { dynamicPart <- dynamicPartComputation combinedLayout = - toCategoryMenuButtonSection ++ - previousPageButtonSection ++ - nextPageButtonSection ++ - dynamicPart + toCategoryMenuButtonSection ++ + previousPageButtonSection ++ + nextPageButtonSection ++ + dynamicPart } yield MenuSlotLayout(combinedLayout) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/achievement/group/AchievementGroupMenuButtons.scala b/src/main/scala/com/github/unchama/seichiassist/menus/achievement/group/AchievementGroupMenuButtons.scala index 0d46f98186..02a6355957 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/achievement/group/AchievementGroupMenuButtons.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/achievement/group/AchievementGroupMenuButtons.scala @@ -8,8 +8,17 @@ import com.github.unchama.menuinventory.slot.button.action.LeftClickButtonEffect import com.github.unchama.menuinventory.slot.button.{Button, RecomputedButton} import com.github.unchama.seichiassist.SeichiAssist import com.github.unchama.seichiassist.achievement.NicknameMapping.NicknameCombination -import com.github.unchama.seichiassist.achievement.SeichiAchievement.{AutoUnlocked, Hidden, ManuallyUnlocked, Normal} -import com.github.unchama.seichiassist.achievement.{AchievementConditions, NicknameMapping, SeichiAchievement} +import com.github.unchama.seichiassist.achievement.SeichiAchievement.{ + AutoUnlocked, + Hidden, + ManuallyUnlocked, + Normal +} +import com.github.unchama.seichiassist.achievement.{ + AchievementConditions, + NicknameMapping, + SeichiAchievement +} import com.github.unchama.seichiassist.menus.ColorScheme import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.player.FocusedSoundEffect @@ -19,114 +28,121 @@ import org.bukkit.{Material, Sound} object AchievementGroupMenuButtons { val achievementButton: ((SeichiAchievement, Boolean)) => Button = - CachedFunction { case (achievement, hasUnlocked) => - val itemStack = { - val material = if (hasUnlocked) Material.DIAMOND_BLOCK else Material.BEDROCK - val title = { - val displayTitleName = - if (hasUnlocked) NicknameMapping.getTitleFor(achievement) else "???" - - s"$YELLOW$UNDERLINE${BOLD}No${achievement.id}「$displayTitleName」" - } - - val lore = { - val conditionDescriptions = - achievement match { - case normal: SeichiAchievement.Normal[_] => - List(normal.condition.parameterizedDescription) - case hidden: SeichiAchievement.Hidden[_] => - val description = - if (hasUnlocked) - hidden.condition.underlying.parameterizedDescription - else - hidden.condition.maskedDescription - List(description) - case g: SeichiAchievement.GrantedByConsole => - List(g.condition) ++ g.explanation.getOrElse(Nil) - } + CachedFunction { + case (achievement, hasUnlocked) => + val itemStack = { + val material = if (hasUnlocked) Material.DIAMOND_BLOCK else Material.BEDROCK + val title = { + val displayTitleName = + if (hasUnlocked) NicknameMapping.getTitleFor(achievement) else "???" + + s"$YELLOW$UNDERLINE${BOLD}No${achievement.id}「$displayTitleName」" + } - val unlockSchemeDescription = - achievement match { - case _: AutoUnlocked => - List(s"$RESET$RED※この実績は自動解禁式です。") - case m: ManuallyUnlocked => - m match { - case _: Hidden[_] => - List(s"$RESET$RED※この実績は手動解禁式です。") - case _ => + val lore = { + val conditionDescriptions = + achievement match { + case normal: SeichiAchievement.Normal[_] => + List(normal.condition.parameterizedDescription) + case hidden: SeichiAchievement.Hidden[_] => + val description = if (hasUnlocked) - List() + hidden.condition.underlying.parameterizedDescription else - List(s"$RESET$GREEN※クリックで実績に挑戦できます") - } - case _ => - List(s"$RESET$RED※この実績は配布解禁式です。") - } - - val hiddenDescription = - achievement match { - case _: Hidden[_] => List(s"$RESET${AQUA}こちらは【隠し実績】となります") - case _ => Nil - } + hidden.condition.maskedDescription + List(description) + case g: SeichiAchievement.GrantedByConsole => + List(g.condition) ++ g.explanation.getOrElse(Nil) + } + + val unlockSchemeDescription = + achievement match { + case _: AutoUnlocked => + List(s"$RESET$RED※この実績は自動解禁式です。") + case m: ManuallyUnlocked => + m match { + case _: Hidden[_] => + List(s"$RESET$RED※この実績は手動解禁式です。") + case _ => + if (hasUnlocked) + List() + else + List(s"$RESET$GREEN※クリックで実績に挑戦できます") + } + case _ => + List(s"$RESET$RED※この実績は配布解禁式です。") + } + + val hiddenDescription = + achievement match { + case _: Hidden[_] => List(s"$RESET${AQUA}こちらは【隠し実績】となります") + case _ => Nil + } + + conditionDescriptions.map(s"$RESET$RED" + _) ++ + unlockSchemeDescription ++ + hiddenDescription + } - conditionDescriptions.map(s"$RESET$RED" + _) ++ - unlockSchemeDescription ++ - hiddenDescription + new IconItemStackBuilder(material).title(title).lore(lore).build() } - new IconItemStackBuilder(material) - .title(title) - .lore(lore) - .build() - } - - val clickEffect = { - import com.github.unchama.targetedeffect._ - val clickSound = FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) - - val effect = - if (hasUnlocked) { - def setNickname(player: Player): Unit = { - val NicknameCombination(firstId, secondId, thirdId) = - NicknameMapping.getNicknameCombinationFor(achievement) - - SeichiAssist - .playermap(player.getUniqueId) - .updateNickname(firstId.getOrElse(0), secondId.getOrElse(0), thirdId.getOrElse(0)) - player.sendMessage(s"二つ名「${NicknameMapping.getTitleFor(achievement)}」が設定されました。") + val clickEffect = { + import com.github.unchama.targetedeffect._ + val clickSound = FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) + + val effect = + if (hasUnlocked) { + def setNickname(player: Player): Unit = { + val NicknameCombination(firstId, secondId, thirdId) = + NicknameMapping.getNicknameCombinationFor(achievement) + + SeichiAssist + .playermap(player.getUniqueId) + .updateNickname( + firstId.getOrElse(0), + secondId.getOrElse(0), + thirdId.getOrElse(0) + ) + player.sendMessage(s"二つ名「${NicknameMapping.getTitleFor(achievement)}」が設定されました。") + } + + TargetedEffect.delay[IO, Player](setNickname) + } else { + achievement match { + case _: AutoUnlocked => + MessageEffect(s"${RED}この実績は自動解禁式です。毎分の処理をお待ちください。") + case achievement: ManuallyUnlocked => + achievement match { + case achievement: Normal[_] => + Kleisli { player: Player => + for { + shouldUnlock <- achievement.condition.shouldUnlock(player) + _ <- + if (shouldUnlock) IO { + SeichiAssist + .playermap(player.getUniqueId) + .TitleFlags + .addOne(achievement.id) + player.sendMessage(s"実績No${achievement.id}を解除しました!おめでとうございます!") + } + else { + MessageEffect(s"${RED}実績No${achievement.id}は条件を満たしていません。")(player) + } + } yield () + } + case _ => + MessageEffect(s"$RESET$RED※この実績は手動解禁式です。") + } + case _ => + MessageEffect(s"$RED※この実績は配布解禁式です。運営チームからの配布タイミングを逃さないようご注意ください。") + } } - TargetedEffect.delay[IO, Player](setNickname) - } else { - achievement match { - case _: AutoUnlocked => - MessageEffect(s"${RED}この実績は自動解禁式です。毎分の処理をお待ちください。") - case achievement: ManuallyUnlocked => - achievement match { - case achievement: Normal[_] => - Kleisli { player: Player => - for { - shouldUnlock <- achievement.condition.shouldUnlock(player) - _ <- if (shouldUnlock) IO { - SeichiAssist.playermap(player.getUniqueId).TitleFlags.addOne(achievement.id) - player.sendMessage(s"実績No${achievement.id}を解除しました!おめでとうございます!") - } else { - MessageEffect(s"${RED}実績No${achievement.id}は条件を満たしていません。")(player) - } - } yield () - } - case _ => - MessageEffect(s"$RESET$RED※この実績は手動解禁式です。") - } - case _ => - MessageEffect(s"$RED※この実績は配布解禁式です。運営チームからの配布タイミングを逃さないようご注意ください。") - } - } - - SequentialEffect(clickSound, effect) - } + SequentialEffect(clickSound, effect) + } - Button(itemStack, LeftClickButtonEffect(clickEffect)) + Button(itemStack, LeftClickButtonEffect(clickEffect)) } import com.github.unchama.targetedeffect._ @@ -139,30 +155,44 @@ object AchievementGroupMenuButtons { LeftClickButtonEffect( FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f), MessageEffect("お疲れ様でした!今日のお給料の代わりに二つ名をどうぞ!"), - TargetedEffect.delay { player => SeichiAssist.playermap(player.getUniqueId).TitleFlags.addOne(8003) } + TargetedEffect.delay { player => + SeichiAssist.playermap(player.getUniqueId).TitleFlags.addOne(8003) + } ) ) - import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.{layoutPreparationContext, onMainThread} + import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.{ + layoutPreparationContext, + onMainThread + } def entryComputationFor(viewer: Player): GroupMenuEntry => IO[Button] = { - case AchievementEntry(achievement) => RecomputedButton { - for { - hasObtained <- IO { SeichiAssist.playermap(viewer.getUniqueId).TitleFlags.contains(achievement.id) } - shouldDisplayToUI <- achievement match { - case hidden: Hidden[_] => hidden.condition.shouldDisplayToUI(viewer) - case _ => IO.pure(true) - } - } yield if (hasObtained || shouldDisplayToUI) achievementButton(achievement, hasObtained) else Button.empty - } + case AchievementEntry(achievement) => + RecomputedButton { + for { + hasObtained <- IO { + SeichiAssist.playermap(viewer.getUniqueId).TitleFlags.contains(achievement.id) + } + shouldDisplayToUI <- achievement match { + case hidden: Hidden[_] => hidden.condition.shouldDisplayToUI(viewer) + case _ => IO.pure(true) + } + } yield + if (hasObtained || shouldDisplayToUI) achievementButton(achievement, hasObtained) + else Button.empty + } - case Achievement8003UnlockEntry => RecomputedButton { - for { - hasObtained8003 <- IO { SeichiAssist.playermap(viewer.getUniqueId).TitleFlags.contains(8003) } - shouldDisplayToUI <- - if (hasObtained8003) IO.pure(false) - else AchievementConditions.SecretAchievementConditions.unlockConditionFor8003(viewer) - } yield if (shouldDisplayToUI) unlock8003Button else Button.empty - } + case Achievement8003UnlockEntry => + RecomputedButton { + for { + hasObtained8003 <- IO { + SeichiAssist.playermap(viewer.getUniqueId).TitleFlags.contains(8003) + } + shouldDisplayToUI <- + if (hasObtained8003) IO.pure(false) + else + AchievementConditions.SecretAchievementConditions.unlockConditionFor8003(viewer) + } yield if (shouldDisplayToUI) unlock8003Button else Button.empty + } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/achievement/group/GroupMenuEntry.scala b/src/main/scala/com/github/unchama/seichiassist/menus/achievement/group/GroupMenuEntry.scala index 822d20731b..950d4cff57 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/achievement/group/GroupMenuEntry.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/achievement/group/GroupMenuEntry.scala @@ -7,7 +7,8 @@ sealed trait GroupMenuEntry case class AchievementEntry(achievement: SeichiAchievement) extends GroupMenuEntry object AchievementEntry { def within(range: Seq[Int]): List[AchievementEntry] = - SeichiAchievement.values + SeichiAchievement + .values .toList .filter(achievement => range.contains(achievement.id)) .map(AchievementEntry.apply) diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/minestack/CategorizedMineStackMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/minestack/CategorizedMineStackMenu.scala index 9ba8ae5b41..ad59068df7 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/minestack/CategorizedMineStackMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/minestack/CategorizedMineStackMenu.scala @@ -15,17 +15,19 @@ import org.bukkit.entity.Player object CategorizedMineStackMenu { - class Environment(implicit - val ioCanOpenMineStackMainMenu: IO CanOpen MineStackMainMenu.type, - val ioCanOpenCategorizedMenu: IO CanOpen CategorizedMineStackMenu, - val onMainThread: OnMinecraftServerThread[IO]) + class Environment( + implicit val ioCanOpenMineStackMainMenu: IO CanOpen MineStackMainMenu.type, + val ioCanOpenCategorizedMenu: IO CanOpen CategorizedMineStackMenu, + val onMainThread: OnMinecraftServerThread[IO] + ) } /** * カテゴリ別マインスタックメニューで [pageIndex] + 1 ページ目の[Menu] */ -case class CategorizedMineStackMenu(category: MineStackObjectCategory, pageIndex: Int = 0) extends Menu { +case class CategorizedMineStackMenu(category: MineStackObjectCategory, pageIndex: Int = 0) + extends Menu { import com.github.unchama.menuinventory.syntax._ import eu.timepit.refined.auto._ @@ -37,9 +39,14 @@ case class CategorizedMineStackMenu(category: MineStackObjectCategory, pageIndex override type Environment = CategorizedMineStackMenu.Environment - override val frame: MenuFrame = MenuFrame((objectSectionRows + 1).chestRows, s"$DARK_BLUE${BOLD}MineStack(${category.uiLabel})") + override val frame: MenuFrame = MenuFrame( + (objectSectionRows + 1).chestRows, + s"$DARK_BLUE${BOLD}MineStack(${category.uiLabel})" + ) - def mineStackMainMenuButtonSection(implicit ioCanOpenMineStackMainMenu: IO CanOpen MineStackMainMenu.type): Seq[(Int, Button)] = + def mineStackMainMenuButtonSection( + implicit ioCanOpenMineStackMainMenu: IO CanOpen MineStackMainMenu.type + ): Seq[(Int, Button)] = Seq( ChestSlotRef(5, 0) -> CommonButtons.transferButton( new SkullItemStackBuilder(SkullOwners.MHF_ArrowLeft), @@ -49,9 +56,9 @@ case class CategorizedMineStackMenu(category: MineStackObjectCategory, pageIndex ) // ページ操作等のボタンを含むレイアウトセクション - def uiOperationSection(totalNumberOfPages: Int) - (category: MineStackObjectCategory, page: Int) - (implicit environment: Environment): Seq[(Int, Button)] = { + def uiOperationSection(totalNumberOfPages: Int)(category: MineStackObjectCategory, page: Int)( + implicit environment: Environment + ): Seq[(Int, Button)] = { import environment._ def buttonToTransferTo(pageIndex: Int, skullOwnerReference: SkullOwnerReference): Button = @@ -76,9 +83,11 @@ case class CategorizedMineStackMenu(category: MineStackObjectCategory, pageIndex mineStackMainMenuButtonSection ++ previousPageButtonSection ++ nextPageButtonSection } - override def open(implicit environment: CategorizedMineStackMenu.Environment, - ctx: LayoutPreparationContext, - onMainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = DeferredEffect { + override def open( + implicit environment: CategorizedMineStackMenu.Environment, + ctx: LayoutPreparationContext, + onMainThread: OnMinecraftServerThread[IO] + ): TargetedEffect[Player] = DeferredEffect { import MineStackObjectCategory._ for { @@ -96,8 +105,9 @@ case class CategorizedMineStackMenu(category: MineStackObjectCategory, pageIndex } } - override def computeMenuLayout(player: Player) - (implicit environment: Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { import MineStackObjectCategory._ import cats.implicits._ import environment._ @@ -114,15 +124,17 @@ case class CategorizedMineStackMenu(category: MineStackObjectCategory, pageIndex // カテゴリ内のMineStackアイテム取り出しボタンを含むセクションの計算 val categorizedItemSectionComputation = categoryItemList - .slice(mineStackObjectPerPage * pageIndex, mineStackObjectPerPage * pageIndex + mineStackObjectPerPage).toList + .slice( + mineStackObjectPerPage * pageIndex, + mineStackObjectPerPage * pageIndex + mineStackObjectPerPage + ) + .toList .traverse(getMineStackItemButtonOf(_)) .map(_.zipWithIndex.map(_.swap)) // 自動スタック機能トグルボタンを含むセクションの計算 val autoMineStackToggleButtonSectionComputation = - List(ChestSlotRef(5, 4) -> computeAutoMineStackToggleButton) - .map(_.sequence) - .sequence + List(ChestSlotRef(5, 4) -> computeAutoMineStackToggleButton).map(_.sequence).sequence for { categorizedItemSection <- categorizedItemSectionComputation diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/minestack/MineStackButtons.scala b/src/main/scala/com/github/unchama/seichiassist/menus/minestack/MineStackButtons.scala index 99079cbdeb..dc13e8a2d2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/minestack/MineStackButtons.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/minestack/MineStackButtons.scala @@ -22,14 +22,16 @@ private object MineStackButtons { import scala.jdk.CollectionConverters._ import scala.util.chaining._ - implicit class ItemStackOps(val itemStack: ItemStack) extends AnyVal { + implicit class ItemStackOps(private val itemStack: ItemStack) extends AnyVal { def withAmount(amount: Int): ItemStack = itemStack.clone().tap(_.setAmount(amount)) } - implicit class MineStackObjectOps(val mineStackObj: MineStackObj) extends AnyVal { + implicit class MineStackObjectOps(private val mineStackObj: MineStackObj) extends AnyVal { def parameterizedWith(player: Player): ItemStack = { // ガチャ品であり、かつがちゃりんごでも経験値瓶でもなければ - if (mineStackObj.stackType == MineStackObjectCategory.GACHA_PRIZES && mineStackObj.gachaType >= 0) { + if ( + mineStackObj.stackType == MineStackObjectCategory.GACHA_PRIZES && mineStackObj.gachaType >= 0 + ) { val gachaData = SeichiAssist.msgachadatalist(mineStackObj.gachaType) if (gachaData.probability < 0.1) { return mineStackObj.itemStack.clone().tap { cloned => @@ -58,37 +60,45 @@ private[minestack] case class MineStackButtons(player: Player) { import scala.jdk.CollectionConverters._ - def getMineStackItemButtonOf(mineStackObj: MineStackObj) - (implicit onMainThread: OnMinecraftServerThread[IO]): IO[Button] = RecomputedButton(IO { + def getMineStackItemButtonOf( + mineStackObj: MineStackObj + )(implicit onMainThread: OnMinecraftServerThread[IO]): IO[Button] = RecomputedButton(IO { val playerData = SeichiAssist.playermap(getUniqueId) val requiredLevel = SeichiAssist.seichiAssistConfig.getMineStacklevel(mineStackObj.level) import scala.util.chaining._ - val itemStack = mineStackObj.itemStack.clone().tap { itemStack => - import itemStack._ - setItemMeta { - getItemMeta.tap { itemMeta => - import itemMeta._ - setDisplayName { - val name = mineStackObj.uiName.getOrElse(if (hasDisplayName) getDisplayName else getType.toString) - - s"$YELLOW$UNDERLINE$BOLD$name" - } - - setLore { - val stackedAmount = playerData.minestack.getStackedAmountOf(mineStackObj) - - List( - s"$RESET$GREEN${stackedAmount.formatted("%,d")}個", - s"$RESET${DARK_GRAY}Lv${requiredLevel}以上でスタック可能", - s"$RESET$DARK_RED${UNDERLINE}左クリックで1スタック取り出し", - s"$RESET$DARK_AQUA${UNDERLINE}右クリックで1個取り出し" - ).asJava - } + val itemStack = + mineStackObj + .itemStack + .clone() + .tap { + itemStack => + import itemStack._ + setItemMeta { + getItemMeta.tap { itemMeta => + import itemMeta._ + setDisplayName { + val name = mineStackObj + .uiName + .getOrElse(if (hasDisplayName) getDisplayName else getType.toString) + + s"$YELLOW$UNDERLINE$BOLD$name" + } + + setLore { + val stackedAmount = playerData.minestack.getStackedAmountOf(mineStackObj) + + List( + s"$RESET$GREEN${stackedAmount.formatted("%,d")}個", + s"$RESET${DARK_GRAY}Lv${requiredLevel}以上でスタック可能", + s"$RESET$DARK_RED${UNDERLINE}左クリックで1スタック取り出し", + s"$RESET$DARK_AQUA${UNDERLINE}右クリックで1個取り出し" + ).asJava + } + } + } } - } - } Button( itemStack, @@ -115,28 +125,31 @@ private[minestack] case class MineStackButtons(player: Player) { ) }) - private def withDrawItemEffect(mineStackObj: MineStackObj, amount: Int) - (implicit onMainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = { + private def withDrawItemEffect(mineStackObj: MineStackObj, amount: Int)( + implicit onMainThread: OnMinecraftServerThread[IO] + ): TargetedEffect[Player] = { for { - pair <- Kleisli((player: Player) => onMainThread.runAction { - for { - playerData <- SyncIO { - SeichiAssist.playermap(player.getUniqueId) - } - currentAmount <- SyncIO { - playerData.minestack.getStackedAmountOf(mineStackObj) - } + pair <- Kleisli((player: Player) => + onMainThread.runAction { + for { + playerData <- SyncIO { + SeichiAssist.playermap(player.getUniqueId) + } + currentAmount <- SyncIO { + playerData.minestack.getStackedAmountOf(mineStackObj) + } - grantAmount = Math.min(amount, currentAmount).toInt + grantAmount = Math.min(amount, currentAmount).toInt - soundEffectPitch = if (grantAmount == amount) 1.0f else 0.5f - itemStackToGrant = mineStackObj.parameterizedWith(player).withAmount(grantAmount) + soundEffectPitch = if (grantAmount == amount) 1.0f else 0.5f + itemStackToGrant = mineStackObj.parameterizedWith(player).withAmount(grantAmount) - _ <- SyncIO { - playerData.minestack.subtractStackedAmountOf(mineStackObj, grantAmount.toLong) - } - } yield (soundEffectPitch, itemStackToGrant) - }) + _ <- SyncIO { + playerData.minestack.subtractStackedAmountOf(mineStackObj, grantAmount.toLong) + } + } yield (soundEffectPitch, itemStackToGrant) + } + ) _ <- SequentialEffect( FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f, pair._1), Util.grantItemStacksEffect(pair._2) @@ -144,7 +157,9 @@ private[minestack] case class MineStackButtons(player: Player) { } yield () } - def computeAutoMineStackToggleButton(implicit onMainThread: OnMinecraftServerThread[IO]): IO[Button] = + def computeAutoMineStackToggleButton( + implicit onMainThread: OnMinecraftServerThread[IO] + ): IO[Button] = RecomputedButton(IO { val playerData = SeichiAssist.playermap(getUniqueId) @@ -156,16 +171,11 @@ private[minestack] case class MineStackButtons(player: Player) { if (playerData.settings.autoMineStack) { baseBuilder .enchanted() - .lore(List( - s"$RESET${GREEN}現在ONです", - s"$RESET$DARK_RED${UNDERLINE}クリックでOFF" - )) + .lore(List(s"$RESET${GREEN}現在ONです", s"$RESET$DARK_RED${UNDERLINE}クリックでOFF")) } else { - baseBuilder - .lore(List( - s"$RESET${RED}現在OFFです", - s"$RESET$DARK_GREEN${UNDERLINE}クリックでON" - )) + baseBuilder.lore( + List(s"$RESET${RED}現在OFFです", s"$RESET$DARK_GREEN${UNDERLINE}クリックでON") + ) } }.build() diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/minestack/MineStackMainMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/minestack/MineStackMainMenu.scala index 30d6d7c365..1e91547dee 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/minestack/MineStackMainMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/minestack/MineStackMainMenu.scala @@ -11,7 +11,14 @@ import com.github.unchama.seichiassist.effects.player.CommonSoundEffects import com.github.unchama.seichiassist.menus.CommonButtons import com.github.unchama.seichiassist.menus.stickmenu.FirstPage import com.github.unchama.seichiassist.minestack.MineStackObjectCategory -import com.github.unchama.seichiassist.minestack.MineStackObjectCategory.{AGRICULTURAL, BUILDING, GACHA_PRIZES, MOB_DROP, ORES, REDSTONE_AND_TRANSPORTATION} +import com.github.unchama.seichiassist.minestack.MineStackObjectCategory.{ + AGRICULTURAL, + BUILDING, + GACHA_PRIZES, + MOB_DROP, + ORES, + REDSTONE_AND_TRANSPORTATION +} import org.bukkit.ChatColor._ import org.bukkit.Material import org.bukkit.entity.Player @@ -21,43 +28,53 @@ object MineStackMainMenu extends Menu { import com.github.unchama.menuinventory.syntax._ import eu.timepit.refined.auto._ - class Environment(implicit - val ioCanOpenCategorizedMineStackMenu: IO CanOpen CategorizedMineStackMenu, - val ioCanOpenFirstPage: IO CanOpen FirstPage.type, - val ioOnMainThread: OnMinecraftServerThread[IO]) + class Environment( + implicit val ioCanOpenCategorizedMineStackMenu: IO CanOpen CategorizedMineStackMenu, + val ioCanOpenFirstPage: IO CanOpen FirstPage.type, + val ioOnMainThread: OnMinecraftServerThread[IO] + ) override val frame: MenuFrame = MenuFrame(6.chestRows, s"$DARK_PURPLE${BOLD}MineStackメインメニュー") - def categoryButtonLayout(implicit ioCanOpenCategorizedMineStackMenu: IO CanOpen CategorizedMineStackMenu): MenuSlotLayout = { + def categoryButtonLayout( + implicit ioCanOpenCategorizedMineStackMenu: IO CanOpen CategorizedMineStackMenu + ): MenuSlotLayout = { def iconMaterialFor(category: MineStackObjectCategory): Material = category match { - case ORES => Material.DIAMOND_ORE - case MOB_DROP => Material.ENDER_PEARL - case AGRICULTURAL => Material.SEEDS - case BUILDING => Material.SMOOTH_BRICK + case ORES => Material.DIAMOND_ORE + case MOB_DROP => Material.ENDER_PEARL + case AGRICULTURAL => Material.SEEDS + case BUILDING => Material.SMOOTH_BRICK case REDSTONE_AND_TRANSPORTATION => Material.REDSTONE - case GACHA_PRIZES => Material.GOLDEN_APPLE + case GACHA_PRIZES => Material.GOLDEN_APPLE } - val layoutMap = MineStackObjectCategory.values.zipWithIndex.map { case (category, index) => - val slotIndex = index + 1 // 0には自動スタック機能トグルが入るので、1から入れ始める - val iconItemStack = new IconItemStackBuilder(iconMaterialFor(category)) - .title(s"$BLUE$UNDERLINE$BOLD${category.uiLabel}") - .build() - - val button = Button( - iconItemStack, - action.LeftClickButtonEffect( - CommonSoundEffects.menuTransitionFenceSound, - ioCanOpenCategorizedMineStackMenu.open(CategorizedMineStackMenu(category)) - ) - ) - slotIndex -> button - }.toMap + val layoutMap = MineStackObjectCategory + .values + .zipWithIndex + .map { + case (category, index) => + val slotIndex = index + 1 // 0には自動スタック機能トグルが入るので、1から入れ始める + val iconItemStack = new IconItemStackBuilder(iconMaterialFor(category)) + .title(s"$BLUE$UNDERLINE$BOLD${category.uiLabel}") + .build() + + val button = Button( + iconItemStack, + action.LeftClickButtonEffect( + CommonSoundEffects.menuTransitionFenceSound, + ioCanOpenCategorizedMineStackMenu.open(CategorizedMineStackMenu(category)) + ) + ) + slotIndex -> button + } + .toMap MenuSlotLayout(layoutMap) } - override def computeMenuLayout(player: Player)(implicit environment: Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { import environment._ for { @@ -67,9 +84,7 @@ object MineStackMainMenu extends Menu { MenuSlotLayout( ChestSlotRef(0, 0) -> autoMineStackToggleButton, ChestSlotRef(5, 0) -> CommonButtons.openStickMenu - ) - .merge(categoryButtonLayout) - .merge(historicalMineStackSection) + ).merge(categoryButtonLayout).merge(historicalMineStackSection) } } @@ -83,19 +98,24 @@ object MineStackMainMenu extends Menu { /** * メインメニュー内の「履歴」機能部分のレイアウトを計算する */ - def computeHistoricalMineStackLayout(implicit ioOnMainThread: OnMinecraftServerThread[IO]): IO[MenuSlotLayout] = { + def computeHistoricalMineStackLayout( + implicit ioOnMainThread: OnMinecraftServerThread[IO] + ): IO[MenuSlotLayout] = { val playerData = SeichiAssist.playermap(getUniqueId) for { usageHistory <- IO { playerData.hisotryData.usageHistory } - buttonMapping <- usageHistory.asScala.zipWithIndex - .map { case (mineStackObject, index) => - val slotIndex = 18 + index // 3行目から入れだす - val button = MineStackButtons(player).getMineStackItemButtonOf(mineStackObject) - - slotIndex -> button + buttonMapping <- usageHistory + .asScala + .zipWithIndex + .map { + case (mineStackObject, index) => + val slotIndex = 18 + index // 3行目から入れだす + val button = MineStackButtons(player).getMineStackItemButtonOf(mineStackObject) + + slotIndex -> button } .toList .map(_.sequence) diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/ranking/RankingMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/ranking/RankingMenu.scala index fbd9a4b80c..8b45a77a6b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/ranking/RankingMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/ranking/RankingMenu.scala @@ -12,20 +12,26 @@ import com.github.unchama.seichiassist.subsystems.breakcount.domain.SeichiAmount import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountData import com.github.unchama.seichiassist.subsystems.ranking.api.RankingProvider import com.github.unchama.seichiassist.subsystems.ranking.domain.values.{LoginTime, VoteCount} -import com.github.unchama.seichiassist.subsystems.ranking.domain.{Ranking, RankingRecord} +import com.github.unchama.seichiassist.subsystems.ranking.domain.{ + Ranking, + RankingRecord, + RankingRecordWithPosition +} import org.bukkit.ChatColor._ import org.bukkit.entity.Player object RankingMenu { - class Environment[R](implicit - val rankingApi: RankingProvider[IO, R], - val ioCanOpenRankingMenuItself: IO CanOpen RankingMenu[R], - val ioCanOpenRankingRootMenu: IO CanOpen RankingRootMenu.type) + class Environment[R]( + implicit val rankingApi: RankingProvider[IO, R], + val ioCanOpenRankingMenuItself: IO CanOpen RankingMenu[R], + val ioCanOpenRankingRootMenu: IO CanOpen RankingRootMenu.type + ) } trait RankingMenuTemplate[R] { + /** * ランキングの名前。 * @@ -50,28 +56,26 @@ trait RankingMenuTemplate[R] { object RankingMenuTemplates { - val seichi: RankingMenuTemplate[SeichiAmountData] = new RankingMenuTemplate[SeichiAmountData] { - override val rankingName: String = "整地神ランキング" - override def recordDataLore(data: SeichiAmountData): List[String] = { - val levelLine = { - val starLevel = data.starLevelCorrespondingToExp.level - val level = data.levelCorrespondingToExp.level - - if (starLevel > 0) - s"整地Lv:$level☆$starLevel" - else - s"整地Lv:$level" + val seichi: RankingMenuTemplate[SeichiAmountData] = + new RankingMenuTemplate[SeichiAmountData] { + override val rankingName: String = "整地神ランキング" + override def recordDataLore(data: SeichiAmountData): List[String] = { + val levelLine = { + val starLevel = data.starLevelCorrespondingToExp.level + val level = data.levelCorrespondingToExp.level + + if (starLevel > 0) + s"整地Lv:$level☆$starLevel" + else + s"整地Lv:$level" + } + + List(s"$RESET$GREEN$levelLine", s"$RESET${GREEN}総整地量:${data.expAmount.formatted}") } - - List( - s"$RESET$GREEN$levelLine", - s"$RESET${GREEN}総整地量:${data.expAmount.formatted}" + override def combinedDataLore(data: SeichiAmountData): List[String] = List( + s"$RESET${AQUA}全プレイヤー総整地量: ${data.expAmount.formatted}" ) } - override def combinedDataLore(data: SeichiAmountData): List[String] = List( - s"$RESET${AQUA}全プレイヤー総整地量: ${data.expAmount.formatted}" - ) - } val build: RankingMenuTemplate[BuildAmountData] = new RankingMenuTemplate[BuildAmountData] { override val rankingName: String = "建築神ランキング" @@ -114,10 +118,12 @@ case class RankingMenu[R](template: RankingMenuTemplate[R], pageIndex: Int = 0) final private val cutoff = 150 override type Environment = RankingMenu.Environment[R] - override val frame: MenuFrame = MenuFrame(6.chestRows, s"$DARK_PURPLE$BOLD${template.rankingName}") + override val frame: MenuFrame = + MenuFrame(6.chestRows, s"$DARK_PURPLE$BOLD${template.rankingName}") - private def uiOperationSection(totalNumberOfPages: Int) - (implicit environment: Environment): Seq[(Int, Button)] = { + private def uiOperationSection( + totalNumberOfPages: Int + )(implicit environment: Environment): Seq[(Int, Button)] = { import environment._ def buttonToTransferTo(pageIndex: Int, skullOwnerReference: SkullOwnerReference): Button = @@ -128,11 +134,13 @@ case class RankingMenu[R](template: RankingMenuTemplate[R], pageIndex: Int = 0) ) val goBackToStickMenuSection = - Seq(ChestSlotRef(5, 0) -> CommonButtons.transferButton( - new SkullItemStackBuilder(SkullOwners.MHF_ArrowLeft), - "ランキングメニューへ戻る", - RankingRootMenu - )) + Seq( + ChestSlotRef(5, 0) -> CommonButtons.transferButton( + new SkullItemStackBuilder(SkullOwners.MHF_ArrowLeft), + "ランキングメニューへ戻る", + RankingRootMenu + ) + ) val previousPageButtonSection = if (pageIndex > 0) @@ -162,10 +170,11 @@ case class RankingMenu[R](template: RankingMenuTemplate[R], pageIndex: Int = 0) ranking .recordsWithPositions .take(cutoff) - .slice(pageIndex * perPage, pageIndex * perPage +perPage) + .slice(pageIndex * perPage, pageIndex * perPage + perPage) .zipWithIndex - .map { case ((position, record), index) => - index -> entry(position, record) + .map { + case (RankingRecordWithPosition(record, position), index) => + index -> entry(position, record) } } @@ -182,15 +191,16 @@ case class RankingMenu[R](template: RankingMenuTemplate[R], pageIndex: Int = 0) } /** - * @return `player`からメニューの[[MenuSlotLayout]]を計算する[[IO]] + * @return + * `player`からメニューの[[MenuSlotLayout]]を計算する[[IO]] */ - override def computeMenuLayout(player: Player) - (implicit environment: Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { for { ranking <- environment.rankingApi.ranking.read } yield { - val records = ranking.recordsWithPositions - val recordsToInclude = records.size min cutoff + val recordsToInclude = ranking.recordCount min cutoff val totalNumberOfPages = Math.ceil(recordsToInclude / 45.0).toInt val combinedLayout = diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/ranking/RankingRootMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/ranking/RankingRootMenu.scala index edaba8fdd5..9ac4dc2f5f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/ranking/RankingRootMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/ranking/RankingRootMenu.scala @@ -20,33 +20,37 @@ import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack object RankingRootMenu extends Menu { - class Environment(implicit - val ioCanOpenFirstPage: IO CanOpen FirstPage.type, - val ioCanOpenSeichiRankingMenu: IO CanOpen RankingMenu[SeichiAmountData], - val ioCanOpenBuildRankingMenu: IO CanOpen RankingMenu[BuildAmountData], - val ioCanOpenLoginTimeRankingMenu: IO CanOpen RankingMenu[LoginTime], - val ioCanOpenVoteCountRankingMenu: IO CanOpen RankingMenu[VoteCount] - ) + class Environment( + implicit val ioCanOpenFirstPage: IO CanOpen FirstPage.type, + val ioCanOpenSeichiRankingMenu: IO CanOpen RankingMenu[SeichiAmountData], + val ioCanOpenBuildRankingMenu: IO CanOpen RankingMenu[BuildAmountData], + val ioCanOpenLoginTimeRankingMenu: IO CanOpen RankingMenu[LoginTime], + val ioCanOpenVoteCountRankingMenu: IO CanOpen RankingMenu[VoteCount] + ) override val frame: MenuFrame = MenuFrame(4.chestRows, s"$DARK_PURPLE${BOLD}ランキング") - override def computeMenuLayout(player: Player)(implicit environment: Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { import environment._ def iconOf(rankingName: String): ItemStack = new IconItemStackBuilder(Material.COOKIE) .title(s"$YELLOW$UNDERLINE$BOLD${rankingName}を見る") - .lore(List( - s"$RESET$RED(${rankingName}150位以内のプレイヤーのみ表記されます)", - s"$RESET$DARK_RED${UNDERLINE}クリックで開く" - )) + .lore( + List( + s"$RESET$RED(${rankingName}150位以内のプレイヤーのみ表記されます)", + s"$RESET$DARK_RED${UNDERLINE}クリックで開く" + ) + ) .build() val seichiGodRankingButton: Button = Button( iconOf("整地神ランキング"), LeftClickButtonEffect( CommonSoundEffects.menuTransitionFenceSound, - ioCanOpenSeichiRankingMenu.open(RankingMenu(RankingMenuTemplates.seichi)), + ioCanOpenSeichiRankingMenu.open(RankingMenu(RankingMenuTemplates.seichi)) ) ) diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/skill/ActiveSkillEffectMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/skill/ActiveSkillEffectMenu.scala index be17f93504..6772a7051c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/skill/ActiveSkillEffectMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/skill/ActiveSkillEffectMenu.scala @@ -10,7 +10,12 @@ import com.github.unchama.menuinventory.{ChestSlotRef, Menu, MenuFrame, MenuSlot import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.menus.CommonButtons import com.github.unchama.seichiassist.seichiskill.effect.ActiveSkillEffect.NoEffect -import com.github.unchama.seichiassist.seichiskill.effect.{ActiveSkillEffect, ActiveSkillNormalEffect, ActiveSkillPremiumEffect, UnlockableActiveSkillEffect} +import com.github.unchama.seichiassist.seichiskill.effect.{ + ActiveSkillEffect, + ActiveSkillNormalEffect, + ActiveSkillPremiumEffect, + UnlockableActiveSkillEffect +} import com.github.unchama.seichiassist.{SeichiAssist, SkullOwners} import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.player.FocusedSoundEffect @@ -26,11 +31,12 @@ object ActiveSkillEffectMenu extends Menu { import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.layoutPreparationContext import com.github.unchama.targetedeffect._ - class Environment(implicit - val ioCanOpenActiveSkillEffectMenu: IO CanOpen ActiveSkillEffectMenu.type, - val ioCanOpenActiveSkillMenu: IO CanOpen ActiveSkillMenu.type, - val ioCanOpenTransactionHistoryMenu: IO CanOpen PremiumPointTransactionHistoryMenu, - val ioOnMainThread: OnMinecraftServerThread[IO]) + class Environment( + implicit val ioCanOpenActiveSkillEffectMenu: IO CanOpen ActiveSkillEffectMenu.type, + val ioCanOpenActiveSkillMenu: IO CanOpen ActiveSkillMenu.type, + val ioCanOpenTransactionHistoryMenu: IO CanOpen PremiumPointTransactionHistoryMenu, + val ioOnMainThread: OnMinecraftServerThread[IO] + ) override val frame: MenuFrame = MenuFrame(6.chestRows, s"$DARK_PURPLE${BOLD}整地スキルエフェクト選択") @@ -58,9 +64,12 @@ object ActiveSkillEffectMenu extends Menu { IO { playerData.effectPoint -= effect.usePoint val state = playerData.skillEffectState - playerData.skillEffectState = state.copy(obtainedEffects = state.obtainedEffects + effect) + playerData.skillEffectState = + state.copy(obtainedEffects = state.obtainedEffects + effect) } >> SequentialEffect( - MessageEffect(s"${LIGHT_PURPLE}エフェクト:${effect.nameOnUI}$RESET$LIGHT_PURPLE${BOLD}を解除しました"), + MessageEffect( + s"${LIGHT_PURPLE}エフェクト:${effect.nameOnUI}$RESET$LIGHT_PURPLE${BOLD}を解除しました" + ), FocusedSoundEffect(Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1.0f, 1.2f) ).apply(player) } @@ -68,7 +77,10 @@ object ActiveSkillEffectMenu extends Menu { def unlockPremiumEffect(effect: ActiveSkillPremiumEffect): IO[Unit] = for { - premiumEffectPoint <- SeichiAssist.databaseGateway.donateDataManipulator.currentPremiumPointFor(player) + premiumEffectPoint <- SeichiAssist + .databaseGateway + .donateDataManipulator + .currentPremiumPointFor(player) _ <- if (premiumEffectPoint < effect.usePoint) { SequentialEffect( @@ -77,14 +89,20 @@ object ActiveSkillEffectMenu extends Menu { ).apply(player) } else { for { - transactionResult <- SeichiAssist.databaseGateway.donateDataManipulator.recordPremiumEffectPurchase(player, effect) + transactionResult <- SeichiAssist + .databaseGateway + .donateDataManipulator + .recordPremiumEffectPurchase(player, effect) _ <- transactionResult match { case ActionStatus.Ok => IO { val state = playerData.skillEffectState - playerData.skillEffectState = state.copy(obtainedEffects = state.obtainedEffects + effect) + playerData.skillEffectState = + state.copy(obtainedEffects = state.obtainedEffects + effect) } >> SequentialEffect( - MessageEffect(s"${LIGHT_PURPLE}プレミアムエフェクト:${effect.nameOnUI}$RESET$LIGHT_PURPLE${BOLD}を解除しました"), + MessageEffect( + s"${LIGHT_PURPLE}プレミアムエフェクト:${effect.nameOnUI}$RESET$LIGHT_PURPLE${BOLD}を解除しました" + ), FocusedSoundEffect(Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1.0f, 1.2f) ).apply(player) case ActionStatus.Fail => @@ -102,11 +120,12 @@ object ActiveSkillEffectMenu extends Menu { _ <- if (unlocked) setEffectSelectionTo(effect)(player) - else effect match { - // 所持していないため、開放しなければならない - case effect: ActiveSkillNormalEffect => unlockNormalEffect(effect) - case effect: ActiveSkillPremiumEffect => unlockPremiumEffect(effect) - } + else + effect match { + // 所持していないため、開放しなければならない + case effect: ActiveSkillNormalEffect => unlockNormalEffect(effect) + case effect: ActiveSkillPremiumEffect => unlockPremiumEffect(effect) + } } yield () } } @@ -119,7 +138,7 @@ object ActiveSkillEffectMenu extends Menu { def effectButton(effect: UnlockableActiveSkillEffect): IO[Button] = { val itemStackComputation = IO { val kindOfPointToUse = effect match { - case _: ActiveSkillNormalEffect => "エフェクトポイント" + case _: ActiveSkillNormalEffect => "エフェクトポイント" case _: ActiveSkillPremiumEffect => "プレミアムエフェクトポイント" } @@ -127,10 +146,9 @@ object ActiveSkillEffectMenu extends Menu { if (playerData.skillEffectState.obtainedEffects.contains(effect)) { val partialBuilder = new IconItemStackBuilder(effect.materialOnUI) .title(effect.nameOnUI) - .lore(List( - s"$RESET$GREEN${effect.explanation}", - s"$RESET$DARK_RED${UNDERLINE}クリックでセット" - )) + .lore( + List(s"$RESET$GREEN${effect.explanation}", s"$RESET$DARK_RED${UNDERLINE}クリックでセット") + ) if (playerData.skillEffectState.selection == effect) { partialBuilder.enchanted() @@ -140,28 +158,30 @@ object ActiveSkillEffectMenu extends Menu { } else { new IconItemStackBuilder(Material.BEDROCK) .title(effect.nameOnUI) - .lore(List( - s"$RESET$GREEN${effect.explanation}", - s"$RESET${YELLOW}必要$kindOfPointToUse:${effect.usePoint}", - s"$RESET$AQUA${UNDERLINE}クリックで解除" - )) + .lore( + List( + s"$RESET$GREEN${effect.explanation}", + s"$RESET${YELLOW}必要$kindOfPointToUse:${effect.usePoint}", + s"$RESET$AQUA${UNDERLINE}クリックで解除" + ) + ) .build() } } itemStackComputation.map(itemStack => ReloadingButton(ActiveSkillEffectMenu)( - Button( - itemStack, - LeftClickButtonEffect(unlockOrSet(effect)) - ) + Button(itemStack, LeftClickButtonEffect(unlockOrSet(effect))) ) ) } val effectDataButton: IO[Button] = for { - premiumEffectPoint <- SeichiAssist.databaseGateway.donateDataManipulator.currentPremiumPointFor(player) + premiumEffectPoint <- SeichiAssist + .databaseGateway + .donateDataManipulator + .currentPremiumPointFor(player) button <- IO { val playerData = SeichiAssist.playermap(getUniqueId) @@ -170,13 +190,15 @@ object ActiveSkillEffectMenu extends Menu { Button( new SkullItemStackBuilder(getUniqueId) .title(s"$UNDERLINE$BOLD$YELLOW${getName}のスキルエフェクトデータ") - .lore(List( - s"$RESET${GREEN}現在選択しているエフェクト:${playerData.skillEffectState.selection.nameOnUI}", - s"$RESET${YELLOW}使えるエフェクトポイント:${playerData.effectPoint}", - s"$RESET$DARK_GRAY※投票すると獲得できます", - s"$RESET${LIGHT_PURPLE}使えるプレミアムポイント$premiumEffectPoint", - s"$RESET$DARK_GRAY※寄付をすると獲得できます" - )) + .lore( + List( + s"$RESET${GREEN}現在選択しているエフェクト:${playerData.skillEffectState.selection.nameOnUI}", + s"$RESET${YELLOW}使えるエフェクトポイント:${playerData.effectPoint}", + s"$RESET$DARK_GRAY※投票すると獲得できます", + s"$RESET${LIGHT_PURPLE}使えるプレミアムポイント$premiumEffectPoint", + s"$RESET$DARK_GRAY※寄付をすると獲得できます" + ) + ) .build(), Nil ) @@ -196,19 +218,19 @@ object ActiveSkillEffectMenu extends Menu { .build(), LeftClickButtonEffect( FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 0.1f), - Kleisli(setEffectSelectionTo(NoEffect)), + Kleisli(setEffectSelectionTo(NoEffect)) ) ) } } - def effectPurchaseHistoryMenuButton(implicit ioCanOpenPremiumPointMenu: IO CanOpen PremiumPointTransactionHistoryMenu): Button = + def effectPurchaseHistoryMenuButton( + implicit ioCanOpenPremiumPointMenu: IO CanOpen PremiumPointTransactionHistoryMenu + ): Button = Button( new IconItemStackBuilder(Material.BOOKSHELF) .title(s"$UNDERLINE$BOLD${BLUE}プレミアムエフェクト購入履歴") - .lore( - s"$RESET$DARK_RED${UNDERLINE}クリックで閲覧", - ) + .lore(s"$RESET$DARK_RED${UNDERLINE}クリックで閲覧") .build(), LeftClickButtonEffect( FocusedSoundEffect(Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1f, 0.1f), @@ -216,7 +238,9 @@ object ActiveSkillEffectMenu extends Menu { ) ) - def goBackToSkillMenuButton(implicit ioCanOpenActiveSkillMenu: IO CanOpen ActiveSkillMenu.type): Button = + def goBackToSkillMenuButton( + implicit ioCanOpenActiveSkillMenu: IO CanOpen ActiveSkillMenu.type + ): Button = CommonButtons.transferButton( new SkullItemStackBuilder(SkullOwners.MHF_ArrowLeft), "スキルメニューへ", @@ -225,9 +249,12 @@ object ActiveSkillEffectMenu extends Menu { } /** - * @return `player`からメニューの[[MenuSlotLayout]]を計算する[[IO]] + * @return + * `player`からメニューの[[MenuSlotLayout]]を計算する[[IO]] */ - override def computeMenuLayout(player: Player)(implicit environment: Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { val c = ButtonComputations(player) import ConstantButtons._ @@ -237,12 +264,15 @@ object ActiveSkillEffectMenu extends Menu { import eu.timepit.refined.auto._ val computeDynamicPart = { - List( - ChestSlotRef(0, 0) -> effectDataButton - ) ++ ActiveSkillNormalEffect.values.zipWithIndex.map { case (effect, index) => - ChestSlotRef(1, 0) + index -> effectButton(effect) - } ++ ActiveSkillPremiumEffect.values.zipWithIndex.map { case (effect, index) => - ChestSlotRef(3, 0) + index -> effectButton(effect) + List(ChestSlotRef(0, 0) -> effectDataButton) ++ ActiveSkillNormalEffect + .values + .zipWithIndex + .map { + case (effect, index) => + ChestSlotRef(1, 0) + index -> effectButton(effect) + } ++ ActiveSkillPremiumEffect.values.zipWithIndex.map { + case (effect, index) => + ChestSlotRef(3, 0) + index -> effectButton(effect) } }.traverse(_.sequence) diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/skill/ActiveSkillMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/skill/ActiveSkillMenu.scala index 62cc71ccb7..7ca667621c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/skill/ActiveSkillMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/skill/ActiveSkillMenu.scala @@ -5,7 +5,12 @@ import cats.effect.concurrent.Ref import cats.effect.{ConcurrentEffect, IO, SyncIO} import com.github.unchama.concurrent.NonServerThreadContextShift import com.github.unchama.generic.effect.concurrent.TryableFiber -import com.github.unchama.itemstackbuilder.{AbstractItemStackBuilder, IconItemStackBuilder, SkullItemStackBuilder, TippedArrowItemStackBuilder} +import com.github.unchama.itemstackbuilder.{ + AbstractItemStackBuilder, + IconItemStackBuilder, + SkullItemStackBuilder, + TippedArrowItemStackBuilder +} import com.github.unchama.menuinventory.router.CanOpen import com.github.unchama.menuinventory.slot.button.action.{ButtonEffect, LeftClickButtonEffect} import com.github.unchama.menuinventory.slot.button.{Button, RecomputedButton, ReloadingButton} @@ -44,16 +49,20 @@ object ActiveSkillMenu extends Menu { private case object Selected extends SkillSelectionState import com.github.unchama.menuinventory.syntax._ - import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.{asyncShift, layoutPreparationContext} + import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.{ + asyncShift, + layoutPreparationContext + } - class Environment(implicit - val breakCountApi: BreakCountAPI[IO, SyncIO, Player], - val manaApi: ManaApi[IO, SyncIO, Player], - val ioCanOpenActiveSkillMenu: IO CanOpen ActiveSkillMenu.type, - val ioCanOpenActiveSkillEffectMenu: IO CanOpen ActiveSkillEffectMenu.type, - val ioCanOpenFirstPage: IO CanOpen FirstPage.type, - val ioOnMainThread: OnMinecraftServerThread[IO], - val globalNotification: DiscordNotificationAPI[IO]) + class Environment( + implicit val breakCountApi: BreakCountAPI[IO, SyncIO, Player], + val manaApi: ManaApi[IO, SyncIO, Player], + val ioCanOpenActiveSkillMenu: IO CanOpen ActiveSkillMenu.type, + val ioCanOpenActiveSkillEffectMenu: IO CanOpen ActiveSkillEffectMenu.type, + val ioCanOpenFirstPage: IO CanOpen FirstPage.type, + val ioOnMainThread: OnMinecraftServerThread[IO], + val globalNotification: DiscordNotificationAPI[IO] + ) override val frame: MenuFrame = MenuFrame(5.chestRows, s"$DARK_PURPLE${BOLD}整地スキル選択") @@ -62,9 +71,12 @@ object ActiveSkillMenu extends Menu { SeichiAssist.playermap(player.getUniqueId).skillState } - private def totalActiveSkillPoint(player: Player)(implicit environment: Environment): IO[Int] = + private def totalActiveSkillPoint( + player: Player + )(implicit environment: Environment): IO[Int] = environment - .breakCountApi.seichiAmountDataRepository(player) + .breakCountApi + .seichiAmountDataRepository(player) .read .map { data => val level = data.levelCorrespondingToExp.level @@ -86,42 +98,42 @@ object ActiveSkillMenu extends Menu { totalPoint - skillState.consumedActiveSkillPoint } - val computeStatusButton: IO[Button] = RecomputedButton( - for { - ref <- skillStateRef(player) - state <- ref.get - availablePoints <- availableActiveSkillPoint - } yield { - val activeSkillSelectionLore: Option[String] = - state.activeSkill.map(activeSkill => - s"$RESET${GREEN}現在選択しているアクティブスキル:${activeSkill.name}" - ) - - val assaultSkillSelectionLore: Option[String] = - state.assaultSkill.map { assaultSkill => - val heading = - if (assaultSkill == SeichiSkill.AssaultArmor) - s"$RESET${GREEN}現在選択しているアサルトスキル:" - else - s"$RESET${GREEN}現在選択している凝固スキル:" - - s"$heading${assaultSkill.name}" - } + val computeStatusButton: IO[Button] = RecomputedButton(for { + ref <- skillStateRef(player) + state <- ref.get + availablePoints <- availableActiveSkillPoint + } yield { + val activeSkillSelectionLore: Option[String] = + state + .activeSkill + .map(activeSkill => s"$RESET${GREEN}現在選択しているアクティブスキル:${activeSkill.name}") + + val assaultSkillSelectionLore: Option[String] = + state.assaultSkill.map { assaultSkill => + val heading = + if (assaultSkill == SeichiSkill.AssaultArmor) + s"$RESET${GREEN}現在選択しているアサルトスキル:" + else + s"$RESET${GREEN}現在選択している凝固スキル:" + + s"$heading${assaultSkill.name}" + } - val itemStack = - new SkullItemStackBuilder(getUniqueId) - .title(s"$YELLOW$UNDERLINE$BOLD${getName}のアクティブスキルデータ") - .lore( - activeSkillSelectionLore.toList ++ - assaultSkillSelectionLore.toList ++ - List(s"$RESET${YELLOW}使えるアクティブスキルポイント:$availablePoints") - ) - .build() - Button(itemStack) - } - ) + val itemStack = + new SkullItemStackBuilder(getUniqueId) + .title(s"$YELLOW$UNDERLINE$BOLD${getName}のアクティブスキルデータ") + .lore( + activeSkillSelectionLore.toList ++ + assaultSkillSelectionLore.toList ++ + List(s"$RESET${YELLOW}使えるアクティブスキルポイント:$availablePoints") + ) + .build() + Button(itemStack) + }) - def computeSkillButtonFor(skill: SeichiSkill)(implicit environment: Environment): IO[Button] = { + def computeSkillButtonFor( + skill: SeichiSkill + )(implicit environment: Environment): IO[Button] = { for { ref <- skillStateRef(player) state <- ref.get @@ -207,9 +219,7 @@ object ActiveSkillMenu extends Menu { } def prerequisiteSkillName(skill: SeichiSkill): String = - SkillDependency.prerequisites(skill) - .headOption.map(_.name) - .getOrElse("なし") + SkillDependency.prerequisites(skill).headOption.map(_.name).getOrElse("なし") def breakRangeDescription(range: SkillRange): String = { val colorPrefix = s"$RESET$GREEN" @@ -244,14 +254,17 @@ object ActiveSkillMenu extends Menu { def coolDownDescription(skill: SeichiSkill): String = { val colorPrefix = s"$RESET$DARK_GRAY" - val coolDownAmount = skill.maxCoolDownTicks.map { ticks => - String.format("%.2f", ticks * 50 / 1000.0) - }.getOrElse("なし") + val coolDownAmount = skill + .maxCoolDownTicks + .map { ticks => String.format("%.2f", ticks * 50 / 1000.0) } + .getOrElse("なし") colorPrefix + coolDownAmount } - def selectionStateOf(skill: SeichiSkill)(skillState: PlayerSkillState): SkillSelectionState = { + def selectionStateOf( + skill: SeichiSkill + )(skillState: PlayerSkillState): SkillSelectionState = { if (skillState.obtainedSkills.contains(skill)) { val selected = skill match { case skill: ActiveSkill => @@ -266,10 +279,12 @@ object ActiveSkillMenu extends Menu { } } - def seichiSkillButton[ - F[_] : ConcurrentEffect : NonServerThreadContextShift : DiscordNotificationAPI - ](state: SkillSelectionState, skill: SeichiSkill) - (implicit environment: Environment): Button = { + def seichiSkillButton[F[ + _ + ]: ConcurrentEffect: NonServerThreadContextShift: DiscordNotificationAPI]( + state: SkillSelectionState, + skill: SeichiSkill + )(implicit environment: Environment): Button = { import environment._ val itemStack = { @@ -293,18 +308,19 @@ object ActiveSkillMenu extends Menu { ) skill match { - case skill: AssaultSkill => skill match { - case SeichiSkill.VenderBlizzard => - List( - requiredPointDescription, - s"$RESET${DARK_RED}水凝固/熔岩凝固の双方を扱える者にのみ発現する上位凝固スキル", - s"$RESET${DARK_RED}アサルト・アーマーの取得には必要ない", - s"$RESET$AQUA${UNDERLINE}クリックで解除" - ) - case SeichiSkill.AssaultArmor => - List(s"$RESET${YELLOW}全てのスキルを獲得すると解除されます") - case _ => defaultDescription - } + case skill: AssaultSkill => + skill match { + case SeichiSkill.VenderBlizzard => + List( + requiredPointDescription, + s"$RESET${DARK_RED}水凝固/熔岩凝固の双方を扱える者にのみ発現する上位凝固スキル", + s"$RESET${DARK_RED}アサルト・アーマーの取得には必要ない", + s"$RESET$AQUA${UNDERLINE}クリックで解除" + ) + case SeichiSkill.AssaultArmor => + List(s"$RESET${YELLOW}全てのスキルを獲得すると解除されます") + case _ => defaultDescription + } case _: ActiveSkill => defaultDescription } case Unlocked => List(s"$RESET$DARK_RED${UNDERLINE}クリックでセット") @@ -317,7 +333,7 @@ object ActiveSkillMenu extends Menu { List( s"$RESET$GREEN${breakRangeDescription(skill.range)}", s"$RESET${DARK_GRAY}クールダウン:${coolDownDescription(skill)}", - s"$RESET${BLUE}消費マナ:${skill.manaCost}", + s"$RESET${BLUE}消費マナ:${skill.manaCost}" ) ++ clickEffectDescription ) @@ -342,10 +358,13 @@ object ActiveSkillMenu extends Menu { case None => val unlockedState = skillState.obtained(skill) val (newState, assaultSkillUnlockEffects) = - if (!unlockedState.obtainedSkills.contains(AssaultArmor) && - unlockedState.lockedDependency(SeichiSkill.AssaultArmor).isEmpty) { + if ( + !unlockedState.obtainedSkills.contains(AssaultArmor) && + unlockedState.lockedDependency(SeichiSkill.AssaultArmor).isEmpty + ) { - val notificationMessage = s"${player.getName}が全てのスキルを習得し、アサルト・アーマーを解除しました!" + val notificationMessage = + s"${player.getName}が全てのスキルを習得し、アサルト・アーマーを解除しました!" import cats.effect.implicits._ @@ -354,11 +373,17 @@ object ActiveSkillMenu extends Menu { SequentialEffect( MessageEffect(s"$YELLOW${BOLD}全てのスキルを習得し、アサルト・アーマーを解除しました"), // アンダーバーがマークダウンの斜体と解釈されることを防ぐ - Kleisli.liftF(DiscordNotificationAPI[F].send(notificationMessage.replaceAllLiterally("_", "\\_")).toIO), + Kleisli.liftF( + DiscordNotificationAPI[F] + .send(notificationMessage.replaceAllLiterally("_", "\\_")) + .toIO + ), Kleisli.liftF(IO { - Util.sendMessageToEveryoneIgnoringPreference(s"$GOLD$BOLD$notificationMessage") + Util.sendMessageToEveryoneIgnoringPreference( + s"$GOLD$BOLD$notificationMessage" + ) }), - BroadcastSoundEffect(Sound.ENTITY_ENDERDRAGON_DEATH, 1.0f, 1.2f), + BroadcastSoundEffect(Sound.ENTITY_ENDERDRAGON_DEATH, 1.0f, 1.2f) ) ) } else (unlockedState, emptyEffect) @@ -391,7 +416,7 @@ object ActiveSkillMenu extends Menu { case Unlocked => val skillType = skill match { - case _: ActiveSkill => "アクティブスキル" + case _: ActiveSkill => "アクティブスキル" case _: AssaultSkill => "アサルトスキル" } @@ -404,7 +429,8 @@ object ActiveSkillMenu extends Menu { import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.sleepAndRoutineContext import environment.manaApi - val tryStartRoutine = TryableFiber.start(AssaultRoutine.tryStart(player, skill)) + val tryStartRoutine = + TryableFiber.start(AssaultRoutine.tryStart(player, skill)) val fiberRepository = SeichiAssist.instance.assaultSkillRoutines val tryStart = fiberRepository(player).stopAnyFiber >> @@ -436,16 +462,14 @@ object ActiveSkillMenu extends Menu { } private object ConstantButtons { - def skillEffectMenuButton(implicit - ioCanOpenActiveSkillEffectMenu: IO CanOpen ActiveSkillEffectMenu.type, - ioOnMainThread: OnMinecraftServerThread[IO]): Button = { + def skillEffectMenuButton( + implicit ioCanOpenActiveSkillEffectMenu: IO CanOpen ActiveSkillEffectMenu.type, + ioOnMainThread: OnMinecraftServerThread[IO] + ): Button = { Button( new IconItemStackBuilder(Material.BOOKSHELF) .title(s"$UNDERLINE$BOLD${LIGHT_PURPLE}演出効果設定") - .lore( - s"$RESET${GRAY}スキル使用時の演出を選択できるゾ", - s"$RESET$UNDERLINE${DARK_RED}クリックで演出一覧を開く", - ) + .lore(s"$RESET${GRAY}スキル使用時の演出を選択できるゾ", s"$RESET$UNDERLINE${DARK_RED}クリックで演出一覧を開く") .build(), LeftClickButtonEffect( FocusedSoundEffect(Sound.BLOCK_BREWING_STAND_BREW, 1f, 0.5f), @@ -478,7 +502,9 @@ object ActiveSkillMenu extends Menu { } } - override def computeMenuLayout(player: Player)(implicit environment: Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { import cats.implicits._ import environment._ import eu.timepit.refined.auto._ @@ -497,21 +523,18 @@ object ActiveSkillMenu extends Menu { val dynamicPartComputation = List( ChestSlotRef(0, 0) -> computeStatusButton, - ChestSlotRef(0, 3) -> computeSkillButtonFor(EbifriDrive), ChestSlotRef(0, 4) -> computeSkillButtonFor(HolyShot), ChestSlotRef(0, 5) -> computeSkillButtonFor(TsarBomba), ChestSlotRef(0, 6) -> computeSkillButtonFor(ArcBlast), ChestSlotRef(0, 7) -> computeSkillButtonFor(PhantasmRay), ChestSlotRef(0, 8) -> computeSkillButtonFor(Supernova), - ChestSlotRef(1, 3) -> computeSkillButtonFor(TomBoy), ChestSlotRef(1, 4) -> computeSkillButtonFor(Thunderstorm), ChestSlotRef(1, 5) -> computeSkillButtonFor(StarlightBreaker), ChestSlotRef(1, 6) -> computeSkillButtonFor(EarthDivide), ChestSlotRef(1, 7) -> computeSkillButtonFor(HeavenGaeBolg), ChestSlotRef(1, 8) -> computeSkillButtonFor(Decision), - ChestSlotRef(2, 0) -> computeSkillButtonFor(DualBreak), ChestSlotRef(2, 1) -> computeSkillButtonFor(TrialBreak), ChestSlotRef(2, 2) -> computeSkillButtonFor(Explosion), @@ -521,20 +544,15 @@ object ActiveSkillMenu extends Menu { ChestSlotRef(2, 6) -> computeSkillButtonFor(BrilliantDetonation), ChestSlotRef(2, 7) -> computeSkillButtonFor(LemuriaImpact), ChestSlotRef(2, 8) -> computeSkillButtonFor(EternalVice), - ChestSlotRef(3, 3) -> computeSkillButtonFor(WhiteBreath), ChestSlotRef(3, 4) -> computeSkillButtonFor(AbsoluteZero), ChestSlotRef(3, 5) -> computeSkillButtonFor(DiamondDust), - ChestSlotRef(4, 3) -> computeSkillButtonFor(LavaCondensation), ChestSlotRef(4, 4) -> computeSkillButtonFor(MoerakiBoulders), ChestSlotRef(4, 5) -> computeSkillButtonFor(Eldfell), - ChestSlotRef(4, 7) -> computeSkillButtonFor(VenderBlizzard), - ChestSlotRef(4, 8) -> computeSkillButtonFor(AssaultArmor), - ) - .map(_.sequence) - .sequence + ChestSlotRef(4, 8) -> computeSkillButtonFor(AssaultArmor) + ).map(_.sequence).sequence for { dynamicPart <- dynamicPartComputation diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/skill/PassiveSkillMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/skill/PassiveSkillMenu.scala index 6aa2eb522f..fcc67675a2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/skill/PassiveSkillMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/skill/PassiveSkillMenu.scala @@ -28,9 +28,10 @@ object PassiveSkillMenu extends Menu { import com.github.unchama.menuinventory.syntax._ - class Environment(implicit - val breakCountApi: BreakCountAPI[IO, SyncIO, Player], - val ioCanOpenFirstPage: IO CanOpen FirstPage.type) + class Environment( + implicit val breakCountApi: BreakCountAPI[IO, SyncIO, Player], + val ioCanOpenFirstPage: IO CanOpen FirstPage.type + ) /** * メニューのサイズとタイトルに関する情報 @@ -38,9 +39,12 @@ object PassiveSkillMenu extends Menu { override val frame: MenuFrame = MenuFrame(4.chestRows, s"$DARK_PURPLE${BOLD}整地スキル切り替え") /** - * @return `player`からメニューの[[MenuSlotLayout]]を計算する[[IO]] + * @return + * `player`からメニューの[[MenuSlotLayout]]を計算する[[IO]] */ - override def computeMenuLayout(player: Player)(implicit environment: Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { import cats.implicits._ import environment._ import eu.timepit.refined.auto._ @@ -48,17 +52,13 @@ object PassiveSkillMenu extends Menu { val buttonComputations = new ButtonComputations(player) import buttonComputations._ - val constantPart = Map( - ChestSlotRef(3, 0) -> CommonButtons.openStickMenu - ) + val constantPart = Map(ChestSlotRef(3, 0) -> CommonButtons.openStickMenu) val dynamicPartComputation = List( ChestSlotRef(0, 0) -> computeToggleMultipleBlockTypeDestructionButton, ChestSlotRef(0, 1) -> computeToggleChestBreakButton, ChestSlotRef(1, 0) -> computeGiganticBerserkButton - ) - .map(_.sequence) - .sequence + ).map(_.sequence).sequence for { dynamicPart <- dynamicPartComputation @@ -67,83 +67,87 @@ object PassiveSkillMenu extends Menu { private class ButtonComputations(player: Player)(implicit environment: Environment) { - import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.{layoutPreparationContext, onMainThread} + import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.{ + layoutPreparationContext, + onMainThread + } import player._ import scala.util.chaining._ val computeToggleMultipleBlockTypeDestructionButton: IO[Button] = RecomputedButton { - environment.breakCountApi + environment + .breakCountApi .seichiAmountDataRepository(player) - .read.toIO - .flatMap(amountData => IO { - val level = amountData.levelCorrespondingToExp.level - val openerData = SeichiAssist.playermap(getUniqueId) - - val baseLore = List( - s"${GREEN}複数種類ブロック同時破壊", - s"${GRAY}ブロックに対応するツールを無視してスキルで", - s"${GRAY}破壊可能な全種類のブロックを同時に破壊します", - s"${DARK_RED}整地ワールドではON/OFFに関わらず同時破壊されます") - val statusLore = if (openerData.settings.multipleidbreakflag) { - List(s"${GREEN}ON", s"${DARK_RED}クリックでOFF") - } else { - List(s"${RED}OFF", s"${DARK_GREEN}クリックでON") - } + .read + .toIO + .flatMap(amountData => + IO { + val level = amountData.levelCorrespondingToExp.level + val openerData = SeichiAssist.playermap(getUniqueId) - Button( - new IconItemStackBuilder(Material.DIAMOND_PICKAXE) - .tap { builder => if (openerData.settings.multipleidbreakflag) builder.enchanted() } - .title(s"$YELLOW$UNDERLINE${BOLD}複数種類同時破壊スキル切替") - .lore(baseLore ++ statusLore) - .build(), - LeftClickButtonEffect { - if (level >= SeichiAssist.seichiAssistConfig.getMultipleIDBlockBreaklevel) { - SequentialEffect( - openerData.settings.toggleMultipleIdBreakFlag, - DeferredEffect(IO { - if (openerData.settings.multipleidbreakflag) { - SequentialEffect( - MessageEffect(s"${GREEN}複数種類同時破壊:ON"), - FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) - ) - } else { - SequentialEffect( - MessageEffect(s"${RED}複数種類同時破壊:OFF"), - FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 0.5f) - ) - } - }) - ) + val baseLore = List( + s"${GREEN}複数種類ブロック同時破壊", + s"${GRAY}ブロックに対応するツールを無視してスキルで", + s"${GRAY}破壊可能な全種類のブロックを同時に破壊します", + s"${DARK_RED}整地ワールドではON/OFFに関わらず同時破壊されます" + ) + val statusLore = + if (openerData.settings.performMultipleIDBlockBreakWhenOutsideSeichiWorld) { + List(s"${GREEN}ON", s"${DARK_RED}クリックでOFF") } else { - SequentialEffect( - MessageEffect("整地Lvが足りません"), - FocusedSoundEffect(Sound.BLOCK_GRASS_PLACE, 1f, 0.1f), - ) + List(s"${RED}OFF", s"${DARK_GREEN}クリックでON") } - } - ) - }) + + Button( + new IconItemStackBuilder(Material.DIAMOND_PICKAXE) + .tap { builder => + if (openerData.settings.performMultipleIDBlockBreakWhenOutsideSeichiWorld) + builder.enchanted() + } + .title(s"$YELLOW$UNDERLINE${BOLD}複数種類同時破壊スキル切替") + .lore(baseLore ++ statusLore) + .build(), + LeftClickButtonEffect { + if (level >= SeichiAssist.seichiAssistConfig.getMultipleIDBlockBreakLevel) { + SequentialEffect( + openerData.settings.toggleMultipleIdBreakFlag, + DeferredEffect(IO { + if ( + openerData.settings.performMultipleIDBlockBreakWhenOutsideSeichiWorld + ) { + SequentialEffect( + MessageEffect(s"${GREEN}複数種類同時破壊:ON"), + FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) + ) + } else { + SequentialEffect( + MessageEffect(s"${RED}複数種類同時破壊:OFF"), + FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 0.5f) + ) + } + }) + ) + } else { + SequentialEffect( + MessageEffect("整地Lvが足りません"), + FocusedSoundEffect(Sound.BLOCK_GRASS_PLACE, 1f, 0.1f) + ) + } + } + ) + } + ) } val computeToggleChestBreakButton: IO[Button] = RecomputedButton(IO { val openerData = SeichiAssist.playermap(getUniqueId) - val baseLore = List( - s"${GREEN}スキルでチェストを破壊するスキル" - ) + val baseLore = List(s"${GREEN}スキルでチェストを破壊するスキル") val statusLore = if (openerData.chestflag) { - List( - s"${RED}整地ワールドのみで発動中(デフォルト)", - "", - s"$DARK_GREEN${UNDERLINE}クリックで切り替え" - ) + List(s"${RED}整地ワールドのみで発動中(デフォルト)", "", s"$DARK_GREEN${UNDERLINE}クリックで切り替え") } else { - List( - s"${RED}発動しません", - "", - s"$DARK_GREEN${UNDERLINE}クリックで切り替え" - ) + List(s"${RED}発動しません", "", s"$DARK_GREEN${UNDERLINE}クリックで切り替え") } val material = if (openerData.chestflag) Material.DIAMOND_AXE else Material.CHEST @@ -169,82 +173,99 @@ object PassiveSkillMenu extends Menu { } }) ) - }) + } + ) }) val computeGiganticBerserkButton: IO[Button] = RecomputedButton { - environment.breakCountApi + environment + .breakCountApi .seichiAmountDataRepository(player) - .read.toIO - .flatMap(amountData => IO { - val level = amountData.levelCorrespondingToExp.level - - val openerData = SeichiAssist.playermap(getUniqueId) - - val material = if (level < 10) Material.STICK else openerData.giganticBerserk.materialOnUI() - val baseLore = if (level < 10) { - List(s"${WHITE}このパッシブスキルは", s"${WHITE}整地Lvが10以上になると解放されます") - } else { - List( - s"${RED}敵MOBを倒した時", - s"${RED}その魂を吸収しマナへと変換するスキル", - s"$DARK_GRAY※成功率は高くなく", - s"${DARK_GRAY}整地中でなければその効果を発揮しない", - "", - s"${DARK_GRAY}実装は試験的であり、変更される場合があります" - ) - } - val lengthInfoLore = if (openerData.giganticBerserk.reachedLimit()) { - List(s"${GRAY}MOBの魂を極限まで吸収し最大限の力を発揮する") - } else { - List( - s"${GRAY}MOBの魂を${openerData.giganticBerserk.requiredExpToNextLevel()}回吸収すると更なる力が得られる", - s"$GRAY${openerData.giganticBerserk.exp}/${openerData.giganticBerserk.requiredExpToNextLevel()}" - ) - } - val probability = 100 * openerData.giganticBerserk.manaRegenerationProbability() - val formatted = f"$probability%2.0f" - // 細かい数字が表示されないようにする - val levelInfoLore = List(s"${GRAY}現在 ${openerData.giganticBerserk.level + 1}レベル,回復率 $formatted%") - val evolutionLore = if (openerData.giganticBerserk.canEvolve) { - List( - "", - s"${DARK_RED}沢山の魂を吸収したことで", - s"${DARK_RED}スキルの秘めたる力を解放できそうだ…!", - s"$DARK_RED${UNDERLINE}クリックで開放する" - ) - } else { - List() - } + .read + .toIO + .flatMap(amountData => + IO { + val level = amountData.levelCorrespondingToExp.level - Button( - new IconItemStackBuilder(material) - .title(s"$YELLOW$UNDERLINE${BOLD}Gigantic$RED$UNDERLINE${BOLD}Berserk") - .lore(baseLore ++ lengthInfoLore ++ levelInfoLore ++ evolutionLore) - .tap(builder => if (openerData.giganticBerserk.canEvolve || openerData.giganticBerserk.reachedLimit()) { - builder.enchanted() - }) - .build(), - LeftClickButtonEffect { - if (level < 10) { - val message = - s"${WHITE}パッシブスキル$YELLOW$UNDERLINE${BOLD}Gigantic$RED$UNDERLINE${BOLD}Berserk${WHITE}はレベル10以上から使用可能です" - SequentialEffect( - MessageEffect(message), - FocusedSoundEffect(Sound.BLOCK_GLASS_PLACE, 1f, 0.1f) - ) - } else if (openerData.giganticBerserk.canEvolve) { - //TODO: メニューに置き換える - SequentialEffect( - ComputedEffect(player => openInventoryEffect(MenuInventoryData.getGiganticBerserkBeforeEvolutionMenu(player))), - FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 0.5f) + val openerData = SeichiAssist.playermap(getUniqueId) + + val material = + if (level < 10) Material.STICK else openerData.giganticBerserk.materialOnUI() + val baseLore = if (level < 10) { + List(s"${WHITE}このパッシブスキルは", s"${WHITE}整地Lvが10以上になると解放されます") + } else { + List( + s"${RED}敵MOBを倒した時", + s"${RED}その魂を吸収しマナへと変換するスキル", + s"$DARK_GRAY※成功率は高くなく", + s"${DARK_GRAY}整地中でなければその効果を発揮しない", + "", + s"${DARK_GRAY}実装は試験的であり、変更される場合があります" + ) + } + val lengthInfoLore = if (openerData.giganticBerserk.reachedLimit()) { + List(s"${GRAY}MOBの魂を極限まで吸収し最大限の力を発揮する") + } else { + List( + s"${GRAY}MOBの魂を${openerData.giganticBerserk.requiredExpToNextLevel()}回吸収すると更なる力が得られる", + s"$GRAY${openerData.giganticBerserk.exp}/${openerData.giganticBerserk.requiredExpToNextLevel()}" + ) + } + val probability = 100 * openerData.giganticBerserk.manaRegenerationProbability() + val formatted = f"$probability%2.0f" + // 細かい数字が表示されないようにする + val levelInfoLore = + List(s"${GRAY}現在 ${openerData.giganticBerserk.level + 1}レベル,回復率 $formatted%") + val evolutionLore = if (openerData.giganticBerserk.canEvolve) { + List( + "", + s"${DARK_RED}沢山の魂を吸収したことで", + s"${DARK_RED}スキルの秘めたる力を解放できそうだ…!", + s"$DARK_RED${UNDERLINE}クリックで開放する" + ) + } else { + List() + } + + Button( + new IconItemStackBuilder(material) + .title(s"$YELLOW$UNDERLINE${BOLD}Gigantic$RED$UNDERLINE${BOLD}Berserk") + .lore(baseLore ++ lengthInfoLore ++ levelInfoLore ++ evolutionLore) + .tap(builder => + if ( + openerData + .giganticBerserk + .canEvolve || openerData.giganticBerserk.reachedLimit() + ) { + builder.enchanted() + } ) - } else { - MessageEffect(s"${RED}進化条件を満たしていません") + .build(), + LeftClickButtonEffect { + if (level < 10) { + val message = + s"${WHITE}パッシブスキル$YELLOW$UNDERLINE${BOLD}Gigantic$RED$UNDERLINE${BOLD}Berserk${WHITE}はレベル10以上から使用可能です" + SequentialEffect( + MessageEffect(message), + FocusedSoundEffect(Sound.BLOCK_GLASS_PLACE, 1f, 0.1f) + ) + } else if (openerData.giganticBerserk.canEvolve) { + // TODO: メニューに置き換える + SequentialEffect( + ComputedEffect(player => + openInventoryEffect( + MenuInventoryData.getGiganticBerserkBeforeEvolutionMenu(player) + ) + ), + FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 0.5f) + ) + } else { + MessageEffect(s"${RED}進化条件を満たしていません") + } } - } - ) - }) + ) + } + ) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/skill/PremiumPointTransactionHistoryMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/skill/PremiumPointTransactionHistoryMenu.scala index 25c5f49653..3e33bd9461 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/skill/PremiumPointTransactionHistoryMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/skill/PremiumPointTransactionHistoryMenu.scala @@ -1,7 +1,11 @@ package com.github.unchama.seichiassist.menus.skill import cats.effect.IO -import com.github.unchama.itemstackbuilder.{IconItemStackBuilder, SkullItemStackBuilder, SkullOwnerReference} +import com.github.unchama.itemstackbuilder.{ + IconItemStackBuilder, + SkullItemStackBuilder, + SkullOwnerReference +} import com.github.unchama.menuinventory.router.CanOpen import com.github.unchama.menuinventory.slot.button.Button import com.github.unchama.menuinventory.{ChestSlotRef, Menu, MenuFrame, MenuSlotLayout} @@ -15,9 +19,10 @@ import org.bukkit.entity.Player object PremiumPointTransactionHistoryMenu { - class Environment(implicit - val ioCanOpenActiveSkillEffectMenu: IO CanOpen ActiveSkillEffectMenu.type, - val ioCanOpenTransactionHistoryMenu: IO CanOpen PremiumPointTransactionHistoryMenu) + class Environment( + implicit val ioCanOpenActiveSkillEffectMenu: IO CanOpen ActiveSkillEffectMenu.type, + val ioCanOpenTransactionHistoryMenu: IO CanOpen PremiumPointTransactionHistoryMenu + ) } @@ -29,53 +34,61 @@ case class PremiumPointTransactionHistoryMenu(pageNumber: Int) extends Menu { override type Environment = PremiumPointTransactionHistoryMenu.Environment override val frame: MenuFrame = MenuFrame(4.chestRows, s"$BLUE${BOLD}プレミアムエフェクト購入履歴") - def buttonToTransferTo(pageNumber: Int, skullOwnerReference: SkullOwnerReference) - (implicit ioCanOpenTransactionHistoryMenu: IO CanOpen PremiumPointTransactionHistoryMenu): Button = + def buttonToTransferTo(pageNumber: Int, skullOwnerReference: SkullOwnerReference)( + implicit ioCanOpenTransactionHistoryMenu: IO CanOpen PremiumPointTransactionHistoryMenu + ): Button = CommonButtons.transferButton( new SkullItemStackBuilder(skullOwnerReference), s"${pageNumber}ページ目へ", PremiumPointTransactionHistoryMenu(pageNumber) ) - def computeDynamicParts(player: Player) - (implicit environment: PremiumPointTransactionHistoryMenu.Environment): IO[List[(Int, Button)]] = { + def computeDynamicParts(player: Player)( + implicit environment: PremiumPointTransactionHistoryMenu.Environment + ): IO[List[(Int, Button)]] = { import environment._ for { - history <- SeichiAssist.databaseGateway.donateDataManipulator.loadTransactionHistoryFor(player) + history <- SeichiAssist + .databaseGateway + .donateDataManipulator + .loadTransactionHistoryFor(player) } yield { val entriesPerPage = 3 * 9 - val slicedHistory = history.slice((pageNumber - 1) * entriesPerPage, pageNumber * entriesPerPage) + val slicedHistory = + history.slice((pageNumber - 1) * entriesPerPage, pageNumber * entriesPerPage) val historySection = - slicedHistory.zipWithIndex.map { case (transaction, index) => - val itemStack = - transaction match { - case Obtained(amount, date) => - new IconItemStackBuilder(Material.DIAMOND) - .title(s"$AQUA$UNDERLINE${BOLD}寄付") - .lore(List( - s"${RESET.toString}${GREEN}金額:${amount * 100}", - s"$RESET${GREEN}プレミアムエフェクトポイント:+$amount", - s"$RESET${GREEN}日時:$date" - )) - .build() - case Used(amount, date, forPurchaseOf) => { - forPurchaseOf match { - case Left(unknownEffectName) => - new IconItemStackBuilder(Material.BEDROCK) - .title(s"$RESET${YELLOW}購入エフェクト:未定義($unknownEffectName)") - case Right(skill) => - new IconItemStackBuilder(skill.materialOnUI) - .title(s"$RESET${YELLOW}購入エフェクト:${skill.nameOnUI}") - } - }.lore( - s"$RESET${GOLD}プレミアムエフェクトポイント: -$amount", - s"$RESET${GOLD}日時:$date" - ).build() - } - - (index, Button(itemStack, Nil)) + slicedHistory.zipWithIndex.map { + case (transaction, index) => + val itemStack = + transaction match { + case Obtained(amount, date) => + new IconItemStackBuilder(Material.DIAMOND) + .title(s"$AQUA$UNDERLINE${BOLD}寄付") + .lore( + List( + s"${RESET.toString}${GREEN}金額:${amount * 100}", + s"$RESET${GREEN}プレミアムエフェクトポイント:+$amount", + s"$RESET${GREEN}日時:$date" + ) + ) + .build() + case Used(amount, date, forPurchaseOf) => + { + forPurchaseOf match { + case Left(unknownEffectName) => + new IconItemStackBuilder(Material.BEDROCK) + .title(s"$RESET${YELLOW}購入エフェクト:未定義($unknownEffectName)") + case Right(skill) => + new IconItemStackBuilder(skill.materialOnUI) + .title(s"$RESET${YELLOW}購入エフェクト:${skill.nameOnUI}") + } + }.lore(s"$RESET${GOLD}プレミアムエフェクトポイント: -$amount", s"$RESET${GOLD}日時:$date") + .build() + } + + (index, Button(itemStack, Nil)) } val previousPageButtonSection = @@ -86,7 +99,9 @@ case class PremiumPointTransactionHistoryMenu(pageNumber: Int) extends Menu { val nextPageButtonSection = if (history.drop(pageNumber * entriesPerPage).nonEmpty) - Seq(ChestSlotRef(3, 8) -> buttonToTransferTo(pageNumber + 1, SkullOwners.MHF_ArrowDown)) + Seq( + ChestSlotRef(3, 8) -> buttonToTransferTo(pageNumber + 1, SkullOwners.MHF_ArrowDown) + ) else Seq() @@ -94,23 +109,24 @@ case class PremiumPointTransactionHistoryMenu(pageNumber: Int) extends Menu { } } - def constantParts(implicit environment: PremiumPointTransactionHistoryMenu.Environment): Map[Int, Button] = { + def constantParts( + implicit environment: PremiumPointTransactionHistoryMenu.Environment + ): Map[Int, Button] = { import environment._ val moveBackButton = CommonButtons.transferButton( new SkullItemStackBuilder(SkullOwners.MHF_ArrowLeft), "整地スキルエフェクト選択メニューへ", - ActiveSkillEffectMenu, + ActiveSkillEffectMenu ) - Map( - ChestSlotRef(3, 0) -> moveBackButton - ) + Map(ChestSlotRef(3, 0) -> moveBackButton) } - override def computeMenuLayout(player: Player) - (implicit environment: PremiumPointTransactionHistoryMenu.Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout(player: Player)( + implicit environment: PremiumPointTransactionHistoryMenu.Environment + ): IO[MenuSlotLayout] = { for { dynamicParts <- computeDynamicParts(player) } yield MenuSlotLayout(constantParts ++ dynamicParts) diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/stickmenu/FirstPage.scala b/src/main/scala/com/github/unchama/seichiassist/menus/stickmenu/FirstPage.scala index bb8609857b..f45644967a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/stickmenu/FirstPage.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/stickmenu/FirstPage.scala @@ -5,7 +5,11 @@ import cats.effect.{IO, SyncIO} import com.github.unchama.itemstackbuilder.{IconItemStackBuilder, SkullItemStackBuilder} import com.github.unchama.menuinventory._ import com.github.unchama.menuinventory.router.CanOpen -import com.github.unchama.menuinventory.slot.button.action.{ClickEventFilter, FilteredButtonEffect, LeftClickButtonEffect} +import com.github.unchama.menuinventory.slot.button.action.{ + ClickEventFilter, + FilteredButtonEffect, + LeftClickButtonEffect +} import com.github.unchama.menuinventory.slot.button.{Button, RecomputedButton, action} import com.github.unchama.seichiassist.data.descrptions.PlayerStatsLoreGenerator import com.github.unchama.seichiassist.data.{GachaSkullData, MenuInventoryData} @@ -14,14 +18,24 @@ import com.github.unchama.seichiassist.menus.achievement.AchievementMenu import com.github.unchama.seichiassist.menus.minestack.MineStackMainMenu import com.github.unchama.seichiassist.menus.ranking.RankingRootMenu import com.github.unchama.seichiassist.menus.skill.{ActiveSkillMenu, PassiveSkillMenu} -import com.github.unchama.seichiassist.menus.{CommonButtons, HomeMenu, RegionMenu, ServerSwitchMenu} +import com.github.unchama.seichiassist.menus.{ + CommonButtons, + HomeMenu, + RegionMenu, + ServerSwitchMenu +} +import com.github.unchama.seichiassist.subsystems.anywhereender.AnywhereEnderChestAPI +import com.github.unchama.seichiassist.subsystems.anywhereender.domain.AccessDenialReason import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountReadAPI import com.github.unchama.seichiassist.subsystems.breakcount.domain.SeichiAmountData import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.SeichiStarLevel import com.github.unchama.seichiassist.subsystems.breakcountbar.BreakCountBarAPI import com.github.unchama.seichiassist.subsystems.breakcountbar.domain.BreakCountBarVisibility import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.settings.FastDiggingEffectSuppressionState -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.{FastDiggingEffectApi, FastDiggingSettingsApi} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.{ + FastDiggingEffectApi, + FastDiggingSettingsApi +} import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.FourDimensionalPocketApi import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.domain.PocketSize import com.github.unchama.seichiassist.subsystems.gachapoint.GachaPointApi @@ -35,7 +49,7 @@ import com.github.unchama.targetedeffect.player.{CommandEffect, FocusedSoundEffe import com.github.unchama.util.InventoryUtil import com.github.unchama.util.external.{ExternalPlugins, WorldGuardWrapper} import io.chrisdavenport.cats.effect.time.JavaTime -import org.bukkit.ChatColor.{DARK_RED, RESET, _} +import org.bukkit.ChatColor._ import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack import org.bukkit.{Material, Sound} @@ -43,40 +57,48 @@ import org.bukkit.{Material, Sound} /** * 木の棒メニュー1ページ目 * - * @author karayuu + * @author + * karayuu */ object FirstPage extends Menu { import com.github.unchama.menuinventory.syntax._ - import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.{layoutPreparationContext, onMainThread} + import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.{ + layoutPreparationContext, + onMainThread + } import com.github.unchama.targetedeffect.player.PlayerEffects._ import eu.timepit.refined.auto._ - class Environment(implicit - val breakCountAPI: BreakCountReadAPI[IO, SyncIO, Player], - val breakCountBarApi: BreakCountBarAPI[SyncIO, Player], - val fourDimensionalPocketApi: FourDimensionalPocketApi[IO, Player], - val fastDiggingEffectApi: FastDiggingEffectApi[IO, Player], - val fastDiggingSettingsApi: FastDiggingSettingsApi[IO, Player], - val rankingApi: RankingProvider[IO, SeichiAmountData], - val gachaPointApi: GachaPointApi[IO, SyncIO, Player], - val ioJavaTime: JavaTime[IO], - val ioCanOpenSecondPage: IO CanOpen SecondPage.type, - val ioCanOpenMineStackMenu: IO CanOpen MineStackMainMenu.type, - val ioCanOpenRegionMenu: IO CanOpen RegionMenu.type, - val ioCanOpenActiveSkillMenu: IO CanOpen ActiveSkillMenu.type, - val ioCanOpenServerSwitchMenu: IO CanOpen ServerSwitchMenu.type, - val ioCanOpenAchievementMenu: IO CanOpen AchievementMenu.type, - val ioCanOpenHomeMenu: IO CanOpen HomeMenu.type, - val ioCanOpenPassiveSkillMenu: IO CanOpen PassiveSkillMenu.type, - val ioCanOpenRankingRootMenu: IO CanOpen RankingRootMenu.type) + class Environment( + implicit val breakCountAPI: BreakCountReadAPI[IO, SyncIO, Player], + val breakCountBarApi: BreakCountBarAPI[SyncIO, Player], + val fourDimensionalPocketApi: FourDimensionalPocketApi[IO, Player], + val fastDiggingEffectApi: FastDiggingEffectApi[IO, Player], + val fastDiggingSettingsApi: FastDiggingSettingsApi[IO, Player], + val rankingApi: RankingProvider[IO, SeichiAmountData], + val gachaPointApi: GachaPointApi[IO, SyncIO, Player], + val ioJavaTime: JavaTime[IO], + val ioCanOpenSecondPage: IO CanOpen SecondPage.type, + val ioCanOpenMineStackMenu: IO CanOpen MineStackMainMenu.type, + val ioCanOpenRegionMenu: IO CanOpen RegionMenu.type, + val ioCanOpenActiveSkillMenu: IO CanOpen ActiveSkillMenu.type, + val ioCanOpenServerSwitchMenu: IO CanOpen ServerSwitchMenu.type, + val ioCanOpenAchievementMenu: IO CanOpen AchievementMenu.type, + val ioCanOpenHomeMenu: IO CanOpen HomeMenu.type, + val ioCanOpenPassiveSkillMenu: IO CanOpen PassiveSkillMenu.type, + val ioCanOpenRankingRootMenu: IO CanOpen RankingRootMenu.type, + val enderChestAccessApi: AnywhereEnderChestAPI[IO] + ) override val frame: MenuFrame = MenuFrame(4.chestRows, s"${LIGHT_PURPLE}木の棒メニュー") import com.github.unchama.targetedeffect._ - override def computeMenuLayout(player: Player)(implicit environment: Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { import ConstantButtons._ val computations = ButtonComputations(player) import computations._ @@ -114,12 +136,11 @@ object FirstPage extends Menu { ChestSlotRef(2, 3) -> computePocketOpenButton, ChestSlotRef(2, 4) -> computeEnderChestButton, ChestSlotRef(2, 6) -> computeMineStackButton, - ChestSlotRef(3, 2) -> computeApologyItemsButton, + ChestSlotRef(3, 2) -> computeApologyItemsButton ).map(_.sequence) - val computeOptionallyShownPart: List[IO[(Int, Option[Button])]] = List( - ChestSlotRef(1, 1) -> computeStarLevelStatsButton - ).map(_.sequence) + val computeOptionallyShownPart: List[IO[(Int, Option[Button])]] = + List(ChestSlotRef(1, 1) -> computeStarLevelStatsButton).map(_.sequence) for { constantlyShownPart <- computeConstantlyShownPart.sequence @@ -147,14 +168,11 @@ object FirstPage extends Menu { for { seichiAmountData <- - environment.breakCountAPI - .seichiAmountDataRepository(player) - .read.toIO + environment.breakCountAPI.seichiAmountDataRepository(player).read.toIO ranking <- environment.rankingApi.ranking.read visibility <- visibilityRef.get.toIO - lore <- new PlayerStatsLoreGenerator( - openerData, ranking, seichiAmountData, visibility - ).computeLore() + lore <- new PlayerStatsLoreGenerator(openerData, ranking, seichiAmountData, visibility) + .computeLore() } yield Button( new SkullItemStackBuilder(getUniqueId) .title(s"$YELLOW$BOLD$UNDERLINE${getName}の統計データ") @@ -162,12 +180,11 @@ object FirstPage extends Menu { .build(), FilteredButtonEffect(ClickEventFilter.LEFT_CLICK) { _ => DeferredEffect { - visibilityRef - .updateAndGet(_.nextValue).toIO - .map { updatedVisibility => - val toggleSoundPitch = if (updatedVisibility == BreakCountBarVisibility.Shown) 1.0f else 0.5f - FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, toggleSoundPitch) - } + visibilityRef.updateAndGet(_.nextValue).toIO.map { updatedVisibility => + val toggleSoundPitch = + if (updatedVisibility == BreakCountBarVisibility.Shown) 1.0f else 0.5f + FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, toggleSoundPitch) + } } } ) @@ -178,7 +195,10 @@ object FirstPage extends Menu { import environment._ val computeButtonLore: IO[List[String]] = for { - currentStatus <- environment.fastDiggingSettingsApi.currentSuppressionSettings(player).read + currentStatus <- environment + .fastDiggingSettingsApi + .currentSuppressionSettings(player) + .read effectList <- environment.fastDiggingEffectApi.currentEffect(player).read currentEffects <- effectList.filteredList[IO] currentAmplifier = currentEffects.map(_.effect.amplifier).combineAll @@ -230,20 +250,19 @@ object FirstPage extends Menu { for { buttonLore <- computeButtonLore - } yield - Button( - new IconItemStackBuilder(Material.DIAMOND_PICKAXE) - .title(s"$YELLOW$UNDERLINE${BOLD}採掘速度上昇効果") - .enchanted() - .lore(buttonLore) - .build(), - action.FilteredButtonEffect(ClickEventFilter.LEFT_CLICK) { _ => - SequentialEffect( - FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f), - environment.fastDiggingSettingsApi.toggleEffectSuppression.as(()), - ) - } - ) + } yield Button( + new IconItemStackBuilder(Material.DIAMOND_PICKAXE) + .title(s"$YELLOW$UNDERLINE${BOLD}採掘速度上昇効果") + .enchanted() + .lore(buttonLore) + .build(), + action.FilteredButtonEffect(ClickEventFilter.LEFT_CLICK) { _ => + SequentialEffect( + FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f), + environment.fastDiggingSettingsApi.toggleEffectSuppression.as(()) + ) + } + ) } val computeRegionMenuButton: IO[Button] = IO { @@ -276,54 +295,60 @@ object FirstPage extends Menu { } val computeMineStackButton: IO[Button] = - environment.breakCountAPI - .seichiAmountDataRepository(player).read + environment + .breakCountAPI + .seichiAmountDataRepository(player) + .read .toIO - .flatMap(seichiAmountData => IO { - val minimumLevelRequired = SeichiAssist.seichiAssistConfig.getMineStacklevel(1) - val hasEnoughLevel = seichiAmountData.levelCorrespondingToExp.level >= minimumLevelRequired - - (minimumLevelRequired, hasEnoughLevel) - }) - .map { case (minimumLevelRequired, hasEnoughLevel) => - val buttonLore: List[String] = { - val explanation = List( - s"$RESET${GREEN}説明しよう!MineStackとは…", - s"${RESET}主要アイテムを無限にスタック出来る!", - s"${RESET}スタックしたアイテムは", - s"${RESET}ここから取り出せるゾ!" - ) + .flatMap(seichiAmountData => + IO { + val minimumLevelRequired = SeichiAssist.seichiAssistConfig.getMineStacklevel(1) + val hasEnoughLevel = + seichiAmountData.levelCorrespondingToExp.level >= minimumLevelRequired - val actionGuidance = if (hasEnoughLevel) { - s"$RESET$DARK_GREEN${UNDERLINE}クリックで開く" - } else { - s"$RESET$DARK_RED${UNDERLINE}整地Lvが${minimumLevelRequired}以上必要です" - } + (minimumLevelRequired, hasEnoughLevel) + } + ) + .map { + case (minimumLevelRequired, hasEnoughLevel) => + val buttonLore: List[String] = { + val explanation = List( + s"$RESET${GREEN}説明しよう!MineStackとは…", + s"${RESET}主要アイテムを無限にスタック出来る!", + s"${RESET}スタックしたアイテムは", + s"${RESET}ここから取り出せるゾ!" + ) - val annotation = List( - s"$RESET$DARK_GRAY※スタックしたアイテムは", - s"$RESET${DARK_GRAY}各サバイバルサーバー間で", - s"$RESET${DARK_GRAY}共有されます" - ) + val actionGuidance = if (hasEnoughLevel) { + s"$RESET$DARK_GREEN${UNDERLINE}クリックで開く" + } else { + s"$RESET$DARK_RED${UNDERLINE}整地Lvが${minimumLevelRequired}以上必要です" + } - explanation ++ List(actionGuidance) ++ annotation - } + val annotation = List( + s"$RESET$DARK_GRAY※スタックしたアイテムは", + s"$RESET${DARK_GRAY}各サバイバルサーバー間で", + s"$RESET${DARK_GRAY}共有されます" + ) - Button( - new IconItemStackBuilder(Material.CHEST) - .title(s"$YELLOW$UNDERLINE${BOLD}MineStack機能") - .lore(buttonLore) - .build(), - LeftClickButtonEffect { - if (hasEnoughLevel) - SequentialEffect( - FocusedSoundEffect(Sound.BLOCK_FENCE_GATE_OPEN, 1f, 0.1f), - environment.ioCanOpenMineStackMenu.open(MineStackMainMenu) - ) - else - FocusedSoundEffect(Sound.BLOCK_GLASS_PLACE, 1f, 0.1f) + explanation ++ List(actionGuidance) ++ annotation } - ) + + Button( + new IconItemStackBuilder(Material.CHEST) + .title(s"$YELLOW$UNDERLINE${BOLD}MineStack機能") + .lore(buttonLore) + .build(), + LeftClickButtonEffect { + if (hasEnoughLevel) + SequentialEffect( + FocusedSoundEffect(Sound.BLOCK_FENCE_GATE_OPEN, 1f, 0.1f), + environment.ioCanOpenMineStackMenu.open(MineStackMainMenu) + ) + else + FocusedSoundEffect(Sound.BLOCK_GLASS_PLACE, 1f, 0.1f) + } + ) } val computePocketOpenButton: IO[Button] = { @@ -358,54 +383,46 @@ object FirstPage extends Menu { } } - val computeEnderChestButton: IO[Button] = - environment - .breakCountAPI - .seichiAmountDataRepository(player) - .read.toIO - .flatMap(breakAmountData => IO { - val level = breakAmountData.levelCorrespondingToExp.level - val minimumRequiredLevel = SeichiAssist.seichiAssistConfig.getDokodemoEnderlevel - val hasEnoughLevel = level >= minimumRequiredLevel - val enderChest = player.getEnderChest - - val iconItemStack = { - val loreHeading = { - if (hasEnoughLevel) { - s"$RESET$DARK_GREEN${UNDERLINE}クリックで開く" - } else { - s"$RESET$DARK_RED${UNDERLINE}整地Lvが${minimumRequiredLevel}以上必要です" - } - } - - new IconItemStackBuilder(Material.ENDER_CHEST) - .title(s"$DARK_PURPLE$UNDERLINE${BOLD}どこでもエンダーチェスト") - .lore(List(loreHeading)) - .build() - } - - Button( - iconItemStack, - LeftClickButtonEffect( - if (hasEnoughLevel) - SequentialEffect( - FocusedSoundEffect(Sound.BLOCK_ENDERCHEST_OPEN, 1.0f, 1.0f), - openInventoryEffect(enderChest) - ) - else - FocusedSoundEffect(Sound.BLOCK_GRASS_PLACE, 1.0f, 0.1f) + val computeEnderChestButton: IO[Button] = for { + canAccess <- environment.enderChestAccessApi.canAccessAnywhereEnderChest(player) + } yield { + val iconItemStack = + new IconItemStackBuilder(Material.ENDER_CHEST) + .title(s"$DARK_PURPLE$UNDERLINE${BOLD}どこでもエンダーチェスト") + .lore( + List( + canAccess.fold( + { + case AccessDenialReason.NotEnoughLevel(current, required) => + s"$RESET$DARK_RED${UNDERLINE}整地Lvが${required.level}以上必要です(現在${current.level})" + }, + _ => s"$RESET$DARK_GREEN${UNDERLINE}クリックで開く" + ) ) ) - }) + .build() + + Button( + iconItemStack, + LeftClickButtonEffect( + environment.enderChestAccessApi.openEnderChestOrNotifyInsufficientLevel.flatMap { + case Right(_) => + // 開くのに成功した場合の音 + FocusedSoundEffect(Sound.BLOCK_GRASS_PLACE, 1.0f, 0.1f) + case Left(_) => + // 開くのに失敗した場合の音 + FocusedSoundEffect(Sound.BLOCK_ENDERCHEST_OPEN, 1.0f, 1.0f) + } + ) + ) + } val computeApologyItemsButton: IO[Button] = RecomputedButton(IO { val playerData = SeichiAssist.playermap(getUniqueId) val iconItemStack = { val lore = { - val explanation = List( - s"$RESET${GRAY}運営からのガチャ券を受け取ります" - ) + val explanation = List(s"$RESET${GRAY}運営からのガチャ券を受け取ります") val obtainableApologyItems = playerData.unclaimedApologyItems val currentStatus = @@ -432,7 +449,8 @@ object FirstPage extends Menu { // NOTE: playerData.unclaimedApologyItemsは信頼できる値ではない // プレーヤーがログインしている最中に配布処理が行われた場合DB上の値とメモリ上の値に差分が出る。 // よって配布処理はすべてバックエンドと協調しながら行わなければならない。 - val numberOfItemsToGive = SeichiAssist.databaseGateway.playerDataManipulator.givePlayerBug(player) + val numberOfItemsToGive = + SeichiAssist.databaseGateway.playerDataManipulator.givePlayerBug(player) if (numberOfItemsToGive > 0) { val itemToGive = GachaSkullData.gachaSkull @@ -444,7 +462,9 @@ object FirstPage extends Menu { playerData.unclaimedApologyItems -= numberOfItemsToGive }, FocusedSoundEffect(Sound.BLOCK_ANVIL_PLACE, 1.0f, 1.0f), - MessageEffect(s"${GREEN}運営チームから${numberOfItemsToGive}枚の${GOLD}ガチャ券${WHITE}を受け取りました") + MessageEffect( + s"${GREEN}運営チームから${numberOfItemsToGive}枚の${GOLD}ガチャ券${WHITE}を受け取りました" + ) ) } else emptyEffect } else emptyEffect @@ -464,7 +484,7 @@ object FirstPage extends Menu { val iconItemStack = { val lore = List( s"$RESET$GREEN$UNDERLINE${BOLD}現在のスターレベル:☆${starLevel.level}", - s"$RESET${AQUA}次の☆まで:あと${seichiAmountData.levelProgress.expAmountToNextLevel.formatted}", + s"$RESET${AQUA}次の☆まで:あと${seichiAmountData.levelProgress.expAmountToNextLevel.formatted}" ) new IconItemStackBuilder(Material.GOLD_INGOT) @@ -482,15 +502,9 @@ object FirstPage extends Menu { val iconItemStack = { val lore = if (Util.seichiSkillsAllowedIn(player.getWorld)) - List( - s"$RESET${GRAY}整地に便利なスキルを使用できるゾ", - s"$RESET$DARK_RED${UNDERLINE}クリックでスキル一覧を開く" - ) + List(s"$RESET${GRAY}整地に便利なスキルを使用できるゾ", s"$RESET$DARK_RED${UNDERLINE}クリックでスキル一覧を開く") else - List( - s"$RESET${RED}このワールドでは", - s"$RESET${RED}整地スキルを使えません" - ) + List(s"$RESET${RED}このワールドでは", s"$RESET${RED}整地スキルを使えません") new IconItemStackBuilder(Material.ENCHANTED_BOOK) .enchanted() @@ -503,24 +517,24 @@ object FirstPage extends Menu { iconItemStack, LeftClickButtonEffect( FocusedSoundEffect(Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1.0f, 0.8f), - environment.ioCanOpenActiveSkillMenu.open(ActiveSkillMenu), + environment.ioCanOpenActiveSkillMenu.open(ActiveSkillMenu) ) ) } val computeGachaTicketButton: IO[Button] = { - val effect: FilteredButtonEffect = LeftClickButtonEffect(environment.gachaPointApi.receiveBatch) + val effect: FilteredButtonEffect = LeftClickButtonEffect( + environment.gachaPointApi.receiveBatch + ) - val computeItemStack: IO[ItemStack] = environment - .gachaPointApi - .gachaPoint(player) - .read.toIO - .map { point => + val computeItemStack: IO[ItemStack] = + environment.gachaPointApi.gachaPoint(player).read.toIO.map { point => val lore = { - val gachaTicketStatus = if (point.availableTickets != BigInt(0)) - s"$RESET${AQUA}未獲得ガチャ券:${point.availableTickets}枚" - else - s"$RESET${RED}獲得できるガチャ券はありません" + val gachaTicketStatus = + if (point.availableTickets != BigInt(0)) + s"$RESET${AQUA}未獲得ガチャ券:${point.availableTickets}枚" + else + s"$RESET${RED}獲得できるガチャ券はありません" val requiredToNextTicket = s"$RESET${AQUA}次のガチャ券まで:${point.amountUntilNextGachaTicket.amount}ブロック" @@ -534,14 +548,18 @@ object FirstPage extends Menu { .build() } - val computeButton: IO[Button] = computeItemStack.map { itemStack => Button(itemStack, effect) } + val computeButton: IO[Button] = computeItemStack.map { itemStack => + Button(itemStack, effect) + } RecomputedButton(computeButton) } } private object ConstantButtons { - def teleportServerButton(implicit ioCanOpenServerSwitchMenu: IO CanOpen ServerSwitchMenu.type): Button = { + def teleportServerButton( + implicit ioCanOpenServerSwitchMenu: IO CanOpen ServerSwitchMenu.type + ): Button = { val buttonLore = List( s"$GRAY・各サバイバルサーバー", s"$GRAY・公共施設サーバー", @@ -583,7 +601,9 @@ object FirstPage extends Menu { ) } - def achievementSystemButton(implicit ioCanOpenAchievementMenu: IO CanOpen AchievementMenu.type): Button = { + def achievementSystemButton( + implicit ioCanOpenAchievementMenu: IO CanOpen AchievementMenu.type + ): Button = { val buttonLore = List( s"${GRAY}様々な実績に挑んで、", s"${GRAY}いろんな二つ名を手に入れよう!", @@ -613,16 +633,18 @@ object FirstPage extends Menu { val iconItemStack = new IconItemStackBuilder(Material.NOTE_BLOCK) .title(s"$YELLOW$UNDERLINE${BOLD}不要ガチャ景品交換システム") - .lore(List( - s"$RESET${GREEN}不必要な当たり、大当たり景品を", - s"$RESET${GREEN}ガチャ券と交換出来ます", - s"$RESET${GREEN}出てきたインベントリ―に", - s"$RESET${GREEN}交換したい景品を入れて", - s"$RESET${GREEN}escキーを押してください", - s"$RESET${DARK_GRAY}たまにアイテムが消失するから", - s"$RESET${DARK_GRAY}大事なものはいれないでネ", - s"$RESET$DARK_RED${UNDERLINE}クリックで開く" - )) + .lore( + List( + s"$RESET${GREEN}不必要な当たり、大当たり景品を", + s"$RESET${GREEN}ガチャ券と交換出来ます", + s"$RESET${GREEN}出てきたインベントリ―に", + s"$RESET${GREEN}交換したい景品を入れて", + s"$RESET${GREEN}escキーを押してください", + s"$RESET${DARK_GRAY}たまにアイテムが消失するから", + s"$RESET${DARK_GRAY}大事なものはいれないでネ", + s"$RESET$DARK_RED${UNDERLINE}クリックで開く" + ) + ) .build() Button( @@ -634,7 +656,7 @@ object FirstPage extends Menu { size = 4.chestRows, title = Some(s"$LIGHT_PURPLE${BOLD}交換したい景品を入れてください") ) - ), + ) ) ) } @@ -643,10 +665,7 @@ object FirstPage extends Menu { val iconItemStack = new IconItemStackBuilder(Material.BED) .title(s"$YELLOW$UNDERLINE${BOLD}ホームメニューを開く") - .lore(List( - s"$RESET${GRAY}ホームポイントに関するメニュー", - s"$RESET$DARK_RED${UNDERLINE}クリックで開く" - )) + .lore(List(s"$RESET${GRAY}ホームポイントに関するメニュー", s"$RESET$DARK_RED${UNDERLINE}クリックで開く")) .build() Button( @@ -662,16 +681,17 @@ object FirstPage extends Menu { val iconItemStack = new IconItemStackBuilder(Material.WORKBENCH) .title(s"$YELLOW$UNDERLINE${BOLD}FastCraft機能") - .lore(List( - s"$RESET$DARK_RED${UNDERLINE}クリックで開く", - s"$RESET${RED}ただの作業台じゃないんです…", - s"$RESET${YELLOW}自動レシピ補完機能付きの", - s"$RESET${YELLOW}最強な作業台はこちら", - s"$RESET${DARK_GRAY}command=>[/fc craft]" - )) + .lore( + List( + s"$RESET$DARK_RED${UNDERLINE}クリックで開く", + s"$RESET${RED}ただの作業台じゃないんです…", + s"$RESET${YELLOW}自動レシピ補完機能付きの", + s"$RESET${YELLOW}最強な作業台はこちら", + s"$RESET${DARK_GRAY}command=>[/fc craft]" + ) + ) .build() - Button( iconItemStack, LeftClickButtonEffect( @@ -681,15 +701,16 @@ object FirstPage extends Menu { ) } - def passiveSkillBookButton(implicit ioCanOpenPassiveSkillMenu: IO CanOpen PassiveSkillMenu.type): Button = { + def passiveSkillBookButton( + implicit ioCanOpenPassiveSkillMenu: IO CanOpen PassiveSkillMenu.type + ): Button = { val iconItemStack = new IconItemStackBuilder(Material.ENCHANTED_BOOK) .enchanted() .title(s"$YELLOW$UNDERLINE${BOLD}パッシブスキルブック") - .lore(List( - s"$RESET${GRAY}整地に便利なスキルを使用できるゾ", - s"$RESET$DARK_RED${UNDERLINE}クリックでスキル一覧を開く" - )) + .lore( + List(s"$RESET${GRAY}整地に便利なスキルを使用できるゾ", s"$RESET$DARK_RED${UNDERLINE}クリックでスキル一覧を開く") + ) .build() Button( @@ -705,16 +726,18 @@ object FirstPage extends Menu { val iconItemStack = new IconItemStackBuilder(Material.DIAMOND_ORE) .title(s"$YELLOW$UNDERLINE${BOLD}鉱石・交換券変換システム") - .lore(List( - s"$RESET${GREEN}不必要な各種鉱石を", - s"$RESET${DARK_RED}交換券$RESET${GREEN}と交換できます", - s"$RESET${GREEN}出てきたインベントリ―に", - s"$RESET${GREEN}交換したい鉱石を入れて", - s"$RESET${GREEN}escキーを押してください", - s"$RESET${DARK_GRAY}たまにアイテムが消失するから", - s"$RESET${DARK_GRAY}大事なものはいれないでネ", - s"$RESET$DARK_RED${UNDERLINE}クリックで開く" - )) + .lore( + List( + s"$RESET${GREEN}不必要な各種鉱石を", + s"$RESET${DARK_RED}交換券$RESET${GREEN}と交換できます", + s"$RESET${GREEN}出てきたインベントリ―に", + s"$RESET${GREEN}交換したい鉱石を入れて", + s"$RESET${GREEN}escキーを押してください", + s"$RESET${DARK_GRAY}たまにアイテムが消失するから", + s"$RESET${DARK_GRAY}大事なものはいれないでネ", + s"$RESET$DARK_RED${UNDERLINE}クリックで開く" + ) + ) .build() Button( @@ -745,7 +768,7 @@ object FirstPage extends Menu { LeftClickButtonEffect( CommonSoundEffects.menuTransitionFenceSound, // TODO メニューに置き換える - ComputedEffect(p => openInventoryEffect(MenuInventoryData.getVotingMenuData(p))), + ComputedEffect(p => openInventoryEffect(MenuInventoryData.getVotingMenuData(p))) ) ) } @@ -754,16 +777,15 @@ object FirstPage extends Menu { Button( new IconItemStackBuilder(Material.MAP) .title(s"${YELLOW}ウェブマップのURLを表示") - .lore(List( - s"$RESET${YELLOW}現在座標を示すウェブマップのURLを表示します!", - s"$RESET$DARK_RED${UNDERLINE}クリックでURLを表示", - s"${DARK_GRAY}command=>[/map]" - )) + .lore( + List( + s"$RESET${YELLOW}現在座標を示すウェブマップのURLを表示します!", + s"$RESET$DARK_RED${UNDERLINE}クリックでURLを表示", + s"${DARK_GRAY}command=>[/map]" + ) + ) .build(), - LeftClickButtonEffect( - closeInventoryEffect, - CommandEffect("map") - ) + LeftClickButtonEffect(closeInventoryEffect, CommandEffect("map")) ) } diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/stickmenu/SecondPage.scala b/src/main/scala/com/github/unchama/seichiassist/menus/stickmenu/SecondPage.scala index 4e150fbf09..69d27dd404 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/stickmenu/SecondPage.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/stickmenu/SecondPage.scala @@ -5,10 +5,18 @@ import com.github.unchama.itemstackbuilder.{IconItemStackBuilder, SkullItemStack import com.github.unchama.menuinventory import com.github.unchama.menuinventory._ import com.github.unchama.menuinventory.router.CanOpen -import com.github.unchama.menuinventory.slot.button.action.{ClickEventFilter, FilteredButtonEffect, LeftClickButtonEffect} +import com.github.unchama.menuinventory.slot.button.action.{ + ClickEventFilter, + FilteredButtonEffect, + LeftClickButtonEffect +} import com.github.unchama.menuinventory.slot.button.{Button, RecomputedButton, action} import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts -import com.github.unchama.seichiassist.data.player.settings.BroadcastMutingSettings.{MuteMessageAndSound, ReceiveMessageAndSound, ReceiveMessageOnly} +import com.github.unchama.seichiassist.data.player.settings.BroadcastMutingSettings.{ + MuteMessageAndSound, + ReceiveMessageAndSound, + ReceiveMessageOnly +} import com.github.unchama.seichiassist.menus.CommonButtons import com.github.unchama.seichiassist.subsystems.seasonalevents.anniversary.Anniversary import com.github.unchama.seichiassist.subsystems.seasonalevents.anniversary.AnniversaryItemData.anniversaryPlayerHead @@ -20,7 +28,11 @@ import com.github.unchama.seichiassist.util.Util import com.github.unchama.seichiassist.util.exp.ExperienceManager import com.github.unchama.seichiassist.{SeichiAssist, SkullOwners} import com.github.unchama.targetedeffect.commandsender.MessageEffect -import com.github.unchama.targetedeffect.player.{CommandEffect, FocusedSoundEffect, PlayerEffects} +import com.github.unchama.targetedeffect.player.{ + CommandEffect, + FocusedSoundEffect, + PlayerEffects +} import org.bukkit.ChatColor._ import org.bukkit.entity.Player import org.bukkit.inventory.meta.SkullMeta @@ -43,7 +55,9 @@ object SecondPage extends Menu { override val frame: MenuFrame = MenuFrame(4.chestRows, s"${LIGHT_PURPLE}木の棒メニュー") - override def computeMenuLayout(player: Player)(implicit environment: Environment): IO[MenuSlotLayout] = { + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { import ConstantButtons._ val computations = ButtonComputations(player) import computations._ @@ -160,11 +174,13 @@ object SecondPage extends Menu { new IconItemStackBuilder(Material.JUKEBOX) .title(s"$YELLOW$UNDERLINE${BOLD}全体通知切替") - .lore(List( - soundConfigurationState, - messageConfigurationState, - s"$RESET$DARK_RED${UNDERLINE}クリックで変更" - )) + .lore( + List( + soundConfigurationState, + messageConfigurationState, + s"$RESET$DARK_RED${UNDERLINE}クリックで変更" + ) + ) .build() } } yield Button( @@ -174,11 +190,15 @@ object SecondPage extends Menu { playerData.settings.toggleBroadcastMutingSettings, FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f, 1.0f), DeferredEffect { - playerData.settings.getBroadcastMutingSettings.map { - case ReceiveMessageAndSound => s"${GREEN}非表示/消音設定を解除しました" - case ReceiveMessageOnly => s"${RED}消音可能な全体通知音を消音します" - case MuteMessageAndSound => s"${RED}非表示可能な全体メッセージを非表示にします" - }.map(MessageEffect(_)) + playerData + .settings + .getBroadcastMutingSettings + .map { + case ReceiveMessageAndSound => s"${GREEN}非表示/消音設定を解除しました" + case ReceiveMessageOnly => s"${RED}消音可能な全体通知音を消音します" + case MuteMessageAndSound => s"${RED}非表示可能な全体メッセージを非表示にします" + } + .map(MessageEffect(_)) } ) } @@ -196,16 +216,9 @@ object SecondPage extends Menu { if (playerData.settings.shouldDisplayDeathMessages) { baseBuilder .enchanted() - .lore(List( - s"$RESET${GREEN}表示する", - s"$RESET$DARK_RED${UNDERLINE}クリックで隠す" - )) + .lore(List(s"$RESET${GREEN}表示する", s"$RESET$DARK_RED${UNDERLINE}クリックで隠す")) } else { - baseBuilder - .lore(List( - s"$RESET${RED}隠す", - s"$RESET$DARK_GREEN${UNDERLINE}クリックで表示する" - )) + baseBuilder.lore(List(s"$RESET${RED}隠す", s"$RESET$DARK_GREEN${UNDERLINE}クリックで表示する")) } }.build() @@ -243,18 +256,13 @@ object SecondPage extends Menu { if (playerData.settings.shouldDisplayWorldGuardLogs) { baseBuilder .enchanted() - .lore(List( - loreHeading, - s"$RESET${GREEN}表示する", - s"$RESET$DARK_RED${UNDERLINE}クリックで隠す" - )) + .lore( + List(loreHeading, s"$RESET${GREEN}表示する", s"$RESET$DARK_RED${UNDERLINE}クリックで隠す") + ) } else { - baseBuilder - .lore(List( - loreHeading, - s"$RESET${RED}隠す", - s"$RESET$DARK_GREEN${UNDERLINE}クリックで表示する" - )) + baseBuilder.lore( + List(loreHeading, s"$RESET${RED}隠す", s"$RESET$DARK_GREEN${UNDERLINE}クリックで表示する") + ) } }.build() @@ -285,11 +293,8 @@ object SecondPage extends Menu { val lore = { val playerData = SeichiAssist.playermap(getUniqueId) - val base = List( - s"$RESET${GREEN}現在の装備・アイテムを移動します。", - s"${RESET}サーバー間のアイテム移動にご利用ください。", - "" - ) + val base = + List(s"$RESET${GREEN}現在の装備・アイテムを移動します。", s"${RESET}サーバー間のアイテム移動にご利用ください。", "") val statusDisplay = if (playerData.contentsPresentInSharedInventory) { List( @@ -298,10 +303,7 @@ object SecondPage extends Menu { s"$RESET${RED}現在の装備・アイテムが空であることを確認してください。" ) } else { - List( - s"$RESET${GREEN}非収納中", - s"$RESET$DARK_RED${UNDERLINE}クリックでアイテムを収納します。" - ) + List(s"$RESET${GREEN}非収納中", s"$RESET$DARK_RED${UNDERLINE}クリックでアイテムを収納します。") } base ++ statusDisplay @@ -321,13 +323,15 @@ object SecondPage extends Menu { val officialWikiNavigationButton: Button = { val iconItemStack = new IconItemStackBuilder(Material.BOOK) .title(s"$YELLOW$UNDERLINE${BOLD}公式サイトにアクセス") - .lore(List( - s"$RESET${GREEN}鯖内の「困った」は公式サイトで解決!", - s"$RESET${DARK_GRAY}クリックするとチャット欄に", - s"$RESET${DARK_GRAY}URLが表示されますので", - s"$RESET${DARK_GRAY}Tキーを押してから", - s"$RESET${DARK_GRAY}そのURLをクリックしてください" - )) + .lore( + List( + s"$RESET${GREEN}鯖内の「困った」は公式サイトで解決!", + s"$RESET${DARK_GRAY}クリックするとチャット欄に", + s"$RESET${DARK_GRAY}URLが表示されますので", + s"$RESET${DARK_GRAY}Tキーを押してから", + s"$RESET${DARK_GRAY}そのURLをクリックしてください" + ) + ) .build() Button( @@ -335,7 +339,9 @@ object SecondPage extends Menu { action.FilteredButtonEffect(ClickEventFilter.LEFT_CLICK) { _ => SequentialEffect( closeInventoryEffect, - MessageEffect(s"$RED$UNDERLINE${SeichiAssist.seichiAssistConfig.getUrl("official")}"), + MessageEffect( + s"$RED$UNDERLINE${SeichiAssist.seichiAssistConfig.getUrl("official")}" + ), FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f, 1.0f) ) } @@ -345,13 +351,15 @@ object SecondPage extends Menu { val rulesPageNavigationButton: Button = { val iconItemStack = new IconItemStackBuilder(Material.PAPER) .title(s"$YELLOW$UNDERLINE${BOLD}運営方針とルールを確認") - .lore(List( - s"$RESET${GREEN}当鯖で遊ぶ前に確認してネ!", - s"$RESET${DARK_GRAY}クリックするとチャット欄に", - s"$RESET${DARK_GRAY}URLが表示されますので", - s"$RESET${DARK_GRAY}Tキーを押してから", - s"$RESET${DARK_GRAY}そのURLをクリックしてください" - )) + .lore( + List( + s"$RESET${GREEN}当鯖で遊ぶ前に確認してネ!", + s"$RESET${DARK_GRAY}クリックするとチャット欄に", + s"$RESET${DARK_GRAY}URLが表示されますので", + s"$RESET${DARK_GRAY}Tキーを押してから", + s"$RESET${DARK_GRAY}そのURLをクリックしてください" + ) + ) .build() Button( @@ -369,14 +377,16 @@ object SecondPage extends Menu { val serverMapNavigationButton: Button = { val iconItemStack = new IconItemStackBuilder(Material.MAP) .title(s"$YELLOW$UNDERLINE${BOLD}鯖Mapを見る") - .lore(List( - s"$RESET${GREEN}webブラウザから鯖Mapを閲覧出来ます", - s"$RESET${GREEN}他人の居場所や保護の場所を確認出来ます", - s"$RESET${DARK_GRAY}クリックするとチャット欄に", - s"$RESET${DARK_GRAY}URLが表示されますので", - s"$RESET${DARK_GRAY}Tキーを押してから", - s"$RESET${DARK_GRAY}そのURLをクリックしてください" - )) + .lore( + List( + s"$RESET${GREEN}webブラウザから鯖Mapを閲覧出来ます", + s"$RESET${GREEN}他人の居場所や保護の場所を確認出来ます", + s"$RESET${DARK_GRAY}クリックするとチャット欄に", + s"$RESET${DARK_GRAY}URLが表示されますので", + s"$RESET${DARK_GRAY}Tキーを押してから", + s"$RESET${DARK_GRAY}そのURLをクリックしてください" + ) + ) .build() Button( @@ -394,12 +404,14 @@ object SecondPage extends Menu { val JMSNavigationButton: Button = { val iconItemStack = new IconItemStackBuilder(Material.SIGN) .title(s"$YELLOW$UNDERLINE${BOLD}JapanMinecraftServerリンク") - .lore(List( - s"$RESET${DARK_GRAY}クリックするとチャット欄に", - s"$RESET${DARK_GRAY}URLが表示されますので", - s"$RESET${DARK_GRAY}Tキーを押してから", - s"$RESET${DARK_GRAY}そのURLをクリックしてください" - )) + .lore( + List( + s"$RESET${DARK_GRAY}クリックするとチャット欄に", + s"$RESET${DARK_GRAY}URLが表示されますので", + s"$RESET${DARK_GRAY}Tキーを押してから", + s"$RESET${DARK_GRAY}そのURLをクリックしてください" + ) + ) .build() Button( @@ -417,18 +429,20 @@ object SecondPage extends Menu { val appleConversionButton: Button = { val iconItemStack = new IconItemStackBuilder(Material.GOLDEN_APPLE, durability = 1) .title(s"$YELLOW$UNDERLINE${BOLD}GT景品→椎名林檎変換システム") - .lore(List( - s"$RESET${GREEN}不必要なGT大当り景品を", - s"$RESET${GOLD}椎名林檎$RESET${GREEN}と交換できます", - s"$RESET${GREEN}出てきたインベントリに", - s"$RESET${GREEN}交換したい景品を入れて", - s"$RESET${GREEN}escキーを押してください", - s"$RESET${DARK_GRAY}たまにアイテムが消失しますが", - s"$RESET${DARK_GRAY}補償はしていません(ごめんなさい)", - s"$RESET${DARK_GRAY}神に祈りながら交換しよう", - s"${RESET}現在の交換レート:GT景品1つにつき${SeichiAssist.seichiAssistConfig.rateGiganticToRingo}個", - s"$RESET$DARK_GRAY$DARK_RED${UNDERLINE}クリックで開く" - )) + .lore( + List( + s"$RESET${GREEN}不必要なGT大当り景品を", + s"$RESET${GOLD}椎名林檎$RESET${GREEN}と交換できます", + s"$RESET${GREEN}出てきたインベントリに", + s"$RESET${GREEN}交換したい景品を入れて", + s"$RESET${GREEN}escキーを押してください", + s"$RESET${DARK_GRAY}たまにアイテムが消失しますが", + s"$RESET${DARK_GRAY}補償はしていません(ごめんなさい)", + s"$RESET${DARK_GRAY}神に祈りながら交換しよう", + s"${RESET}現在の交換レート:GT景品1つにつき${SeichiAssist.seichiAssistConfig.rateGiganticToRingo}個", + s"$RESET$DARK_GRAY$DARK_RED${UNDERLINE}クリックで開く" + ) + ) .build() Button( @@ -438,7 +452,10 @@ object SecondPage extends Menu { FocusedSoundEffect(Sound.BLOCK_CHEST_OPEN, 1.0f, 0.5f), // TODO メニューインベントリに差し替える openInventoryEffect( - createInventory(size = 4.chestRows, title = Some(s"$GOLD${BOLD}椎名林檎と交換したい景品を入れてネ")) + createInventory( + size = 4.chestRows, + title = Some(s"$GOLD${BOLD}椎名林檎と交換したい景品を入れてネ") + ) ) ) } @@ -448,11 +465,13 @@ object SecondPage extends Menu { val recycleBinButton: Button = { val iconItemStack = new IconItemStackBuilder(Material.BUCKET) .title(s"$YELLOW$UNDERLINE${BOLD}ゴミ箱を開く") - .lore(List( - s"$RESET${GREEN}不用品の大量処分にドウゾ!", - s"$RESET${RED}復活しないので取扱注意", - s"$RESET$DARK_RED${UNDERLINE}クリックで開く" - )) + .lore( + List( + s"$RESET${GREEN}不用品の大量処分にドウゾ!", + s"$RESET${RED}復活しないので取扱注意", + s"$RESET$DARK_RED${UNDERLINE}クリックで開く" + ) + ) .build() Button( @@ -472,10 +491,9 @@ object SecondPage extends Menu { val hubCommandButton: Button = { val iconItemStack = new IconItemStackBuilder(Material.NETHER_STAR) .title(s"$YELLOW$UNDERLINE${BOLD}ロビーサーバーへ移動") - .lore(List( - s"$RESET$DARK_RED${UNDERLINE}クリックすると移動します", - s"$RESET${DARK_GRAY}command=>[/hub]" - )) + .lore( + List(s"$RESET$DARK_RED${UNDERLINE}クリックすると移動します", s"$RESET${DARK_GRAY}command=>[/hub]") + ) .build() Button( diff --git a/src/main/scala/com/github/unchama/seichiassist/meta/subsystem/Subsystem.scala b/src/main/scala/com/github/unchama/seichiassist/meta/subsystem/Subsystem.scala index 90870d0521..483e06cadc 100644 --- a/src/main/scala/com/github/unchama/seichiassist/meta/subsystem/Subsystem.scala +++ b/src/main/scala/com/github/unchama/seichiassist/meta/subsystem/Subsystem.scala @@ -12,9 +12,11 @@ import org.bukkit.event.Listener * 幾つかのプラグイン機能は他の概念と関連性が薄く、独立した一つのシステムとして機能することがある。 * このtraitは、それらのシステムが親Bukkitプラグインに露出すべき情報をまとめている。 * - * @tparam F ファイナライザの作用のコンテキスト + * @tparam F + * ファイナライザの作用のコンテキスト */ trait Subsystem[F[_]] { + /** * サブシステムが持つリスナ */ diff --git a/src/main/scala/com/github/unchama/seichiassist/minestack/MineStackObj.scala b/src/main/scala/com/github/unchama/seichiassist/minestack/MineStackObj.scala index 3dec402422..913b4643f8 100644 --- a/src/main/scala/com/github/unchama/seichiassist/minestack/MineStackObj.scala +++ b/src/main/scala/com/github/unchama/seichiassist/minestack/MineStackObj.scala @@ -3,13 +3,15 @@ package com.github.unchama.seichiassist.minestack import org.bukkit.Material import org.bukkit.inventory.ItemStack -class MineStackObj(val mineStackObjName: String, - val uiName: Option[String], - val level: Int, - private val _itemStack: ItemStack, - val hasNameLore: Boolean, - val gachaType: Int, - val stackType: MineStackObjectCategory) { +class MineStackObj( + val mineStackObjName: String, + val uiName: Option[String], + val level: Int, + private val _itemStack: ItemStack, + val hasNameLore: Boolean, + val gachaType: Int, + val stackType: MineStackObjectCategory +) { val itemStack: ItemStack = { val cloned = _itemStack.clone() @@ -17,12 +19,14 @@ class MineStackObj(val mineStackObjName: String, cloned } - def this(category: MineStackObjectCategory, - objname: String, - japanesename: String, - level: Int, - material: Material, - durability: Short) = + def this( + category: MineStackObjectCategory, + objname: String, + japanesename: String, + level: Int, + material: Material, + durability: Short + ) = this( objname, Some(japanesename), @@ -33,11 +37,24 @@ class MineStackObj(val mineStackObjName: String, category ) - def this(objName: String, uiName: Option[String], - level: Int, material: Material, durability: Int, - nameLoreFlag: Boolean, gachaType: Int, stackType: MineStackObjectCategory) = + def this( + objName: String, + uiName: Option[String], + level: Int, + material: Material, + durability: Int, + nameLoreFlag: Boolean, + gachaType: Int, + stackType: MineStackObjectCategory + ) = this( - objName, uiName, level, new ItemStack(material, 1, durability.toShort), nameLoreFlag, gachaType, stackType + objName, + uiName, + level, + new ItemStack(material, 1, durability.toShort), + nameLoreFlag, + gachaType, + stackType ) def material: Material = itemStack.getType @@ -47,7 +64,7 @@ class MineStackObj(val mineStackObjName: String, override def equals(other: Any): Boolean = other match { case that: MineStackObj => (that canEqual this) && - mineStackObjName == that.mineStackObjName + mineStackObjName == that.mineStackObjName case _ => false } diff --git a/src/main/scala/com/github/unchama/seichiassist/minestack/MineStackObjectCategory.scala b/src/main/scala/com/github/unchama/seichiassist/minestack/MineStackObjectCategory.scala index 7dac371a32..b4c28ecfa8 100644 --- a/src/main/scala/com/github/unchama/seichiassist/minestack/MineStackObjectCategory.scala +++ b/src/main/scala/com/github/unchama/seichiassist/minestack/MineStackObjectCategory.scala @@ -5,16 +5,20 @@ import enumeratum._ /** * マインスタックに収納するアイテムのカテゴリを表すオブジェクト. * - * @param serializedValue この列挙体を永続化する際の識別子となる整数. - * @param uiLabel UI上で表示する際のカテゴリの名前 + * @param serializedValue + * この列挙体を永続化する際の識別子となる整数. + * @param uiLabel + * UI上で表示する際のカテゴリの名前 */ -sealed abstract class MineStackObjectCategory(val serializedValue: Int, val uiLabel: String) extends EnumEntry +sealed abstract class MineStackObjectCategory(val serializedValue: Int, val uiLabel: String) + extends EnumEntry case object MineStackObjectCategory extends Enum[MineStackObjectCategory] { val values: IndexedSeq[MineStackObjectCategory] = findValues - def fromSerializedValue(value: Int): Option[MineStackObjectCategory] = values.find(_.serializedValue == value) + def fromSerializedValue(value: Int): Option[MineStackObjectCategory] = + values.find(_.serializedValue == value) case object ORES extends MineStackObjectCategory(0, "鉱石系アイテム") @@ -28,7 +32,7 @@ case object MineStackObjectCategory extends Enum[MineStackObjectCategory] { case object GACHA_PRIZES extends MineStackObjectCategory(5, "ガチャ品") - implicit class MineStackObjOps(val mineStackObj: MineStackObj) extends AnyVal { + implicit class MineStackObjOps(private val mineStackObj: MineStackObj) extends AnyVal { def category(): MineStackObjectCategory = mineStackObj.stackType } diff --git a/src/main/scala/com/github/unchama/seichiassist/seichiskill/BlockSearching.scala b/src/main/scala/com/github/unchama/seichiassist/seichiskill/BlockSearching.scala index cdda6758a3..9f97c27841 100644 --- a/src/main/scala/com/github/unchama/seichiassist/seichiskill/BlockSearching.scala +++ b/src/main/scala/com/github/unchama/seichiassist/seichiskill/BlockSearching.scala @@ -12,54 +12,63 @@ import org.bukkit.entity.Player import scala.collection.{Set, mutable} object BlockSearching { - case class Result(solids: List[BlockBreakableBySkill], waters: List[Block], lavas: List[Block]) { + case class Result( + solids: List[BlockBreakableBySkill], + waters: List[Block], + lavas: List[Block] + ) { def filterSolids(f: Block => Boolean): Result = copy(solids = solids.filter(f)) - def filterAll(f: Block => Boolean): Result = copy(solids = solids.filter(f), waters = waters.filter(f), lavas = lavas.filter(f)) + def filterAll(f: Block => Boolean): Result = + copy(solids = solids.filter(f), waters = waters.filter(f), lavas = lavas.filter(f)) } - def searchForBlocksBreakableWithSkill(player: Player, - relativeVectors: Seq[XYZTuple], - referencePoint: Block): SyncIO[Result] = SyncIO { + def searchForBlocksBreakableWithSkill( + player: Player, + relativeVectors: Seq[XYZTuple], + referencePoint: Block + ): SyncIO[Result] = SyncIO { val solidBlocks = new mutable.HashSet[BlockBreakableBySkill] val waterBlocks = new mutable.HashSet[Block] - val lavaBlocks = new mutable.HashSet[Block] + val lavaBlocks = new mutable.HashSet[Block] val lockedBlocks = BreakUtil.unsafeGetLockedBlocks() - relativeVectors.collect { case XYZTuple(x, y, z) => - val targetBlock = referencePoint.getRelative(x, y, z) + relativeVectors.collect { + case XYZTuple(x, y, z) => + val targetBlock = referencePoint.getRelative(x, y, z) - if (BreakUtil.canBreakWithSkill(player, targetBlock, lockedBlocks)) - targetBlock.getType match { - case Material.STATIONARY_LAVA | Material.LAVA => - lavaBlocks.add(targetBlock) - case Material.STATIONARY_WATER | Material.WATER => - waterBlocks.add(targetBlock) - case _ => - MaterialSets.refineBlock(targetBlock, MaterialSets.materials) - .foreach(b => solidBlocks.add(b)) - } + if (BreakUtil.canBreakWithSkill(player, targetBlock, lockedBlocks)) + targetBlock.getType match { + case Material.STATIONARY_LAVA | Material.LAVA => + lavaBlocks.add(targetBlock) + case Material.STATIONARY_WATER | Material.WATER => + waterBlocks.add(targetBlock) + case _ => + MaterialSets + .refineBlock(targetBlock, MaterialSets.materials) + .foreach(b => solidBlocks.add(b)) + } } Result(solidBlocks.toList, waterBlocks.toList, lavaBlocks.toList) } /** - * `referenceBlock`をプレーヤーが破壊しスキルを発動させようとしているとき、 - * 複数idブロック破壊を防ぐ必要がある場合がある。 + * `referenceBlock`をプレーヤーが破壊しスキルを発動させようとしているとき、 複数idブロック破壊を防ぐ必要がある場合がある。 * * この関数は、そのような状況下で破壊可能ブロックを`filter`するための述語を与える。 */ - def multiTypeBreakingFilterPredicate(referenceBlock: Block): Block => Boolean = { targetBlock => - val blockMaterials = Set(referenceBlock.getType, targetBlock.getType) + def multiTypeBreakingFilterPredicate(referenceBlock: Block): Block => Boolean = { + targetBlock => + val blockMaterials = Set(referenceBlock.getType, targetBlock.getType) - val identifications = List( - Set(Material.DIRT, Material.GRASS), - Set(Material.REDSTONE_ORE, Material.GLOWING_REDSTONE_ORE) - ) + val identifications = List( + Set(Material.DIRT, Material.GRASS), + Set(Material.REDSTONE_ORE, Material.GLOWING_REDSTONE_ORE) + ) - // マテリアルが同一視により等しくなるかどうか - blockMaterials.size == 1 || identifications.exists(blockMaterials.subsetOf) + // マテリアルが同一視により等しくなるかどうか + blockMaterials.size == 1 || identifications.exists(blockMaterials.subsetOf) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/seichiskill/BreakArea.scala b/src/main/scala/com/github/unchama/seichiassist/seichiskill/BreakArea.scala index 11af7dd6e7..df70037bc1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/seichiskill/BreakArea.scala +++ b/src/main/scala/com/github/unchama/seichiassist/seichiskill/BreakArea.scala @@ -10,115 +10,116 @@ import com.github.unchama.seichiassist.util.BreakUtil import org.bukkit.entity.Player class BreakArea private (skill: SeichiSkill, usageIntention: SeichiSkillUsageMode) { - //南向きを基準として破壊の範囲座標 + // 南向きを基準として破壊の範囲座標 val breakLength: XYZTuple = skill.range.effectChunkSize - //破壊回数 + // 破壊回数 val breakNum: Int = skill.range match { - case range: ActiveSkillRange => range match { - case ActiveSkillRange.MultiArea(_, areaCount) => areaCount - case ActiveSkillRange.RemoteArea(_) => 1 - } + case range: ActiveSkillRange => + range match { + case ActiveSkillRange.MultiArea(_, areaCount) => areaCount + case ActiveSkillRange.RemoteArea(_) => 1 + } case _: AssaultSkillRange => 1 } val isAssaultSkill: Boolean = skill.range.isInstanceOf[AssaultSkillRange] - private val breakAreaListFromDirection: CardinalDirection => List[AxisAlignedCuboid] = CachedFunction { dir => - import BreakArea.CoordinateManipulation._ - import syntax._ - - val firstShift: AxisAlignedCuboid => AxisAlignedCuboid = - if (isAssaultSkill) { - if (skill == AssaultArmor) { - areaShift(XYZTuple(0, (breakLength.y - 1) / 2 - 1, 0)) + private val breakAreaListFromDirection: CardinalDirection => List[AxisAlignedCuboid] = + CachedFunction { dir => + import BreakArea.CoordinateManipulation._ + import syntax._ + + val firstShift: AxisAlignedCuboid => AxisAlignedCuboid = + if (isAssaultSkill) { + if (skill == AssaultArmor) { + areaShift(XYZTuple(0, (breakLength.y - 1) / 2 - 1, 0)) + } else { + identity + } + } else if (dir == CardinalDirection.Up || dir == CardinalDirection.Down) { + // 上向きまたは下向きの時 + if (Seq(DualBreak, TrialBreak).contains(skill)) { + identity + } else { + areaShift(XYZTuple(0, (breakLength.y - 1) / 2, 0)) + } } else { - identity + // それ以外の範囲 + areaShift(XYZTuple(0, (breakLength.y - 1) / 2 - 1, (breakLength.z - 1) / 2)) } - } else if (dir == CardinalDirection.Up || dir == CardinalDirection.Down) { - //上向きまたは下向きの時 - if (Seq(DualBreak, TrialBreak).contains(skill)) { + + val secondShift: AxisAlignedCuboid => AxisAlignedCuboid = + if (Seq(DualBreak, TrialBreak).contains(skill)) + incrementYOfEnd + else identity - } else { - areaShift(XYZTuple(0, (breakLength.y - 1) / 2, 0)) + + val thirdShift: AxisAlignedCuboid => AxisAlignedCuboid = + if (Seq(DualBreak, TrialBreak).contains(skill) && usageIntention == Active) + areaShift(XYZTuple(0, 1, 0)) + else + identity + + val directionalShift: AxisAlignedCuboid => AxisAlignedCuboid = + dir match { + case CardinalDirection.North | CardinalDirection.East | CardinalDirection.South | + CardinalDirection.West => + areaShift(XYZTuple(0, 0, breakLength.z)) + case CardinalDirection.Up | CardinalDirection.Down + if !Seq(DualBreak, TrialBreak).contains(skill) => + areaShift(XYZTuple(0, breakLength.y, 0)) + case _ => identity } - } else { - //それ以外の範囲 - areaShift(XYZTuple(0, (breakLength.y - 1) / 2 - 1, (breakLength.z - 1) / 2)) - } - val secondShift: AxisAlignedCuboid => AxisAlignedCuboid = - if (Seq(DualBreak, TrialBreak).contains(skill)) - incrementYOfEnd - else - identity - - val thirdShift: AxisAlignedCuboid => AxisAlignedCuboid = - if (Seq(DualBreak, TrialBreak).contains(skill) && usageIntention == Active) - areaShift(XYZTuple(0, 1, 0)) - else - identity - - val directionalShift: AxisAlignedCuboid => AxisAlignedCuboid = - dir match { - case CardinalDirection.North | CardinalDirection.East | CardinalDirection.South | CardinalDirection.West => - areaShift(XYZTuple(0, 0, breakLength.z)) - case CardinalDirection.Up | CardinalDirection.Down if !Seq(DualBreak, TrialBreak).contains(skill) => - areaShift(XYZTuple(0, breakLength.y, 0)) - case _ => identity - } + val rotation: AxisAlignedCuboid => AxisAlignedCuboid = + dir match { + case CardinalDirection.North => rotateXZ(180) + case CardinalDirection.East => rotateXZ(270) + case CardinalDirection.West => rotateXZ(90) + case CardinalDirection.Down if !isAssaultSkill => invertY + // 横向きのスキル発動の場合Sが基準となり、 + // 縦向きの場合Uが基準となっているため回転しないで良い + case CardinalDirection.South | CardinalDirection.Up | _ => identity + } - val rotation: AxisAlignedCuboid => AxisAlignedCuboid = - dir match { - case CardinalDirection.North => rotateXZ(180) - case CardinalDirection.East => rotateXZ(270) - case CardinalDirection.West => rotateXZ(90) - case CardinalDirection.Down if !isAssaultSkill => invertY - // 横向きのスキル発動の場合Sが基準となり、 - // 縦向きの場合Uが基準となっているため回転しないで良い - case CardinalDirection.South | CardinalDirection.Up | _ => identity - } + val firstArea = { + // 中心が(0,0,0)である領域(start = -end)を変形していく。 + val end = (breakLength - XYZTuple(1, 1, 1)) / 2.0 + val start = end.negative - val firstArea = { - // 中心が(0,0,0)である領域(start = -end)を変形していく。 - val end = (breakLength - XYZTuple(1, 1, 1)) / 2.0 - val start = end.negative + import scala.util.chaining._ - import scala.util.chaining._ + AxisAlignedCuboid(end, start).pipe(firstShift).pipe(secondShift).pipe(thirdShift) + } - AxisAlignedCuboid(end, start) - .pipe(firstShift) - .pipe(secondShift) - .pipe(thirdShift) + LazyList.iterate(firstArea)(directionalShift).map(rotation).take(breakNum).toList } - LazyList - .iterate(firstArea)(directionalShift) - .map(rotation) - .take(breakNum) - .toList - } - def makeBreakArea(player: Player): IO[List[AxisAlignedCuboid]] = BreakArea.getCardinalDirection(player).map(breakAreaListFromDirection) } object BreakArea { - private val getCardinalDirection: Player => IO[CardinalDirection] = { player => IO { BreakUtil.getCardinalDirection(player) } } + private val getCardinalDirection: Player => IO[CardinalDirection] = { player => + IO { BreakUtil.getCardinalDirection(player) } + } object CoordinateManipulation { import syntax._ val incrementYOfEnd: AxisAlignedCuboid => AxisAlignedCuboid = { - case area@AxisAlignedCuboid(_, end@XYZTuple(_, y, _)) => + case area @ AxisAlignedCuboid(_, end @ XYZTuple(_, y, _)) => area.copy(end = end.copy(y = y + 1)) } - val invertY: AxisAlignedCuboid => AxisAlignedCuboid = { case AxisAlignedCuboid(begin, end) => - def invertYOfVector(vector: XYZTuple): XYZTuple = XYZTuple(vector.x, -vector.y, vector.z) + val invertY: AxisAlignedCuboid => AxisAlignedCuboid = { + case AxisAlignedCuboid(begin, end) => + def invertYOfVector(vector: XYZTuple): XYZTuple = + XYZTuple(vector.x, -vector.y, vector.z) - AxisAlignedCuboid(invertYOfVector(begin), invertYOfVector(end)) + AxisAlignedCuboid(invertYOfVector(begin), invertYOfVector(end)) } def areaShift(vector: XYZTuple): AxisAlignedCuboid => AxisAlignedCuboid = { @@ -126,17 +127,27 @@ object BreakArea { AxisAlignedCuboid(begin + vector, end + vector) } - def rotateXZ(d: Int): AxisAlignedCuboid => AxisAlignedCuboid = { case AxisAlignedCuboid(begin, end) => - d match { - case 90 => - AxisAlignedCuboid(XYZTuple(-end.z, begin.y, begin.x), XYZTuple(-begin.z, end.y, end.x)) - case 180 => - AxisAlignedCuboid(XYZTuple(begin.x, begin.y, -end.z), XYZTuple(end.x, end.y, -begin.z)) - case 270 => - AxisAlignedCuboid(XYZTuple(begin.z, begin.y, begin.x), XYZTuple(end.z, end.y, end.x)) - case 360 => - AxisAlignedCuboid(begin, end) - } + def rotateXZ(d: Int): AxisAlignedCuboid => AxisAlignedCuboid = { + case AxisAlignedCuboid(begin, end) => + d match { + case 90 => + AxisAlignedCuboid( + XYZTuple(-end.z, begin.y, begin.x), + XYZTuple(-begin.z, end.y, end.x) + ) + case 180 => + AxisAlignedCuboid( + XYZTuple(begin.x, begin.y, -end.z), + XYZTuple(end.x, end.y, -begin.z) + ) + case 270 => + AxisAlignedCuboid( + XYZTuple(begin.z, begin.y, begin.x), + XYZTuple(end.z, end.y, end.x) + ) + case 360 => + AxisAlignedCuboid(begin, end) + } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/seichiskill/SeichiSkill.scala b/src/main/scala/com/github/unchama/seichiassist/seichiskill/SeichiSkill.scala index 807a50a75e..d3668aa262 100644 --- a/src/main/scala/com/github/unchama/seichiassist/seichiskill/SeichiSkill.scala +++ b/src/main/scala/com/github/unchama/seichiassist/seichiskill/SeichiSkill.scala @@ -10,20 +10,24 @@ sealed trait SeichiSkill extends EnumEntry { val requiredActiveSkillPoint: Int } -sealed abstract case class ActiveSkill(stringId: String, - override val name: String, - override val range: ActiveSkillRange, - override val maxCoolDownTicks: Option[Int], - override val manaCost: Int, - override val requiredActiveSkillPoint: Int) extends SeichiSkill { +sealed abstract case class ActiveSkill( + stringId: String, + override val name: String, + override val range: ActiveSkillRange, + override val maxCoolDownTicks: Option[Int], + override val manaCost: Int, + override val requiredActiveSkillPoint: Int +) extends SeichiSkill { override val entryName: String = stringId } -sealed abstract case class AssaultSkill(stringId: String, - override val name: String, - override val range: AssaultSkillRange, - override val manaCost: Int, - override val requiredActiveSkillPoint: Int) extends SeichiSkill { +sealed abstract case class AssaultSkill( + stringId: String, + override val name: String, + override val range: AssaultSkillRange, + override val manaCost: Int, + override val requiredActiveSkillPoint: Int +) extends SeichiSkill { override val entryName: String = stringId override val maxCoolDownTicks: None.type = None } @@ -32,41 +36,108 @@ object SeichiSkill extends Enum[SeichiSkill] { import ActiveSkillRange._ import AssaultSkillRange._ - object DualBreak extends ActiveSkill("dual_break", "デュアル・ブレイク", singleArea(1, 2, 1), None, 1, 10) - object TrialBreak extends ActiveSkill("trial_break", "トリアル・ブレイク", singleArea(3, 2, 1), None, 3, 20) - object Explosion extends ActiveSkill("explosion", "エクスプロージョン", singleArea(3, 3, 3), None, 12, 30) - object MirageFlare extends ActiveSkill("mirage_flare", "ミラージュ・フレア", singleArea(5, 3, 5), Some(14), 30, 40) + object DualBreak + extends ActiveSkill("dual_break", "デュアル・ブレイク", singleArea(1, 2, 1), None, 1, 10) + object TrialBreak + extends ActiveSkill("trial_break", "トリアル・ブレイク", singleArea(3, 2, 1), None, 3, 20) + object Explosion + extends ActiveSkill("explosion", "エクスプロージョン", singleArea(3, 3, 3), None, 12, 30) + object MirageFlare + extends ActiveSkill("mirage_flare", "ミラージュ・フレア", singleArea(5, 3, 5), Some(14), 30, 40) object Dockarn extends ActiveSkill("dockarn", "ドッ・カーン", singleArea(7, 5, 7), Some(30), 70, 50) - object GiganticBomb extends ActiveSkill("gigantic_bomb", "ギガンティック・ボム", singleArea(9, 7, 9), Some(50), 100, 60) - object BrilliantDetonation extends ActiveSkill("brilliant_detonation", "ブリリアント・デトネーション", singleArea(11, 9, 11), Some(70), 200, 70) - object LemuriaImpact extends ActiveSkill("lemuria_impact", "レムリア・インパクト", singleArea(13, 11, 13), Some(100), 350, 80) - object EternalVice extends ActiveSkill("eternal_vice", "エターナル・ヴァイス", singleArea(15, 13, 15), Some(140), 500, 90) + object GiganticBomb + extends ActiveSkill("gigantic_bomb", "ギガンティック・ボム", singleArea(9, 7, 9), Some(50), 100, 60) + object BrilliantDetonation + extends ActiveSkill( + "brilliant_detonation", + "ブリリアント・デトネーション", + singleArea(11, 9, 11), + Some(70), + 200, + 70 + ) + object LemuriaImpact + extends ActiveSkill( + "lemuria_impact", + "レムリア・インパクト", + singleArea(13, 11, 13), + Some(100), + 350, + 80 + ) + object EternalVice + extends ActiveSkill( + "eternal_vice", + "エターナル・ヴァイス", + singleArea(15, 13, 15), + Some(140), + 500, + 90 + ) object TomBoy extends ActiveSkill("tomboy", "トム・ボウイ", MultiArea(3, 3, 3)(3), Some(12), 28, 40) - object Thunderstorm extends ActiveSkill("thunderstorm", "サンダー・ストーム", MultiArea(3, 3, 3)(7), Some(28), 65, 50) - object StarlightBreaker extends ActiveSkill("starlight_breaker", "スターライト・ブレイカー", MultiArea(5, 5, 5)(3), Some(48), 90, 60) - object EarthDivide extends ActiveSkill("earth_divide", "アース・ディバイド", MultiArea(5, 5, 5)(5), Some(68), 185, 70) - object HeavenGaeBolg extends ActiveSkill("heaven_gaebolg", "ヘヴン・ゲイボルグ", MultiArea(7, 7, 7)(3), Some(96), 330, 80) - object Decision extends ActiveSkill("decision", "ディシジョン", MultiArea(7, 7, 7)(7), Some(136), 480, 90) + object Thunderstorm + extends ActiveSkill("thunderstorm", "サンダー・ストーム", MultiArea(3, 3, 3)(7), Some(28), 65, 50) + object StarlightBreaker + extends ActiveSkill( + "starlight_breaker", + "スターライト・ブレイカー", + MultiArea(5, 5, 5)(3), + Some(48), + 90, + 60 + ) + object EarthDivide + extends ActiveSkill("earth_divide", "アース・ディバイド", MultiArea(5, 5, 5)(5), Some(68), 185, 70) + object HeavenGaeBolg + extends ActiveSkill( + "heaven_gaebolg", + "ヘヴン・ゲイボルグ", + MultiArea(7, 7, 7)(3), + Some(96), + 330, + 80 + ) + object Decision + extends ActiveSkill("decision", "ディシジョン", MultiArea(7, 7, 7)(7), Some(136), 480, 90) - object EbifriDrive extends ActiveSkill("ebifri_drive", "エビフライ・ドライブ", RemoteArea(3, 3, 3), Some(4), 18, 40) - object HolyShot extends ActiveSkill("holy_shot", "ホーリー・ショット", RemoteArea(5, 3, 5), Some(26), 35, 50) - object TsarBomba extends ActiveSkill("tsar_bomba", "ツァーリ・ボンバ", RemoteArea(7, 5, 7), Some(32), 80, 60) - object ArcBlast extends ActiveSkill("arc_blast", "アーク・ブラスト", RemoteArea(9, 7, 9), Some(54), 110, 70) - object PhantasmRay extends ActiveSkill("phantasm_ray", "ファンタズム・レイ", RemoteArea(11, 9, 11), Some(76), 220, 80) - object Supernova extends ActiveSkill("supernova", "スーパー・ノヴァ", RemoteArea(13, 11, 13), Some(110), 380, 90) + object EbifriDrive + extends ActiveSkill("ebifri_drive", "エビフライ・ドライブ", RemoteArea(3, 3, 3), Some(4), 18, 40) + object HolyShot + extends ActiveSkill("holy_shot", "ホーリー・ショット", RemoteArea(5, 3, 5), Some(26), 35, 50) + object TsarBomba + extends ActiveSkill("tsar_bomba", "ツァーリ・ボンバ", RemoteArea(7, 5, 7), Some(32), 80, 60) + object ArcBlast + extends ActiveSkill("arc_blast", "アーク・ブラスト", RemoteArea(9, 7, 9), Some(54), 110, 70) + object PhantasmRay + extends ActiveSkill("phantasm_ray", "ファンタズム・レイ", RemoteArea(11, 9, 11), Some(76), 220, 80) + object Supernova + extends ActiveSkill("supernova", "スーパー・ノヴァ", RemoteArea(13, 11, 13), Some(110), 380, 90) - object WhiteBreath extends AssaultSkill("white_breath", "ホワイト・ブレス", condenseWater(7, 7, 7), 30, 70) - object AbsoluteZero extends AssaultSkill("absolute_zero", "アブソリュート・ゼロ", condenseWater(11, 11, 11), 80, 80) - object DiamondDust extends AssaultSkill("diamond_dust", "ダイヤモンド・ダスト", condenseWater(15, 15, 15), 160, 90) + object WhiteBreath + extends AssaultSkill("white_breath", "ホワイト・ブレス", condenseWater(7, 7, 7), 30, 70) + object AbsoluteZero + extends AssaultSkill("absolute_zero", "アブソリュート・ゼロ", condenseWater(11, 11, 11), 80, 80) + object DiamondDust + extends AssaultSkill("diamond_dust", "ダイヤモンド・ダスト", condenseWater(15, 15, 15), 160, 90) - object LavaCondensation extends AssaultSkill("lava_condensation", "ラヴァ・コンデンセーション", condenseLava(7, 7, 7), 20, 70) - object MoerakiBoulders extends AssaultSkill("moeraki_boulders", "モエラキ・ボールダーズ", condenseLava(11, 11, 11), 60, 80) + object LavaCondensation + extends AssaultSkill("lava_condensation", "ラヴァ・コンデンセーション", condenseLava(7, 7, 7), 20, 70) + object MoerakiBoulders + extends AssaultSkill("moeraki_boulders", "モエラキ・ボールダーズ", condenseLava(11, 11, 11), 60, 80) object Eldfell extends AssaultSkill("eldfell", "エルト・フェットル", condenseLava(13, 13, 13), 150, 90) - object VenderBlizzard extends AssaultSkill("vender_blizzard", "ヴェンダー・ブリザード", condenseLiquid(15, 15, 15), 170, 110) + object VenderBlizzard + extends AssaultSkill( + "vender_blizzard", + "ヴェンダー・ブリザード", + condenseLiquid(15, 15, 15), + 170, + 110 + ) - object AssaultArmor extends AssaultSkill("assault_armor", "アサルト・アーマー", armor(11, 11, 11), 600, 0) + object AssaultArmor + extends AssaultSkill("assault_armor", "アサルト・アーマー", armor(11, 11, 11), 600, 0) override def values: IndexedSeq[SeichiSkill] = findValues } diff --git a/src/main/scala/com/github/unchama/seichiassist/seichiskill/SeichiSkillUsageMode.scala b/src/main/scala/com/github/unchama/seichiassist/seichiskill/SeichiSkillUsageMode.scala index ed732667cd..692051a278 100644 --- a/src/main/scala/com/github/unchama/seichiassist/seichiskill/SeichiSkillUsageMode.scala +++ b/src/main/scala/com/github/unchama/seichiassist/seichiskill/SeichiSkillUsageMode.scala @@ -4,12 +4,12 @@ import com.github.unchama.seichiassist.seichiskill.SeichiSkill.{DualBreak, Trial import enumeratum.values.{IntEnum, IntEnumEntry} /** - * プレーヤーがスキルを使用するかどうか、 - * 使用するなら下側または上側のどちらを破壊するかの設定状態を表す列挙型。 + * プレーヤーがスキルを使用するかどうか、 使用するなら下側または上側のどちらを破壊するかの設定状態を表す列挙型。 * * デュアルブレイクとトリアルブレイクに限り「下側破壊」の概念が存在する。 */ sealed abstract class SeichiSkillUsageMode(val value: Int) extends IntEnumEntry { + /** * [[SeichiSkillUsageMode.toggle()]]を`skill`とこの値について計算する */ @@ -31,19 +31,21 @@ object SeichiSkillUsageMode extends IntEnum[SeichiSkillUsageMode] { /** * 選択されたスキルに応じて、設定項目のトグル先を計算する。 * - * 選択されたスキルが「デュアル・ブレイク」または「トリアル・ブレイク」である場合は - * Disabled => Active => LowerActive => Disabled + * 選択されたスキルが「デュアル・ブレイク」または「トリアル・ブレイク」である場合は Disabled => Active => LowerActive => Disabled * の順で巡回し、それ以外ではDisabled => Active => Disabledのように巡回する。 */ - def toggle(selectedSkill: SeichiSkill)(usageIntention: SeichiSkillUsageMode): SeichiSkillUsageMode = + def toggle( + selectedSkill: SeichiSkill + )(usageIntention: SeichiSkillUsageMode): SeichiSkillUsageMode = usageIntention match { case Disabled => Active - case Active => selectedSkill match { - case DualBreak | TrialBreak => - LowerActive - case _ => - Disabled - } + case Active => + selectedSkill match { + case DualBreak | TrialBreak => + LowerActive + case _ => + Disabled + } case LowerActive => Disabled } @@ -51,8 +53,8 @@ object SeichiSkillUsageMode extends IntEnum[SeichiSkillUsageMode] { val dualOrTrialBreak = Seq(DualBreak, TrialBreak).contains(selectedSkill) usageIntention match { - case Disabled => "OFF" - case Active => if (dualOrTrialBreak) "ON-Above(上向き)" else "ON" + case Disabled => "OFF" + case Active => if (dualOrTrialBreak) "ON-Above(上向き)" else "ON" case LowerActive => if (dualOrTrialBreak) "ON-Under(下向き)" else "ON" } } diff --git a/src/main/scala/com/github/unchama/seichiassist/seichiskill/SkillDependency.scala b/src/main/scala/com/github/unchama/seichiassist/seichiskill/SkillDependency.scala index 5b3164a2ed..16ddef9730 100644 --- a/src/main/scala/com/github/unchama/seichiassist/seichiskill/SkillDependency.scala +++ b/src/main/scala/com/github/unchama/seichiassist/seichiskill/SkillDependency.scala @@ -4,7 +4,7 @@ import com.github.unchama.generic.CachedFunction object SkillDependency { import SeichiSkill._ - + type Dependency = (SeichiSkill, SeichiSkill) val dependency: Seq[Dependency] = { @@ -17,31 +17,26 @@ object SkillDependency { GiganticBomb -> BrilliantDetonation, BrilliantDetonation -> LemuriaImpact, LemuriaImpact -> EternalVice, - Explosion -> TomBoy, TomBoy -> Thunderstorm, Thunderstorm -> StarlightBreaker, StarlightBreaker -> EarthDivide, EarthDivide -> HeavenGaeBolg, HeavenGaeBolg -> Decision, - Explosion -> EbifriDrive, EbifriDrive -> HolyShot, HolyShot -> TsarBomba, TsarBomba -> ArcBlast, ArcBlast -> PhantasmRay, PhantasmRay -> Supernova, - Explosion -> WhiteBreath, WhiteBreath -> AbsoluteZero, AbsoluteZero -> DiamondDust, - Explosion -> LavaCondensation, LavaCondensation -> MoerakiBoulders, MoerakiBoulders -> Eldfell, - DiamondDust -> VenderBlizzard, - Eldfell -> VenderBlizzard, + Eldfell -> VenderBlizzard ) val assaultArmorDependency = for { diff --git a/src/main/scala/com/github/unchama/seichiassist/seichiskill/SkillRange.scala b/src/main/scala/com/github/unchama/seichiassist/seichiskill/SkillRange.scala index bbb1df9de2..d5731b0b78 100644 --- a/src/main/scala/com/github/unchama/seichiassist/seichiskill/SkillRange.scala +++ b/src/main/scala/com/github/unchama/seichiassist/seichiskill/SkillRange.scala @@ -17,12 +17,15 @@ object ActiveSkillRange { MultiArea(XYZTuple(width, height, depth), count) } - def singleArea(width: Int, height: Int, depth: Int): MultiArea = MultiArea(width, height, depth)(1) + def singleArea(width: Int, height: Int, depth: Int): MultiArea = + MultiArea(width, height, depth)(1) case class RemoteArea(effectChunkSize: XYZTuple) extends ActiveSkillRange object RemoteArea { - def apply(width: Int, height: Int, depth: Int): RemoteArea = RemoteArea(XYZTuple(width, height, depth)) + def apply(width: Int, height: Int, depth: Int): RemoteArea = RemoteArea( + XYZTuple(width, height, depth) + ) } } @@ -40,22 +43,22 @@ object AssaultSkillRange { case class Water(effectChunkSize: XYZTuple) extends AssaultSkillRange { override val blockMaterialConversion: Material => Material = { case Material.WATER => Material.ICE - case x => x + case x => x } } case class Lava(effectChunkSize: XYZTuple) extends AssaultSkillRange { override val blockMaterialConversion: Material => Material = { case Material.LAVA => Material.MAGMA - case x => x + case x => x } } case class Liquid(effectChunkSize: XYZTuple) extends AssaultSkillRange { override val blockMaterialConversion: Material => Material = { case Material.WATER => Material.ICE - case Material.LAVA => Material.MAGMA - case x => x + case Material.LAVA => Material.MAGMA + case x => x } } diff --git a/src/main/scala/com/github/unchama/seichiassist/seichiskill/assault/AssaultRoutine.scala b/src/main/scala/com/github/unchama/seichiassist/seichiskill/assault/AssaultRoutine.scala index 000d469ba8..190aee7bab 100644 --- a/src/main/scala/com/github/unchama/seichiassist/seichiskill/assault/AssaultRoutine.scala +++ b/src/main/scala/com/github/unchama/seichiassist/seichiskill/assault/AssaultRoutine.scala @@ -5,10 +5,15 @@ import com.github.unchama.concurrent.{RepeatingRoutine, RepeatingTaskContext} import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.ManagedWorld._ import com.github.unchama.seichiassist.MaterialSets.BreakTool -import com.github.unchama.seichiassist.seichiskill.{AssaultSkill, AssaultSkillRange, BlockSearching, BreakArea} +import com.github.unchama.seichiassist.seichiskill.{ + AssaultSkill, + AssaultSkillRange, + BlockSearching, + BreakArea +} import com.github.unchama.seichiassist.subsystems.mana.ManaWriteApi import com.github.unchama.seichiassist.subsystems.mana.domain.ManaAmount -import com.github.unchama.seichiassist.util.{BreakUtil, Util} +import com.github.unchama.seichiassist.util.BreakUtil import com.github.unchama.seichiassist.{DefaultEffectEnvironment, MaterialSets, SeichiAssist} import org.bukkit.ChatColor._ import org.bukkit.enchantments.Enchantment @@ -24,9 +29,11 @@ object AssaultRoutine { projections.exists(p => (p(l1) - p(l2)).abs >= 10) } - def tryStart(player: Player, skill: AssaultSkill) - (implicit ioOnMainThread: OnMinecraftServerThread[IO], ctx: RepeatingTaskContext, - manaApi: ManaWriteApi[SyncIO, Player]): IO[Unit] = { + def tryStart(player: Player, skill: AssaultSkill)( + implicit ioOnMainThread: OnMinecraftServerThread[IO], + ctx: RepeatingTaskContext, + manaApi: ManaWriteApi[SyncIO, Player] + ): IO[Unit] = { for { offHandTool <- IO { player.getInventory.getItemInOffHand @@ -34,17 +41,19 @@ object AssaultRoutine { refinedTool = MaterialSets.refineItemStack(offHandTool, MaterialSets.breakToolMaterials) _ <- refinedTool match { case Some(tool) => AssaultRoutine(player, tool, skill) - case None => IO { - player.sendMessage(s"${GREEN}使うツールをオフハンドにセット(fキー)してください") - } + case None => + IO { + player.sendMessage(s"${GREEN}使うツールをオフハンドにセット(fキー)してください") + } } } yield () } - - def apply(player: Player, toolToBeUsed: BreakTool, skill: AssaultSkill) - (implicit ioOnMainThread: OnMinecraftServerThread[IO], ctx: RepeatingTaskContext, - manaApi: ManaWriteApi[SyncIO, Player]): IO[Unit] = { + def apply(player: Player, toolToBeUsed: BreakTool, skill: AssaultSkill)( + implicit ioOnMainThread: OnMinecraftServerThread[IO], + ctx: RepeatingTaskContext, + manaApi: ManaWriteApi[SyncIO, Player] + ): IO[Unit] = { val idleCountLimit = 20 val playerData = SeichiAssist.playermap(player.getUniqueId) @@ -78,12 +87,12 @@ object AssaultRoutine { } } - //プレイヤーの足のy座標 + // プレイヤーの足のy座標 val playerLocY = player.getLocation.getBlockY - 1 val block = player.getLocation.getBlock - //最初に登録したツールと今のツールが違う場合 + // 最初に登録したツールと今のツールが違う場合 if (toolToBeUsed != player.getInventory.getItemInOffHand) return None val skillArea = BreakArea(assaultSkill, skillState.usageMode) @@ -94,16 +103,16 @@ object AssaultRoutine { val (shouldBreakAllBlocks, shouldRemoveOrCondenseWater, shouldRemoveOrCondenseLava) = assaultSkill.range match { - case AssaultSkillRange.Armor(_) => (true, true, true) - case AssaultSkillRange.Water(_) => (false, true, false) - case AssaultSkillRange.Lava(_) => (false, false, true) + case AssaultSkillRange.Armor(_) => (true, true, true) + case AssaultSkillRange.Water(_) => (false, true, false) + case AssaultSkillRange.Lava(_) => (false, false, true) case AssaultSkillRange.Liquid(_) => (false, true, true) } - //重力値計算 + // 重力値計算 val gravity = BreakUtil.getGravity(player, block, isAssault = true) - //重力値の判定 + // 重力値の判定 if (gravity > 15) { player.sendMessage(s"${RED}スキルを使用するには上から掘ってください。") return None @@ -111,7 +120,8 @@ object AssaultRoutine { import com.github.unchama.seichiassist.data.syntax._ val BlockSearching.Result(foundBlocks, foundWaters, foundLavas) = - BlockSearching.searchForBlocksBreakableWithSkill(player, breakArea.gridPoints(), block) + BlockSearching + .searchForBlocksBreakableWithSkill(player, breakArea.gridPoints(), block) .unsafeRunSync() .filterAll(targetBlock => player.isSneaking || !shouldBreakAllBlocks || @@ -126,20 +136,28 @@ object AssaultRoutine { // 減るマナ計算 // 実際に破壊するブロック数 * 全てのブロックを破壊したときの消費経験値÷すべての破壊するブロック数 * 重力 - val manaUsage = breakTargets.toDouble * (gravity + 1) * assaultSkill.manaCost / areaTotalBlockCount + val manaUsage = + breakTargets.toDouble * (gravity + 1) * assaultSkill.manaCost / areaTotalBlockCount - //減る耐久値の計算 + // 減る耐久値の計算 val durability = (toolToBeUsed.getDurability + - BreakUtil.calcDurability(toolToBeUsed.getEnchantmentLevel(Enchantment.DURABILITY), breakTargets)).toShort + BreakUtil.calcDurability( + toolToBeUsed.getEnchantmentLevel(Enchantment.DURABILITY), + breakTargets + )).toShort // 実際に耐久値を減らせるか判定 - if (toolToBeUsed.getType.getMaxDurability <= durability && !toolToBeUsed.getItemMeta.isUnbreakable) return None + if ( + toolToBeUsed.getType.getMaxDurability <= durability && !toolToBeUsed + .getItemMeta + .isUnbreakable + ) return None // マナを消費する manaApi.manaAmount(player).tryAcquire(ManaAmount(manaUsage)).unsafeRunSync() match { case Some(_) => - case None => return None + case None => return None } // 耐久値を減らす @@ -150,7 +168,13 @@ object AssaultRoutine { (foundWaters ++ foundLavas).foreach(_.setType(Material.AIR)) DefaultEffectEnvironment.unsafeRunEffectAsync( "ブロックを大量破壊する", - BreakUtil.massBreakBlock(player, foundBlocks, player.getLocation, toolToBeUsed, shouldPlayBreakSound = false) + BreakUtil.massBreakBlock( + player, + foundBlocks, + player.getLocation, + toolToBeUsed, + shouldPlayBreakSound = false + ) ) } else { if (shouldRemoveOrCondenseWater) foundWaters.foreach(_.setType(Material.PACKED_ICE)) @@ -171,21 +195,23 @@ object AssaultRoutine { currentLoc <- IO { player.getLocation } - _ <- RepeatingRoutine.recMTask(IterationState(currentLoc, 0))(s => - ioOnMainThread.runAction(SyncIO(routineAction(s))) - )(IO.pure(500.millis)).guaranteeCase { - case ExitCase.Error(_) | ExitCase.Completed => - IO { - // 継続条件が満たされなかった場合の表示 - player.sendMessage(s"${YELLOW}アサルトスキルがOFFになりました") - player.playSound(currentLoc, Sound.BLOCK_LEVER_CLICK, 1f, 0.7f) - } - case ExitCase.Canceled => - IO { - // 明示的にプレーヤーが切り替えた場合 - player.sendMessage(s"${GREEN}アサルトスキル:${skill.name} OFF") - } - } + _ <- RepeatingRoutine + .recMTask(IterationState(currentLoc, 0))(s => + ioOnMainThread.runAction(SyncIO(routineAction(s))) + )(IO.pure(500.millis)) + .guaranteeCase { + case ExitCase.Error(_) | ExitCase.Completed => + IO { + // 継続条件が満たされなかった場合の表示 + player.sendMessage(s"${YELLOW}アサルトスキルがOFFになりました") + player.playSound(currentLoc, Sound.BLOCK_LEVER_CLICK, 1f, 0.7f) + } + case ExitCase.Canceled => + IO { + // 明示的にプレーヤーが切り替えた場合 + player.sendMessage(s"${GREEN}アサルトスキル:${skill.name} OFF") + } + } } yield () } } diff --git a/src/main/scala/com/github/unchama/seichiassist/seichiskill/effect/ActiveSkillEffect.scala b/src/main/scala/com/github/unchama/seichiassist/seichiskill/effect/ActiveSkillEffect.scala index 7d67046830..b8b8775ab8 100644 --- a/src/main/scala/com/github/unchama/seichiassist/seichiskill/effect/ActiveSkillEffect.scala +++ b/src/main/scala/com/github/unchama/seichiassist/seichiskill/effect/ActiveSkillEffect.scala @@ -7,7 +7,11 @@ import com.github.unchama.seichiassist.SeichiAssist import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts import com.github.unchama.seichiassist.data.{AxisAlignedCuboid, XYZTuple} import com.github.unchama.seichiassist.seichiskill.SeichiSkill.{DualBreak, TrialBreak} -import com.github.unchama.seichiassist.seichiskill.effect.ActiveSkillNormalEffect.{Blizzard, Explosion, Meteor} +import com.github.unchama.seichiassist.seichiskill.effect.ActiveSkillNormalEffect.{ + Blizzard, + Explosion, + Meteor +} import com.github.unchama.seichiassist.seichiskill.effect.arrow.ArrowEffects import com.github.unchama.seichiassist.seichiskill.{ActiveSkill, ActiveSkillRange} import com.github.unchama.seichiassist.util.BreakUtil @@ -27,29 +31,41 @@ sealed trait ActiveSkillEffect { def arrowEffect(implicit ioOnMainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] - def runBreakEffect(player: Player, - usedSkill: ActiveSkill, - tool: BreakTool, - breakBlocks: Set[BlockBreakableBySkill], - breakArea: AxisAlignedCuboid, - standard: Location)(implicit ioOnMainThread: OnMinecraftServerThread[IO]): IO[Unit] + def runBreakEffect( + player: Player, + usedSkill: ActiveSkill, + tool: BreakTool, + breakBlocks: Set[BlockBreakableBySkill], + breakArea: AxisAlignedCuboid, + standard: Location + )(implicit ioOnMainThread: OnMinecraftServerThread[IO]): IO[Unit] } object ActiveSkillEffect { object NoEffect extends ActiveSkillEffect { override val nameOnUI: String = "未設定" - override def arrowEffect(implicit ioOnMainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = + override def arrowEffect( + implicit ioOnMainThread: OnMinecraftServerThread[IO] + ): TargetedEffect[Player] = ArrowEffects.normalArrowEffect - override def runBreakEffect(player: Player, - usedSkill: ActiveSkill, - tool: BreakTool, - breakBlocks: Set[BlockBreakableBySkill], - breakArea: AxisAlignedCuboid, - standard: Location) - (implicit ioOnMainThread: OnMinecraftServerThread[IO]): IO[Unit] = - BreakUtil.massBreakBlock(player, breakBlocks, player.getLocation, tool, shouldPlayBreakSound = false, Material.AIR) + override def runBreakEffect( + player: Player, + usedSkill: ActiveSkill, + tool: BreakTool, + breakBlocks: Set[BlockBreakableBySkill], + breakArea: AxisAlignedCuboid, + standard: Location + )(implicit ioOnMainThread: OnMinecraftServerThread[IO]): IO[Unit] = + BreakUtil.massBreakBlock( + player, + breakBlocks, + player.getLocation, + tool, + shouldPlayBreakSound = false, + Material.AIR + ) } } @@ -65,22 +81,24 @@ object UnlockableActiveSkillEffect extends Enum[UnlockableActiveSkillEffect] { ActiveSkillNormalEffect.values ++ ActiveSkillPremiumEffect.values } -sealed abstract class ActiveSkillNormalEffect(stringId: String, - override val nameOnUI: String, - override val explanation: String, - override val usePoint: Int, - override val materialOnUI: Material) - extends UnlockableActiveSkillEffect { +sealed abstract class ActiveSkillNormalEffect( + stringId: String, + override val nameOnUI: String, + override val explanation: String, + override val usePoint: Int, + override val materialOnUI: Material +) extends UnlockableActiveSkillEffect { override val entryName: String = stringId - override def runBreakEffect(player: Player, - usedSkill: ActiveSkill, - tool: BreakTool, - breakBlocks: Set[BlockBreakableBySkill], - breakArea: AxisAlignedCuboid, - standard: Location) - (implicit ioOnMainThread: OnMinecraftServerThread[IO]): IO[Unit] = { + override def runBreakEffect( + player: Player, + usedSkill: ActiveSkill, + tool: BreakTool, + breakBlocks: Set[BlockBreakableBySkill], + breakArea: AxisAlignedCuboid, + standard: Location + )(implicit ioOnMainThread: OnMinecraftServerThread[IO]): IO[Unit] = { import PluginExecutionContexts.{asyncShift, cachedThreadPool} import com.github.unchama.concurrent.syntax._ import com.github.unchama.seichiassist.data.syntax._ @@ -103,7 +121,13 @@ sealed abstract class ActiveSkillNormalEffect(stringId: String, .map(XYZTuple.of(standard) + _) .filter(PositionSearching.containsOneOfPositionsAround(_, 1, blockPositions)) } - _ <- BreakUtil.massBreakBlock(player, breakBlocks, standard, tool, isSkillDualBreakOrTrialBreak) + _ <- BreakUtil.massBreakBlock( + player, + breakBlocks, + standard, + tool, + isSkillDualBreakOrTrialBreak + ) _ <- IO { explosionLocations.foreach(coordinates => world.createExplosion(coordinates.toLocation(world), 0f, false) @@ -115,19 +139,24 @@ sealed abstract class ActiveSkillNormalEffect(stringId: String, for { _ <- BreakUtil.massBreakBlock( - player, breakBlocks, standard, tool, - shouldPlayBreakSound = false, Material.PACKED_ICE + player, + breakBlocks, + standard, + tool, + shouldPlayBreakSound = false, + Material.PACKED_ICE ) _ <- IO.sleep(10.ticks) _ <- ioOnMainThread.runAction(SyncIO { breakBlocks .map(_.getLocation) .foreach(location => - player.getWorld.spawnParticle(Particle.SNOWBALL, location, 1)) + player.getWorld.spawnParticle(Particle.SNOWBALL, location, 1) + ) val setEffectRadius = usedSkill.range match { case ActiveSkillRange.MultiArea(_, areaCount) => areaCount == 1 - case ActiveSkillRange.RemoteArea(_) => false + case ActiveSkillRange.RemoteArea(_) => false } breakBlocks.foreach { b => @@ -156,7 +185,10 @@ sealed abstract class ActiveSkillNormalEffect(stringId: String, val effectCoordinate = XYZTuple.of(standard) + xyzTuple val effectLocation = effectCoordinate.toLocation(world) - if (PositionSearching.containsOneOfPositionsAround(effectCoordinate, 1, blockPositions)) { + if ( + PositionSearching + .containsOneOfPositionsAround(effectCoordinate, 1, blockPositions) + ) { world.spawnParticle(Particle.EXPLOSION_HUGE, effectLocation, 1) } } @@ -164,7 +196,13 @@ sealed abstract class ActiveSkillNormalEffect(stringId: String, // [0.8, 1.2) vol <- IO { new Random().nextFloat() * 0.4f + 0.8f } _ <- FocusedSoundEffect(Sound.ENTITY_WITHER_BREAK_BLOCK, 1.0f, vol).run(player) - _ <- BreakUtil.massBreakBlock(player, breakBlocks, standard, tool, isSkillDualBreakOrTrialBreak) + _ <- BreakUtil.massBreakBlock( + player, + breakBlocks, + standard, + tool, + isSkillDualBreakOrTrialBreak + ) } yield () } } @@ -172,11 +210,13 @@ sealed abstract class ActiveSkillNormalEffect(stringId: String, /** * エフェクト選択時の遠距離エフェクト */ - override def arrowEffect(implicit ioOnMainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = + override def arrowEffect( + implicit ioOnMainThread: OnMinecraftServerThread[IO] + ): TargetedEffect[Player] = this match { case Explosion => ArrowEffects.singleArrowExplosionEffect - case Blizzard => ArrowEffects.singleArrowBlizzardEffect - case Meteor => ArrowEffects.singleArrowMeteoEffect + case Blizzard => ArrowEffects.singleArrowBlizzardEffect + case Meteor => ArrowEffects.singleArrowMeteoEffect } } @@ -184,41 +224,53 @@ object ActiveSkillNormalEffect extends Enum[ActiveSkillNormalEffect] { val values: IndexedSeq[ActiveSkillNormalEffect] = findValues - case object Explosion extends ActiveSkillNormalEffect( - "ef_explosion", - s"${RED}エクスプロージョン", "単純な爆発", 50, - Material.TNT - ) - - case object Blizzard extends ActiveSkillNormalEffect( - "ef_blizzard", - s"${AQUA}ブリザード", "凍らせる", 70, - Material.PACKED_ICE - ) - - case object Meteor extends ActiveSkillNormalEffect( - "ef_meteor", - s"${DARK_RED}メテオ", "隕石を落とす", 100, - Material.FIREBALL - ) + case object Explosion + extends ActiveSkillNormalEffect( + "ef_explosion", + s"${RED}エクスプロージョン", + "単純な爆発", + 50, + Material.TNT + ) + + case object Blizzard + extends ActiveSkillNormalEffect( + "ef_blizzard", + s"${AQUA}ブリザード", + "凍らせる", + 70, + Material.PACKED_ICE + ) + + case object Meteor + extends ActiveSkillNormalEffect( + "ef_meteor", + s"${DARK_RED}メテオ", + "隕石を落とす", + 100, + Material.FIREBALL + ) } -sealed abstract class ActiveSkillPremiumEffect(stringId: String, - override val nameOnUI: String, - override val explanation: String, - override val usePoint: Int, - override val materialOnUI: Material) - extends UnlockableActiveSkillEffect { +sealed abstract class ActiveSkillPremiumEffect( + stringId: String, + override val nameOnUI: String, + override val explanation: String, + override val usePoint: Int, + override val materialOnUI: Material +) extends UnlockableActiveSkillEffect { override val entryName: String = stringId - def runBreakEffect(player: Player, - usedSkill: ActiveSkill, - tool: BreakTool, - breakBlocks: Set[BlockBreakableBySkill], - breakArea: AxisAlignedCuboid, - standard: Location)(implicit ioOnMainThread: OnMinecraftServerThread[IO]): IO[Unit] = { + def runBreakEffect( + player: Player, + usedSkill: ActiveSkill, + tool: BreakTool, + breakBlocks: Set[BlockBreakableBySkill], + breakArea: AxisAlignedCuboid, + standard: Location + )(implicit ioOnMainThread: OnMinecraftServerThread[IO]): IO[Unit] = { import PluginExecutionContexts.{asyncShift, timer} import com.github.unchama.concurrent.syntax._ import com.github.unchama.seichiassist.data.syntax._ @@ -227,18 +279,23 @@ sealed abstract class ActiveSkillPremiumEffect(stringId: String, case ActiveSkillPremiumEffect.MAGIC => val colors = Array(DyeColor.RED, DyeColor.BLUE, DyeColor.YELLOW, DyeColor.GREEN) - //破壊するブロックの中心位置 + // 破壊するブロックの中心位置 val centerBreak: Location = standard + ((breakArea.begin + breakArea.end) / 2) for { randomColor <- IO { colors(Random.nextInt(colors.length)) } - _ <- BreakUtil.massBreakBlock(player, breakBlocks, standard, tool, shouldPlayBreakSound = false, Material.WOOL) + _ <- BreakUtil.massBreakBlock( + player, + breakBlocks, + standard, + tool, + shouldPlayBreakSound = false, + Material.WOOL + ) _ <- IO { breakBlocks.foreach { b => val state = b.getState - state - .getData.asInstanceOf[Wool] - .setColor(randomColor) + state.getData.asInstanceOf[Wool].setColor(randomColor) state.update() } } @@ -246,8 +303,13 @@ sealed abstract class ActiveSkillPremiumEffect(stringId: String, period <- IO { if (SeichiAssist.DEBUG) 100 else 10 } _ <- IO.sleep(period.ticks) - _ <- SeichiAssist.instance.magicEffectEntityScope - .useTrackedForSome(BukkitResources.vanishingEntityResource[IO, Chicken](centerBreak, classOf[Chicken])) { e => + _ <- SeichiAssist + .instance + .magicEffectEntityScope + .useTrackedForSome( + BukkitResources + .vanishingEntityResource[IO, Chicken](centerBreak, classOf[Chicken]) + ) { e => for { _ <- IO { e.playEffect(EntityEffect.WITCH_MAGIC) @@ -271,7 +333,9 @@ sealed abstract class ActiveSkillPremiumEffect(stringId: String, /** * エフェクト選択時の遠距離エフェクト */ - override def arrowEffect(implicit ioOnMainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = + override def arrowEffect( + implicit ioOnMainThread: OnMinecraftServerThread[IO] + ): TargetedEffect[Player] = this match { case ActiveSkillPremiumEffect.MAGIC => ArrowEffects.singleArrowMagicEffect } @@ -281,10 +345,13 @@ case object ActiveSkillPremiumEffect extends Enum[ActiveSkillPremiumEffect] { val values: IndexedSeq[ActiveSkillPremiumEffect] = findValues - case object MAGIC extends ActiveSkillPremiumEffect( - "ef_magic", - s"$RED$UNDERLINE${BOLD}マジック", "鶏が出る手品", 10, - Material.RED_ROSE - ) + case object MAGIC + extends ActiveSkillPremiumEffect( + "ef_magic", + s"$RED$UNDERLINE${BOLD}マジック", + "鶏が出る手品", + 10, + Material.RED_ROSE + ) } diff --git a/src/main/scala/com/github/unchama/seichiassist/seichiskill/effect/PositionSearching.scala b/src/main/scala/com/github/unchama/seichiassist/seichiskill/effect/PositionSearching.scala index 428085e094..17000360c6 100644 --- a/src/main/scala/com/github/unchama/seichiassist/seichiskill/effect/PositionSearching.scala +++ b/src/main/scala/com/github/unchama/seichiassist/seichiskill/effect/PositionSearching.scala @@ -3,18 +3,20 @@ package com.github.unchama.seichiassist.seichiskill.effect import com.github.unchama.seichiassist.data.{AxisAlignedCuboid, XYZTuple} object PositionSearching { + /** - * [location]を中心としたチェビシェフ距離が[distance]以下の領域に - * [matchAgainst]の[[XYZTuple]]が一つでも含まれているかを返す。 + * [location]を中心としたチェビシェフ距離が[distance]以下の領域に [matchAgainst]の[[XYZTuple]]が一つでも含まれているかを返す。 */ - def containsOneOfPositionsAround(center: XYZTuple, distance: Int, matchAgainst: Set[XYZTuple]): Boolean = { + def containsOneOfPositionsAround( + center: XYZTuple, + distance: Int, + matchAgainst: Set[XYZTuple] + ): Boolean = { import com.github.unchama.seichiassist.data.syntax._ val sphereVertex = XYZTuple(distance, distance, distance) val cuboidToLookFor = AxisAlignedCuboid(sphereVertex.negative, sphereVertex) - cuboidToLookFor.gridPoints() - .map(center + _) - .exists(matchAgainst.contains) + cuboidToLookFor.gridPoints().map(center + _).exists(matchAgainst.contains) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/seichiskill/effect/arrow/ArrowEffects.scala b/src/main/scala/com/github/unchama/seichiassist/seichiskill/effect/arrow/ArrowEffects.scala index f685b006f2..a4b55429a7 100644 --- a/src/main/scala/com/github/unchama/seichiassist/seichiskill/effect/arrow/ArrowEffects.scala +++ b/src/main/scala/com/github/unchama/seichiassist/seichiskill/effect/arrow/ArrowEffects.scala @@ -25,29 +25,30 @@ object ArrowEffects { implicit val plugin: JavaPlugin = SeichiAssist.instance - def normalArrowEffect(implicit mainThread: OnMinecraftServerThread[IO]) : TargetedEffect[Player] = + def normalArrowEffect( + implicit mainThread: OnMinecraftServerThread[IO] + ): TargetedEffect[Player] = arrowEffect[Arrow]( - ProjectileSpawnConfiguration( - 1.0, - (0.0, 1.6, 0.0) - ), + ProjectileSpawnConfiguration(1.0, (0.0, 1.6, 0.0)), Some(Sound.ENTITY_ARROW_SHOOT) ) - def singleArrowBlizzardEffect(implicit mainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = + def singleArrowBlizzardEffect( + implicit mainThread: OnMinecraftServerThread[IO] + ): TargetedEffect[Player] = arrowEffect[Snowball]( - ProjectileSpawnConfiguration( - 1.0, - (0.0, 1.6, 0.0) - ), + ProjectileSpawnConfiguration(1.0, (0.0, 1.6, 0.0)), Some(Sound.ENTITY_SNOWBALL_THROW) ) - def singleArrowMagicEffect(implicit mainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = { + def singleArrowMagicEffect( + implicit mainThread: OnMinecraftServerThread[IO] + ): TargetedEffect[Player] = { import scala.util.chaining._ val thrownPotionItem = new ItemStack(Material.SPLASH_POTION).tap { itemStack => itemStack.setItemMeta { - Bukkit.getItemFactory + Bukkit + .getItemFactory .getItemMeta(Material.SPLASH_POTION) .asInstanceOf[PotionMeta] .tap(_.setBasePotionData(new PotionData(PotionType.INSTANT_HEAL))) @@ -55,44 +56,39 @@ object ArrowEffects { } arrowEffect[ThrownPotion]( - ProjectileSpawnConfiguration( - 0.8, - (0.0, 1.6, 0.0) - ), + ProjectileSpawnConfiguration(0.8, (0.0, 1.6, 0.0)), Some(Sound.ENTITY_WITCH_THROW), _.setItem(thrownPotionItem) ) } - def singleArrowMeteoEffect(implicit mainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = + def singleArrowMeteoEffect( + implicit mainThread: OnMinecraftServerThread[IO] + ): TargetedEffect[Player] = arrowEffect[Arrow]( - ProjectileSpawnConfiguration( - 1.0, - (0.0, 1.6, 0.0) - ), + ProjectileSpawnConfiguration(1.0, (0.0, 1.6, 0.0)), Some(Sound.ENTITY_ARROW_SHOOT), _.setGlowing(true) ) - def singleArrowExplosionEffect(implicit mainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = + def singleArrowExplosionEffect( + implicit mainThread: OnMinecraftServerThread[IO] + ): TargetedEffect[Player] = arrowEffect[SmallFireball]( - ProjectileSpawnConfiguration( - 0.4, - (0.0, 1.6, 0.0) - ), + ProjectileSpawnConfiguration(0.4, (0.0, 1.6, 0.0)), Some(Sound.ENTITY_GHAST_SHOOT) ) - def arrowEffect[ - P <: Projectile : ClassTag - ](spawnConfiguration: ProjectileSpawnConfiguration, + def arrowEffect[P <: Projectile: ClassTag]( + spawnConfiguration: ProjectileSpawnConfiguration, sound: Option[Sound] = None, - projectileModifier: P => Unit = (_: P) => ()) - (implicit mainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = { + projectileModifier: P => Unit = (_: P) => () + )(implicit mainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = { val runtimeClass = implicitly[ClassTag[P]].runtimeClass.asInstanceOf[Class[P]] - val soundEffect = sound.map(FocusedSoundEffect(_, 1.0f, 1.3f)).getOrElse(TargetedEffect.emptyEffect) + val soundEffect = + sound.map(FocusedSoundEffect(_, 1.0f, 1.3f)).getOrElse(TargetedEffect.emptyEffect) val waitForCollision = IO.sleep(100.ticks)(IO.timer(ExecutionContext.global)) @@ -101,32 +97,39 @@ object ArrowEffects { soundEffect, Kleisli(player => for { - playerLocation <- PluginExecutionContexts.onMainThread.runAction(SyncIO { - player.getLocation.clone() - }) - spawnLocation = playerLocation.clone() + playerLocation <- PluginExecutionContexts + .onMainThread + .runAction(SyncIO { + player.getLocation.clone() + }) + spawnLocation = playerLocation + .clone() .add(playerLocation.getDirection) .add(spawnConfiguration.offset) - modifyProjectile = (projectile: P) => IO { - projectile.tap { p => - import p._ - setShooter(player) - setGravity(spawnConfiguration.gravity) - setVelocity(playerLocation.getDirection.clone().multiply(spawnConfiguration.speed)) + modifyProjectile = (projectile: P) => + IO { + projectile.tap { p => + import p._ + setShooter(player) + setGravity(spawnConfiguration.gravity) + setVelocity( + playerLocation.getDirection.clone().multiply(spawnConfiguration.speed) + ) + } + projectile.tap(projectileModifier) } - projectile.tap(projectileModifier) - } /** * 100ティック衝突を待ってから開放する。 * - * 飛翔体をスコープ内でのリソースとしているのは、 - * サーバーが停止したときにも開放するためである。 + * 飛翔体をスコープ内でのリソースとしているのは、 サーバーが停止したときにも開放するためである。 */ - _ <- SeichiAssist.instance.arrowSkillProjectileScope - .useTracked(BukkitResources.vanishingEntityResource[IO, P](spawnLocation, runtimeClass)) { projectile => - modifyProjectile(projectile) >> waitForCollision - } + _ <- SeichiAssist + .instance + .arrowSkillProjectileScope + .useTracked( + BukkitResources.vanishingEntityResource[IO, P](spawnLocation, runtimeClass) + ) { projectile => modifyProjectile(projectile) >> waitForCollision } } yield () ) ) diff --git a/src/main/scala/com/github/unchama/seichiassist/seichiskill/effect/arrow/ProjectileSpawnConfiguration.scala b/src/main/scala/com/github/unchama/seichiassist/seichiskill/effect/arrow/ProjectileSpawnConfiguration.scala index 499c871b75..ffc8a615a4 100644 --- a/src/main/scala/com/github/unchama/seichiassist/seichiskill/effect/arrow/ProjectileSpawnConfiguration.scala +++ b/src/main/scala/com/github/unchama/seichiassist/seichiskill/effect/arrow/ProjectileSpawnConfiguration.scala @@ -2,10 +2,11 @@ package com.github.unchama.seichiassist.seichiskill.effect.arrow import org.bukkit.util.Vector -case class ProjectileSpawnConfiguration(speed: Double, - private val offsetComponents: (Double, Double, Double) = (0.0, 0.0, 0.0), - gravity: Boolean = false - ) { +case class ProjectileSpawnConfiguration( + speed: Double, + private val offsetComponents: (Double, Double, Double) = (0.0, 0.0, 0.0), + gravity: Boolean = false +) { val offset: Vector = offsetComponents match { case (x, y, z) => new Vector(x, y, z) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/AnywhereEnderChestAPI.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/AnywhereEnderChestAPI.scala new file mode 100644 index 0000000000..ab679681ed --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/AnywhereEnderChestAPI.scala @@ -0,0 +1,24 @@ +package com.github.unchama.seichiassist.subsystems.anywhereender + +import cats.data.Kleisli +import com.github.unchama.seichiassist.subsystems.anywhereender.domain.AnywhereEnderAccessPermitted +import org.bukkit.entity.Player + +trait AnywhereEnderChestAPI[F[_]] { + + /** + * 与えられたプレーヤーがどこでもエンダーチェストにアクセスできるかどうかを確認する作用。 + */ + def canAccessAnywhereEnderChest(player: Player): F[AnywhereEnderAccessPermitted] + + /** + * [[canAccessAnywhereEnderChest]]が + * - `Left` を返す場合はエラーメッセージを表示し。 + * - `true`を返す場合はどこでもエンダーチェストを開ける。 + * + * @return + * 上記したような作用を記述する[[Kleisli]] + */ + def openEnderChestOrNotifyInsufficientLevel: Kleisli[F, Player, AnywhereEnderAccessPermitted] + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/System.scala new file mode 100644 index 0000000000..10912c7afa --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/System.scala @@ -0,0 +1,69 @@ +package com.github.unchama.seichiassist.subsystems.anywhereender + +import cats.Functor +import cats.data.Kleisli +import cats.effect.{Effect, IO, LiftIO} +import com.github.unchama.generic.ContextCoercion +import com.github.unchama.minecraft.actions.OnMinecraftServerThread +import com.github.unchama.seichiassist.meta.subsystem.Subsystem +import com.github.unchama.seichiassist.subsystems.anywhereender.bukkit.command.EnderChestCommand +import com.github.unchama.seichiassist.subsystems.anywhereender.domain.{ + AccessDenialReason, + AnywhereEnderAccessPermitted +} +import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountReadAPI +import com.github.unchama.targetedeffect.commandsender.MessageEffectF +import com.github.unchama.targetedeffect.player.PlayerEffects +import org.bukkit.command.TabExecutor +import org.bukkit.entity.Player + +trait System[G[_]] extends Subsystem[G] { + def accessApi: AnywhereEnderChestAPI[G] +} + +object System { + import ContextCoercion._ + import cats.implicits._ + + def wired[F[_]: BreakCountReadAPI[IO, *[_], Player]: Functor: ContextCoercion[*[_], G], G[ + _ + ]: Effect]( + configuration: SystemConfiguration + )(implicit onMainThread: OnMinecraftServerThread[IO]): System[G] = new System[G] { + + override implicit val accessApi: AnywhereEnderChestAPI[G] = new AnywhereEnderChestAPI[G] { + override def canAccessAnywhereEnderChest( + player: Player + ): G[AnywhereEnderAccessPermitted] = { + implicitly[BreakCountReadAPI[IO, F, Player]] + .seichiAmountDataRepository(player) + .read + .coerceTo[G] + .map { _.levelCorrespondingToExp } + .map { currentLevel => + if (currentLevel < configuration.requiredMinimumLevel) { + Left( + AccessDenialReason + .NotEnoughLevel(currentLevel, configuration.requiredMinimumLevel) + ) + } else { + Right(()) + } + } + } + + override def openEnderChestOrNotifyInsufficientLevel + : Kleisli[G, Player, AnywhereEnderAccessPermitted] = + Kleisli(canAccessAnywhereEnderChest).flatTap { + case Left(AccessDenialReason.NotEnoughLevel(_, minimumLevel)) => + MessageEffectF[G](s"どこでもエンダーチェストを開くには整地レベルがLv${minimumLevel}以上である必要があります。") + case Right(_) => + Kleisli((player: Player) => + PlayerEffects.openInventoryEffect(player.getEnderChest).run(player) + ).mapK(LiftIO.liftK) + } + } + + override val commands: Map[String, TabExecutor] = Map("ec" -> EnderChestCommand.executor[G]) + } +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/SystemConfiguration.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/SystemConfiguration.scala new file mode 100644 index 0000000000..38479b1599 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/SystemConfiguration.scala @@ -0,0 +1,12 @@ +package com.github.unchama.seichiassist.subsystems.anywhereender + +import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.SeichiLevel + +trait SystemConfiguration { + + /** + * 「どこでもエンダーチェスト」を開くために必要な最小の整地レベル + */ + val requiredMinimumLevel: SeichiLevel + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/bukkit/command/EnderChestCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/bukkit/command/EnderChestCommand.scala new file mode 100644 index 0000000000..d4c7ba0cb1 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/bukkit/command/EnderChestCommand.scala @@ -0,0 +1,22 @@ +package com.github.unchama.seichiassist.subsystems.anywhereender.bukkit.command + +import cats.effect.Effect +import com.github.unchama.seichiassist.commands.contextual.builder.BuilderTemplates.playerCommandBuilder +import com.github.unchama.seichiassist.subsystems.anywhereender.AnywhereEnderChestAPI +import org.bukkit.command.TabExecutor + +/** + * エンダーチェストを開くコマンド + */ +object EnderChestCommand { + def executor[F[_]: Effect]( + implicit enderChestAccessApi: AnywhereEnderChestAPI[F] + ): TabExecutor = + playerCommandBuilder + .argumentsParsers(List()) + .withEffectAsExecution { + enderChestAccessApi.openEnderChestOrNotifyInsufficientLevel.mapK(Effect.toIOK) + } + .build() + .asNonBlockingTabExecutor() +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/domain/AccessDenialReason.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/domain/AccessDenialReason.scala new file mode 100644 index 0000000000..259b931114 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/domain/AccessDenialReason.scala @@ -0,0 +1,14 @@ +package com.github.unchama.seichiassist.subsystems.anywhereender.domain + +import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.SeichiLevel + +sealed trait AccessDenialReason +object AccessDenialReason { + import SeichiLevel._ + import cats.implicits._ + + case class NotEnoughLevel(current: SeichiLevel, required: SeichiLevel) + extends AccessDenialReason { + require(current < required) + } +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/domain/package.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/domain/package.scala new file mode 100644 index 0000000000..618161e1c6 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/domain/package.scala @@ -0,0 +1,7 @@ +package com.github.unchama.seichiassist.subsystems.anywhereender + +package object domain { + + type AnywhereEnderAccessPermitted = Either[AccessDenialReason, Unit] + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/System.scala index b4c38766c6..6245a398ec 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/System.scala @@ -3,14 +3,21 @@ package com.github.unchama.seichiassist.subsystems.autosave import cats.effect.{Sync, Timer} import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts -import com.github.unchama.seichiassist.subsystems.autosave.application.{CanNotifySaves, CanSaveWorlds, SystemConfiguration, WorldSaveRoutine} -import com.github.unchama.seichiassist.subsystems.autosave.bukkit.instances.{SyncCanNotifyBukkitSaves, SyncCanSaveBukkitWorlds} +import com.github.unchama.seichiassist.subsystems.autosave.application.{ + CanNotifySaves, + CanSaveWorlds, + SystemConfiguration, + WorldSaveRoutine +} +import com.github.unchama.seichiassist.subsystems.autosave.bukkit.instances.{ + SyncCanNotifyBukkitSaves, + SyncCanSaveBukkitWorlds +} object System { - def backgroundProcess[ - F[_] : Sync : Timer : OnMinecraftServerThread, - G[_] - ](configuration: SystemConfiguration): F[Nothing] = { + def backgroundProcess[F[_]: Sync: Timer: OnMinecraftServerThread, G[_]]( + configuration: SystemConfiguration + ): F[Nothing] = { import PluginExecutionContexts._ implicit val _configuration: SystemConfiguration = configuration diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/application/CanNotifySaves.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/application/CanNotifySaves.scala index 90fd00399a..416deb4b28 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/application/CanNotifySaves.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/application/CanNotifySaves.scala @@ -8,4 +8,4 @@ trait CanNotifySaves[F[_]] { object CanNotifySaves { def apply[F[_]: CanNotifySaves]: CanNotifySaves[F] = implicitly -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/application/CanSaveWorlds.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/application/CanSaveWorlds.scala index 95a0f663ec..c6a1d3ea27 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/application/CanSaveWorlds.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/application/CanSaveWorlds.scala @@ -8,4 +8,4 @@ trait CanSaveWorlds[F[_]] extends AnyRef { object CanSaveWorlds { def apply[F[_]: CanSaveWorlds]: CanSaveWorlds[F] = implicitly -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/application/WorldSaveRoutine.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/application/WorldSaveRoutine.scala index c897c9f400..bd4c25c2b8 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/application/WorldSaveRoutine.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/application/WorldSaveRoutine.scala @@ -6,13 +6,10 @@ import com.github.unchama.concurrent.{RepeatingRoutine, RepeatingTaskContext} import org.bukkit.ChatColor.AQUA object WorldSaveRoutine { - def apply[ - F[_] - : Sync - : CanSaveWorlds - : CanNotifySaves - : Timer - ]()(implicit configuration: SystemConfiguration, context: RepeatingTaskContext): F[Nothing] = { + def apply[F[_]: Sync: CanSaveWorlds: CanNotifySaves: Timer]()( + implicit configuration: SystemConfiguration, + context: RepeatingTaskContext + ): F[Nothing] = { import cats.implicits._ import scala.concurrent.duration._ diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/bukkit/instances/SyncCanNotifyBukkitSaves.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/bukkit/instances/SyncCanNotifyBukkitSaves.scala index b82d65c055..bd463a0547 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/bukkit/instances/SyncCanNotifyBukkitSaves.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/bukkit/instances/SyncCanNotifyBukkitSaves.scala @@ -8,9 +8,10 @@ import org.bukkit.Bukkit object SyncCanNotifyBukkitSaves { - def apply[F[_] : Sync]: CanNotifySaves[F] = (message: String) => Sync[F].delay { - Util.sendMessageToEveryoneIgnoringPreference(message) - Bukkit.getLogger.info(message) - } + def apply[F[_]: Sync]: CanNotifySaves[F] = (message: String) => + Sync[F].delay { + Util.sendMessageToEveryoneIgnoringPreference(message) + Bukkit.getLogger.info(message) + } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/bukkit/instances/SyncCanSaveBukkitWorlds.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/bukkit/instances/SyncCanSaveBukkitWorlds.scala index 6ed0d2ab58..1a1e6188d8 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/bukkit/instances/SyncCanSaveBukkitWorlds.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/autosave/bukkit/instances/SyncCanSaveBukkitWorlds.scala @@ -10,16 +10,20 @@ import scala.annotation.tailrec object SyncCanSaveBukkitWorlds { - def apply[F[_] : Sync : OnMinecraftServerThread]: CanSaveWorlds[F] = new CanSaveWorlds[F] { + def apply[F[_]: Sync: OnMinecraftServerThread]: CanSaveWorlds[F] = new CanSaveWorlds[F] { val save: SyncIO[Unit] = SyncIO { def saveWorld(world: World): Unit = { // WARNを防ぐためMinecraftサーバーデフォルトの自動セーブは無効化 val server = getFieldAsAccessibleField(Bukkit.getServer.getClass, "console") - .getOrElse(return) + .getOrElse( + return + ) .get(Bukkit.getServer) getFieldAsAccessibleField(server.getClass, "autosavePeriod") - .getOrElse(return) + .getOrElse( + return + ) .set(server, 0) world.save() @@ -28,13 +32,13 @@ object SyncCanSaveBukkitWorlds { @tailrec def getFieldAsAccessibleField(clazz: Class[_], name: String): Option[Field] = { clazz.getDeclaredFields.find(_.getName.equals(name)) match { - case s@Some(field) => + case s @ Some(field) => field.setAccessible(true) s case None => clazz.getSuperclass match { case null => None - case s => getFieldAsAccessibleField(s, name) + case s => getFieldAsAccessibleField(s, name) } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/System.scala index 3306d231e1..20117f1bbe 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/System.scala @@ -15,10 +15,9 @@ import org.bukkit.event.Listener import java.util.UUID object System { - def wired[ - F[_] : ConcurrentEffect : NonServerThreadContextShift, - G[_] - ](implicit effectEnvironment: EffectEnvironment): Subsystem[G] = { + def wired[F[_]: ConcurrentEffect: NonServerThreadContextShift, G[_]]( + implicit effectEnvironment: EffectEnvironment + ): Subsystem[G] = { implicit val repository: BookedAchievementPersistenceRepository[F, UUID] = new JdbcBookedAchievementPersistenceRepository[F] @@ -27,9 +26,7 @@ object System { new AchievementBookingService[F] new Subsystem[G] { - override val listeners: Seq[Listener] = Seq( - new GrantBookedAchievementListener() - ) + override val listeners: Seq[Listener] = Seq(new GrantBookedAchievementListener()) override val commands: Map[String, TabExecutor] = Map( "achievement" -> AchievementCommand.executor ) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/bukkit/command/AchievementCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/bukkit/command/AchievementCommand.scala index 1e13d81d5c..edd0084742 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/bukkit/command/AchievementCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/bukkit/command/AchievementCommand.scala @@ -25,16 +25,14 @@ object AchievementCommand { AchievementOperation.fromString, MessageEffect("操作はgive/depriveで与えてください。") ) + /** * TODO * [旧実装](https://github.com/GiganticMinecraft/SeichiAssist/blob/310ee1f438cf7ca5b202392fd77826b2ed245a58/src/main/java/com/github/unchama/seichiassist/commands/legacy/UnlockAchievementCommand.java#L50-L53) * には実績の存在確認のロジックが入っていたが、これは必要であったか? */ private val achievementNumberParser = - Parsers.closedRangeInt( - 1000, 9999, - MessageEffect(s"${RED}操作の対象として指定できるのはNo1000~9999の実績です。") - ) + Parsers.closedRangeInt(1000, 9999, MessageEffect(s"${RED}操作の対象として指定できるのはNo1000~9999の実績です。")) private val scopeParser = Parsers.fromOptionParser( ScopeSpecification.fromString, MessageEffect(s"${RED}スコープ指定子はuser [ユーザー名], server, worldのみ入力できます。") @@ -45,10 +43,10 @@ object AchievementCommand { object ScopeSpecification { def fromString(string: String): Option[ScopeSpecification] = string match { - case "user" => Some(USER) + case "user" => Some(USER) case "server" => Some(SERVER) - case "world" => Some(WORLD) - case _ => None + case "world" => Some(WORLD) + case _ => None } case object USER extends ScopeSpecification @@ -69,9 +67,10 @@ object AchievementCommand { ) ) - def executor[ - F[_] : ConcurrentEffect - ](implicit service: AchievementBookingService[F]): TabExecutor = ContextualExecutorBuilder.beginConfiguration() + def executor[F[_]: ConcurrentEffect]( + implicit service: AchievementBookingService[F] + ): TabExecutor = ContextualExecutorBuilder + .beginConfiguration() .argumentsParsers( List(operationParser, achievementNumberParser, scopeParser), onMissingArguments = descriptionPrintExecutor @@ -83,19 +82,24 @@ object AchievementCommand { val achievementNumber = context.args.parsed(1).asInstanceOf[Int] def execution(): IO[TargetedEffect[CommandSender]] = { - val targetPlayerNames: List[String] = context.args.parsed(2).asInstanceOf[ScopeSpecification] match { - case ScopeSpecification.USER => - val targetPlayerName = - context.args.yetToBeParsed.headOption - .getOrElse(return IO.pure(MessageEffect(s"${RED}プレーヤー名が未入力です。"))) - List(targetPlayerName) - case ScopeSpecification.SERVER => Bukkit.getServer.getOnlinePlayers.asScala.map(_.getName).toList - case ScopeSpecification.WORLD => - sender match { - case player: Player => player.getWorld.getPlayers.asScala.map(_.getName).toList - case _ => return IO.pure(MessageEffect("コンソール実行の場合は「world」処理は実行できません。")) - } - } + val targetPlayerNames: List[String] = + context.args.parsed(2).asInstanceOf[ScopeSpecification] match { + case ScopeSpecification.USER => + val targetPlayerName = + context + .args + .yetToBeParsed + .headOption + .getOrElse(return IO.pure(MessageEffect(s"${RED}プレーヤー名が未入力です。"))) + List(targetPlayerName) + case ScopeSpecification.SERVER => + Bukkit.getServer.getOnlinePlayers.asScala.map(_.getName).toList + case ScopeSpecification.WORLD => + sender match { + case player: Player => player.getWorld.getPlayers.asScala.map(_.getName).toList + case _ => return IO.pure(MessageEffect("コンソール実行の場合は「world」処理は実行できません。")) + } + } import cats.effect.implicits._ import cats.implicits._ @@ -106,23 +110,27 @@ object AchievementCommand { case Some(player) => val playerData = SeichiAssist.playermap(player.getUniqueId) operation match { - case AchievementOperation.GIVE => playerData.tryForcefullyUnlockAchievement(achievementNumber) - case AchievementOperation.DEPRIVE => playerData.forcefullyDepriveAchievement(achievementNumber) + case AchievementOperation.GIVE => + playerData.tryForcefullyUnlockAchievement(achievementNumber) + case AchievementOperation.DEPRIVE => + playerData.forcefullyDepriveAchievement(achievementNumber) } case None => SequentialEffect( Kleisli.liftF( - service.writeAchivementId(playerName, achievementNumber, operation).start.toIO.as(()) + service + .writeAchivementId(playerName, achievementNumber, operation) + .start + .toIO + .as(()) ), MessageEffect( List( - s"$playerName の No.$achievementNumber の実績を${ - operation match { - case AchievementOperation.GIVE => "付与" + s"$playerName の No.$achievementNumber の実績を${operation match { + case AchievementOperation.GIVE => "付与" case AchievementOperation.DEPRIVE => "剥奪" - } - }します。", + }}します。", s"$playerName は現在サーバーにログインしていません。\n予約システムに書き込みました。" ) ) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/bukkit/listener/GrantBookedAchievementListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/bukkit/listener/GrantBookedAchievementListener.scala index 3681c0723e..e961be94a7 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/bukkit/listener/GrantBookedAchievementListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/bukkit/listener/GrantBookedAchievementListener.scala @@ -10,8 +10,8 @@ import org.bukkit.Bukkit import org.bukkit.event.player.PlayerJoinEvent import org.bukkit.event.{EventHandler, Listener} -class GrantBookedAchievementListener[F[_] : ConcurrentEffect : NonServerThreadContextShift](implicit - effectEnvironment: EffectEnvironment, +class GrantBookedAchievementListener[F[_]: ConcurrentEffect: NonServerThreadContextShift]( + implicit effectEnvironment: EffectEnvironment, service: AchievementBookingService[F] ) extends Listener { @@ -24,9 +24,9 @@ class GrantBookedAchievementListener[F[_] : ConcurrentEffect : NonServerThreadCo val playerData = SeichiAssist.playermap(uuid) val effectRunner = Bukkit.getConsoleSender + /** - * `.shift`を行ってから書き込みを行うわずかな時間にプレイヤーが退出し終了処理が行われる可能性があるが、 - * パフォーマンスを取り、妥協してこの実装としている。 + * `.shift`を行ってから書き込みを行うわずかな時間にプレイヤーが退出し終了処理が行われる可能性があるが、 パフォーマンスを取り、妥協してこの実装としている。 */ val program = for { _ <- NonServerThreadContextShift[F].shift diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/domain/AchievementOperation.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/domain/AchievementOperation.scala index 1315789878..2b67157b4c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/domain/AchievementOperation.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/domain/AchievementOperation.scala @@ -15,8 +15,8 @@ object AchievementOperation { } def fromString(string: String): Option[AchievementOperation] = string match { - case "give" => Some(GIVE) + case "give" => Some(GIVE) case "deprive" => Some(DEPRIVE) - case _ => None + case _ => None } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/infrastructure/JdbcBookedAchievementPersistenceRepository.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/infrastructure/JdbcBookedAchievementPersistenceRepository.scala index 8d8b0eb4a4..cf318220e4 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/infrastructure/JdbcBookedAchievementPersistenceRepository.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/infrastructure/JdbcBookedAchievementPersistenceRepository.scala @@ -1,24 +1,32 @@ package com.github.unchama.seichiassist.subsystems.bookedachivement.infrastructure -import java.util.UUID - import cats.effect.Sync -import com.github.unchama.seichiassist.subsystems.bookedachivement.domain.{AchievementOperation, BookedAchievementPersistenceRepository} +import com.github.unchama.seichiassist.subsystems.bookedachivement.domain.{ + AchievementOperation, + BookedAchievementPersistenceRepository +} import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} +import java.util.UUID + class JdbcBookedAchievementPersistenceRepository[F[_]](implicit SyncContext: Sync[F]) - extends BookedAchievementPersistenceRepository[F, UUID] { + extends BookedAchievementPersistenceRepository[F, UUID] { /** * 指定した `achievementId` の実績付与・剥奪をプレイヤーの UUID である `key` とともに記録します. */ - override def bookAchievement(key: UUID, achievementId: Int, operation: AchievementOperation): F[Unit] = { + override def bookAchievement( + key: UUID, + achievementId: Int, + operation: AchievementOperation + ): F[Unit] = { SyncContext.delay { DB.localTx { implicit session => sql"""|insert into booked_achievement_status_change (player_uuid, achievement_id, operation) | values (${key.toString}, $achievementId, ${operation.toString})""" .stripMargin - .update().apply() + .update() + .apply() } } } @@ -26,17 +34,22 @@ class JdbcBookedAchievementPersistenceRepository[F[_]](implicit SyncContext: Syn /** * `key` を UUID に持つプレイヤーに適用されていない予約済み実績の番号を返します. */ - override def loadBookedAchievementsYetToBeAppliedOf(key: UUID): F[List[(AchievementOperation, Int)]] = { + override def loadBookedAchievementsYetToBeAppliedOf( + key: UUID + ): F[List[(AchievementOperation, Int)]] = { SyncContext.delay { DB.localTx { implicit session => sql"""|select achievement_id, operation from booked_achievement_status_change | where player_uuid = ${key.toString} and completed_at is null""" .stripMargin .map { rs => - (AchievementOperation.fromString(rs.string("operation")).get, - rs.int("achievement_id")) + ( + AchievementOperation.fromString(rs.string("operation")).get, + rs.int("achievement_id") + ) } - .toList().apply() + .toList() + .apply() } } } @@ -50,7 +63,8 @@ class JdbcBookedAchievementPersistenceRepository[F[_]](implicit SyncContext: Syn sql"""|update booked_achievement_status_change set completed_at = cast(now() as datetime) | where player_uuid = ${key.toString} and completed_at is null""" .stripMargin - .update().apply() + .update() + .apply() } } } @@ -63,8 +77,10 @@ class JdbcBookedAchievementPersistenceRepository[F[_]](implicit SyncContext: Syn DB.localTx { implicit session => UUID.fromString( sql"select (uuid) from playerdata where name = $playerName" - .map { rs => rs.string("uuid")} - .toList().apply().head + .map { rs => rs.string("uuid") } + .toList() + .apply() + .head ) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/service/AchievementBookingService.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/service/AchievementBookingService.scala index 43ecf5fd91..c1f32b9564 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/service/AchievementBookingService.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/bookedachivement/service/AchievementBookingService.scala @@ -1,13 +1,16 @@ package com.github.unchama.seichiassist.subsystems.bookedachivement.service -import java.util.UUID - import cats.FlatMap -import com.github.unchama.seichiassist.subsystems.bookedachivement.domain.{AchievementOperation, BookedAchievementPersistenceRepository} +import com.github.unchama.seichiassist.subsystems.bookedachivement.domain.{ + AchievementOperation, + BookedAchievementPersistenceRepository +} + +import java.util.UUID -class AchievementBookingService[ - F[_] : FlatMap -](implicit persistenceRepository: BookedAchievementPersistenceRepository[F, UUID]) { +class AchievementBookingService[F[_]: FlatMap]( + implicit persistenceRepository: BookedAchievementPersistenceRepository[F, UUID] +) { import cats.implicits._ import persistenceRepository._ @@ -19,7 +22,11 @@ class AchievementBookingService[ } yield result } - def writeAchivementId(playerName: String, achievementId: Int, operation: AchievementOperation): F[Unit] = { + def writeAchivementId( + playerName: String, + achievementId: Int, + operation: AchievementOperation + ): F[Unit] = { for { uuid <- findPlayerUuid(playerName) _ <- bookAchievement(uuid, achievementId, operation) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/BreakCountAPI.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/BreakCountAPI.scala index ee79e090a8..821130d486 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/BreakCountAPI.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/BreakCountAPI.scala @@ -6,13 +6,21 @@ import com.github.unchama.generic.Diff import com.github.unchama.generic.effect.concurrent.ReadOnlyRef import com.github.unchama.generic.effect.stream.StreamExtra import com.github.unchama.seichiassist.subsystems.breakcount.application.actions.IncrementSeichiExp -import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.{SeichiExpAmount, SeichiLevel, SeichiStarLevel} -import com.github.unchama.seichiassist.subsystems.breakcount.domain.{BatchedSeichiExpMap, SeichiAmountData} +import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.{ + SeichiExpAmount, + SeichiLevel, + SeichiStarLevel +} +import com.github.unchama.seichiassist.subsystems.breakcount.domain.{ + BatchedSeichiExpMap, + SeichiAmountData +} import java.util.UUID import scala.concurrent.duration.FiniteDuration trait BreakCountWriteAPI[G[_], Player] { + /** * プレーヤーの整地量データを増加させるアクション */ @@ -20,6 +28,7 @@ trait BreakCountWriteAPI[G[_], Player] { } trait BreakCountReadAPI[F[_], G[_], Player] { + /** * プレーヤーの整地量データの読み取り専用リポジトリ */ @@ -28,8 +37,7 @@ trait BreakCountReadAPI[F[_], G[_], Player] { /** * プレーヤーの永続化された整地量データの読み取り専用リポジトリ。 * - * このリポジトリは統計量表示等には利用できるが、 - * 最新の整地量データは [[seichiAmountDataRepository]] より取得すること。 + * このリポジトリは統計量表示等には利用できるが、 最新の整地量データは [[seichiAmountDataRepository]] より取得すること。 */ val persistedSeichiAmountDataRepository: UUID => ReadOnlyRef[G, Option[SeichiAmountData]] @@ -50,41 +58,47 @@ trait BreakCountReadAPI[F[_], G[_], Player] { * プレーヤーの整地レベルの更新差分が流れる [[fs2.Stream]] */ final lazy val seichiLevelUpdates: fs2.Stream[F, (Player, Diff[SeichiLevel])] = - seichiAmountUpdateDiffs.mapFilter { case (player, Diff(left, right)) => - Diff - .fromValues(left.levelCorrespondingToExp, right.levelCorrespondingToExp) - .map((player, _)) + seichiAmountUpdateDiffs.mapFilter { + case (player, Diff(left, right)) => + Diff + .fromValues(left.levelCorrespondingToExp, right.levelCorrespondingToExp) + .map((player, _)) } final lazy val seichiStarLevelUpdates: fs2.Stream[F, (Player, Diff[SeichiStarLevel])] = - seichiAmountUpdateDiffs.mapFilter { case (player, Diff(left, right)) => - Diff - .fromValues(left.starLevelCorrespondingToExp, right.starLevelCorrespondingToExp) - .map((player, _)) + seichiAmountUpdateDiffs.mapFilter { + case (player, Diff(left, right)) => + Diff + .fromValues(left.starLevelCorrespondingToExp, right.starLevelCorrespondingToExp) + .map((player, _)) } /** * プレーヤーの整地量データの増加分が流れる [[fs2.Stream]]。 */ final lazy val seichiAmountIncreases: fs2.Stream[F, (Player, SeichiExpAmount)] = - seichiAmountUpdateDiffs.map { case (player, Diff(oldData, newData)) => - val expDiff = SeichiExpAmount.orderedMonus.subtractTruncate(newData.expAmount, oldData.expAmount) - (player, expDiff) + seichiAmountUpdateDiffs.map { + case (player, Diff(oldData, newData)) => + val expDiff = + SeichiExpAmount.orderedMonus.subtractTruncate(newData.expAmount, oldData.expAmount) + (player, expDiff) } /** * `duration` 毎に纏められた、プレーヤーの整地量増加を流すストリーム。 */ - def batchedIncreases(duration: FiniteDuration) - (implicit FTimer: Timer[F], - FConcurrent: Concurrent[F]): fs2.Stream[F, BatchedSeichiExpMap[Player]] = - StreamExtra - .foldGate( - seichiAmountIncreases, - fs2.Stream.awakeEvery[F](duration), - BatchedSeichiExpMap.empty[Player] - )(_.combine) + def batchedIncreases(duration: FiniteDuration)( + implicit FTimer: Timer[F], + FConcurrent: Concurrent[F] + ): fs2.Stream[F, BatchedSeichiExpMap[Player]] = + StreamExtra.foldGate( + seichiAmountIncreases, + fs2.Stream.awakeEvery[F](duration), + BatchedSeichiExpMap.empty[Player] + )(_.combine) } -trait BreakCountAPI[F[_], G[_], Player] extends BreakCountWriteAPI[G, Player] with BreakCountReadAPI[F, G, Player] +trait BreakCountAPI[F[_], G[_], Player] + extends BreakCountWriteAPI[G, Player] + with BreakCountReadAPI[F, G, Player] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/System.scala index a283db3c6d..e7356b25ac 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/System.scala @@ -10,9 +10,15 @@ import com.github.unchama.generic.effect.unsafe.EffectEnvironment import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.meta.subsystem.Subsystem import com.github.unchama.seichiassist.subsystems.breakcount.application.BreakCountRepositoryDefinition -import com.github.unchama.seichiassist.subsystems.breakcount.application.actions.{ClassifyPlayerWorld, IncrementSeichiExp} +import com.github.unchama.seichiassist.subsystems.breakcount.application.actions.{ + ClassifyPlayerWorld, + IncrementSeichiExp +} import com.github.unchama.seichiassist.subsystems.breakcount.bukkit.actions.SyncClassifyBukkitPlayerWorld -import com.github.unchama.seichiassist.subsystems.breakcount.domain.{SeichiAmountData, SeichiAmountDataPersistence} +import com.github.unchama.seichiassist.subsystems.breakcount.domain.{ + SeichiAmountData, + SeichiAmountDataPersistence +} import com.github.unchama.seichiassist.subsystems.breakcount.infrastructure.JdbcSeichiAmountDataPersistence import io.chrisdavenport.log4cats.ErrorLogger import org.bukkit.entity.Player @@ -20,12 +26,11 @@ import org.bukkit.entity.Player import java.util.UUID /** - * 整地量データを管理するシステム。 - * このシステムは次の責務を持つ。 + * 整地量データを管理するシステム。 このシステムは次の責務を持つ。 * - * - 整地量データを永続化する - * - 整地量データの読み取りとインクリメント操作を他システムへ露出する - * - 整地量データの変更を他システムやプレーヤーへ通知する + * - 整地量データを永続化する + * - 整地量データの読み取りとインクリメント操作を他システムへ露出する + * - 整地量データの変更を他システムやプレーヤーへ通知する */ trait System[F[_], G[_]] extends Subsystem[F] { @@ -38,11 +43,13 @@ object System { import cats.effect.implicits._ import cats.implicits._ - def wired[ - F[_] : ConcurrentEffect : OnMinecraftServerThread : ErrorLogger, - G[_] : SyncEffect : ContextCoercion[*[_], F] - ](implicit effectEnvironment: EffectEnvironment): F[System[F, G]] = { - implicit val persistence: SeichiAmountDataPersistence[G] = new JdbcSeichiAmountDataPersistence[G] + def wired[F[_]: ConcurrentEffect: OnMinecraftServerThread: ErrorLogger, G[ + _ + ]: SyncEffect: ContextCoercion[*[_], F]]( + implicit effectEnvironment: EffectEnvironment + ): F[System[F, G]] = { + implicit val persistence: SeichiAmountDataPersistence[G] = + new JdbcSeichiAmountDataPersistence[G] val createSystem: F[System[F, G]] = for { breakCountTopic <- Fs3Topic[F, Option[(Player, SeichiAmountData)]](None) @@ -60,22 +67,27 @@ object System { breakCountRepositoryControls <- ContextCoercion( BukkitRepositoryControls.createHandles( - BreakCountRepositoryDefinition.withContext[F, G, Player](breakCountTopic, persistence) + BreakCountRepositoryDefinition + .withContext[F, G, Player](breakCountTopic, persistence) ) ) } yield { - implicit val classifyPlayerWorld: ClassifyPlayerWorld[G, Player] = SyncClassifyBukkitPlayerWorld[G] + implicit val classifyPlayerWorld: ClassifyPlayerWorld[G, Player] = + SyncClassifyBukkitPlayerWorld[G] val breakCountRepository = breakCountRepositoryControls.repository new System[F, G] { override val api: BreakCountAPI[F, G, Player] = new BreakCountAPI[F, G, Player] { - override val seichiAmountDataRepository: KeyedDataRepository[Player, ReadOnlyRef[G, SeichiAmountData]] = + override val seichiAmountDataRepository + : KeyedDataRepository[Player, ReadOnlyRef[G, SeichiAmountData]] = breakCountRepository.map(ReadOnlyRef.fromRef) - override val persistedSeichiAmountDataRepository: UUID => ReadOnlyRef[G, Option[SeichiAmountData]] = - uuid => ReadOnlyRef.fromAnySource { - persistence.read(uuid) - } + override val persistedSeichiAmountDataRepository + : UUID => ReadOnlyRef[G, Option[SeichiAmountData]] = + uuid => + ReadOnlyRef.fromAnySource { + persistence.read(uuid) + } override val incrementSeichiExp: IncrementSeichiExp[G, Player] = IncrementSeichiExp.using(breakCountRepository, breakCountTopic) override val seichiAmountUpdates: fs2.Stream[F, (Player, SeichiAmountData)] = @@ -88,9 +100,7 @@ object System { } createSystem.flatTap { system => - subsystems.notification.System - .backgroundProcess(system.api) - .start + subsystems.notification.System.backgroundProcess(system.api).start } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/application/BreakCountRepositoryDefinition.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/application/BreakCountRepositoryDefinition.scala index dc0aa56599..cbe4ddcd57 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/application/BreakCountRepositoryDefinition.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/application/BreakCountRepositoryDefinition.scala @@ -6,16 +6,19 @@ import com.github.unchama.datarepository.definitions.RefDictBackedRepositoryDefi import com.github.unchama.datarepository.template.RepositoryDefinition import com.github.unchama.fs2.workaround.fs3.Fs3Topic import com.github.unchama.generic.effect.EffectExtra -import com.github.unchama.seichiassist.subsystems.breakcount.domain.{SeichiAmountData, SeichiAmountDataPersistence} +import com.github.unchama.seichiassist.subsystems.breakcount.domain.{ + SeichiAmountData, + SeichiAmountDataPersistence +} object BreakCountRepositoryDefinition { import cats.implicits._ - def withContext[ - F[_] : Effect, G[_] : Sync, Player - ](topic: Fs3Topic[F, Option[(Player, SeichiAmountData)]], - persistence: SeichiAmountDataPersistence[G]): RepositoryDefinition[G, Player, Ref[G, SeichiAmountData]] = + def withContext[F[_]: Effect, G[_]: Sync, Player]( + topic: Fs3Topic[F, Option[(Player, SeichiAmountData)]], + persistence: SeichiAmountDataPersistence[G] + ): RepositoryDefinition[G, Player, Ref[G, SeichiAmountData]] = RefDictBackedRepositoryDefinition .usingUuidRefDict[G, Player, SeichiAmountData](persistence)(SeichiAmountData.initial) .withAnotherTappingAction { (player, data) => diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/application/actions/ClassifyPlayerWorld.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/application/actions/ClassifyPlayerWorld.scala index 5f542c079d..61041f4086 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/application/actions/ClassifyPlayerWorld.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/application/actions/ClassifyPlayerWorld.scala @@ -11,6 +11,8 @@ trait ClassifyPlayerWorld[F[_], Player] { object ClassifyPlayerWorld { - def apply[F[_], Player](implicit ev: ClassifyPlayerWorld[F, Player]): ClassifyPlayerWorld[F, Player] = ev + def apply[F[_], Player]( + implicit ev: ClassifyPlayerWorld[F, Player] + ): ClassifyPlayerWorld[F, Player] = ev } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/application/actions/IncrementSeichiExp.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/application/actions/IncrementSeichiExp.scala index 25ba77e387..0eb97809c1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/application/actions/IncrementSeichiExp.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/application/actions/IncrementSeichiExp.scala @@ -20,20 +20,19 @@ object IncrementSeichiExp { import cats.implicits._ - def apply[F[_], Player](implicit ev: IncrementSeichiExp[F, Player]): IncrementSeichiExp[F, Player] = ev + def apply[F[_], Player]( + implicit ev: IncrementSeichiExp[F, Player] + ): IncrementSeichiExp[F, Player] = ev /** - * 与えられたデータレポジトリと更新を流すトピックを用いてプレーヤーの整地量を増加させるような - * 代数を作成する。 + * 与えられたデータレポジトリと更新を流すトピックを用いてプレーヤーの整地量を増加させるような 代数を作成する。 */ - def using[ - F[_] - : Sync - : ClassifyPlayerWorld[*[_], Player], - G[_] : Effect : ContextCoercion[F, *[_]], - Player - ](dataRepository: KeyedDataRepository[Player, Ref[F, SeichiAmountData]], - dataTopic: Fs3Topic[G, Option[(Player, SeichiAmountData)]]): IncrementSeichiExp[F, Player] = + def using[F[_]: Sync: ClassifyPlayerWorld[*[_], Player], G[_]: Effect: ContextCoercion[F, *[ + _ + ]], Player]( + dataRepository: KeyedDataRepository[Player, Ref[F, SeichiAmountData]], + dataTopic: Fs3Topic[G, Option[(Player, SeichiAmountData)]] + ): IncrementSeichiExp[F, Player] = (player, by) => { val F: Monad[F] = implicitly diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/bukkit/actions/SyncClassifyBukkitPlayerWorld.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/bukkit/actions/SyncClassifyBukkitPlayerWorld.scala index 3c984dfe8a..4a25aca3aa 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/bukkit/actions/SyncClassifyBukkitPlayerWorld.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/bukkit/actions/SyncClassifyBukkitPlayerWorld.scala @@ -9,7 +9,7 @@ object SyncClassifyBukkitPlayerWorld { import ManagedWorld._ - def apply[F[_] : Sync]: ClassifyPlayerWorld[F, Player] = + def apply[F[_]: Sync]: ClassifyPlayerWorld[F, Player] = (player: Player) => Sync[F].delay(player.getWorld.isSeichi) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/BatchedSeichiExpMap.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/BatchedSeichiExpMap.scala index b70fc66e29..0aabc3758e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/BatchedSeichiExpMap.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/BatchedSeichiExpMap.scala @@ -4,7 +4,7 @@ import com.github.unchama.generic.MapExtra import com.github.unchama.minecraft.algebra.HasUuid import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.SeichiExpAmount -class BatchedSeichiExpMap[Player] private(private val map: Map[Player, SeichiExpAmount]) { +class BatchedSeichiExpMap[Player] private (private val map: Map[Player, SeichiExpAmount]) { def combine(pair: (Player, SeichiExpAmount)): BatchedSeichiExpMap[Player] = { val (player, amount) = pair @@ -19,15 +19,13 @@ class BatchedSeichiExpMap[Player] private(private val map: Map[Player, SeichiExp /** * Uuidごとにバッチに纏められた整地量マップを取得する。 * - * NOTE: - * CraftBukkitにおいては、Uuid比較よりも厳しいエンティティId比較がプレーヤーのequals比較によって行われている。 - * これは整地量集計において厳しすぎる等価性のため、 - * [[HasUuid]] が提供するUuid情報での等価性によってコレクションをまとめ直す。 + * NOTE: CraftBukkitにおいては、Uuid比較よりも厳しいエンティティId比較がプレーヤーのequals比較によって行われている。 + * これは整地量集計において厳しすぎる等価性のため、 [[HasUuid]] が提供するUuid情報での等価性によってコレクションをまとめ直す。 */ - def toUuidCollatedList(implicit playerHasUuid: HasUuid[Player]): List[(Player, SeichiExpAmount)] = - MapExtra - .collapseKeysThrough(playerHasUuid.of)(map) - .toList + def toUuidCollatedList( + implicit playerHasUuid: HasUuid[Player] + ): List[(Player, SeichiExpAmount)] = + MapExtra.collapseKeysThrough(playerHasUuid.of)(map).toList } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/SeichiAmountData.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/SeichiAmountData.scala index 8e98e8ded7..1c9fe37d21 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/SeichiAmountData.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/SeichiAmountData.scala @@ -25,15 +25,22 @@ case class SeichiAmountData(expAmount: SeichiExpAmount) { lazy val levelProgress: SeichiLevelProgress = { import com.github.unchama.generic.algebra.typeclasses.OrderedMonus._ - val (nextThreshold, previousThreshold) = if (starLevelCorrespondingToExp != SeichiStarLevel.zero) { - val nextLevel = starLevelCorrespondingToExp.increment - - (SeichiStarLevelTable.expAt(nextLevel), SeichiStarLevelTable.expAt(starLevelCorrespondingToExp)) - } else { - val nextLevel = levelCorrespondingToExp.increment - - (SeichiLevelTable.table.expAt(nextLevel), SeichiLevelTable.table.expAt(levelCorrespondingToExp)) - } + val (nextThreshold, previousThreshold) = + if (starLevelCorrespondingToExp != SeichiStarLevel.zero) { + val nextLevel = starLevelCorrespondingToExp.increment + + ( + SeichiStarLevelTable.expAt(nextLevel), + SeichiStarLevelTable.expAt(starLevelCorrespondingToExp) + ) + } else { + val nextLevel = levelCorrespondingToExp.increment + + ( + SeichiLevelTable.table.expAt(nextLevel), + SeichiLevelTable.table.expAt(levelCorrespondingToExp) + ) + } val required = nextThreshold |-| previousThreshold val achieved = expAmount |-| previousThreshold @@ -41,7 +48,9 @@ case class SeichiAmountData(expAmount: SeichiExpAmount) { SeichiLevelProgress.fromRequiredAndAchievedPair(required, achieved) } - def addExpAmount(another: SeichiExpAmount): SeichiAmountData = SeichiAmountData(expAmount.add(another)) + def addExpAmount(another: SeichiExpAmount): SeichiAmountData = SeichiAmountData( + expAmount.add(another) + ) } @@ -52,5 +61,6 @@ object SeichiAmountData { implicit val order: Order[SeichiAmountData] = Order.by(_.expAmount) - implicit val monoid: Monoid[SeichiAmountData] = Monoid.instance(initial, (a, b) => a.addExpAmount(b.expAmount)) + implicit val monoid: Monoid[SeichiAmountData] = + Monoid.instance(initial, (a, b) => a.addExpAmount(b.expAmount)) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiExpAmount.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiExpAmount.scala index 35224f6a63..d286b7c41c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiExpAmount.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiExpAmount.scala @@ -8,7 +8,7 @@ import scala.math.BigDecimal.RoundingMode /** * 整地量を表す値のクラス。非負の値に対応する。 */ -case class SeichiExpAmount private(amount: BigDecimal) extends AnyVal { +case class SeichiExpAmount private (amount: BigDecimal) extends AnyVal { def mapAmount(f: BigDecimal => BigDecimal): SeichiExpAmount = SeichiExpAmount.ofNonNegative(f(amount)) @@ -23,10 +23,7 @@ object SeichiExpAmount { final private val seichiExpScale = 1 def ofNonNegative(amount: BigDecimal): SeichiExpAmount = { - require( - amount >= 0L, - "整地経験値量は非負である必要があります。" - ) + require(amount >= 0L, "整地経験値量は非負である必要があります。") SeichiExpAmount(amount.setScale(seichiExpScale, RoundingMode.DOWN)) } @@ -34,22 +31,26 @@ object SeichiExpAmount { lazy val zero: SeichiExpAmount = ofNonNegative(0) // region instances - implicit lazy val orderedMonus: OrderedMonus[SeichiExpAmount] = new OrderedMonus[SeichiExpAmount] { - override def |-|(x: SeichiExpAmount, y: SeichiExpAmount): SeichiExpAmount = ofNonNegative { - if (x.amount > y.amount) x.amount - y.amount else 0 - } + implicit lazy val orderedMonus: OrderedMonus[SeichiExpAmount] = + new OrderedMonus[SeichiExpAmount] { + override def |-|(x: SeichiExpAmount, y: SeichiExpAmount): SeichiExpAmount = + ofNonNegative { + if (x.amount > y.amount) x.amount - y.amount else 0 + } - override def empty: SeichiExpAmount = zero + override def empty: SeichiExpAmount = zero - override def combine(x: SeichiExpAmount, y: SeichiExpAmount): SeichiExpAmount = ofNonNegative(x.amount + y.amount) + override def combine(x: SeichiExpAmount, y: SeichiExpAmount): SeichiExpAmount = + ofNonNegative(x.amount + y.amount) - override def compare(x: SeichiExpAmount, y: SeichiExpAmount): Int = x.amount.compareTo(y.amount) - } + override def compare(x: SeichiExpAmount, y: SeichiExpAmount): Int = + x.amount.compareTo(y.amount) + } - implicit lazy val lowerBounded: LowerBounded[SeichiExpAmount] = new LowerBounded[SeichiExpAmount] { - override val partialOrder: PartialOrder[SeichiExpAmount] = orderedMonus - override val minBound: SeichiExpAmount = zero - } - //endregion + implicit lazy val lowerBounded: LowerBounded[SeichiExpAmount] = + new LowerBounded[SeichiExpAmount] { + override val partialOrder: PartialOrder[SeichiExpAmount] = orderedMonus + override val minBound: SeichiExpAmount = zero + } + // endregion } - diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevel.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevel.scala index 18a3335273..45a7239524 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevel.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevel.scala @@ -6,7 +6,7 @@ import com.github.unchama.generic.algebra.typeclasses.{HasSuccessor, PositiveInt /** * 整地レベル。正の[[Int]]と対応する。 */ -case class SeichiLevel private(level: Int) extends AnyVal { +case class SeichiLevel private (level: Int) extends AnyVal { def increment: SeichiLevel = SeichiLevel.ofPositive(level + 1) } @@ -25,7 +25,8 @@ private[level] abstract class SeichiLevelInstances { implicit lazy val order: Order[SeichiLevel] = Order.by(_.level) - implicit lazy val hasSuccessor: HasSuccessor[SeichiLevel] = HasSuccessor.positiveIntHasSuccessor + implicit lazy val hasSuccessor: HasSuccessor[SeichiLevel] = + HasSuccessor.positiveIntHasSuccessor } object SeichiLevel extends SeichiLevelInstances { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevelProgress.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevelProgress.scala index 5233bd868e..6344d278d0 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevelProgress.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevelProgress.scala @@ -3,10 +3,15 @@ package com.github.unchama.seichiassist.subsystems.breakcount.domain.level /** * レベルの進行度を表す値。 * - * @param progress 現在のレベルから次のレベルまでへの進行度。0以上1未満の[[Double]]値である。 - * @param expAmountToNextLevel 次のレベルに到達するのに必要な整地経験値量 + * @param progress + * 現在のレベルから次のレベルまでへの進行度。0以上1未満の[[Double]]値である。 + * @param expAmountToNextLevel + * 次のレベルに到達するのに必要な整地経験値量 */ -case class SeichiLevelProgress private(progress: Double, expAmountToNextLevel: SeichiExpAmount) { +case class SeichiLevelProgress private ( + progress: Double, + expAmountToNextLevel: SeichiExpAmount +) { require(0.0 <= progress && progress < 1.0, "レベル進捗は[0.0, 1.0)の要素である必要があります") } @@ -15,10 +20,12 @@ object SeichiLevelProgress { import com.github.unchama.generic.algebra.typeclasses.OrderedMonus._ /** - * 必須経験値量と達成済み経験値量から [[SeichiLevelProgress]] を得る。 - * 必要経験値量は達成済み経験値量未満である必要がある。 + * 必須経験値量と達成済み経験値量から [[SeichiLevelProgress]] を得る。 必要経験値量は達成済み経験値量未満である必要がある。 */ - def fromRequiredAndAchievedPair(required: SeichiExpAmount, achieved: SeichiExpAmount): SeichiLevelProgress = { + def fromRequiredAndAchievedPair( + required: SeichiExpAmount, + achieved: SeichiExpAmount + ): SeichiLevelProgress = { SeichiLevelProgress( achieved.amount.toDouble / required.amount.toDouble, required |-| achieved diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevelTable.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevelTable.scala index 56b11ce2d7..82cd42f224 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevelTable.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevelTable.scala @@ -49,8 +49,9 @@ object SeichiLevelTable { * 整地量をレベルに関連付ける経験値テーブル */ val table: FiniteExpLevelTable[SeichiLevel, SeichiExpAmount] = { - linearIncreases.foldLeft(tableUpToLevel51) { case (table, (level, amount)) => - table.extendToLevel(level).withLinearIncreaseOf(amount) + linearIncreases.foldLeft(tableUpToLevel51) { + case (table, (level, amount)) => + table.extendToLevel(level).withLinearIncreaseOf(amount) } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiStarLevel.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiStarLevel.scala index 1d515788ac..47a5b52db1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiStarLevel.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiStarLevel.scala @@ -6,7 +6,7 @@ import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.Seichi /** * 整地スターレベル。 */ -case class SeichiStarLevel private(level: BigInt) extends AnyVal { +case class SeichiStarLevel private (level: BigInt) extends AnyVal { def increment: SeichiStarLevel = ofNonNegative(level + 1) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiStarLevelTable.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiStarLevelTable.scala index dabe431684..755681c2b2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiStarLevelTable.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiStarLevelTable.scala @@ -5,22 +5,24 @@ import com.github.unchama.seichiassist.domain.explevel.ExpLevelConversion /** * 経験値量と整地スターレベルの相互変換を与えるオブジェクト。 * - * 必要な整地経験値量は整地スターレベルに比例し、 - * 比例係数は [[SeichiLevelTable]] が定義する最大レベルに到達した瞬間の経験値量に等しい。 + * 必要な整地経験値量は整地スターレベルに比例し、 比例係数は [[SeichiLevelTable]] が定義する最大レベルに到達した瞬間の経験値量に等しい。 */ object SeichiStarLevelTable extends ExpLevelConversion[SeichiStarLevel, SeichiExpAmount] { val gradient: SeichiExpAmount = SeichiLevelTable.table.expAt(SeichiLevelTable.table.maxLevel) - override def levelAt(expAmount: SeichiExpAmount): SeichiStarLevel = SeichiStarLevel.ofNonNegative { - expAmount.amount.bigDecimal.divide( - gradient.amount.bigDecimal, - java.math.BigDecimal.ROUND_DOWN - ).toBigInteger - } + override def levelAt(expAmount: SeichiExpAmount): SeichiStarLevel = + SeichiStarLevel.ofNonNegative { + expAmount + .amount + .bigDecimal + .divide(gradient.amount.bigDecimal, java.math.BigDecimal.ROUND_DOWN) + .toBigInteger + } - override def expAt(starLevel: SeichiStarLevel): SeichiExpAmount = SeichiExpAmount.ofNonNegative { - gradient.amount * BigDecimal(starLevel.level) - } + override def expAt(starLevel: SeichiStarLevel): SeichiExpAmount = + SeichiExpAmount.ofNonNegative { + gradient.amount * BigDecimal(starLevel.level) + } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/infrastructure/JdbcSeichiAmountDataPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/infrastructure/JdbcSeichiAmountDataPersistence.scala index 9540f674d8..8ef24a54dd 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/infrastructure/JdbcSeichiAmountDataPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/infrastructure/JdbcSeichiAmountDataPersistence.scala @@ -2,22 +2,28 @@ package com.github.unchama.seichiassist.subsystems.breakcount.infrastructure import cats.effect.Sync import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.SeichiExpAmount -import com.github.unchama.seichiassist.subsystems.breakcount.domain.{SeichiAmountData, SeichiAmountDataPersistence} +import com.github.unchama.seichiassist.subsystems.breakcount.domain.{ + SeichiAmountData, + SeichiAmountDataPersistence +} import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} import java.util.UUID class JdbcSeichiAmountDataPersistence[F[_]](implicit F: Sync[F]) - extends SeichiAmountDataPersistence[F] { + extends SeichiAmountDataPersistence[F] { override def read(key: UUID): F[Option[SeichiAmountData]] = F.delay { DB.localTx { implicit session => sql"select totalbreaknum from playerdata where uuid = ${key.toString}" .map { rs => - SeichiAmountData(SeichiExpAmount.ofNonNegative(rs.bigInt("totalbreaknum").longValueExact())) + SeichiAmountData( + SeichiExpAmount.ofNonNegative(rs.bigInt("totalbreaknum").longValueExact()) + ) } - .first().apply() + .first() + .apply() } } @@ -25,7 +31,8 @@ class JdbcSeichiAmountDataPersistence[F[_]](implicit F: Sync[F]) F.delay { DB.localTx { implicit session => sql"update playerdata set totalbreaknum = ${value.expAmount.amount} where uuid = ${key.toString}" - .update().apply() + .update() + .apply() } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/subsystems/notification/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/subsystems/notification/System.scala index e7ae1abeb6..862c5d426b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/subsystems/notification/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/subsystems/notification/System.scala @@ -11,11 +11,9 @@ import org.bukkit.entity.Player object System { - def backgroundProcess[ - F[_] : Concurrent : OnMinecraftServerThread : ErrorLogger, - G[_], - A - ](breakCountReadAPI: BreakCountReadAPI[F, G, Player]) : F[A] = { + def backgroundProcess[F[_]: Concurrent: OnMinecraftServerThread: ErrorLogger, G[_], A]( + breakCountReadAPI: BreakCountReadAPI[F, G, Player] + ): F[A] = { val action: NotifyLevelUp[F, Player] = BukkitNotifyLevelUp[F] StreamExtra.compileToRestartingStream("[breakcount.notification]") { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/subsystems/notification/application/actions/NotifyLevelUp.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/subsystems/notification/application/actions/NotifyLevelUp.scala index 8d09097b76..38b805b663 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/subsystems/notification/application/actions/NotifyLevelUp.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/subsystems/notification/application/actions/NotifyLevelUp.scala @@ -1,7 +1,10 @@ package com.github.unchama.seichiassist.subsystems.breakcount.subsystems.notification.application.actions import com.github.unchama.generic.Diff -import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.{SeichiLevel, SeichiStarLevel} +import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.{ + SeichiLevel, + SeichiStarLevel +} trait NotifyLevelUp[F[_], Player] { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/subsystems/notification/bukkit/actions/BukkitNotifyLevelUp.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/subsystems/notification/bukkit/actions/BukkitNotifyLevelUp.scala index b5540c0ed8..83b9a95bff 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/subsystems/notification/bukkit/actions/BukkitNotifyLevelUp.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcount/subsystems/notification/bukkit/actions/BukkitNotifyLevelUp.scala @@ -4,7 +4,10 @@ import cats.Applicative import cats.effect.{Sync, SyncIO} import com.github.unchama.generic.Diff import com.github.unchama.minecraft.actions.OnMinecraftServerThread -import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.{SeichiLevel, SeichiStarLevel} +import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.{ + SeichiLevel, + SeichiStarLevel +} import com.github.unchama.seichiassist.subsystems.breakcount.subsystems.notification.application.actions.NotifyLevelUp import com.github.unchama.seichiassist.util.Util import org.bukkit.ChatColor.GOLD @@ -14,42 +17,41 @@ object BukkitNotifyLevelUp { import cats.implicits._ - def apply[ - F[_] : OnMinecraftServerThread : Sync, - ]: NotifyLevelUp[F, Player] = new NotifyLevelUp[F, Player] { - override def ofSeichiLevelTo(player: Player)(diff: Diff[SeichiLevel]): F[Unit] = { - val Diff(oldLevel, newLevel) = diff + def apply[F[_]: OnMinecraftServerThread: Sync]: NotifyLevelUp[F, Player] = + new NotifyLevelUp[F, Player] { + override def ofSeichiLevelTo(player: Player)(diff: Diff[SeichiLevel]): F[Unit] = { + val Diff(oldLevel, newLevel) = diff + + val titleMessage = s"【Lv${oldLevel.level}→Lv${newLevel.level}】" + val subtitleMessage = s"${GOLD}ムムッwwwwwwwレベルアップwwwwwww" + + if (oldLevel < newLevel) { + OnMinecraftServerThread[F].runAction(SyncIO { + player.sendTitle(titleMessage, subtitleMessage, 1, 20 * 5, 1) + player.sendMessage(s"$subtitleMessage$titleMessage") + Util.launchFireWorks(player.getLocation) + }) + } else Applicative[F].unit + } - val titleMessage = s"【Lv${oldLevel.level}→Lv${newLevel.level}】" - val subtitleMessage = s"${GOLD}ムムッwwwwwwwレベルアップwwwwwww" + override def ofSeichiStarLevelTo(player: Player)(diff: Diff[SeichiStarLevel]): F[Unit] = { + val Diff(oldStars, newStars) = diff + val titleMessage = { + if (oldStars == SeichiStarLevel.zero) { + s"【Lv199→Lv200(☆${newStars.level})】" + } else { + s"【Lv200(☆${oldStars.level})→Lv200(☆${newStars.level})】" + } + } + val subTitleMessage = s"$GOLD★☆★ムムッwwwwwwwレベルアップwwwwwww★☆★" - if (oldLevel < newLevel) { - OnMinecraftServerThread[F].runAction(SyncIO { - player.sendTitle(titleMessage, subtitleMessage, 1, 20 * 5, 1) - player.sendMessage(s"$subtitleMessage$titleMessage") + if (oldStars < newStars) Sync[F].delay { + player.sendTitle(titleMessage, subTitleMessage, 1, 20 * 5, 1) + player.sendMessage(s"$subTitleMessage$titleMessage") Util.launchFireWorks(player.getLocation) - }) - } else Applicative[F].unit - } - - override def ofSeichiStarLevelTo(player: Player)(diff: Diff[SeichiStarLevel]): F[Unit] = { - val Diff(oldStars, newStars) = diff - val titleMessage = { - if (oldStars == SeichiStarLevel.zero) { - s"【Lv199→Lv200(☆${newStars.level})】" - } else { - s"【Lv200(☆${oldStars.level})→Lv200(☆${newStars.level})】" } + else Applicative[F].unit } - val subTitleMessage = s"$GOLD★☆★ムムッwwwwwwwレベルアップwwwwwww★☆★" - - if (oldStars < newStars) Sync[F].delay { - player.sendTitle(titleMessage, subTitleMessage, 1, 20 * 5, 1) - player.sendMessage(s"$subTitleMessage$titleMessage") - Util.launchFireWorks(player.getLocation) - } - else Applicative[F].unit } - } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/System.scala index ed011dd155..fd0b6ca3ed 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/System.scala @@ -9,9 +9,15 @@ import com.github.unchama.fs2.workaround.fs3.Fs3Topic import com.github.unchama.generic.ContextCoercion import com.github.unchama.seichiassist.meta.subsystem.Subsystem import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountReadAPI -import com.github.unchama.seichiassist.subsystems.breakcountbar.application.{BreakCountBarVisibilityRepositoryDefinition, ExpBarSynchronizationRepositoryTemplate} +import com.github.unchama.seichiassist.subsystems.breakcountbar.application.{ + BreakCountBarVisibilityRepositoryDefinition, + ExpBarSynchronizationRepositoryTemplate +} import com.github.unchama.seichiassist.subsystems.breakcountbar.bukkit.CreateFreshBossBar -import com.github.unchama.seichiassist.subsystems.breakcountbar.domain.{BreakCountBarVisibility, BreakCountBarVisibilityPersistence} +import com.github.unchama.seichiassist.subsystems.breakcountbar.domain.{ + BreakCountBarVisibility, + BreakCountBarVisibilityPersistence +} import com.github.unchama.seichiassist.subsystems.breakcountbar.infrastructure.JdbcBreakCountBarVisibilityPersistence import io.chrisdavenport.log4cats.ErrorLogger import org.bukkit.entity.Player @@ -28,10 +34,9 @@ object System { private final val topicSubscriptionSize = 10 - def wired[ - G[_] : SyncEffect, - F[_] : ConcurrentEffect : ContextCoercion[G, *[_]] : ErrorLogger, - ](breakCountReadAPI: BreakCountReadAPI[F, G, Player]): F[System[F, G, Player]] = { + def wired[G[_]: SyncEffect, F[_]: ConcurrentEffect: ContextCoercion[G, *[_]]: ErrorLogger]( + breakCountReadAPI: BreakCountReadAPI[F, G, Player] + ): F[System[F, G, Player]] = { import com.github.unchama.minecraft.bukkit.algebra.BukkitPlayerHasUuid.instance val persistence: BreakCountBarVisibilityPersistence[G] = @@ -56,23 +61,29 @@ object System { expBarSynchronizationRepositoryHandles <- { ContextCoercion { BukkitRepositoryControls.createHandles( - RepositoryDefinition.Phased.TwoPhased( - ExpBarSynchronizationRepositoryTemplate - .initialization[G, F, Player](breakCountReadAPI, visibilityValues)(CreateFreshBossBar.in[G, F]), - ExpBarSynchronizationRepositoryTemplate.finalization[G, F, Player] - ) + RepositoryDefinition + .Phased + .TwoPhased( + ExpBarSynchronizationRepositoryTemplate.initialization[G, F, Player]( + breakCountReadAPI, + visibilityValues + )(CreateFreshBossBar.in[G, F]), + ExpBarSynchronizationRepositoryTemplate.finalization[G, F, Player] + ) ) } } } yield { new System[F, G, Player] { override val api: BreakCountBarAPI[G, Player] = new BreakCountBarAPI[G, Player] { - override val breakCountBarVisibility: KeyedDataRepository[Player, Ref[G, BreakCountBarVisibility]] = + override val breakCountBarVisibility + : KeyedDataRepository[Player, Ref[G, BreakCountBarVisibility]] = visibilityRepositoryHandles.repository } - override val managedRepositoryControls: Seq[BukkitRepositoryControls[F, _]] = Seq( - visibilityRepositoryHandles, expBarSynchronizationRepositoryHandles - ).map(_.coerceFinalizationContextTo[F]) + override val managedRepositoryControls: Seq[BukkitRepositoryControls[F, _]] = + Seq(visibilityRepositoryHandles, expBarSynchronizationRepositoryHandles).map( + _.coerceFinalizationContextTo[F] + ) } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/application/BreakCountBarManipulation.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/application/BreakCountBarManipulation.scala index d3623dd52c..ff9a62c97f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/application/BreakCountBarManipulation.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/application/BreakCountBarManipulation.scala @@ -3,7 +3,10 @@ package com.github.unchama.seichiassist.subsystems.breakcountbar.application import cats.Applicative import com.github.unchama.minecraft.objects.MinecraftBossBar import com.github.unchama.seichiassist.subsystems.breakcount.domain.SeichiAmountData -import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.{SeichiExpAmount, SeichiStarLevel} +import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.{ + SeichiExpAmount, + SeichiStarLevel +} import org.bukkit.ChatColor.{BOLD, GOLD} import java.text.DecimalFormat @@ -12,7 +15,10 @@ object BreakCountBarManipulation { import cats.implicits._ - def write[F[_] : Applicative](seichiAmountData: SeichiAmountData, bossBar: MinecraftBossBar[F]): F[Unit] = { + def write[F[_]: Applicative]( + seichiAmountData: SeichiAmountData, + bossBar: MinecraftBossBar[F] + ): F[Unit] = { val currentExp = seichiAmountData.expAmount val levelProgress = seichiAmountData.levelProgress val level = seichiAmountData.levelCorrespondingToExp @@ -30,14 +36,14 @@ object BreakCountBarManipulation { s"$GOLD$BOLD$levelText $breakAmountText" } else { val levelText = s"Lv ${level.level}" - val progressText = s"(総整地量: ${formatAmount(currentExp)} | 次のレベルまで: ${formatAmount(levelProgress.expAmountToNextLevel)})" + val progressText = + s"(総整地量: ${formatAmount(currentExp)} | 次のレベルまで: ${formatAmount(levelProgress.expAmountToNextLevel)})" s"$GOLD$BOLD$levelText $progressText" } - List( - bossBar.progress.write(levelProgress.progress), - bossBar.title.write(text) - ).sequence.as(()) + List(bossBar.progress.write(levelProgress.progress), bossBar.title.write(text)) + .sequence + .as(()) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/application/BreakCountBarVisibilityRepositoryDefinition.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/application/BreakCountBarVisibilityRepositoryDefinition.scala index 5fd1dd1dc4..46e18ea9a4 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/application/BreakCountBarVisibilityRepositoryDefinition.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/application/BreakCountBarVisibilityRepositoryDefinition.scala @@ -2,24 +2,33 @@ package com.github.unchama.seichiassist.subsystems.breakcountbar.application import cats.effect.concurrent.Ref import cats.effect.{ConcurrentEffect, Sync} -import com.github.unchama.datarepository.definitions.{RefDictBackedRepositoryDefinition, SignallingRepositoryDefinition} +import com.github.unchama.datarepository.definitions.{ + RefDictBackedRepositoryDefinition, + SignallingRepositoryDefinition +} import com.github.unchama.datarepository.template._ import com.github.unchama.generic.ContextCoercion import com.github.unchama.minecraft.algebra.HasUuid -import com.github.unchama.seichiassist.subsystems.breakcountbar.domain.{BreakCountBarVisibility, BreakCountBarVisibilityPersistence} +import com.github.unchama.seichiassist.subsystems.breakcountbar.domain.{ + BreakCountBarVisibility, + BreakCountBarVisibilityPersistence +} import fs2.Pipe import io.chrisdavenport.log4cats.ErrorLogger object BreakCountBarVisibilityRepositoryDefinition { - def withContext[ - G[_] : Sync, - F[_] : ConcurrentEffect : ContextCoercion[G, *[_]] : ErrorLogger, - Player: HasUuid, - ](persistence: BreakCountBarVisibilityPersistence[G], - publishChanges: Pipe[F, (Player, BreakCountBarVisibility), Unit]): RepositoryDefinition[G, Player, Ref[G, BreakCountBarVisibility]] = + def withContext[G[_]: Sync, F[_]: ConcurrentEffect: ContextCoercion[ + G, + *[_] + ]: ErrorLogger, Player: HasUuid]( + persistence: BreakCountBarVisibilityPersistence[G], + publishChanges: Pipe[F, (Player, BreakCountBarVisibility), Unit] + ): RepositoryDefinition[G, Player, Ref[G, BreakCountBarVisibility]] = SignallingRepositoryDefinition.withPublishSinkHidden(publishChanges) { - RefDictBackedRepositoryDefinition.usingUuidRefDict(persistence)(BreakCountBarVisibility.Shown) + RefDictBackedRepositoryDefinition.usingUuidRefDict(persistence)( + BreakCountBarVisibility.Shown + ) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/application/ExpBarSynchronizationRepositoryTemplate.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/application/ExpBarSynchronizationRepositoryTemplate.scala index bafa03ef2f..4d5e1b8ed9 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/application/ExpBarSynchronizationRepositoryTemplate.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/application/ExpBarSynchronizationRepositoryTemplate.scala @@ -15,18 +15,16 @@ import io.chrisdavenport.log4cats.ErrorLogger object ExpBarSynchronizationRepositoryTemplate { - type BossBarWithPlayer[F[_], P] = MinecraftBossBar[F] {type Player = P} + type BossBarWithPlayer[F[_], P] = MinecraftBossBar[F] { type Player = P } /** * レポジトリが保持する値の型。 * - * 一つ目の成分にプレーヤーが持つ整地量ボスバー、 - * 二つ目の成分にボスバーを可視設定と同期するためのファイバーへの参照を持つ。 + * 一つ目の成分にプレーヤーが持つ整地量ボスバー、 二つ目の成分にボスバーを可視設定と同期するためのファイバーへの参照を持つ。 * - * ファイバーへの参照は、プレーヤーがサーバーに参加しているほとんどのタイミングにおいて - * すでにcompleteされていることが期待される。 - * このようなデザインになっているのは、[[F]] とは異なる文脈でレポジトリのデータを初期化する必要があり、 - * `Fiber[F, Unit]` が `G` のコンテキストで入手できない可能性があるからである。 + * ファイバーへの参照は、プレーヤーがサーバーに参加しているほとんどのタイミングにおいて すでにcompleteされていることが期待される。 + * このようなデザインになっているのは、[[F]] とは異なる文脈でレポジトリのデータを初期化する必要があり、 `Fiber[F, Unit]` が `G` + * のコンテキストで入手できない可能性があるからである。 */ type RepositoryValueType[F[_], P] = (BossBarWithPlayer[F, P], Deferred[F, Fiber[F, Unit]]) @@ -34,22 +32,28 @@ object ExpBarSynchronizationRepositoryTemplate { import cats.effect.implicits._ import cats.implicits._ - def initialization[ - G[_] : Sync, - F[_] : ConcurrentEffect : ContextCoercion[G, *[_]] : ErrorLogger, - Player: HasUuid, - ](breakCountReadAPI: BreakCountReadAPI[F, G, Player], - visibilityValues: fs2.Stream[F, (Player, BreakCountBarVisibility)]) - (createFreshBossBar: G[BossBarWithPlayer[F, Player]]) - : TwoPhasedRepositoryInitialization[G, Player, RepositoryValueType[F, Player]] = + def initialization[G[_]: Sync, F[_]: ConcurrentEffect: ContextCoercion[ + G, + *[_] + ]: ErrorLogger, Player: HasUuid]( + breakCountReadAPI: BreakCountReadAPI[F, G, Player], + visibilityValues: fs2.Stream[F, (Player, BreakCountBarVisibility)] + )( + createFreshBossBar: G[BossBarWithPlayer[F, Player]] + ): TwoPhasedRepositoryInitialization[G, Player, RepositoryValueType[F, Player]] = TwoPhasedRepositoryInitialization.withoutPrefetching { player => for { bossBar <- createFreshBossBar - synchronization = fs2.Stream + synchronization = fs2 + .Stream .eval(breakCountReadAPI.seichiAmountDataRepository(player).read) .translate(ContextCoercion.asFunctionK[G, F]) - .append(breakCountReadAPI.seichiAmountUpdates.through(StreamExtra.valuesWithKeyOfSameUuidAs(player))) + .append( + breakCountReadAPI + .seichiAmountUpdates + .through(StreamExtra.valuesWithKeyOfSameUuidAs(player)) + ) .evalTap(BreakCountBarManipulation.write(_, bossBar)) switching = visibilityValues @@ -60,19 +64,19 @@ object ExpBarSynchronizationRepositoryTemplate { _ <- EffectExtra.runAsyncAndForget[F, G, Unit](bossBar.players.add(player)) _ <- EffectExtra.runAsyncAndForget[F, G, Unit] { - StreamExtra.compileToRestartingStream[F, Unit]("[ExpBarSynchronizationRepositoryTemplate]") { - switching.concurrently(synchronization) - }.start >>= fiberPromise.complete + StreamExtra + .compileToRestartingStream[F, Unit]("[ExpBarSynchronizationRepositoryTemplate]") { + switching.concurrently(synchronization) + } + .start >>= fiberPromise.complete } } yield (bossBar, fiberPromise) } - def finalization[ - G[_] : Sync, - F[_] : ConcurrentEffect : ContextCoercion[G, *[_]], - Player, - ]: RepositoryFinalization[G, Player, RepositoryValueType[F, Player]] = - RepositoryFinalization.withoutAnyPersistence { case (_, (_, fiberPromise)) => - EffectExtra.runAsyncAndForget[F, G, Unit](fiberPromise.get.flatMap(_.cancel)) + def finalization[G[_]: Sync, F[_]: ConcurrentEffect: ContextCoercion[G, *[_]], Player] + : RepositoryFinalization[G, Player, RepositoryValueType[F, Player]] = + RepositoryFinalization.withoutAnyPersistence { + case (_, (_, fiberPromise)) => + EffectExtra.runAsyncAndForget[F, G, Unit](fiberPromise.get.flatMap(_.cancel)) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/bukkit/CreateFreshBossBar.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/bukkit/CreateFreshBossBar.scala index 082cce3418..e36bd606cd 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/bukkit/CreateFreshBossBar.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/bukkit/CreateFreshBossBar.scala @@ -10,7 +10,7 @@ object CreateFreshBossBar { import cats.implicits._ - def in[G[_] : Sync, F[_] : Sync]: G[MinecraftBossBar[F] {type Player = BukkitPlayer}] = + def in[G[_]: Sync, F[_]: Sync]: G[MinecraftBossBar[F] { type Player = BukkitPlayer }] = BukkitBossBar.in[G, F]("", BarColor.YELLOW, BarStyle.SOLID).widen } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/infrastructure/JdbcBreakCountBarVisibilityPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/infrastructure/JdbcBreakCountBarVisibilityPersistence.scala index f6f2e6a0ab..5f12a59787 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/infrastructure/JdbcBreakCountBarVisibilityPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakcountbar/infrastructure/JdbcBreakCountBarVisibilityPersistence.scala @@ -1,13 +1,16 @@ package com.github.unchama.seichiassist.subsystems.breakcountbar.infrastructure import cats.effect.Sync -import com.github.unchama.seichiassist.subsystems.breakcountbar.domain.{BreakCountBarVisibility, BreakCountBarVisibilityPersistence} +import com.github.unchama.seichiassist.subsystems.breakcountbar.domain.{ + BreakCountBarVisibility, + BreakCountBarVisibilityPersistence +} import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} import java.util.UUID class JdbcBreakCountBarVisibilityPersistence[F[_]](implicit F: Sync[F]) - extends BreakCountBarVisibilityPersistence[F] { + extends BreakCountBarVisibilityPersistence[F] { override def read(key: UUID): F[Option[BreakCountBarVisibility]] = F.delay { @@ -20,7 +23,8 @@ class JdbcBreakCountBarVisibilityPersistence[F[_]](implicit F: Sync[F]) BreakCountBarVisibility.Hidden } } - .first().apply() + .first() + .apply() } } @@ -28,7 +32,8 @@ class JdbcBreakCountBarVisibilityPersistence[F[_]](implicit F: Sync[F]) F.delay { DB.localTx { implicit session => sql"update playerdata set expvisible = ${value == BreakCountBarVisibility.Shown} where uuid = ${key.toString}" - .update().apply() + .update() + .apply() } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/BuildCountAPI.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/BuildCountAPI.scala index 7d10e99321..7ab6b25c9d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/BuildCountAPI.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/BuildCountAPI.scala @@ -2,15 +2,24 @@ package com.github.unchama.seichiassist.subsystems.buildcount import com.github.unchama.datarepository.KeyedDataRepository import com.github.unchama.generic.effect.concurrent.ReadOnlyRef -import com.github.unchama.seichiassist.subsystems.buildcount.application.actions.{IncrementBuildExpWhenBuiltByHand, IncrementBuildExpWhenBuiltWithSkill} +import com.github.unchama.seichiassist.subsystems.buildcount.application.actions.{ + IncrementBuildExpWhenBuiltByHand, + IncrementBuildExpWhenBuiltWithSkill +} import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountData trait BuildCountAPI[F[_], Player] { implicit val incrementBuildExpWhenBuiltByHand: IncrementBuildExpWhenBuiltByHand[F, Player] - implicit val incrementBuildExpWhenBuiltWithSkill: IncrementBuildExpWhenBuiltWithSkill[F, Player] + implicit val incrementBuildExpWhenBuiltWithSkill: IncrementBuildExpWhenBuiltWithSkill[ + F, + Player + ] - implicit val playerBuildAmountRepository: KeyedDataRepository[Player, ReadOnlyRef[F, BuildAmountData]] + implicit val playerBuildAmountRepository: KeyedDataRepository[ + Player, + ReadOnlyRef[F, BuildAmountData] + ] } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/System.scala index 13d3919d02..5645f1667c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/System.scala @@ -9,14 +9,27 @@ import com.github.unchama.generic.ContextCoercion import com.github.unchama.generic.effect.concurrent.ReadOnlyRef import com.github.unchama.generic.ratelimiting.RateLimiter import com.github.unchama.seichiassist.meta.subsystem.Subsystem -import com.github.unchama.seichiassist.subsystems.buildcount.application.actions.{ClassifyPlayerWorld, IncrementBuildExpWhenBuiltByHand, IncrementBuildExpWhenBuiltWithSkill} -import com.github.unchama.seichiassist.subsystems.buildcount.application.application.{BuildAmountDataRepositoryDefinition, RateLimiterRepositoryDefinitions} -import com.github.unchama.seichiassist.subsystems.buildcount.application.{BuildExpMultiplier, Configuration} +import com.github.unchama.seichiassist.subsystems.buildcount.application.actions.{ + ClassifyPlayerWorld, + IncrementBuildExpWhenBuiltByHand, + IncrementBuildExpWhenBuiltWithSkill +} +import com.github.unchama.seichiassist.subsystems.buildcount.application.application.{ + BuildAmountDataRepositoryDefinition, + RateLimiterRepositoryDefinitions +} +import com.github.unchama.seichiassist.subsystems.buildcount.application.{ + BuildExpMultiplier, + Configuration +} import com.github.unchama.seichiassist.subsystems.buildcount.bukkit.actions.ClassifyBukkitPlayerWorld import com.github.unchama.seichiassist.subsystems.buildcount.bukkit.listeners.BuildExpIncrementer import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountData -import com.github.unchama.seichiassist.subsystems.buildcount.infrastructure.{JdbcBuildAmountDataPersistence, JdbcBuildAmountRateLimitPersistence} +import com.github.unchama.seichiassist.subsystems.buildcount.infrastructure.{ + JdbcBuildAmountDataPersistence, + JdbcBuildAmountRateLimitPersistence +} import com.github.unchama.util.logging.log4cats.PrefixedLogger import io.chrisdavenport.cats.effect.time.JavaTime import io.chrisdavenport.log4cats.Logger @@ -35,26 +48,31 @@ object System { import cats.implicits._ - def wired[ - F[_] : ConcurrentEffect : NonServerThreadContextShift, - G[_] : SyncEffect : ContextCoercion[*[_], F] : Clock - ](rootLogger: Logger[F]) - (implicit configuration: Configuration): G[System[F, G]] = { + def wired[F[_]: ConcurrentEffect: NonServerThreadContextShift, G[ + _ + ]: SyncEffect: ContextCoercion[*[_], F]: Clock]( + rootLogger: Logger[F] + )(implicit configuration: Configuration): G[System[F, G]] = { import com.github.unchama.minecraft.bukkit.actions.SendBukkitMessage._ implicit val expMultiplier: BuildExpMultiplier = configuration.multipliers - implicit val persistence: JdbcBuildAmountDataPersistence[G] = new JdbcBuildAmountDataPersistence[G]() - implicit val rateLimitPersistence: JdbcBuildAmountRateLimitPersistence[G] = new JdbcBuildAmountRateLimitPersistence[G]() + implicit val persistence: JdbcBuildAmountDataPersistence[G] = + new JdbcBuildAmountDataPersistence[G]() + implicit val rateLimitPersistence: JdbcBuildAmountRateLimitPersistence[G] = + new JdbcBuildAmountRateLimitPersistence[G]() implicit val logger: Logger[F] = PrefixedLogger[F]("BuildAssist-BuildAmount")(rootLogger) implicit val javaTimeG: JavaTime[G] = JavaTime.fromClock for { rateLimiterRepositoryControls <- BukkitRepositoryControls.createHandles( - RepositoryDefinition.Phased.SinglePhased.withoutTappingAction[G, Player, RateLimiter[G, BuildExpAmount]]( - RateLimiterRepositoryDefinitions.initialization[G], - RateLimiterRepositoryDefinitions.finalization[G, UUID] - ) + RepositoryDefinition + .Phased + .SinglePhased + .withoutTappingAction[G, Player, RateLimiter[G, BuildExpAmount]]( + RateLimiterRepositoryDefinitions.initialization[G], + RateLimiterRepositoryDefinitions.finalization[G, UUID] + ) ) buildAmountDataRepositoryControls <- @@ -69,27 +87,29 @@ object System { rateLimiterRepositoryControls.repository, buildAmountDataRepositoryControls.repository ) - implicit val incrementBuildExpWhenBuiltBySkills: IncrementBuildExpWhenBuiltWithSkill[G, Player] = + implicit val incrementBuildExpWhenBuiltBySkills + : IncrementBuildExpWhenBuiltWithSkill[G, Player] = IncrementBuildExpWhenBuiltWithSkill.withConfig(expMultiplier) new System[F, G] { override val api: BuildCountAPI[G, Player] = new BuildCountAPI[G, Player] { - override val incrementBuildExpWhenBuiltByHand: IncrementBuildExpWhenBuiltByHand[G, Player] = + override val incrementBuildExpWhenBuiltByHand + : IncrementBuildExpWhenBuiltByHand[G, Player] = incrementBuildExp - override val incrementBuildExpWhenBuiltWithSkill: IncrementBuildExpWhenBuiltWithSkill[G, Player] = + override val incrementBuildExpWhenBuiltWithSkill + : IncrementBuildExpWhenBuiltWithSkill[G, Player] = incrementBuildExpWhenBuiltBySkills - override val playerBuildAmountRepository: KeyedDataRepository[Player, ReadOnlyRef[G, BuildAmountData]] = + override val playerBuildAmountRepository + : KeyedDataRepository[Player, ReadOnlyRef[G, BuildAmountData]] = buildAmountDataRepositoryControls.repository.map(ref => ReadOnlyRef.fromRef(ref)) } - override val managedRepositoryControls: Seq[BukkitRepositoryControls[F, _]] = List( - rateLimiterRepositoryControls, - buildAmountDataRepositoryControls - ).map(_.coerceFinalizationContextTo[F]) + override val managedRepositoryControls: Seq[BukkitRepositoryControls[F, _]] = + List(rateLimiterRepositoryControls, buildAmountDataRepositoryControls).map( + _.coerceFinalizationContextTo[F] + ) - override val listeners: Seq[Listener] = List( - new BuildExpIncrementer[G], - ) + override val listeners: Seq[Listener] = List(new BuildExpIncrementer[G]) } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/ClassifyPlayerWorld.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/ClassifyPlayerWorld.scala index 044f9d9831..c74e8b5c52 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/ClassifyPlayerWorld.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/ClassifyPlayerWorld.scala @@ -16,6 +16,8 @@ trait ClassifyPlayerWorld[F[_], Player] { object ClassifyPlayerWorld { - def apply[F[_], Player](implicit ev: ClassifyPlayerWorld[F, Player]): ClassifyPlayerWorld[F, Player] = ev + def apply[F[_], Player]( + implicit ev: ClassifyPlayerWorld[F, Player] + ): ClassifyPlayerWorld[F, Player] = ev } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/IncrementBuildExpWhenBuiltByHand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/IncrementBuildExpWhenBuiltByHand.scala index ff2aea5b5d..71ac44071d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/IncrementBuildExpWhenBuiltByHand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/IncrementBuildExpWhenBuiltByHand.scala @@ -25,19 +25,17 @@ object IncrementBuildExpWhenBuiltByHand { import cats.implicits._ - def apply[ - F[_], Player - ](implicit ev: IncrementBuildExpWhenBuiltByHand[F, Player]): IncrementBuildExpWhenBuiltByHand[F, Player] = ev - - def using[ - F[_] - : Monad - : ClassifyPlayerWorld[*[_], Player] - : SendMinecraftMessage[*[_], Player], + def apply[F[_], Player]( + implicit ev: IncrementBuildExpWhenBuiltByHand[F, Player] + ): IncrementBuildExpWhenBuiltByHand[F, Player] = ev + + def using[F[_]: Monad: ClassifyPlayerWorld[*[_], Player]: SendMinecraftMessage[ + *[_], Player - ](rateLimiterRepository: KeyedDataRepository[Player, RateLimiter[F, BuildExpAmount]], - dataRepository: KeyedDataRepository[Player, Ref[F, BuildAmountData]]) - (implicit multiplier: BuildExpMultiplier): IncrementBuildExpWhenBuiltByHand[F, Player] = + ], Player]( + rateLimiterRepository: KeyedDataRepository[Player, RateLimiter[F, BuildExpAmount]], + dataRepository: KeyedDataRepository[Player, Ref[F, BuildAmountData]] + )(implicit multiplier: BuildExpMultiplier): IncrementBuildExpWhenBuiltByHand[F, Player] = (player: Player, by: BuildExpAmount) => { val F: Monad[F] = Monad[F] @@ -52,7 +50,9 @@ object IncrementBuildExpWhenBuiltByHand { ) amountToIncrement <- rateLimiterRepository(player).requestPermission(amountToRequestIncrement) - dataPair <- RefExtra.getAndUpdateAndGet(dataRepository(player))(_.modifyExpAmount(_.add(amountToIncrement))) + dataPair <- RefExtra.getAndUpdateAndGet(dataRepository(player))( + _.modifyExpAmount(_.add(amountToIncrement)) + ) _ <- Diff .ofPairBy(dataPair)(_.levelCorrespondingToExp) .traverse(LevelUpNotifier[F, Player].notifyTo(player)) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/IncrementBuildExpWhenBuiltWithSkill.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/IncrementBuildExpWhenBuiltWithSkill.scala index bcc4e5a724..009f60c833 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/IncrementBuildExpWhenBuiltWithSkill.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/IncrementBuildExpWhenBuiltWithSkill.scala @@ -13,14 +13,12 @@ trait IncrementBuildExpWhenBuiltWithSkill[F[_], Player] { object IncrementBuildExpWhenBuiltWithSkill { - def apply[ - F[_] : IncrementBuildExpWhenBuiltWithSkill[*[_], Player], - Player - ]: IncrementBuildExpWhenBuiltWithSkill[F, Player] = implicitly - - def withConfig[ - F[_] : IncrementBuildExpWhenBuiltByHand[*[_], Player], Player - ](config: BuildExpMultiplier): IncrementBuildExpWhenBuiltWithSkill[F, Player] = + def apply[F[_]: IncrementBuildExpWhenBuiltWithSkill[*[_], Player], Player] + : IncrementBuildExpWhenBuiltWithSkill[F, Player] = implicitly + + def withConfig[F[_]: IncrementBuildExpWhenBuiltByHand[*[_], Player], Player]( + config: BuildExpMultiplier + ): IncrementBuildExpWhenBuiltWithSkill[F, Player] = (player: Player, by: BuildExpAmount) => IncrementBuildExpWhenBuiltByHand[F, Player] .of(player, by.mapAmount(_ * config.withBuildSkills)) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/LevelUpNotifier.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/LevelUpNotifier.scala index 66d0089913..067d7637da 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/LevelUpNotifier.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/actions/LevelUpNotifier.scala @@ -3,7 +3,10 @@ package com.github.unchama.seichiassist.subsystems.buildcount.application.action import cats.{Applicative, ~>} import com.github.unchama.generic.Diff import com.github.unchama.minecraft.actions.SendMinecraftMessage -import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.{BuildAssistExpTable, BuildLevel} +import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.{ + BuildAssistExpTable, + BuildLevel +} import org.bukkit.ChatColor.GOLD /** @@ -11,7 +14,10 @@ import org.bukkit.ChatColor.GOLD * * TODO Tagless algebraにするべきぽい */ -case class LevelUpNotifier[F[_], Player]()(implicit F: Applicative[F], send: SendMinecraftMessage[F, Player]) { +case class LevelUpNotifier[F[_], Player]()( + implicit F: Applicative[F], + send: SendMinecraftMessage[F, Player] +) { def notifyTo(player: Player)(diff: Diff[BuildLevel]): F[Unit] = { import cats.implicits._ @@ -28,7 +34,7 @@ case class LevelUpNotifier[F[_], Player]()(implicit F: Applicative[F], send: Sen Applicative[F].unit } - def mapK[G[_] : Applicative](fg: F ~> G): LevelUpNotifier[G, Player] = { + def mapK[G[_]: Applicative](fg: F ~> G): LevelUpNotifier[G, Player] = { implicit val e: SendMinecraftMessage[G, Player] = send.mapK(fg) new LevelUpNotifier[G, Player]() diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/BuildAmountDataRepositoryDefinition.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/BuildAmountDataRepositoryDefinition.scala index fd92bd3601..2bd58b974a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/BuildAmountDataRepositoryDefinition.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/BuildAmountDataRepositoryDefinition.scala @@ -4,13 +4,16 @@ import cats.effect.Sync import cats.effect.concurrent.Ref import com.github.unchama.datarepository.definitions.RefDictBackedRepositoryDefinition import com.github.unchama.datarepository.template.RepositoryDefinition -import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.{BuildAmountData, BuildAmountDataPersistence} +import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.{ + BuildAmountData, + BuildAmountDataPersistence +} object BuildAmountDataRepositoryDefinition { - def withPersistence[ - F[_] : Sync, Player - ](persistence: BuildAmountDataPersistence[F]): RepositoryDefinition[F, Player, Ref[F, BuildAmountData]] = + def withPersistence[F[_]: Sync, Player]( + persistence: BuildAmountDataPersistence[F] + ): RepositoryDefinition[F, Player, Ref[F, BuildAmountData]] = RefDictBackedRepositoryDefinition .usingUuidRefDict(persistence)(BuildAmountData.initial) .toRefRepository diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala index 2f775814a5..a6c5b3311c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/application/application/RateLimiterRepositoryDefinitions.scala @@ -20,27 +20,23 @@ object RateLimiterRepositoryDefinitions { import scala.concurrent.duration._ - def initialization[ - G[_] : Sync: JavaTime : Clock - ]( - implicit config: Configuration, - persistence: BuildAmountRateLimitPersistence[G] - ): SinglePhasedRepositoryInitialization[G, RateLimiter[G, BuildExpAmount]] = { + def initialization[G[_]: Sync: JavaTime: Clock]( + implicit config: Configuration, + persistence: BuildAmountRateLimitPersistence[G] + ): SinglePhasedRepositoryInitialization[G, RateLimiter[G, BuildExpAmount]] = { val max = config.oneMinuteBuildExpLimit val span = 1.minute RefDictBackedRepositoryDefinition .usingUuidRefDictWithoutDefault(persistence) .initialization - .extendPreparation { (_, _) => - loadedRecordOpt => { + .extendPreparation { (_, _) => loadedRecordOpt => + { for { currentLocalTime <- JavaTime[G].getLocalDateTime(ZoneId.systemDefault()) initialPermitCount = loadedRecordOpt.fold(max) { loadedRecord => val duration = FiniteDuration( - java.time.Duration - .between(loadedRecord.recordTime, currentLocalTime) - .toNanos, + java.time.Duration.between(loadedRecord.recordTime, currentLocalTime).toNanos, TimeUnit.NANOSECONDS ) // NOTE: これはファイナライゼーションされたときのレートリミッターと @@ -55,21 +51,23 @@ object RateLimiterRepositoryDefinitions { loadedRecord.amount } } - rateLimiter <- FixedWindowRateLimiter.in[G, BuildExpAmount](max, span, Some(initialPermitCount)) + rateLimiter <- FixedWindowRateLimiter + .in[G, BuildExpAmount](max, span, Some(initialPermitCount)) } yield rateLimiter } } } - def finalization[ - F[_] : Sync : JavaTime, - Player: HasUuid - ](implicit config: Configuration, persistence: BuildAmountRateLimitPersistence[F]): RepositoryFinalization[F, Player, RateLimiter[F, BuildExpAmount]] = - RepositoryFinalization.withoutAnyFinalization { case (p, rateLimiter) => - for { - currentRecord <- rateLimiter.peekAvailablePermissions - persistenceRecord <- BuildAmountRateLimiterSnapshot.now(currentRecord) - _ <- persistence.write(HasUuid[Player].of(p), persistenceRecord) - } yield () + def finalization[F[_]: Sync: JavaTime, Player: HasUuid]( + implicit config: Configuration, + persistence: BuildAmountRateLimitPersistence[F] + ): RepositoryFinalization[F, Player, RateLimiter[F, BuildExpAmount]] = + RepositoryFinalization.withoutAnyFinalization { + case (p, rateLimiter) => + for { + currentRecord <- rateLimiter.peekAvailablePermissions + persistenceRecord <- BuildAmountRateLimiterSnapshot.now(currentRecord) + _ <- persistence.write(HasUuid[Player].of(p), persistenceRecord) + } yield () } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/bukkit/actions/ClassifyBukkitPlayerWorld.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/bukkit/actions/ClassifyBukkitPlayerWorld.scala index 602f7b649a..92e25369e2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/bukkit/actions/ClassifyBukkitPlayerWorld.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/bukkit/actions/ClassifyBukkitPlayerWorld.scala @@ -4,7 +4,7 @@ import cats.effect.Sync import com.github.unchama.seichiassist.subsystems.buildcount.application.actions.ClassifyPlayerWorld import org.bukkit.entity.Player -class ClassifyBukkitPlayerWorld[F[_] : Sync] extends ClassifyPlayerWorld[F, Player] { +class ClassifyBukkitPlayerWorld[F[_]: Sync] extends ClassifyPlayerWorld[F, Player] { import com.github.unchama.seichiassist.ManagedWorld._ diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/bukkit/listeners/BuildExpIncrementer.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/bukkit/listeners/BuildExpIncrementer.scala index 08ee6a7a9f..ed93eba06c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/bukkit/listeners/BuildExpIncrementer.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/bukkit/listeners/BuildExpIncrementer.scala @@ -9,11 +9,8 @@ import org.bukkit.event.{EventHandler, Listener} /** * Created by karayuu on 2020/10/07 */ -class BuildExpIncrementer[ - F[_] - : IncrementBuildExpWhenBuiltByHand[*[_], Player] - : SyncEffect -] extends Listener { +class BuildExpIncrementer[F[_]: IncrementBuildExpWhenBuiltByHand[*[_], Player]: SyncEffect] + extends Listener { import cats.effect.implicits._ @@ -22,7 +19,8 @@ class BuildExpIncrementer[ if (event.getBlockPlaced.getType.isSolid) { IncrementBuildExpWhenBuiltByHand[F, Player] .of(event.getPlayer) - .runSync[SyncIO].unsafeRunSync() + .runSync[SyncIO] + .unsafeRunSync() } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala index 3d68683f75..59e92d8bf7 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/BuildAmountRateLimiterSnapshot.scala @@ -9,14 +9,19 @@ import java.time.{LocalDateTime, ZoneId} /** * `RateLimiter[F, BuildExpAmount]`によって保持された残量について日時付きで保存するクラス - * @param amount そのタイムスライスにおけるリクエスト量の上限 - * @param recordTime 取得した時間 + * @param amount + * そのタイムスライスにおけるリクエスト量の上限 + * @param recordTime + * 取得した時間 */ case class BuildAmountRateLimiterSnapshot(amount: BuildExpAmount, recordTime: LocalDateTime) object BuildAmountRateLimiterSnapshot { - def now[F[_]: JavaTime: Functor](buildExpAmount: BuildExpAmount): F[BuildAmountRateLimiterSnapshot] = { - JavaTime[F].getLocalDateTime(ZoneId.systemDefault()) + def now[F[_]: JavaTime: Functor]( + buildExpAmount: BuildExpAmount + ): F[BuildAmountRateLimiterSnapshot] = { + JavaTime[F] + .getLocalDateTime(ZoneId.systemDefault()) .map(ldt => BuildAmountRateLimiterSnapshot(buildExpAmount, ldt)) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/explevel/BuildAssistExpTable.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/explevel/BuildAssistExpTable.scala index 4df7d7513c..39c6b282cb 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/explevel/BuildAssistExpTable.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/explevel/BuildAssistExpTable.scala @@ -4,28 +4,19 @@ import com.github.unchama.seichiassist.domain.explevel.FiniteExpLevelTable private object Constant { // 経験値テーブルの生の値 - val internalTable: Vector[BuildExpAmount] = Vector( - 0, 50, 100, 200, 300, - 450, 600, 900, 1200, 1600, //10 - 2000, 2500, 3000, 3600, 4300, - 5100, 6000, 7000, 8200, 9400, //20 - 10800, 12200, 13800, 15400, 17200, - 19000, 21000, 23000, 25250, 27500, //30 - 30000, 32500, 35500, 38500, 42000, - 45500, 49500, 54000, 59000, 64000, //40 - 70000, 76000, 83000, 90000, 98000, - 106000, 115000, 124000, 133000, 143000, //50 - 153000, 163000, 174000, 185000, 196000, - 208000, 220000, 232000, 245000, 258000, //60 - 271000, 285000, 299000, 313000, 328000, - 343000, 358000, 374000, 390000, 406000, //70 - 423000, 440000, 457000, 475000, 493000, - 511000, 530000, 549000, 568000, 588000, //80 - 608000, 628000, 648000, 668000, 688000, - 708000, 728000, 748000, 768000, 788000, //90 - 808000, 828000, 848000, 868000, 888000, - 908000, 928000, 948000, 968000, 1000000, //100 - ).map(BuildExpAmount.ofNonNegative) + val internalTable: Vector[BuildExpAmount] = + Vector(0, 50, 100, 200, 300, 450, 600, 900, 1200, 1600, // 10 + 2000, 2500, 3000, 3600, 4300, 5100, 6000, 7000, 8200, 9400, // 20 + 10800, 12200, 13800, 15400, 17200, 19000, 21000, 23000, 25250, 27500, // 30 + 30000, 32500, 35500, 38500, 42000, 45500, 49500, 54000, 59000, 64000, // 40 + 70000, 76000, 83000, 90000, 98000, 106000, 115000, 124000, 133000, 143000, // 50 + 153000, 163000, 174000, 185000, 196000, 208000, 220000, 232000, 245000, 258000, // 60 + 271000, 285000, 299000, 313000, 328000, 343000, 358000, 374000, 390000, 406000, // 70 + 423000, 440000, 457000, 475000, 493000, 511000, 530000, 549000, 568000, 588000, // 80 + 608000, 628000, 648000, 668000, 688000, 708000, 728000, 748000, 768000, 788000, // 90 + 808000, 828000, 848000, 868000, 888000, 908000, 928000, 948000, 968000, 1000000 // 100 + ).map(BuildExpAmount.ofNonNegative) } -object BuildAssistExpTable extends FiniteExpLevelTable[BuildLevel, BuildExpAmount](Constant.internalTable) +object BuildAssistExpTable + extends FiniteExpLevelTable[BuildLevel, BuildExpAmount](Constant.internalTable) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/explevel/BuildExpAmount.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/explevel/BuildExpAmount.scala index 7979c70854..63191804ad 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/explevel/BuildExpAmount.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/explevel/BuildExpAmount.scala @@ -4,13 +4,15 @@ import cats.Order import cats.kernel.{LowerBounded, PartialOrder} import com.github.unchama.generic.algebra.typeclasses.OrderedMonus -case class BuildExpAmount private(amount: BigDecimal) extends AnyVal { +case class BuildExpAmount private (amount: BigDecimal) extends AnyVal { - def mapAmount(f: BigDecimal => BigDecimal): BuildExpAmount = BuildExpAmount.ofNonNegative(f(amount)) + def mapAmount(f: BigDecimal => BigDecimal): BuildExpAmount = + BuildExpAmount.ofNonNegative(f(amount)) def add(a: BuildExpAmount): BuildExpAmount = mapAmount(_ + a.amount) - def toPlainString: String = amount.setScale(1, BigDecimal.RoundingMode.HALF_UP).bigDecimal.toPlainString + def toPlainString: String = + amount.setScale(1, BigDecimal.RoundingMode.HALF_UP).bigDecimal.toPlainString } @@ -21,10 +23,11 @@ private[explevel] abstract class BuildExpAmountInstances { lazy val zero: BuildExpAmount = BuildExpAmount.ofNonNegative(0) implicit lazy val order: Order[BuildExpAmount] = Order.by(_.amount) - implicit lazy val lowerBounded: LowerBounded[BuildExpAmount] = new LowerBounded[BuildExpAmount] { - override val partialOrder: PartialOrder[BuildExpAmount] = order - override val minBound: BuildExpAmount = zero - } + implicit lazy val lowerBounded: LowerBounded[BuildExpAmount] = + new LowerBounded[BuildExpAmount] { + override val partialOrder: PartialOrder[BuildExpAmount] = order + override val minBound: BuildExpAmount = zero + } implicit lazy val orderedMonus: OrderedMonus[BuildExpAmount] = { new OrderedMonus[BuildExpAmount] { @@ -36,24 +39,21 @@ private[explevel] abstract class BuildExpAmountInstances { override def combine(x: BuildExpAmount, y: BuildExpAmount): BuildExpAmount = BuildExpAmount.ofNonNegative(x.amount + y.amount) - override def |-|(x: BuildExpAmount, y: BuildExpAmount): BuildExpAmount = BuildExpAmount.ofNonNegative { - (x.amount - y.amount) max BigDecimal(0) - } + override def |-|(x: BuildExpAmount, y: BuildExpAmount): BuildExpAmount = + BuildExpAmount.ofNonNegative { + (x.amount - y.amount) max BigDecimal(0) + } } } } object BuildExpAmount extends BuildExpAmountInstances { def ofNonNegative(amount: BigDecimal): BuildExpAmount = { - require( - amount >= BigDecimal(0), - "建築経験値量は非負である必要があります。" - ) + require(amount >= BigDecimal(0), "建築経験値量は非負である必要があります。") BuildExpAmount(amount) } def ofNonNegative(amount: Int): BuildExpAmount = ofNonNegative(BigDecimal(amount)) - private def apply(amount: Int): BuildExpAmount = BuildExpAmount(BigDecimal(amount)) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/explevel/BuildLevel.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/explevel/BuildLevel.scala index 5a7413367c..53c4fbb97f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/explevel/BuildLevel.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/explevel/BuildLevel.scala @@ -3,7 +3,7 @@ package com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel import cats.{Eq, Order} import com.github.unchama.generic.algebra.typeclasses.PositiveInt -case class BuildLevel private(level: Int) extends AnyVal { +case class BuildLevel private (level: Int) extends AnyVal { def incremented: BuildLevel = BuildLevel(level + 1) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/explevel/BuildLevelProgress.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/explevel/BuildLevelProgress.scala index 4e1f84e8e6..d13150664e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/explevel/BuildLevelProgress.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/explevel/BuildLevelProgress.scala @@ -3,10 +3,12 @@ package com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel /** * レベルの進行度を表す値。 * - * @param progress 現在のレベルから次のレベルまでへの進行度。0以上1未満の[[Double]]値である。 - * @param expAmountToNextLevel 次のレベルに到達するのに必要な整地経験値量 + * @param progress + * 現在のレベルから次のレベルまでへの進行度。0以上1未満の[[Double]]値である。 + * @param expAmountToNextLevel + * 次のレベルに到達するのに必要な整地経験値量 */ -case class BuildLevelProgress private(progress: Double, expAmountToNextLevel: BuildExpAmount) { +case class BuildLevelProgress private (progress: Double, expAmountToNextLevel: BuildExpAmount) { require(0.0 <= progress && progress < 1.0, "レベル進捗は[0.0, 1.0)の要素である必要があります") } @@ -15,10 +17,12 @@ object BuildLevelProgress { import com.github.unchama.generic.algebra.typeclasses.OrderedMonus._ /** - * 必須経験値量と達成済み経験値量から [[BuildLevelProgress]] を得る。 - * 必要経験値量は達成済み経験値量未満である必要がある。 + * 必須経験値量と達成済み経験値量から [[BuildLevelProgress]] を得る。 必要経験値量は達成済み経験値量未満である必要がある。 */ - def fromRequiredAndAchievedPair(required: BuildExpAmount, achieved: BuildExpAmount): BuildLevelProgress = { + def fromRequiredAndAchievedPair( + required: BuildExpAmount, + achieved: BuildExpAmount + ): BuildLevelProgress = { BuildLevelProgress( achieved.amount.toDouble / required.amount.toDouble, required |-| achieved diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountData.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountData.scala index fc55002e78..01047d211e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountData.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountData.scala @@ -2,7 +2,12 @@ package com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata import cats.Order import cats.kernel.Monoid -import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.{BuildAssistExpTable, BuildExpAmount, BuildLevel, BuildLevelProgress} +import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.{ + BuildAssistExpTable, + BuildExpAmount, + BuildLevel, + BuildLevelProgress +} /** * BuildAssistが管理する建築量データ。 @@ -23,7 +28,10 @@ case class BuildAmountData(expAmount: BuildExpAmount) { val (nextThreshold, previousThreshold) = { val nextLevel = levelCorrespondingToExp.incremented - (BuildAssistExpTable.expAt(nextLevel), BuildAssistExpTable.expAt(levelCorrespondingToExp)) + ( + BuildAssistExpTable.expAt(nextLevel), + BuildAssistExpTable.expAt(levelCorrespondingToExp) + ) } val required = nextThreshold |-| previousThreshold @@ -33,7 +41,8 @@ case class BuildAmountData(expAmount: BuildExpAmount) { } } - def modifyExpAmount(f: BuildExpAmount => BuildExpAmount): BuildAmountData = copy(expAmount = f(expAmount)) + def modifyExpAmount(f: BuildExpAmount => BuildExpAmount): BuildAmountData = + copy(expAmount = f(expAmount)) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountRateLimitPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountRateLimitPersistence.scala index 87a2a1c57b..4a876cb0dd 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountRateLimitPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/domain/playerdata/BuildAmountRateLimitPersistence.scala @@ -5,4 +5,5 @@ import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountR import java.util.UUID -trait BuildAmountRateLimitPersistence[F[_]] extends RefDict[F, UUID, BuildAmountRateLimiterSnapshot] +trait BuildAmountRateLimitPersistence[F[_]] + extends RefDict[F, UUID, BuildAmountRateLimiterSnapshot] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountDataPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountDataPersistence.scala index 8d0fa29130..bb8a1d12b6 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountDataPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountDataPersistence.scala @@ -2,13 +2,16 @@ package com.github.unchama.seichiassist.subsystems.buildcount.infrastructure import cats.effect.Sync import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount -import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.{BuildAmountData, BuildAmountDataPersistence} +import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.{ + BuildAmountData, + BuildAmountDataPersistence +} import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} import java.util.UUID class JdbcBuildAmountDataPersistence[F[_]](implicit F: Sync[F]) - extends BuildAmountDataPersistence[F] { + extends BuildAmountDataPersistence[F] { override def read(key: UUID): F[Option[BuildAmountData]] = F.delay { @@ -20,7 +23,8 @@ class JdbcBuildAmountDataPersistence[F[_]](implicit F: Sync[F]) BuildAmountData(exp) } - .first().apply() + .first() + .apply() } } @@ -29,7 +33,8 @@ class JdbcBuildAmountDataPersistence[F[_]](implicit F: Sync[F]) DB.localTx { implicit session => sql"update playerdata set build_count = ${value.expAmount.amount} where uuid = ${key.toString}" .stripMargin - .update().apply() + .update() + .apply() } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala index 236e7ae53f..3f7f1d8046 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/infrastructure/JdbcBuildAmountRateLimitPersistence.scala @@ -9,8 +9,10 @@ import scalikejdbc._ import java.util.UUID -class JdbcBuildAmountRateLimitPersistence[SyncContext[_]](implicit SyncContext: Sync[SyncContext], config: Configuration) - extends BuildAmountRateLimitPersistence[SyncContext] { +class JdbcBuildAmountRateLimitPersistence[SyncContext[_]]( + implicit SyncContext: Sync[SyncContext], + config: Configuration +) extends BuildAmountRateLimitPersistence[SyncContext] { override def read(key: UUID): SyncContext[Option[BuildAmountRateLimiterSnapshot]] = SyncContext.delay { @@ -23,7 +25,8 @@ class JdbcBuildAmountRateLimitPersistence[SyncContext[_]](implicit SyncContext: BuildAmountRateLimiterSnapshot(exp, ldt) } - .first().apply() + .first() + .apply() } } @@ -31,13 +34,13 @@ class JdbcBuildAmountRateLimitPersistence[SyncContext[_]](implicit SyncContext: SyncContext.delay { DB.localTx { implicit session => sql""" - |insert into build_count_rate_limit values (${key.toString}, ${value.amount.toPlainString}, ${value.recordTime}) - | on duplicate key update - | available_permission = ${value.amount.toPlainString}, - | record_date = ${value.recordTime} - |""" - .stripMargin - .update().apply() + |insert into build_count_rate_limit values (${key.toString}, ${value + .amount + .toPlainString}, ${value.recordTime}) + | on duplicate key update + | available_permission = ${value.amount.toPlainString}, + | record_date = ${value.recordTime} + |""".stripMargin.update().apply() } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/ObtainChatPermission.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/ObtainChatPermission.scala index b466642f69..ca9673447e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/ObtainChatPermission.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/ObtainChatPermission.scala @@ -4,7 +4,10 @@ import cats.Monad import cats.effect.concurrent.Ref import com.github.unchama.datarepository.bukkit.player.PlayerDataRepository import com.github.unchama.generic.ratelimiting.RateLimiter -import com.github.unchama.seichiassist.subsystems.chatratelimiter.domain.{ChatCount, ChatPermissionRequestResult} +import com.github.unchama.seichiassist.subsystems.chatratelimiter.domain.{ + ChatCount, + ChatPermissionRequestResult +} import org.bukkit.entity.Player trait ObtainChatPermission[F[_], Player] { @@ -14,17 +17,21 @@ trait ObtainChatPermission[F[_], Player] { object ObtainChatPermission { import cats.implicits._ - def from[G[_] : Monad]( - repository: PlayerDataRepository[Ref[G, Option[RateLimiter[G, ChatCount]]]] - ): ObtainChatPermission[G, Player] = - player => for { - rateLimiterOpt <- repository.apply(player).get - folded <- rateLimiterOpt.fold( - Monad[G].pure[ChatPermissionRequestResult](ChatPermissionRequestResult.Success) - ) { rateLimiter => - rateLimiter.requestPermission(ChatCount.One).map(count => - if (count == ChatCount.One) ChatPermissionRequestResult.Success - else ChatPermissionRequestResult.Failed) - } - } yield folded -} \ No newline at end of file + def from[G[_]: Monad]( + repository: PlayerDataRepository[Ref[G, Option[RateLimiter[G, ChatCount]]]] + ): ObtainChatPermission[G, Player] = + player => + for { + rateLimiterOpt <- repository.apply(player).get + folded <- rateLimiterOpt.fold( + Monad[G].pure[ChatPermissionRequestResult](ChatPermissionRequestResult.Success) + ) { rateLimiter => + rateLimiter + .requestPermission(ChatCount.One) + .map(count => + if (count == ChatCount.One) ChatPermissionRequestResult.Success + else ChatPermissionRequestResult.Failed + ) + } + } yield folded +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/System.scala index c04a78ed32..3b42b50204 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/System.scala @@ -15,23 +15,24 @@ import org.bukkit.entity.Player import org.bukkit.event.Listener object System { - def wired[ - F[_] : ConcurrentEffect : ErrorLogger, - G[_] : SyncEffect : ContextCoercion[*[_], F] : Timer, - ](implicit breakCountAPI: BreakCountReadAPI[F, G, Player]): F[Subsystem[F]] = { + def wired[F[_]: ConcurrentEffect: ErrorLogger, G[_]: SyncEffect: ContextCoercion[ + *[_], + F + ]: Timer](implicit breakCountAPI: BreakCountReadAPI[F, G, Player]): F[Subsystem[F]] = { val repository = ChatRateLimitRepositoryDefinition.withContext[F, G, Player] for { handle <- ContextCoercion(BukkitRepositoryControls.createHandles(repository)) _ <- Concurrent[F].start[Nothing]( // NOTE: This explicit type argument is needed - StreamExtra.compileToRestartingStream("チャットのレートリミット")( - breakCountAPI.seichiLevelUpdates.evalTap { case (p, _) => - ContextCoercion(handle.repository(p).set(None)) - } - ) + StreamExtra + .compileToRestartingStream("チャットのレートリミット")(breakCountAPI.seichiLevelUpdates.evalTap { + case (p, _) => + ContextCoercion(handle.repository(p).set(None)) + }) ) } yield { new Subsystem[F] { - implicit val api: ObtainChatPermission[G, Player] = ObtainChatPermission.from(handle.repository) + implicit val api: ObtainChatPermission[G, Player] = + ObtainChatPermission.from(handle.repository) override val listeners: Seq[Listener] = Seq(new RateLimitCheckListener) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/bukkit/listeners/RateLimitCheckListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/bukkit/listeners/RateLimitCheckListener.scala index 87df23cdff..36e9e27bad 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/bukkit/listeners/RateLimitCheckListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/bukkit/listeners/RateLimitCheckListener.scala @@ -8,7 +8,8 @@ import org.bukkit.entity.Player import org.bukkit.event.player.AsyncPlayerChatEvent import org.bukkit.event.{EventHandler, Listener} -class RateLimitCheckListener[F[_] : SyncEffect](implicit api: ObtainChatPermission[F, Player]) extends Listener { +class RateLimitCheckListener[F[_]: SyncEffect](implicit api: ObtainChatPermission[F, Player]) + extends Listener { @EventHandler def onEvent(e: AsyncPlayerChatEvent): Unit = { val player = e.getPlayer diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/domain/ChatCount.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/domain/ChatCount.scala index 59f32774b2..9655955144 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/domain/ChatCount.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/domain/ChatCount.scala @@ -1,6 +1,5 @@ package com.github.unchama.seichiassist.subsystems.chatratelimiter.domain -import cats.Order import com.github.unchama.generic.algebra.typeclasses.OrderedMonus sealed trait ChatCount @@ -9,23 +8,23 @@ object ChatCount { case object Zero extends ChatCount case object One extends ChatCount implicit val orderedMonus: OrderedMonus[ChatCount] = new OrderedMonus[ChatCount] { + /** - * 切り捨て減算。 - * `x: A, y: A` について、 `z: A` = `|-|(x, y)` は - * `x <= y + z` となるような最小の `z` として定義される。 + * 切り捨て減算。 `x: A, y: A` について、 `z: A` = `|-|(x, y)` は `x <= y + z` となるような最小の `z` として定義される。 */ override def |-|(x: ChatCount, y: ChatCount): ChatCount = (x, y) match { case (One, Zero) => One - case _ => Zero + case _ => Zero } - override def compare(x: ChatCount, y: ChatCount): Int = if (x == y) 0 else if (x == One) 1 else -1 + override def compare(x: ChatCount, y: ChatCount): Int = + if (x == y) 0 else if (x == One) 1 else -1 override def empty: ChatCount = Zero override def combine(x: ChatCount, y: ChatCount): ChatCount = (x, y) match { case (Zero, Zero) => Zero - case _ => One + case _ => One } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/domain/ChatPermissionRequestResult.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/domain/ChatPermissionRequestResult.scala index bc450e83c7..966a81b646 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/domain/ChatPermissionRequestResult.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/domain/ChatPermissionRequestResult.scala @@ -5,4 +5,4 @@ sealed trait ChatPermissionRequestResult object ChatPermissionRequestResult { case object Success extends ChatPermissionRequestResult case object Failed extends ChatPermissionRequestResult -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/domain/ChatRateLimitRepositoryDefinition.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/domain/ChatRateLimitRepositoryDefinition.scala index 24fc1700ef..a4428179c9 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/domain/ChatRateLimitRepositoryDefinition.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/chatratelimiter/domain/ChatRateLimitRepositoryDefinition.scala @@ -3,7 +3,10 @@ package com.github.unchama.seichiassist.subsystems.chatratelimiter.domain import cats.effect.concurrent.Ref import cats.effect.{Sync, Timer} import cats.implicits._ -import com.github.unchama.datarepository.template.RepositoryDefinition.Phased.{SinglePhased, TwoPhased} +import com.github.unchama.datarepository.template.RepositoryDefinition.Phased.{ + SinglePhased, + TwoPhased +} import com.github.unchama.datarepository.template.finalization.RepositoryFinalization import com.github.unchama.datarepository.template.initialization.TwoPhasedRepositoryInitialization import com.github.unchama.generic.ratelimiting.{FixedWindowRateLimiter, RateLimiter} @@ -13,19 +16,20 @@ import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountReadAPI import scala.concurrent.duration.DurationInt object ChatRateLimitRepositoryDefinition { - def withContext[ - F[_], - G[_] : Sync : Timer, - Player : HasUuid - ](implicit breakCountAPI: BreakCountReadAPI[F, G, Player]): TwoPhased[G, Player, Ref[G, Option[RateLimiter[G, ChatCount]]]] = { + def withContext[F[_], G[_]: Sync: Timer, Player: HasUuid]( + implicit breakCountAPI: BreakCountReadAPI[F, G, Player] + ): TwoPhased[G, Player, Ref[G, Option[RateLimiter[G, ChatCount]]]] = { TwoPhased( - TwoPhasedRepositoryInitialization.augment( - SinglePhased.trivial[G, Player].initialization - )((p, _) => for { - seichiAmount <- breakCountAPI.seichiAmountDataRepository(p).read - rateLimiter <- FixedWindowRateLimiter.in[G, ChatCount](ChatCount.One, 30.seconds) - ref <- Ref[G].of(Option.when(seichiAmount.levelCorrespondingToExp.level == 1)(rateLimiter)) - } yield ref), + TwoPhasedRepositoryInitialization.augment(SinglePhased.trivial[G, Player].initialization)( + (p, _) => + for { + seichiAmount <- breakCountAPI.seichiAmountDataRepository(p).read + rateLimiter <- FixedWindowRateLimiter.in[G, ChatCount](ChatCount.One, 30.seconds) + ref <- Ref[G].of( + Option.when(seichiAmount.levelCorrespondingToExp.level == 1)(rateLimiter) + ) + } yield ref + ), // does not need any finalization RepositoryFinalization.trivial ) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/System.scala index 06f9ddcf0e..f4e84a3810 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/System.scala @@ -2,7 +2,10 @@ package com.github.unchama.seichiassist.subsystems.discordnotification import cats.effect.{ContextShift, LiftIO, Sync} import com.github.unchama.seichiassist.meta.subsystem.Subsystem -import com.github.unchama.seichiassist.subsystems.discordnotification.infrastructure.{DefaultDiscordNotificationSender, WebhookDiscordNotificationSender} +import com.github.unchama.seichiassist.subsystems.discordnotification.infrastructure.{ + DefaultDiscordNotificationSender, + WebhookDiscordNotificationSender +} import io.chrisdavenport.log4cats.Logger trait System[F[_]] extends Subsystem[F] { @@ -10,9 +13,13 @@ trait System[F[_]] extends Subsystem[F] { } object System { - def wired[F[_] : Sync : ContextShift : Logger : LiftIO](configuration: SystemConfiguration): System[F] = new System[F] { + def wired[F[_]: Sync: ContextShift: Logger: LiftIO]( + configuration: SystemConfiguration + ): System[F] = new System[F] { implicit override val globalNotification: DiscordNotificationAPI[F] = { - WebhookDiscordNotificationSender.tryCreate(configuration.webhookUrl).getOrElse(new DefaultDiscordNotificationSender) + WebhookDiscordNotificationSender + .tryCreate(configuration.webhookUrl) + .getOrElse(new DefaultDiscordNotificationSender) } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/infrastructure/DefaultDiscordNotificationSender.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/infrastructure/DefaultDiscordNotificationSender.scala index 56f9fa0ccc..1af9f4be91 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/infrastructure/DefaultDiscordNotificationSender.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/infrastructure/DefaultDiscordNotificationSender.scala @@ -8,8 +8,13 @@ import io.chrisdavenport.log4cats.Logger /** * この実装は[[send]]が呼ばれるたびに警告をロガーに流す以外は何もしない。 */ -final class DefaultDiscordNotificationSender[F[_]: Logger: LiftIO] extends DiscordNotificationAPI[F] { +final class DefaultDiscordNotificationSender[F[_]: Logger: LiftIO] + extends DiscordNotificationAPI[F] { override def send(message: String): F[Unit] = { - SeichiAssist.instance.loggerF.warn("Discordへの送信が試みられましたが、URLが無効、もしくは与えられていません。コンフィグを確認してください。").to + SeichiAssist + .instance + .loggerF + .warn("Discordへの送信が試みられましたが、URLが無効、もしくは与えられていません。コンフィグを確認してください。") + .to } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/infrastructure/WebhookDiscordNotificationSender.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/infrastructure/WebhookDiscordNotificationSender.scala index 3cc95cf50f..4eda951a68 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/infrastructure/WebhookDiscordNotificationSender.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/discordnotification/infrastructure/WebhookDiscordNotificationSender.scala @@ -3,7 +3,6 @@ package com.github.unchama.seichiassist.subsystems.discordnotification.infrastru import cats.effect.{ContextShift, Sync} import com.github.unchama.seichiassist.subsystems.discordnotification.DiscordNotificationAPI import io.chrisdavenport.log4cats.Logger -import org.bukkit.Bukkit import java.io.IOException import java.net.{HttpURLConnection, MalformedURLException, URL} @@ -11,11 +10,9 @@ import java.nio.charset.StandardCharsets import scala.util.Using import scala.util.chaining.scalaUtilChainingOps -class WebhookDiscordNotificationSender[F[_]: Sync: ContextShift] private(webhookURL: String) extends DiscordNotificationAPI[F] { - assert( - webhookURL.nonEmpty, - "GlobalNotificationSenderのURLに空文字列が指定されました。コンフィグを確認してください。" - ) +class WebhookDiscordNotificationSender[F[_]: Sync: ContextShift] private (webhookURL: String) + extends DiscordNotificationAPI[F] { + assert(webhookURL.nonEmpty, "GlobalNotificationSenderのURLに空文字列が指定されました。コンフィグを確認してください。") import cats.implicits._ @@ -44,26 +41,35 @@ class WebhookDiscordNotificationSender[F[_]: Sync: ContextShift] private(webhook } _ <- responseCode match { case HttpURLConnection.HTTP_OK | HttpURLConnection.HTTP_NO_CONTENT => Sync[F].unit - case code @ _ => Sync[F].raiseError { - new IOException(s"GlobalNotificationSender: Bad Response Code: $code with $webhookURL") - } + case code @ _ => + Sync[F].raiseError { + new IOException( + s"GlobalNotificationSender: Bad Response Code: $code with $webhookURL" + ) + } } } yield () } object WebhookDiscordNotificationSender { + /** * [[WebhookDiscordNotificationSender]] を作成することを試みる。 - * @param webhookURL Discordに送信されるwebhookのURL - * @tparam F 文脈 - * @return 初期化に成功した場合はSome、初期化中に特定の例外が送出された場合はNone。マスクされない例外が送出されたときは、再送出する。 + * @param webhookURL + * Discordに送信されるwebhookのURL + * @tparam F + * 文脈 + * @return + * 初期化に成功した場合はSome、初期化中に特定の例外が送出された場合はNone。マスクされない例外が送出されたときは、再送出する。 */ - def tryCreate[F[_]: Sync: ContextShift: Logger](webhookURL: String): Option[WebhookDiscordNotificationSender[F]] = { + def tryCreate[F[_]: Sync: ContextShift: Logger]( + webhookURL: String + ): Option[WebhookDiscordNotificationSender[F]] = { try { Some(new WebhookDiscordNotificationSender[F](webhookURL)) } catch { case _: MalformedURLException => None - case _: AssertionError => None + case _: AssertionError => None } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/System.scala index 9ad1272da4..cc484f3374 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/System.scala @@ -9,14 +9,11 @@ import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.FastDiggingE import com.github.unchama.seichiassist.subsystems.mana.ManaApi object System { - def backgroundProcess[ - F[_] : Concurrent : Timer, - G[_] : ContextCoercion[*[_], F], - Player - ](implicit - ctx: RepeatingTaskContext, + def backgroundProcess[F[_]: Concurrent: Timer, G[_]: ContextCoercion[*[_], F], Player]( + implicit ctx: RepeatingTaskContext, fastDiggingEffectApi: FastDiggingEffectWriteApi[F, Player], - manaApi: ManaApi[F, G, Player]): F[Nothing] = { + manaApi: ManaApi[F, G, Player] + ): F[Nothing] = { implicit val _notifiable: Notifiable[F] = SyncNotifiable[F] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/application/DragonNightTimeRoutine.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/application/DragonNightTimeRoutine.scala index 5bebfeb169..1b6e679ff2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/application/DragonNightTimeRoutine.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/application/DragonNightTimeRoutine.scala @@ -4,7 +4,11 @@ import cats.effect.{Concurrent, Timer} import com.github.unchama.concurrent.{RepeatingRoutine, RepeatingTaskContext} import com.github.unchama.generic.ContextCoercion import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.FastDiggingEffectWriteApi -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.effect.{FastDiggingAmplifier, FastDiggingEffect, FastDiggingEffectCause} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.effect.{ + FastDiggingAmplifier, + FastDiggingEffect, + FastDiggingEffectCause +} import com.github.unchama.seichiassist.subsystems.mana.ManaApi import com.github.unchama.seichiassist.subsystems.mana.domain.ManaMultiplier import com.github.unchama.util.time.LocalTimeUtil @@ -13,13 +17,11 @@ import java.time.{Instant, LocalDateTime, LocalTime, ZoneId} import java.util.concurrent.TimeUnit object DragonNightTimeRoutine { - def apply[ - F[_] : Concurrent : Notifiable : Timer, - G[_] : ContextCoercion[*[_], F], Player - ](implicit - context: RepeatingTaskContext, + def apply[F[_]: Concurrent: Notifiable: Timer, G[_]: ContextCoercion[*[_], F], Player]( + implicit context: RepeatingTaskContext, fastDiggingEffectApi: FastDiggingEffectWriteApi[F, Player], - manaApi: ManaApi[F, G, Player]): F[Nothing] = { + manaApi: ManaApi[F, G, Player] + ): F[Nothing] = { import cats.implicits._ @@ -27,27 +29,29 @@ object DragonNightTimeRoutine { val dailyDragonNightTime = LocalTime.of(20, 0, 0) - val effectToAdd = FastDiggingEffect(FastDiggingAmplifier(10.0), FastDiggingEffectCause.FromDragonNightTime) + val effectToAdd = + FastDiggingEffect(FastDiggingAmplifier(10.0), FastDiggingEffectCause.FromDragonNightTime) val getRepeatInterval: F[FiniteDuration] = { import cats.implicits._ Timer[F].clock.realTime(TimeUnit.MILLISECONDS).map { currentEpochMilli => val currentLocalTime = - LocalDateTime.ofInstant( - Instant.ofEpochMilli(currentEpochMilli), - ZoneId.systemDefault() - ).toLocalTime + LocalDateTime + .ofInstant(Instant.ofEpochMilli(currentEpochMilli), ZoneId.systemDefault()) + .toLocalTime LocalTimeUtil.getDurationToNextTimeOfDay(currentLocalTime, dailyDragonNightTime) } } val routineAction: F[Unit] = { - val manipulateManaMultiplier = Concurrent[F].start { - ContextCoercion(manaApi.setGlobalManaMultiplier(ManaMultiplier(0.8))) >> - Timer[F].sleep(1.hour) >> - ContextCoercion(manaApi.setGlobalManaMultiplier(ManaMultiplier(1))) - }.as(()) + val manipulateManaMultiplier = Concurrent[F] + .start { + ContextCoercion(manaApi.setGlobalManaMultiplier(ManaMultiplier(0.8))) >> + Timer[F].sleep(1.hour) >> + ContextCoercion(manaApi.setGlobalManaMultiplier(ManaMultiplier(1))) + } + .as(()) Notifiable[F].notify("ドラゲナイタイム開始!") >> Notifiable[F].notify("採掘速度上昇Lv10のバフが1時間付与され、マナ使用率が80%になりました") >> diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/application/Notifiable.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/application/Notifiable.scala index 1f43db443f..832a475936 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/application/Notifiable.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/application/Notifiable.scala @@ -6,4 +6,4 @@ trait Notifiable[F[_]] { object Notifiable { def apply[F[_]: Notifiable]: Notifiable[F] = implicitly -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/bukkit/instances/SyncNotifiable.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/bukkit/instances/SyncNotifiable.scala index 47e986fc96..7ae6da3ca0 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/bukkit/instances/SyncNotifiable.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/bukkit/instances/SyncNotifiable.scala @@ -7,8 +7,9 @@ import com.github.unchama.seichiassist.util.Util import org.bukkit.Bukkit object SyncNotifiable { - def apply[F[_] : Sync]: Notifiable[F] = (message: String) => Sync[F].delay { - Util.sendMessageToEveryoneIgnoringPreference(message) - Bukkit.getLogger.info(message) - } + def apply[F[_]: Sync]: Notifiable[F] = (message: String) => + Sync[F].delay { + Util.sendMessageToEveryoneIgnoringPreference(message) + Bukkit.getLogger.info(message) + } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/expbottlestack/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/expbottlestack/System.scala index 2bf8a2ed77..59141e6d57 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/expbottlestack/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/expbottlestack/System.scala @@ -16,11 +16,9 @@ trait System[F[_], G[_], H[_]] extends Subsystem[H] { } object System { - def wired[ - F[_] : ConcurrentEffect, - G[_] : SyncEffect : ContextCoercion[*[_], F], - H[_] - ](implicit effectEnvironment: EffectEnvironment): F[System[F, G, H]] = { + def wired[F[_]: ConcurrentEffect, G[_]: SyncEffect: ContextCoercion[*[_], F], H[_]]( + implicit effectEnvironment: EffectEnvironment + ): F[System[F, G, H]] = { import cats.implicits._ for { @@ -29,9 +27,7 @@ object System { implicit val scope: ResourceScope[F, G, ThrownExpBottle] = managedExpBottleScope new System[F, G, H] { - override val listeners: Seq[Listener] = Seq( - new ExpBottleStackUsageController[F, G]() - ) + override val listeners: Seq[Listener] = Seq(new ExpBottleStackUsageController[F, G]()) override val managedBottleScope: ResourceScope[F, G, ThrownExpBottle] = scope } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/expbottlestack/bukkit/Resources.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/expbottlestack/bukkit/Resources.scala index 97caae8d19..d28e1c7411 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/expbottlestack/bukkit/Resources.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/expbottlestack/bukkit/Resources.scala @@ -7,24 +7,22 @@ import org.bukkit.Location import org.bukkit.entity.{ExperienceOrb, ThrownExpBottle} object Resources { - def bottleResourceSpawningAt[F[_]](loc: Location, originalCount: BottleCount) - (implicit F: Sync[F]): Resource[F, ThrownExpBottle] = { + def bottleResourceSpawningAt[F[_]](loc: Location, originalCount: BottleCount)( + implicit F: Sync[F] + ): Resource[F, ThrownExpBottle] = { import cats.implicits._ - Resource - .make( - EntityUtil.spawn[F, ThrownExpBottle](loc) - ) { bottle => - for { - _ <- F.delay { - bottle.remove() - } - expAmount <- originalCount.randomlyGenerateExpAmount[F] - orb <- EntityUtil.spawn[F, ExperienceOrb](loc) - _ <- F.delay { - orb.setExperience(expAmount) - } - } yield () - } + Resource.make(EntityUtil.spawn[F, ThrownExpBottle](loc)) { bottle => + for { + _ <- F.delay { + bottle.remove() + } + expAmount <- originalCount.randomlyGenerateExpAmount[F] + orb <- EntityUtil.spawn[F, ExperienceOrb](loc) + _ <- F.delay { + orb.setExperience(expAmount) + } + } yield () + } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/expbottlestack/bukkit/listeners/ExpBottleStackUsageController.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/expbottlestack/bukkit/listeners/ExpBottleStackUsageController.scala index 5c59538916..5cce9248f3 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/expbottlestack/bukkit/listeners/ExpBottleStackUsageController.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/expbottlestack/bukkit/listeners/ExpBottleStackUsageController.scala @@ -13,11 +13,10 @@ import org.bukkit.event.player.PlayerInteractEvent import org.bukkit.event.{EventHandler, Listener} import org.bukkit.inventory.ItemStack -class ExpBottleStackUsageController[ - F[_] : Effect, - G[_] : SyncEffect -](implicit managedBottleScope: ResourceScope[F, G, ThrownExpBottle], effectEnvironment: EffectEnvironment) - extends Listener { +class ExpBottleStackUsageController[F[_]: Effect, G[_]: SyncEffect]( + implicit managedBottleScope: ResourceScope[F, G, ThrownExpBottle], + effectEnvironment: EffectEnvironment +) extends Listener { import cats.effect.implicits._ @@ -28,8 +27,11 @@ class ExpBottleStackUsageController[ if (managedBottleScope.isTracked(bottle).runSync[SyncIO].unsafeRunSync()) { event.setExperience(0) managedBottleScope - .getReleaseAction(bottle).runSync[SyncIO].unsafeRunSync() - .toIO.unsafeRunSync() + .getReleaseAction(bottle) + .runSync[SyncIO] + .unsafeRunSync() + .toIO + .unsafeRunSync() } } @@ -41,17 +43,22 @@ class ExpBottleStackUsageController[ val action = event.getAction // 経験値瓶を持った状態でShift右クリックをした場合 - if (player.isSneaking + if ( + player.isSneaking && playerInventory.getItemInMainHand != null && playerInventory.getItemInMainHand.getType == Material.EXP_BOTTLE - && (action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK)) { + && (action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK) + ) { val bottleCount = BottleCount(playerInventory.getItemInMainHand.getAmount) - val bottleResource = Resources.bottleResourceSpawningAt[F](player.getLocation, bottleCount) + val bottleResource = + Resources.bottleResourceSpawningAt[F](player.getLocation, bottleCount) effectEnvironment.unsafeRunEffectAsync( "経験値瓶の消費を待つ", - managedBottleScope.useTracked[ThrownExpBottle, Nothing](bottleResource) { _ => Effect[F].never } + managedBottleScope.useTracked[ThrownExpBottle, Nothing](bottleResource) { _ => + Effect[F].never + } ) playerInventory.setItemInMainHand(new ItemStack(Material.AIR)) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/expbottlestack/domain/BottleCount.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/expbottlestack/domain/BottleCount.scala index d7ed249212..e5a575165c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/expbottlestack/domain/BottleCount.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/expbottlestack/domain/BottleCount.scala @@ -7,9 +7,7 @@ import scala.util.Random case class BottleCount(amount: Int) extends AnyVal { // 経験値瓶一つに付きもたらされる経験値量は3..11。ソースはGamepedia - def randomlyGenerateExpAmount[F[_] : Sync]: F[Int] = Sync[F].delay { - (1 to amount) - .map(_ => Random.nextInt(9 /* Exclusive */) + 3) - .sum + def randomlyGenerateExpAmount[F[_]: Sync]: F[Int] = Sync[F].delay { + (1 to amount).map(_ => Random.nextInt(9 /* Exclusive */ ) + 3).sum } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/FastDiggingEffectApi.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/FastDiggingEffectApi.scala index 0d52fc8829..2fe77ede3d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/FastDiggingEffectApi.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/FastDiggingEffectApi.scala @@ -3,7 +3,10 @@ package com.github.unchama.seichiassist.subsystems.fastdiggingeffect import cats.data.Kleisli import com.github.unchama.datarepository.KeyedDataRepository import com.github.unchama.generic.effect.concurrent.ReadOnlyRef -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.effect.{FastDiggingEffect, FastDiggingEffectList} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.effect.{ + FastDiggingEffect, + FastDiggingEffectList +} import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.settings.FastDiggingEffectSuppressionState import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.stats.FastDiggingEffectStatsSettings @@ -38,20 +41,18 @@ trait FastDiggingEffectReadApi[F[_], Player] { } trait FastDiggingEffectApi[F[_], Player] - extends FastDiggingEffectReadApi[F, Player] + extends FastDiggingEffectReadApi[F, Player] with FastDiggingEffectWriteApi[F, Player] trait FastDiggingSettingsWriteApi[F[_], Player] { /** - * 採掘速度上昇抑制の設定をトグルする作用。 - * 作用は結果値として変更後の設定を返す。 + * 採掘速度上昇抑制の設定をトグルする作用。 作用は結果値として変更後の設定を返す。 */ val toggleEffectSuppression: Kleisli[F, Player, FastDiggingEffectSuppressionState] /** - * 採掘速度上昇効果の統計を受け取るかどうかの設定をトグルする作用。 - * 作用は結果値として変更後の設定を返す。 + * 採掘速度上昇効果の統計を受け取るかどうかの設定をトグルする作用。 作用は結果値として変更後の設定を返す。 */ val toggleStatsSettings: Kleisli[F, Player, FastDiggingEffectStatsSettings] @@ -62,15 +63,21 @@ trait FastDiggingSettingsReadApi[F[_], Player] { /** * 採掘速度上昇抑制の設定をプレーヤーごとに保持するデータレポジトリ。 */ - val currentSuppressionSettings: KeyedDataRepository[Player, ReadOnlyRef[F, FastDiggingEffectSuppressionState]] + val currentSuppressionSettings: KeyedDataRepository[ + Player, + ReadOnlyRef[F, FastDiggingEffectSuppressionState] + ] /** * 採掘速度上昇効果の統計を受け取るかどうかの設定をプレーヤーごとに保持するデータレポジトリ。 */ - val currentStatsSettings: KeyedDataRepository[Player, ReadOnlyRef[F, FastDiggingEffectStatsSettings]] + val currentStatsSettings: KeyedDataRepository[ + Player, + ReadOnlyRef[F, FastDiggingEffectStatsSettings] + ] } trait FastDiggingSettingsApi[F[_], Player] - extends FastDiggingSettingsReadApi[F, Player] + extends FastDiggingSettingsReadApi[F, Player] with FastDiggingSettingsWriteApi[F, Player] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/System.scala index cafce8162b..a9b135df79 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/System.scala @@ -9,20 +9,46 @@ import com.github.unchama.fs2.workaround.fs3.Fs3Topic import com.github.unchama.generic.ContextCoercion import com.github.unchama.generic.effect.concurrent.ReadOnlyRef import com.github.unchama.generic.effect.stream.StreamExtra -import com.github.unchama.minecraft.actions.{GetConnectedPlayers, OnMinecraftServerThread, SendMinecraftMessage} +import com.github.unchama.minecraft.actions.{ + GetConnectedPlayers, + OnMinecraftServerThread, + SendMinecraftMessage +} import com.github.unchama.minecraft.bukkit.actions.SendBukkitMessage import com.github.unchama.seichiassist.domain.actions.GetNetworkConnectionCount import com.github.unchama.seichiassist.meta.subsystem.Subsystem import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountReadAPI import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.application.Configuration -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.application.process.{BreakCountEffectSynchronization, EffectStatsNotification, PlayerCountEffectSynchronization, SynchronizationProcess} -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.application.repository.{EffectListRepositoryDefinitions, EffectStatsSettingsRepositoryDefinition, SuppressionSettingsRepositoryDefinition} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.application.process.{ + BreakCountEffectSynchronization, + EffectStatsNotification, + PlayerCountEffectSynchronization, + SynchronizationProcess +} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.application.repository.{ + EffectListRepositoryDefinitions, + EffectStatsSettingsRepositoryDefinition, + SuppressionSettingsRepositoryDefinition +} import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.bukkit.actions.GrantBukkitFastDiggingEffect import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.actions.GrantFastDiggingEffect -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.effect.{FastDiggingEffect, FastDiggingEffectList} -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.settings.{FastDiggingEffectSuppressionState, FastDiggingEffectSuppressionStatePersistence} -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.stats.{EffectListDiff, FastDiggingEffectStatsSettings, FastDiggingEffectStatsSettingsPersistence} -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.infrastructure.{JdbcFastDiggingEffectStatsSettingsPersistence, JdbcFastDiggingEffectSuppressionStatePersistence} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.effect.{ + FastDiggingEffect, + FastDiggingEffectList +} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.settings.{ + FastDiggingEffectSuppressionState, + FastDiggingEffectSuppressionStatePersistence +} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.stats.{ + EffectListDiff, + FastDiggingEffectStatsSettings, + FastDiggingEffectStatsSettingsPersistence +} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.infrastructure.{ + JdbcFastDiggingEffectStatsSettingsPersistence, + JdbcFastDiggingEffectSuppressionStatePersistence +} import io.chrisdavenport.log4cats.ErrorLogger import org.bukkit.Bukkit import org.bukkit.entity.Player @@ -44,19 +70,15 @@ object System { import cats.implicits._ import com.github.unchama.minecraft.bukkit.algebra.BukkitPlayerHasUuid._ - def wired[ - G[_] - : SyncEffect, - F[_] - : OnMinecraftServerThread - : Timer - : ConcurrentEffect - : ErrorLogger - : ContextCoercion[G, *[_]] - : GetConnectedPlayers[*[_], Player] - : GetNetworkConnectionCount, - H[_] - ](implicit breakCountReadAPI: BreakCountReadAPI[F, H, Player], config: Configuration): F[System[F, F, Player]] = { + def wired[G[_]: SyncEffect, F[ + _ + ]: OnMinecraftServerThread: Timer: ConcurrentEffect: ErrorLogger: ContextCoercion[ + G, + *[_] + ]: GetConnectedPlayers[*[_], Player]: GetNetworkConnectionCount, H[_]]( + implicit breakCountReadAPI: BreakCountReadAPI[F, H, Player], + config: Configuration + ): F[System[F, F, Player]] = { val settingsPersistence: FastDiggingEffectStatsSettingsPersistence[G] = new JdbcFastDiggingEffectStatsSettingsPersistence[G] @@ -64,23 +86,28 @@ object System { val suppressionStatePersistence: FastDiggingEffectSuppressionStatePersistence[G] = new JdbcFastDiggingEffectSuppressionStatePersistence[G] - implicit val FSendMinecraftMessage: SendMinecraftMessage[F, Player] = new SendBukkitMessage[F] + implicit val FSendMinecraftMessage: SendMinecraftMessage[F, Player] = + new SendBukkitMessage[F] implicit val grantFastDiggingEffect: GrantFastDiggingEffect[F, Player] = new GrantBukkitFastDiggingEffect[F] val yieldSystem: F[System[F, F, Player]] = for { effectListTopic <- Fs3Topic[F, Option[(Player, FastDiggingEffectList)]](None) - effectListDiffTopic <- Fs3Topic[F, Option[(Player, (EffectListDiff, FastDiggingEffectStatsSettings))]](None) + effectListDiffTopic <- Fs3Topic[F, Option[ + (Player, (EffectListDiff, FastDiggingEffectStatsSettings)) + ]](None) effectListRepositoryHandles <- { ContextCoercion { BukkitRepositoryControls.createHandles( - RepositoryDefinition.Phased.SinglePhased( - EffectListRepositoryDefinitions.initialization[F, G], - EffectListRepositoryDefinitions.tappingAction[F, G, Player](effectListTopic), - EffectListRepositoryDefinitions.finalization[F, G, UUID] - ) + RepositoryDefinition + .Phased + .SinglePhased( + EffectListRepositoryDefinitions.initialization[F, G], + EffectListRepositoryDefinitions.tappingAction[F, G, Player](effectListTopic), + EffectListRepositoryDefinitions.finalization[F, G, UUID] + ) ) } } @@ -106,64 +133,84 @@ object System { } _ <- - StreamExtra.compileToRestartingStream[F, Unit]("[FastDiggingEffect/EffectStatsNotification]") { - EffectStatsNotification.using[F, Player](effectListDiffTopic.subscribe(1).mapFilter(identity)) - }.start - - } yield new System[F, F, Player] { - override val effectApi: FastDiggingEffectApi[F, Player] = new FastDiggingEffectApi[F, Player] { - override val currentEffect: KeyedDataRepository[Player, ReadOnlyRef[F, FastDiggingEffectList]] = - effectListRepositoryHandles.repository.map { case (ref, _) => - ReadOnlyRef.fromAnySource(ContextCoercion(ref.readLatest)) - } - - override val effectClock: fs2.Stream[F, (Player, FastDiggingEffectList)] = - effectListTopic.subscribe(1).mapFilter(identity) - - override def addEffect(effect: FastDiggingEffect, duration: FiniteDuration): Kleisli[F, Player, Unit] = - Kleisli { player => - effectListRepositoryHandles - .repository - .lift(player) - .traverse { pair => - pair._1.lockAndUpdate(_.appendEffect[F](effect, duration)).as(()) - } - .as(()) + StreamExtra + .compileToRestartingStream[F, Unit]("[FastDiggingEffect/EffectStatsNotification]") { + EffectStatsNotification.using[F, Player]( + effectListDiffTopic.subscribe(1).mapFilter(identity) + ) } + .start - override def addEffectToAllPlayers(effect: FastDiggingEffect, duration: FiniteDuration): F[Unit] = { - import cats.implicits._ - - import scala.concurrent.duration._ - import scala.jdk.CollectionConverters._ - - for { - players <- OnMinecraftServerThread[F].runAction(SyncIO(Bukkit.getOnlinePlayers.asScala.toList)) - _ <- players.traverse(addEffect(effect, 1.hour).run) - } yield () - }.as(()) + } yield new System[F, F, Player] { + override val effectApi: FastDiggingEffectApi[F, Player] = + new FastDiggingEffectApi[F, Player] { + override val currentEffect + : KeyedDataRepository[Player, ReadOnlyRef[F, FastDiggingEffectList]] = + effectListRepositoryHandles.repository.map { + case (ref, _) => + ReadOnlyRef.fromAnySource(ContextCoercion(ref.readLatest)) + } + + override val effectClock: fs2.Stream[F, (Player, FastDiggingEffectList)] = + effectListTopic.subscribe(1).mapFilter(identity) + + override def addEffect( + effect: FastDiggingEffect, + duration: FiniteDuration + ): Kleisli[F, Player, Unit] = + Kleisli { player => + effectListRepositoryHandles + .repository + .lift(player) + .traverse { pair => + pair._1.lockAndUpdate(_.appendEffect[F](effect, duration)).as(()) + } + .as(()) + } + + override def addEffectToAllPlayers( + effect: FastDiggingEffect, + duration: FiniteDuration + ): F[Unit] = { + import cats.implicits._ + + import scala.concurrent.duration._ + import scala.jdk.CollectionConverters._ + + for { + players <- OnMinecraftServerThread[F].runAction( + SyncIO(Bukkit.getOnlinePlayers.asScala.toList) + ) + _ <- players.traverse(addEffect(effect, 1.hour).run) + } yield () + }.as(()) - } - override val settingsApi: FastDiggingSettingsApi[F, Player] = new FastDiggingSettingsApi[F, Player] { - override val currentSuppressionSettings: KeyedDataRepository[Player, ReadOnlyRef[F, FastDiggingEffectSuppressionState]] = - suppressionSettingsRepositoryHandles - .repository - .map(ref => ReadOnlyRef.fromRef(ref.mapK(ContextCoercion.asFunctionK))) - override val toggleEffectSuppression: Kleisli[F, Player, FastDiggingEffectSuppressionState] = Kleisli { player => - ContextCoercion { - suppressionSettingsRepositoryHandles.repository(player).updateAndGet(_.nextState) - } } - override val toggleStatsSettings: Kleisli[F, Player, FastDiggingEffectStatsSettings] = Kleisli { player => - ContextCoercion { - statsSettingsRepositoryHandles.repository(player)._1.updateAndGet(_.nextValue) + override val settingsApi: FastDiggingSettingsApi[F, Player] = + new FastDiggingSettingsApi[F, Player] { + override val currentSuppressionSettings + : KeyedDataRepository[Player, ReadOnlyRef[F, FastDiggingEffectSuppressionState]] = + suppressionSettingsRepositoryHandles + .repository + .map(ref => ReadOnlyRef.fromRef(ref.mapK(ContextCoercion.asFunctionK))) + override val toggleEffectSuppression + : Kleisli[F, Player, FastDiggingEffectSuppressionState] = Kleisli { player => + ContextCoercion { + suppressionSettingsRepositoryHandles.repository(player).updateAndGet(_.nextState) + } } + override val toggleStatsSettings: Kleisli[F, Player, FastDiggingEffectStatsSettings] = + Kleisli { player => + ContextCoercion { + statsSettingsRepositoryHandles.repository(player)._1.updateAndGet(_.nextValue) + } + } + override val currentStatsSettings + : KeyedDataRepository[Player, ReadOnlyRef[F, FastDiggingEffectStatsSettings]] = + statsSettingsRepositoryHandles + .repository + .map(pair => ReadOnlyRef.fromRef(pair._1.mapK(ContextCoercion.asFunctionK))) } - override val currentStatsSettings: KeyedDataRepository[Player, ReadOnlyRef[F, FastDiggingEffectStatsSettings]] = - statsSettingsRepositoryHandles - .repository - .map(pair => ReadOnlyRef.fromRef(pair._1.mapK(ContextCoercion.asFunctionK))) - } override val managedRepositoryControls: Seq[BukkitRepositoryControls[F, _]] = Seq( effectListRepositoryHandles, @@ -176,14 +223,16 @@ object System { implicit val api: FastDiggingEffectApi[F, Player] = system.effectApi List( - "BreakCountEffectSynchronization" -> BreakCountEffectSynchronization.using[F, H, Player], + "BreakCountEffectSynchronization" -> BreakCountEffectSynchronization + .using[F, H, Player], "PlayerCountEffectSynchronization" -> PlayerCountEffectSynchronization.using[F, Player], "SynchronizationProcess" -> SynchronizationProcess.using[F, Player]( system.settingsApi.currentSuppressionSettings, system.effectApi.effectClock ) - ).traverse { case (str, stream) => - StreamExtra.compileToRestartingStream(s"[FastDiggingEffect/$str]")(stream).start + ).traverse { + case (str, stream) => + StreamExtra.compileToRestartingStream(s"[FastDiggingEffect/$str]")(stream).start } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/process/BreakCountEffectSynchronization.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/process/BreakCountEffectSynchronization.scala index 5e83b5648a..28df1d61cb 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/process/BreakCountEffectSynchronization.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/process/BreakCountEffectSynchronization.scala @@ -5,7 +5,11 @@ import com.github.unchama.minecraft.algebra.HasUuid import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountReadAPI import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.FastDiggingEffectWriteApi import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.application.Configuration -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.effect.{FastDiggingAmplifier, FastDiggingEffect, FastDiggingEffectCause} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.effect.{ + FastDiggingAmplifier, + FastDiggingEffect, + FastDiggingEffectCause +} import scala.concurrent.duration.DurationInt @@ -13,29 +17,29 @@ object BreakCountEffectSynchronization { import cats.implicits._ - def using[ - F[_] : ConcurrentEffect : Timer, - G[_], - Player: HasUuid - ](implicit - configuration: Configuration, + def using[F[_]: ConcurrentEffect: Timer, G[_], Player: HasUuid]( + implicit configuration: Configuration, api: FastDiggingEffectWriteApi[F, Player], - breakCountReadAPI: BreakCountReadAPI[F, G, Player]): fs2.Stream[F, Unit] = { + breakCountReadAPI: BreakCountReadAPI[F, G, Player] + ): fs2.Stream[F, Unit] = { breakCountReadAPI .batchedIncreases(1.minute) .evalTap(batch => - batch - .toUuidCollatedList - .traverse { case (player, amount) => - api.addEffect( - FastDiggingEffect( - FastDiggingAmplifier((amount.amount * configuration.amplifierPerBlockMined).toDouble), - FastDiggingEffectCause.FromMinuteBreakCount(amount) - ), - 1.minute - ).run(player) - } + batch.toUuidCollatedList.traverse { + case (player, amount) => + api + .addEffect( + FastDiggingEffect( + FastDiggingAmplifier( + (amount.amount * configuration.amplifierPerBlockMined).toDouble + ), + FastDiggingEffectCause.FromMinuteBreakCount(amount) + ), + 1.minute + ) + .run(player) + } ) .as(()) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/process/EffectStatsNotification.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/process/EffectStatsNotification.scala index 409dd1ec07..23b26e718c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/process/EffectStatsNotification.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/process/EffectStatsNotification.scala @@ -2,45 +2,50 @@ package com.github.unchama.seichiassist.subsystems.fastdiggingeffect.application import cats.effect.ConcurrentEffect import com.github.unchama.minecraft.actions.SendMinecraftMessage -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.stats.{EffectListDiff, FastDiggingEffectStatsSettings} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.stats.{ + EffectListDiff, + FastDiggingEffectStatsSettings +} import org.bukkit.ChatColor.{RED, RESET, WHITE, YELLOW} object EffectStatsNotification { - def using[ - F[_] : ConcurrentEffect : SendMinecraftMessage[*[_], Player], - Player - ](effectDiffWithSettings: fs2.Stream[F, (Player, (EffectListDiff, FastDiggingEffectStatsSettings))]): fs2.Stream[F, Unit] = - effectDiffWithSettings.evalMap { case (player, (effectListDiff, settings)) => - val shouldNotifyNewValue = settings match { - case FastDiggingEffectStatsSettings.AlwaysReceiveDetails => true - case FastDiggingEffectStatsSettings.ReceiveTotalAmplifierOnUpdate => effectListDiff.hasDifference - } - - val newEffectList = effectListDiff.newList - - val shouldNotifyEffectStats = - (settings == FastDiggingEffectStatsSettings.AlwaysReceiveDetails) && - newEffectList.nonEmpty - - val messages = { - val ofNewValue = { - val normalizedLevel = effectListDiff.newEffectAmplifier.normalizedEffectLevel - s"$YELLOW★${WHITE}採掘速度上昇レベルが$YELLOW$normalizedLevel${WHITE}になりました" + def using[F[_]: ConcurrentEffect: SendMinecraftMessage[*[_], Player], Player]( + effectDiffWithSettings: fs2.Stream[ + F, + (Player, (EffectListDiff, FastDiggingEffectStatsSettings)) + ] + ): fs2.Stream[F, Unit] = + effectDiffWithSettings.evalMap { + case (player, (effectListDiff, settings)) => + val shouldNotifyNewValue = settings match { + case FastDiggingEffectStatsSettings.AlwaysReceiveDetails => true + case FastDiggingEffectStatsSettings.ReceiveTotalAmplifierOnUpdate => + effectListDiff.hasDifference } - val ofDetails = List( - "----------------------------内訳-----------------------------" - ) ++ newEffectList.map { effect => - s"$RESET$RED+${effect.amplifier.formatted} ${effect.cause.description}" - } ++ List( - "-------------------------------------------------------------" - ) + val newEffectList = effectListDiff.newList + + val shouldNotifyEffectStats = + (settings == FastDiggingEffectStatsSettings.AlwaysReceiveDetails) && + newEffectList.nonEmpty + + val messages = { + val ofNewValue = { + val normalizedLevel = effectListDiff.newEffectAmplifier.normalizedEffectLevel + s"$YELLOW★${WHITE}採掘速度上昇レベルが$YELLOW$normalizedLevel${WHITE}になりました" + } - Option.when(shouldNotifyNewValue)(ofNewValue).toList ++ - Option.when(shouldNotifyEffectStats)(ofDetails).toList.flatten - } + val ofDetails = List( + "----------------------------内訳-----------------------------" + ) ++ newEffectList.map { effect => + s"$RESET$RED+${effect.amplifier.formatted} ${effect.cause.description}" + } ++ List("-------------------------------------------------------------") + + Option.when(shouldNotifyNewValue)(ofNewValue).toList ++ + Option.when(shouldNotifyEffectStats)(ofDetails).toList.flatten + } - SendMinecraftMessage[F, Player].list(player, messages) + SendMinecraftMessage[F, Player].list(player, messages) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/process/PlayerCountEffectSynchronization.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/process/PlayerCountEffectSynchronization.scala index 01c56edbf6..0f9c2d60c1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/process/PlayerCountEffectSynchronization.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/process/PlayerCountEffectSynchronization.scala @@ -6,7 +6,11 @@ import com.github.unchama.minecraft.algebra.HasUuid import com.github.unchama.seichiassist.domain.actions.GetNetworkConnectionCount import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.FastDiggingEffectApi import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.application.Configuration -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.effect.{FastDiggingAmplifier, FastDiggingEffect, FastDiggingEffectCause} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.effect.{ + FastDiggingAmplifier, + FastDiggingEffect, + FastDiggingEffectCause +} object PlayerCountEffectSynchronization { @@ -14,30 +18,31 @@ object PlayerCountEffectSynchronization { import scala.concurrent.duration._ - def using[ - F[_] : ConcurrentEffect : Timer : GetConnectedPlayers[*[_], Player] : GetNetworkConnectionCount, - Player: HasUuid - ](implicit - configuration: Configuration, - api: FastDiggingEffectApi[F, Player]): fs2.Stream[F, Unit] = { + def using[F[_]: ConcurrentEffect: Timer: GetConnectedPlayers[ + *[_], + Player + ]: GetNetworkConnectionCount, Player: HasUuid]( + implicit configuration: Configuration, + api: FastDiggingEffectApi[F, Player] + ): fs2.Stream[F, Unit] = { - fs2.Stream - .awakeEvery[F](1.minute) - .evalMap { _ => - for { - count <- GetNetworkConnectionCount[F].now - players <- GetConnectedPlayers[F, Player].now - _ <- players.traverse { player => - api.addEffect( + fs2.Stream.awakeEvery[F](1.minute).evalMap { _ => + for { + count <- GetNetworkConnectionCount[F].now + players <- GetConnectedPlayers[F, Player].now + _ <- players.traverse { player => + api + .addEffect( FastDiggingEffect( FastDiggingAmplifier(configuration.amplifierPerPlayerConnection * count), FastDiggingEffectCause.FromConnectionNumber(count) ), 1.minute - ).run(player) - } - } yield () - } + ) + .run(player) + } + } yield () + } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/process/SynchronizationProcess.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/process/SynchronizationProcess.scala index 818d0612f7..cdf99006c3 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/process/SynchronizationProcess.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/process/SynchronizationProcess.scala @@ -12,26 +12,32 @@ object SynchronizationProcess { import cats.implicits._ - def using[ - F[_] : GrantFastDiggingEffect[*[_], Player] : MonadError[*[_], Throwable] : JavaTime, - Player - ](suppressionState: KeyedDataRepository[Player, ReadOnlyRef[F, FastDiggingEffectSuppressionState]], - effectClock: fs2.Stream[F, (Player, FastDiggingEffectList)]): fs2.Stream[F, Unit] = + def using[F[_]: GrantFastDiggingEffect[*[_], Player]: MonadError[ + *[_], + Throwable + ]: JavaTime, Player]( + suppressionState: KeyedDataRepository[ + Player, + ReadOnlyRef[F, FastDiggingEffectSuppressionState] + ], + effectClock: fs2.Stream[F, (Player, FastDiggingEffectList)] + ): fs2.Stream[F, Unit] = effectClock - .evalTap { case (player, list) => - for { - state <- suppressionState - .lift(player) - .map(_.read) - .getOrElse(Monad[F].pure(FastDiggingEffectSuppressionState.Disabled)) + .evalTap { + case (player, list) => + for { + state <- suppressionState + .lift(player) + .map(_.read) + .getOrElse(Monad[F].pure(FastDiggingEffectSuppressionState.Disabled)) - totalAmplifier <- list.totalPotionAmplifier[F](state) + totalAmplifier <- list.totalPotionAmplifier[F](state) - _ <- totalAmplifier.traverse { - // クロックが完全に同期していない(厳密に20ティックに1回ではない)ため、2秒間の効果を付与する - GrantFastDiggingEffect[F, Player].forTwoSeconds(player) - } - } yield () + _ <- totalAmplifier.traverse { + // クロックが完全に同期していない(厳密に20ティックに1回ではない)ため、2秒間の効果を付与する + GrantFastDiggingEffect[F, Player].forTwoSeconds(player) + } + } yield () } .as(()) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/repository/EffectListRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/repository/EffectListRepositoryDefinitions.scala index c1b39e6b25..a1c1280502 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/repository/EffectListRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/repository/EffectListRepositoryDefinitions.scala @@ -4,7 +4,10 @@ import cats.effect.concurrent.Deferred import cats.effect.{Concurrent, ConcurrentEffect, Effect, Fiber, Sync, SyncEffect, Timer} import com.github.unchama.datarepository.definitions.FiberAdjoinedRepositoryDefinition.FiberAdjoined import com.github.unchama.datarepository.template.finalization.RepositoryFinalization -import com.github.unchama.datarepository.template.initialization.{PrefetchResult, SinglePhasedRepositoryInitialization} +import com.github.unchama.datarepository.template.initialization.{ + PrefetchResult, + SinglePhasedRepositoryInitialization +} import com.github.unchama.fs2.workaround.fs3.Fs3Topic import com.github.unchama.generic.ContextCoercion import com.github.unchama.generic.effect.EffectExtra @@ -25,10 +28,8 @@ object EffectListRepositoryDefinitions { */ type RepositoryValue[F[_], G[_]] = Mutex[F, G, FastDiggingEffectList] FiberAdjoined F - def initialization[ - F[_] : Concurrent, - G[_] : Sync : ContextCoercion[*[_], F] - ]: SinglePhasedRepositoryInitialization[G, RepositoryValue[F, G]] = + def initialization[F[_]: Concurrent, G[_]: Sync: ContextCoercion[*[_], F]] + : SinglePhasedRepositoryInitialization[G, RepositoryValue[F, G]] = (_, _) => { for { ref <- Mutex.of[F, G, FastDiggingEffectList](FastDiggingEffectList.empty) @@ -36,39 +37,37 @@ object EffectListRepositoryDefinitions { } yield PrefetchResult.Success(ref, deferred) } - def tappingAction[ - F[_] : ConcurrentEffect : Timer : ErrorLogger, - G[_] : SyncEffect : ContextCoercion[*[_], F], - Player - ](effectTopic: Fs3Topic[F, Option[(Player, FastDiggingEffectList)]]): (Player, RepositoryValue[F, G]) => G[Unit] = { + def tappingAction[F[_]: ConcurrentEffect: Timer: ErrorLogger, G[ + _ + ]: SyncEffect: ContextCoercion[*[_], F], Player]( + effectTopic: Fs3Topic[F, Option[(Player, FastDiggingEffectList)]] + ): (Player, RepositoryValue[F, G]) => G[Unit] = { case (player, (mutexRef, fiberPromise)) => val programToRun: F[Unit] = - StreamExtra.compileToRestartingStream("[EffectListRepositoryDefinitions]") { - fs2.Stream - .fixedRate[F](1.second) - .evalMap { _ => + StreamExtra + .compileToRestartingStream("[EffectListRepositoryDefinitions]") { + fs2.Stream.fixedRate[F](1.second).evalMap { _ => ContextCoercion(mutexRef.readLatest).flatMap { latestEffectList => effectTopic.publish1(Some(player, latestEffectList)) } } - }.start >>= fiberPromise.complete + } + .start >>= fiberPromise.complete EffectExtra.runAsyncAndForget[F, G, Unit](programToRun) } - def finalization[ - F[_] : Effect, - G[_] : SyncEffect, - Player - ]: RepositoryFinalization[G, Player, RepositoryValue[F, G]] = - RepositoryFinalization.withoutAnyPersistence[G, Player, RepositoryValue[F, G]] { (_, value) => - val (_, fiberPromise) = value + def finalization[F[_]: Effect, G[_]: SyncEffect, Player] + : RepositoryFinalization[G, Player, RepositoryValue[F, G]] = + RepositoryFinalization.withoutAnyPersistence[G, Player, RepositoryValue[F, G]] { + (_, value) => + val (_, fiberPromise) = value - EffectExtra.runAsyncAndForget[F, G, Unit] { - for { - fiber <- fiberPromise.get - _ <- fiber.cancel - } yield () - } + EffectExtra.runAsyncAndForget[F, G, Unit] { + for { + fiber <- fiberPromise.get + _ <- fiber.cancel + } yield () + } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/repository/EffectStatsSettingsRepositoryDefinition.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/repository/EffectStatsSettingsRepositoryDefinition.scala index 0effeb12ff..4d6842013c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/repository/EffectStatsSettingsRepositoryDefinition.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/repository/EffectStatsSettingsRepositoryDefinition.scala @@ -2,14 +2,21 @@ package com.github.unchama.seichiassist.subsystems.fastdiggingeffect.application import cats.effect.concurrent.{Deferred, Ref} import cats.effect.{ConcurrentEffect, Fiber, Sync} -import com.github.unchama.datarepository.definitions.{FiberAdjoinedRepositoryDefinition, RefDictBackedRepositoryDefinition} +import com.github.unchama.datarepository.definitions.{ + FiberAdjoinedRepositoryDefinition, + RefDictBackedRepositoryDefinition +} import com.github.unchama.datarepository.template.RepositoryDefinition import com.github.unchama.generic.ContextCoercion import com.github.unchama.generic.effect.EffectExtra import com.github.unchama.generic.effect.stream.StreamExtra import com.github.unchama.minecraft.algebra.HasUuid import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.effect.FastDiggingEffectList -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.stats.{EffectListDiff, FastDiggingEffectStatsSettings, FastDiggingEffectStatsSettingsPersistence} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.stats.{ + EffectListDiff, + FastDiggingEffectStatsSettings, + FastDiggingEffectStatsSettingsPersistence +} import fs2.Pipe import io.chrisdavenport.cats.effect.time.JavaTime import io.chrisdavenport.log4cats.ErrorLogger @@ -19,49 +26,61 @@ object EffectStatsSettingsRepositoryDefinition { /** * [[FastDiggingEffectStatsSettings]] と、それをトピックに60秒に一度通知するプロセスの組 */ - type RepositoryValue[F[_], G[_]] = (Ref[G, FastDiggingEffectStatsSettings], Deferred[F, Fiber[F, Nothing]]) + type RepositoryValue[F[_], G[_]] = + (Ref[G, FastDiggingEffectStatsSettings], Deferred[F, Fiber[F, Nothing]]) import cats.effect.implicits._ import cats.implicits._ - def withContext[ - F[_] : ConcurrentEffect : JavaTime : ErrorLogger, - G[_] : Sync : ContextCoercion[*[_], F], - Player: HasUuid - ](persistence: FastDiggingEffectStatsSettingsPersistence[G], - publishEffectDiff: Pipe[F, (Player, (EffectListDiff, FastDiggingEffectStatsSettings)), Unit], - effectClock: fs2.Stream[F, (Player, FastDiggingEffectList)]): RepositoryDefinition[G, Player, RepositoryValue[F, G]] = { - FiberAdjoinedRepositoryDefinition.extending { - RefDictBackedRepositoryDefinition - .usingUuidRefDict[G, Player, FastDiggingEffectStatsSettings](persistence)(FastDiggingEffectStatsSettings.AlwaysReceiveDetails) - .toRefRepository - }.withAnotherTappingAction { case (player, pair) => - val (ref, fiberPromise) = pair + def withContext[F[_]: ConcurrentEffect: JavaTime: ErrorLogger, G[_]: Sync: ContextCoercion[*[ + _ + ], F], Player: HasUuid]( + persistence: FastDiggingEffectStatsSettingsPersistence[G], + publishEffectDiff: Pipe[ + F, + (Player, (EffectListDiff, FastDiggingEffectStatsSettings)), + Unit + ], + effectClock: fs2.Stream[F, (Player, FastDiggingEffectList)] + ): RepositoryDefinition[G, Player, RepositoryValue[F, G]] = { + FiberAdjoinedRepositoryDefinition + .extending { + RefDictBackedRepositoryDefinition + .usingUuidRefDict[G, Player, FastDiggingEffectStatsSettings](persistence)( + FastDiggingEffectStatsSettings.AlwaysReceiveDetails + ) + .toRefRepository + } + .withAnotherTappingAction { + case (player, pair) => + val (ref, fiberPromise) = pair - val processStream: fs2.Stream[F, Unit] = { - effectClock - .through(StreamExtra.valuesWithKeyOfSameUuidAs(player)) - .through(StreamExtra.takeEvery(60)) - .evalMap { list => list.filteredList } - .map(_.map(_.effect)) - .sliding(2) - .mapFilter { queue => - queue.lastOption.map { latest => - val previous = queue.dropRight(1).lastOption + val processStream: fs2.Stream[F, Unit] = { + effectClock + .through(StreamExtra.valuesWithKeyOfSameUuidAs(player)) + .through(StreamExtra.takeEvery(60)) + .evalMap { list => list.filteredList } + .map(_.map(_.effect)) + .sliding(2) + .mapFilter { queue => + queue.lastOption.map { latest => + val previous = queue.dropRight(1).lastOption - EffectListDiff(previous, latest) - } + EffectListDiff(previous, latest) + } + } + .evalMap { diff => ContextCoercion(ref.get.map(diff -> _)) } + .map(player -> _) + .through(publishEffectDiff) } - .evalMap { diff => ContextCoercion(ref.get.map(diff -> _)) } - .map(player -> _) - .through(publishEffectDiff) - } - EffectExtra.runAsyncAndForget[F, G, Unit] { - StreamExtra.compileToRestartingStream("[EffectStatsSettingsRepository]") { - processStream - }.start >>= fiberPromise.complete + EffectExtra.runAsyncAndForget[F, G, Unit] { + StreamExtra + .compileToRestartingStream("[EffectStatsSettingsRepository]") { + processStream + } + .start >>= fiberPromise.complete + } } - } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/repository/SuppressionSettingsRepositoryDefinition.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/repository/SuppressionSettingsRepositoryDefinition.scala index 955f16c157..9d14a7ad08 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/repository/SuppressionSettingsRepositoryDefinition.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/application/repository/SuppressionSettingsRepositoryDefinition.scala @@ -4,13 +4,16 @@ import cats.effect.Sync import cats.effect.concurrent.Ref import com.github.unchama.datarepository.definitions.RefDictBackedRepositoryDefinition import com.github.unchama.datarepository.template._ -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.settings.{FastDiggingEffectSuppressionState, FastDiggingEffectSuppressionStatePersistence} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.settings.{ + FastDiggingEffectSuppressionState, + FastDiggingEffectSuppressionStatePersistence +} object SuppressionSettingsRepositoryDefinition { - def withContext[ - G[_] : Sync, Player - ](persistence: FastDiggingEffectSuppressionStatePersistence[G]): RepositoryDefinition[G, Player, Ref[G, FastDiggingEffectSuppressionState]] = + def withContext[G[_]: Sync, Player]( + persistence: FastDiggingEffectSuppressionStatePersistence[G] + ): RepositoryDefinition[G, Player, Ref[G, FastDiggingEffectSuppressionState]] = RefDictBackedRepositoryDefinition .usingUuidRefDict(persistence)(FastDiggingEffectSuppressionState.EnabledWithoutLimit) .toRefRepository diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/bukkit/actions/GrantBukkitFastDiggingEffect.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/bukkit/actions/GrantBukkitFastDiggingEffect.scala index 812ddd8c39..174c6976b0 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/bukkit/actions/GrantBukkitFastDiggingEffect.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/bukkit/actions/GrantBukkitFastDiggingEffect.scala @@ -6,9 +6,8 @@ import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.actio import org.bukkit.entity.Player import org.bukkit.potion.{PotionEffect, PotionEffectType} -class GrantBukkitFastDiggingEffect[ - F[_] : Sync : OnMinecraftServerThread -] extends GrantFastDiggingEffect[F, Player] { +class GrantBukkitFastDiggingEffect[F[_]: Sync: OnMinecraftServerThread] + extends GrantFastDiggingEffect[F, Player] { override def forTwoSeconds(player: Player)(amount: Int): F[Unit] = { val potionEffect = new PotionEffect(PotionEffectType.FAST_DIGGING, 40, amount, false, false) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/actions/GrantFastDiggingEffect.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/actions/GrantFastDiggingEffect.scala index 75946c1e50..bd9ebac296 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/actions/GrantFastDiggingEffect.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/actions/GrantFastDiggingEffect.scala @@ -14,6 +14,8 @@ trait GrantFastDiggingEffect[F[_], Player] { object GrantFastDiggingEffect { - def apply[F[_], Player](implicit ev: GrantFastDiggingEffect[F, Player]): GrantFastDiggingEffect[F, Player] = ev + def apply[F[_], Player]( + implicit ev: GrantFastDiggingEffect[F, Player] + ): GrantFastDiggingEffect[F, Player] = ev } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingAmplifier.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingAmplifier.scala index a796313582..10503a1815 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingAmplifier.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingAmplifier.scala @@ -3,7 +3,8 @@ package com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.effe import cats.kernel.{Monoid, Order} /** - * @param hasteEffectLevel 付与すべき Haste 効果の強さ。たとえば、この値が 2.0 であれば Haste II が付与される。 + * @param hasteEffectLevel + * 付与すべき Haste 効果の強さ。たとえば、この値が 2.0 であれば Haste II が付与される。 */ case class FastDiggingAmplifier(hasteEffectLevel: Double) { @@ -16,22 +17,18 @@ case class FastDiggingAmplifier(hasteEffectLevel: Double) { val formatted: String = String.format("%,.2f", hasteEffectLevel) /** - * [[hasteEffectLevel]] を切り捨て、 0 とのmaxを取った値。 - * ポーション効果の強さとしてクライアントのUIに表示される値と同じ値が得られる。 + * [[hasteEffectLevel]] を切り捨て、 0 とのmaxを取った値。 ポーション効果の強さとしてクライアントのUIに表示される値と同じ値が得られる。 */ val normalizedEffectLevel: Int = hasteEffectLevel.floor.toInt max 0 /** * マインクラフトの "amplifier" 値としてこの値を変換する。 * - * マインクラフトはポーション効果値を "amplifier" という値を持っているが、 - * たとえば Haste II に対応する "amplifier" 値は 1 である。 + * マインクラフトはポーション効果値を "amplifier" という値を持っているが、 たとえば Haste II に対応する "amplifier" 値は 1 である。 * - * このように、表示される値より 1 少ない値を内部的に保持しているため、 - * この関数では [[normalizedEffectLevel]] から1を引いた数と0とのmaxを取っている。 + * このように、表示される値より 1 少ない値を内部的に保持しているため、 この関数では [[normalizedEffectLevel]] から1を引いた数と0とのmaxを取っている。 * - * [[hasteEffectLevel]] が一未満、つまり [[normalizedEffectLevel]] が0以下であるならば、 - * [[None]] となる。 + * [[hasteEffectLevel]] が一未満、つまり [[normalizedEffectLevel]] が0以下であるならば、 [[None]] となる。 */ val toMinecraftPotionAmplifier: Option[Int] = { val level = normalizedEffectLevel - 1 diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingEffect.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingEffect.scala index 9648690032..590a353e1f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingEffect.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingEffect.scala @@ -1,7 +1,6 @@ package com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.effect /** - * 単一の採掘速度上昇効果。 - * [[amplifier]]はマインクラフトの "Haste" ポーション効果と同等のスケールを持つ数値である。 + * 単一の採掘速度上昇効果。 [[amplifier]]はマインクラフトの "Haste" ポーション効果と同等のスケールを持つ数値である。 */ case class FastDiggingEffect(amplifier: FastDiggingAmplifier, cause: FastDiggingEffectCause) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingEffectCause.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingEffectCause.scala index bbe79877f2..b04553951b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingEffectCause.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingEffectCause.scala @@ -10,12 +10,11 @@ sealed abstract class FastDiggingEffectCause(val description: String) object FastDiggingEffectCause { case class FromConnectionNumber(number: Int) - extends FastDiggingEffectCause(s"接続人数(${number}人)") + extends FastDiggingEffectCause(s"接続人数(${number}人)") case class FromMinuteBreakCount(seichiExpAmount: SeichiExpAmount) - extends FastDiggingEffectCause(s"1分間の整地量(${seichiExpAmount.amount})") + extends FastDiggingEffectCause(s"1分間の整地量(${seichiExpAmount.amount})") - case object FromDragonNightTime - extends FastDiggingEffectCause("ドラゲナイタイムから") + case object FromDragonNightTime extends FastDiggingEffectCause("ドラゲナイタイムから") } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingEffectList.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingEffectList.scala index 5c27f79017..4adf5b1dfa 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingEffectList.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingEffectList.scala @@ -10,46 +10,42 @@ class FastDiggingEffectList(private val list: List[FastDiggingEffectTimings]) { import cats.implicits._ - def filterInactive[F[_] : JavaTime : Functor]: F[FastDiggingEffectList] = { + def filterInactive[F[_]: JavaTime: Functor]: F[FastDiggingEffectList] = { JavaTime[F].getLocalDateTimeUTC.fmap { currentTime => new FastDiggingEffectList(list.filter(_.isActiveAt(currentTime))) } } - def filteredList[F[_] : JavaTime : Functor]: F[List[FastDiggingEffectTimings]] = filterInactive[F].fmap(_.list) + def filteredList[F[_]: JavaTime: Functor]: F[List[FastDiggingEffectTimings]] = + filterInactive[F].fmap(_.list) /** * 効果を追加し、不要になった効果を削除した新しいリストを作成する作用。 */ - def appendEffect[ - F[_] : JavaTime : Applicative - ](effect: FastDiggingEffect, duration: FiniteDuration): F[FastDiggingEffectList] = { + def appendEffect[F[_]: JavaTime: Applicative]( + effect: FastDiggingEffect, + duration: FiniteDuration + ): F[FastDiggingEffectList] = { Applicative[F].map2( JavaTime[F].getLocalDateTimeUTC.fmap { currentTime => FastDiggingEffectTimings(currentTime, duration, effect) }, filteredList[F] - ) { (timings, filteredList) => - new FastDiggingEffectList(filteredList.appended(timings)) - } + ) { (timings, filteredList) => new FastDiggingEffectList(filteredList.appended(timings)) } } /** - * [[FastDiggingEffectSuppressionState]] を考慮した、 - * 現在有効な採掘速度上昇効果の合計値をMinecraftのポーション効果値として得る作用。 + * [[FastDiggingEffectSuppressionState]] を考慮した、 現在有効な採掘速度上昇効果の合計値をMinecraftのポーション効果値として得る作用。 * * 合計値が1未満だった場合、[[None]]が得られる。 */ - def totalPotionAmplifier[ - F[_] : JavaTime : Functor - ](suppressionSettings: FastDiggingEffectSuppressionState): F[Option[Int]] = { + def totalPotionAmplifier[F[_]: JavaTime: Functor]( + suppressionSettings: FastDiggingEffectSuppressionState + ): F[Option[Int]] = { filteredList[F].map { list => val totalAmplifier: FastDiggingAmplifier = list.map(_.effect.amplifier).combineAll val capped = - FastDiggingAmplifier.order.min( - totalAmplifier, - suppressionSettings.effectAmplifierCap - ) + FastDiggingAmplifier.order.min(totalAmplifier, suppressionSettings.effectAmplifierCap) capped.toMinecraftPotionAmplifier } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingEffectTimings.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingEffectTimings.scala index 46d01c5bbf..a70d2842c0 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingEffectTimings.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/effect/FastDiggingEffectTimings.scala @@ -8,9 +8,11 @@ import scala.concurrent.duration.{DurationInt, FiniteDuration} /** * 採掘速度上昇効果の付与時刻と効果時間をセットで持つデータ型。 */ -case class FastDiggingEffectTimings(givenAt: LocalDateTime, - totalDuration: FiniteDuration, - effect: FastDiggingEffect) { +case class FastDiggingEffectTimings( + givenAt: LocalDateTime, + totalDuration: FiniteDuration, + effect: FastDiggingEffect +) { def isActiveAt(time: LocalDateTime): Boolean = { val pastTime = LocalDateTimeUtil.difference(time, givenAt) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/settings/FastDiggingEffectSuppressionState.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/settings/FastDiggingEffectSuppressionState.scala index 6e20d30f40..c2852fd7e1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/settings/FastDiggingEffectSuppressionState.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/settings/FastDiggingEffectSuppressionState.scala @@ -2,27 +2,26 @@ package com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.sett import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.effect.FastDiggingAmplifier - sealed trait FastDiggingEffectSuppressionState { import FastDiggingEffectSuppressionState._ lazy val nextState: FastDiggingEffectSuppressionState = this match { - case EnabledWithoutLimit => EnabledWithLimit.Of_127 + case EnabledWithoutLimit => EnabledWithLimit.Of_127 case EnabledWithLimit.Of_127 => EnabledWithLimit.Of_200 case EnabledWithLimit.Of_200 => EnabledWithLimit.Of_400 case EnabledWithLimit.Of_400 => EnabledWithLimit.Of_600 case EnabledWithLimit.Of_600 => Disabled - case Disabled => EnabledWithoutLimit + case Disabled => EnabledWithoutLimit } lazy val effectAmplifierCap: FastDiggingAmplifier = { FastDiggingAmplifier { this match { - case EnabledWithoutLimit => 65535 + case EnabledWithoutLimit => 65535 case withLimit: EnabledWithLimit => withLimit.limit - case Disabled => 0 + case Disabled => 0 } } } @@ -32,7 +31,8 @@ object FastDiggingEffectSuppressionState { case object EnabledWithoutLimit extends FastDiggingEffectSuppressionState - abstract sealed class EnabledWithLimit(val limit: Int) extends FastDiggingEffectSuppressionState + abstract sealed class EnabledWithLimit(val limit: Int) + extends FastDiggingEffectSuppressionState object EnabledWithLimit { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/settings/FastDiggingEffectSuppressionStatePersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/settings/FastDiggingEffectSuppressionStatePersistence.scala index b59fe2b238..5ba2a09a5a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/settings/FastDiggingEffectSuppressionStatePersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/settings/FastDiggingEffectSuppressionStatePersistence.scala @@ -4,4 +4,5 @@ import com.github.unchama.generic.RefDict import java.util.UUID -trait FastDiggingEffectSuppressionStatePersistence[F[_]] extends RefDict[F, UUID, FastDiggingEffectSuppressionState] +trait FastDiggingEffectSuppressionStatePersistence[F[_]] + extends RefDict[F, UUID, FastDiggingEffectSuppressionState] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/stats/EffectListDiff.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/stats/EffectListDiff.scala index db35a00b6b..c06ff15176 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/stats/EffectListDiff.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/stats/EffectListDiff.scala @@ -1,8 +1,14 @@ package com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.stats -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.effect.{FastDiggingAmplifier, FastDiggingEffect} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.effect.{ + FastDiggingAmplifier, + FastDiggingEffect +} -case class EffectListDiff(oldList: Option[List[FastDiggingEffect]], newList: List[FastDiggingEffect]) { +case class EffectListDiff( + oldList: Option[List[FastDiggingEffect]], + newList: List[FastDiggingEffect] +) { import cats.implicits._ diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/stats/FastDiggingEffectStatsSettings.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/stats/FastDiggingEffectStatsSettings.scala index 501229bf7b..c54d72ab59 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/stats/FastDiggingEffectStatsSettings.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/stats/FastDiggingEffectStatsSettings.scala @@ -4,8 +4,10 @@ sealed trait FastDiggingEffectStatsSettings { final lazy val nextValue: FastDiggingEffectStatsSettings = this match { - case FastDiggingEffectStatsSettings.AlwaysReceiveDetails => FastDiggingEffectStatsSettings.ReceiveTotalAmplifierOnUpdate - case FastDiggingEffectStatsSettings.ReceiveTotalAmplifierOnUpdate => FastDiggingEffectStatsSettings.AlwaysReceiveDetails + case FastDiggingEffectStatsSettings.AlwaysReceiveDetails => + FastDiggingEffectStatsSettings.ReceiveTotalAmplifierOnUpdate + case FastDiggingEffectStatsSettings.ReceiveTotalAmplifierOnUpdate => + FastDiggingEffectStatsSettings.AlwaysReceiveDetails } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/stats/FastDiggingEffectStatsSettingsPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/stats/FastDiggingEffectStatsSettingsPersistence.scala index 0ee41044c2..55d906809c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/stats/FastDiggingEffectStatsSettingsPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/domain/stats/FastDiggingEffectStatsSettingsPersistence.scala @@ -5,4 +5,4 @@ import com.github.unchama.generic.RefDict import java.util.UUID trait FastDiggingEffectStatsSettingsPersistence[F[_]] - extends RefDict[F, UUID, FastDiggingEffectStatsSettings] + extends RefDict[F, UUID, FastDiggingEffectStatsSettings] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/infrastructure/JdbcFastDiggingEffectStatsSettingsPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/infrastructure/JdbcFastDiggingEffectStatsSettingsPersistence.scala index 40f30be19b..56d8fd3e63 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/infrastructure/JdbcFastDiggingEffectStatsSettingsPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/infrastructure/JdbcFastDiggingEffectStatsSettingsPersistence.scala @@ -1,15 +1,18 @@ package com.github.unchama.seichiassist.subsystems.fastdiggingeffect.infrastructure import cats.effect.Sync -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.stats.{FastDiggingEffectStatsSettings, FastDiggingEffectStatsSettingsPersistence} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.stats.{ + FastDiggingEffectStatsSettings, + FastDiggingEffectStatsSettingsPersistence +} import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} import java.util.UUID -class JdbcFastDiggingEffectStatsSettingsPersistence[F[_] : Sync] - extends FastDiggingEffectStatsSettingsPersistence[F] { +class JdbcFastDiggingEffectStatsSettingsPersistence[F[_]: Sync] + extends FastDiggingEffectStatsSettingsPersistence[F] { - //region コーデック + // region コーデック private def booleanToSettings(b: Boolean): FastDiggingEffectStatsSettings = if (b) @@ -19,11 +22,11 @@ class JdbcFastDiggingEffectStatsSettingsPersistence[F[_] : Sync] private def settingsToBoolean(s: FastDiggingEffectStatsSettings): Boolean = s match { - case FastDiggingEffectStatsSettings.AlwaysReceiveDetails => true + case FastDiggingEffectStatsSettings.AlwaysReceiveDetails => true case FastDiggingEffectStatsSettings.ReceiveTotalAmplifierOnUpdate => false } - //endregion + // endregion override def read(key: UUID): F[Option[FastDiggingEffectStatsSettings]] = Sync[F].delay { DB.localTx { implicit session => @@ -34,11 +37,14 @@ class JdbcFastDiggingEffectStatsSettingsPersistence[F[_] : Sync] } } - override def write(key: UUID, value: FastDiggingEffectStatsSettings): F[Unit] = Sync[F].delay { - DB.localTx { implicit session => - val encoded = settingsToBoolean(value) + override def write(key: UUID, value: FastDiggingEffectStatsSettings): F[Unit] = + Sync[F].delay { + DB.localTx { implicit session => + val encoded = settingsToBoolean(value) - sql"update playerdata set messageflag = $encoded where uuid = ${key.toString}".update().apply() + sql"update playerdata set messageflag = $encoded where uuid = ${key.toString}" + .update() + .apply() + } } - } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/infrastructure/JdbcFastDiggingEffectSuppressionStatePersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/infrastructure/JdbcFastDiggingEffectSuppressionStatePersistence.scala index 31df2f3fa0..79be1e9472 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/infrastructure/JdbcFastDiggingEffectSuppressionStatePersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fastdiggingeffect/infrastructure/JdbcFastDiggingEffectSuppressionStatePersistence.scala @@ -2,15 +2,18 @@ package com.github.unchama.seichiassist.subsystems.fastdiggingeffect.infrastruct import cats.effect.Sync import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.settings.FastDiggingEffectSuppressionState.EnabledWithLimit -import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.settings.{FastDiggingEffectSuppressionState, FastDiggingEffectSuppressionStatePersistence} +import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.settings.{ + FastDiggingEffectSuppressionState, + FastDiggingEffectSuppressionStatePersistence +} import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} import java.util.UUID -class JdbcFastDiggingEffectSuppressionStatePersistence[F[_] : Sync] - extends FastDiggingEffectSuppressionStatePersistence[F] { +class JdbcFastDiggingEffectSuppressionStatePersistence[F[_]: Sync] + extends FastDiggingEffectSuppressionStatePersistence[F] { - //region コーデック + // region コーデック private def intToSuppressionState(n: Int): FastDiggingEffectSuppressionState = n match { @@ -35,7 +38,7 @@ class JdbcFastDiggingEffectSuppressionStatePersistence[F[_] : Sync] case FastDiggingEffectSuppressionState.Disabled => 5 } - //endregion + // endregion override def read(key: UUID): F[Option[FastDiggingEffectSuppressionState]] = Sync[F].delay { DB.localTx { implicit session => @@ -46,12 +49,15 @@ class JdbcFastDiggingEffectSuppressionStatePersistence[F[_] : Sync] } } - override def write(key: UUID, value: FastDiggingEffectSuppressionState): F[Unit] = Sync[F].delay { - DB.localTx { implicit session => - val encoded = suppressionStateToInt(value) + override def write(key: UUID, value: FastDiggingEffectSuppressionState): F[Unit] = + Sync[F].delay { + DB.localTx { implicit session => + val encoded = suppressionStateToInt(value) - sql"update playerdata set effectflag = $encoded where uuid = ${key.toString}".update().apply() + sql"update playerdata set effectflag = $encoded where uuid = ${key.toString}" + .update() + .apply() + } } - } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/FourDimensionalPocketApi.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/FourDimensionalPocketApi.scala index 8c145ee984..080471e834 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/FourDimensionalPocketApi.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/FourDimensionalPocketApi.scala @@ -10,8 +10,7 @@ trait FourDimensionalPocketApi[F[_], Player] { /** * プレーヤーに関連付けられた四次元ポケットをプレーヤーに開かせる作用。 * - * プレーヤーが四次元ポケットへのアクセス権を(整地レベル制約により)持っていない場合、 - * この作用による副作用は一切起こらない。 + * プレーヤーが四次元ポケットへのアクセス権を(整地レベル制約により)持っていない場合、 この作用による副作用は一切起こらない。 */ val openPocketInventory: Kleisli[F, Player, Unit] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/System.scala index 03670ac035..b5f373a366 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/System.scala @@ -1,7 +1,6 @@ package com.github.unchama.seichiassist.subsystems.fourdimensionalpocket import cats.data.Kleisli -import cats.effect.IO import cats.effect.{ConcurrentEffect, Sync, SyncEffect} import com.github.unchama.datarepository.KeyedDataRepository import com.github.unchama.datarepository.bukkit.player.BukkitRepositoryControls @@ -12,12 +11,23 @@ import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.meta.subsystem.Subsystem import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountReadAPI import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.application.PocketInventoryRepositoryDefinition -import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.bukkit.commands.OpenPocketCommand +import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.bukkit.commands.{ + FourDimensionalPocketCommand, + OpenPocketCommand +} import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.bukkit.listeners.OpenPocketInventoryOnPlacingEnderPortalFrame -import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.bukkit.{CreateBukkitInventory, InteractBukkitInventory} -import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.bukkit.commands.FourDimensionalPocketCommand -import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.domain.actions.{CreateInventory, InteractInventory} -import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.domain.{PocketInventoryPersistence, PocketSize} +import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.bukkit.{ + CreateBukkitInventory, + InteractBukkitInventory +} +import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.domain.actions.{ + CreateInventory, + InteractInventory +} +import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.domain.{ + PocketInventoryPersistence, + PocketSize +} import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.infrastructure.JdbcBukkitPocketInventoryPersistence import io.chrisdavenport.log4cats.ErrorLogger import org.bukkit.Sound @@ -37,11 +47,11 @@ object System { import cats.implicits._ import com.github.unchama.minecraft.bukkit.algebra.BukkitPlayerHasUuid._ - def wired[ - F[_] : ConcurrentEffect : OnMinecraftServerThread : ErrorLogger, - G[_] : SyncEffect : ContextCoercion[*[_], F] - ](breakCountReadAPI: BreakCountReadAPI[F, G, Player]) - (implicit effectEnvironment: EffectEnvironment): F[System[F, Player]] = { + def wired[F[_]: ConcurrentEffect: OnMinecraftServerThread: ErrorLogger, G[ + _ + ]: SyncEffect: ContextCoercion[*[_], F]]( + breakCountReadAPI: BreakCountReadAPI[F, G, Player] + )(implicit effectEnvironment: EffectEnvironment): F[System[F, Player]] = { val persistence: PocketInventoryPersistence[G, Inventory] = new JdbcBukkitPocketInventoryPersistence[G] @@ -55,27 +65,25 @@ object System { pocketInventoryRepositoryHandles <- ContextCoercion { BukkitRepositoryControls.createHandles( - PocketInventoryRepositoryDefinition.withContext(persistence, breakCountReadAPI.seichiLevelUpdates) + PocketInventoryRepositoryDefinition + .withContext(persistence, breakCountReadAPI.seichiLevelUpdates) ) } } yield { implicit val systemApi = new FourDimensionalPocketApi[F, Player] { override val openPocketInventory: Kleisli[F, Player, Unit] = Kleisli { player => Sync[F].delay { - //開く音を再生 + // 開く音を再生 player.playSound(player.getLocation, Sound.BLOCK_ENDERCHEST_OPEN, 1f, 0.1f) } >> ContextCoercion { - pocketInventoryRepositoryHandles - .repository(player)._1 - .readLatest + pocketInventoryRepositoryHandles.repository(player)._1.readLatest }.flatMap(inventory => interactInventory.open(inventory)(player)) } - override val currentPocketSize: KeyedDataRepository[Player, ReadOnlyRef[F, PocketSize]] = { + override val currentPocketSize + : KeyedDataRepository[Player, ReadOnlyRef[F, PocketSize]] = { KeyedDataRepository.unlift { player => - pocketInventoryRepositoryHandles - .repository - .lift(player) - .map { case (mutex, _) => + pocketInventoryRepositoryHandles.repository.lift(player).map { + case (mutex, _) => ReadOnlyRef.fromAnySource { ContextCoercion { mutex @@ -83,7 +91,7 @@ object System { .map(inventory => PocketSize.fromTotalStackCount(inventory.getSize)) } } - } + } } } } @@ -93,11 +101,9 @@ object System { val openPocketCommand = new OpenPocketCommand[F]( - pocketInventoryRepositoryHandles - .repository - .map { - case (mutex, _) => ReadOnlyRef.fromAnySource(ContextCoercion(mutex.readLatest)) - }, + pocketInventoryRepositoryHandles.repository.map { + case (mutex, _) => ReadOnlyRef.fromAnySource(ContextCoercion(mutex.readLatest)) + }, persistence.coerceContextTo[F] ) new System[F, Player] { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/application/PocketInventoryRepositoryDefinition.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/application/PocketInventoryRepositoryDefinition.scala index 40fd42e04f..2e1a9ba7dd 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/application/PocketInventoryRepositoryDefinition.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/application/PocketInventoryRepositoryDefinition.scala @@ -2,7 +2,11 @@ package com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.applica import cats.effect.concurrent.Deferred import cats.effect.{ConcurrentEffect, Fiber, Sync} -import com.github.unchama.datarepository.definitions.{FiberAdjoinedRepositoryDefinition, MutexRepositoryDefinition, RefDictBackedRepositoryDefinition} +import com.github.unchama.datarepository.definitions.{ + FiberAdjoinedRepositoryDefinition, + MutexRepositoryDefinition, + RefDictBackedRepositoryDefinition +} import com.github.unchama.datarepository.template.RepositoryDefinition import com.github.unchama.generic.effect.EffectExtra import com.github.unchama.generic.effect.concurrent.Mutex @@ -10,8 +14,14 @@ import com.github.unchama.generic.effect.stream.StreamExtra import com.github.unchama.generic.{ContextCoercion, Diff} import com.github.unchama.minecraft.algebra.HasUuid import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.SeichiLevel -import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.domain.actions.{CreateInventory, InteractInventory} -import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.domain.{PocketInventoryPersistence, PocketSizeTable} +import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.domain.actions.{ + CreateInventory, + InteractInventory +} +import com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.domain.{ + PocketInventoryPersistence, + PocketSizeTable +} import io.chrisdavenport.log4cats.ErrorLogger object PocketInventoryRepositoryDefinition { @@ -22,41 +32,45 @@ object PocketInventoryRepositoryDefinition { /** * プレーヤーのポケットインベントリと、それを整地レベルに応じて更新するプロセスの組 */ - type RepositoryValue[F[_], G[_], Inventory] = (Mutex[F, G, Inventory], Deferred[F, Fiber[F, Nothing]]) - - def withContext[ - F[_] : ConcurrentEffect : ErrorLogger, - G[_] : Sync : ContextCoercion[*[_], F], - Player: HasUuid, - Inventory: CreateInventory[G, *] : InteractInventory[F, Player, *] - ](persistence: PocketInventoryPersistence[G, Inventory], levelStream: fs2.Stream[F, (Player, Diff[SeichiLevel])]) - : RepositoryDefinition[G, Player, RepositoryValue[F, G, Inventory]] = - FiberAdjoinedRepositoryDefinition.extending { - MutexRepositoryDefinition.over[F, G, Player, Inventory] { - RefDictBackedRepositoryDefinition.usingUuidRefDictWithEffectfulDefault(persistence) { - CreateInventory[G, Inventory].create(PocketSizeTable.default) + type RepositoryValue[F[_], G[_], Inventory] = + (Mutex[F, G, Inventory], Deferred[F, Fiber[F, Nothing]]) + + def withContext[F[_]: ConcurrentEffect: ErrorLogger, G[_]: Sync: ContextCoercion[ + *[_], + F + ], Player: HasUuid, Inventory: CreateInventory[G, *]: InteractInventory[F, Player, *]]( + persistence: PocketInventoryPersistence[G, Inventory], + levelStream: fs2.Stream[F, (Player, Diff[SeichiLevel])] + ): RepositoryDefinition[G, Player, RepositoryValue[F, G, Inventory]] = + FiberAdjoinedRepositoryDefinition + .extending { + MutexRepositoryDefinition.over[F, G, Player, Inventory] { + RefDictBackedRepositoryDefinition.usingUuidRefDictWithEffectfulDefault(persistence) { + CreateInventory[G, Inventory].create(PocketSizeTable.default) + } } } - }.withAnotherTappingAction { - (player, pair) => { - val (ref, fiberPromise) = pair - - val processStream: fs2.Stream[F, Unit] = { - levelStream - .through(StreamExtra.valuesWithKeyOfSameUuidAs(player)) - .evalMap { case Diff(_, right) => - val newSize = PocketSizeTable(right) - val update: Inventory => F[Inventory] = - InteractInventory[F, Player, Inventory].extendSize(newSize)(_) - - ref.lockAndUpdate(update).as(()) + .withAnotherTappingAction { (player, pair) => + { + val (ref, fiberPromise) = pair + + val processStream: fs2.Stream[F, Unit] = { + levelStream.through(StreamExtra.valuesWithKeyOfSameUuidAs(player)).evalMap { + case Diff(_, right) => + val newSize = PocketSizeTable(right) + val update: Inventory => F[Inventory] = + InteractInventory[F, Player, Inventory].extendSize(newSize)(_) + + ref.lockAndUpdate(update).as(()) } - } + } - EffectExtra.runAsyncAndForget[F, G, Unit] { - StreamExtra.compileToRestartingStream("[PocketInventoryRepository]")(processStream).start >>= - fiberPromise.complete + EffectExtra.runAsyncAndForget[F, G, Unit] { + StreamExtra + .compileToRestartingStream("[PocketInventoryRepository]")(processStream) + .start >>= + fiberPromise.complete + } } } - } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/CreateBukkitInventory.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/CreateBukkitInventory.scala index d4867e9046..9068f1e47c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/CreateBukkitInventory.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/CreateBukkitInventory.scala @@ -7,9 +7,7 @@ import org.bukkit.Bukkit import org.bukkit.ChatColor.{BOLD, DARK_PURPLE} import org.bukkit.inventory.Inventory -class CreateBukkitInventory[ - F[_] : Sync -] extends CreateInventory[F, Inventory] { +class CreateBukkitInventory[F[_]: Sync] extends CreateInventory[F, Inventory] { override def create(size: PocketSize): F[Inventory] = Sync[F].delay { Bukkit diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/InteractBukkitInventory.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/InteractBukkitInventory.scala index 56b364dbc7..cd8e57448f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/InteractBukkitInventory.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/InteractBukkitInventory.scala @@ -9,22 +9,20 @@ import org.bukkit.Material import org.bukkit.entity.Player import org.bukkit.inventory.{Inventory, ItemStack} -class InteractBukkitInventory[ - F[_] : Sync : OnMinecraftServerThread -] extends InteractInventory[F, Player, Inventory] { +class InteractBukkitInventory[F[_]: Sync: OnMinecraftServerThread] + extends InteractInventory[F, Player, Inventory] { import cats.implicits._ import scala.jdk.CollectionConverters._ override def open(inventory: Inventory)(player: Player): F[Unit] = - // インベントリの開閉はパケットが送られるためメインスレッドからのみ許可される(Spigot 1.12.2) + // インベントリの開閉はパケットが送られるためメインスレッドからのみ許可される(Spigot 1.12.2) OnMinecraftServerThread[F].runAction(SyncIO[Unit] { player.openInventory(inventory) }) - override def extendSize(newSize: PocketSize) - (inventory: Inventory): F[Inventory] = { + override def extendSize(newSize: PocketSize)(inventory: Inventory): F[Inventory] = { val shouldCreateNew: F[Boolean] = Sync[F].delay(inventory.getSize < newSize.totalStackCount) Monad[F].ifM(shouldCreateNew)( @@ -37,9 +35,10 @@ class InteractBukkitInventory[ newInventory <- new CreateBukkitInventory[F].create(newSize) _ <- Sync[F].delay { // 内容物を移動する。 - inventory.asScala.zipWithIndex.foreach { case (stack, i) => - inventory.setItem(i, new ItemStack(Material.AIR, 0)) - newInventory.setItem(i, stack) + inventory.asScala.zipWithIndex.foreach { + case (stack, i) => + inventory.setItem(i, new ItemStack(Material.AIR, 0)) + newInventory.setItem(i, stack) } } } yield newInventory, diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/commands/FourDimensionalPocketCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/commands/FourDimensionalPocketCommand.scala index 8a8109a490..d2c9e590b6 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/commands/FourDimensionalPocketCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/commands/FourDimensionalPocketCommand.scala @@ -7,15 +7,15 @@ import org.bukkit.command.TabExecutor import org.bukkit.entity.Player /** - * プレイヤーが自分の四次元ポケットを開くコマンド - * StickMenuCommand.scalaを参考に作成 + * プレイヤーが自分の四次元ポケットを開くコマンド StickMenuCommand.scalaを参考に作成 */ object FourDimensionalPocketCommand { - def executor[F[_]: ConcurrentEffect](implicit api: FourDimensionalPocketApi[F, Player]): TabExecutor = { + def executor[F[_]: ConcurrentEffect]( + implicit api: FourDimensionalPocketApi[F, Player] + ): TabExecutor = { playerCommandBuilder .executionF(context => api.openPocketInventory(context.sender)) .build() .asNonBlockingTabExecutor() } } - diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/commands/OpenPocketCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/commands/OpenPocketCommand.scala index 426891ac9d..be10e1cf2f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/commands/OpenPocketCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/commands/OpenPocketCommand.scala @@ -19,19 +19,13 @@ import org.bukkit.inventory.Inventory import java.util.UUID -class OpenPocketCommand[ - F[_] - : Effect - : InteractInventory[*[_], Player, Inventory] -](repository: KeyedDataRepository[Player, ReadOnlyRef[F, Inventory]], - persistence: RefDict[F, UUID, Inventory]) { +class OpenPocketCommand[F[_]: Effect: InteractInventory[*[_], Player, Inventory]]( + repository: KeyedDataRepository[Player, ReadOnlyRef[F, Inventory]], + persistence: RefDict[F, UUID, Inventory] +) { private val descriptionPrintExecutor = new EchoExecutor(MessageEffect { - List( - s"$RED/openpocket [プレイヤー名]", - "対象プレイヤーの四次元ポケットを開きます。", - "編集結果はオンラインのプレイヤーにのみ反映されます。" - ) + List(s"$RED/openpocket [プレイヤー名]", "対象プレイヤーの四次元ポケットを開きます。", "編集結果はオンラインのプレイヤーにのみ反映されます。") }) import cats.effect.implicits._ diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/listeners/OpenPocketInventoryOnPlacingEnderPortalFrame.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/listeners/OpenPocketInventoryOnPlacingEnderPortalFrame.scala index e2b52fea6f..94dd36e456 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/listeners/OpenPocketInventoryOnPlacingEnderPortalFrame.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/bukkit/listeners/OpenPocketInventoryOnPlacingEnderPortalFrame.scala @@ -10,9 +10,10 @@ import org.bukkit.event.player.PlayerInteractEvent import org.bukkit.event.{EventHandler, Listener} import org.bukkit.inventory.EquipmentSlot -class OpenPocketInventoryOnPlacingEnderPortalFrame[ - F[_] : Effect -](api: FourDimensionalPocketApi[F, Player], effectEnvironment: EffectEnvironment) extends Listener { +class OpenPocketInventoryOnPlacingEnderPortalFrame[F[_]: Effect]( + api: FourDimensionalPocketApi[F, Player], + effectEnvironment: EffectEnvironment +) extends Listener { @EventHandler def onInteractEvent(event: PlayerInteractEvent): Unit = { @@ -25,16 +26,15 @@ class OpenPocketInventoryOnPlacingEnderPortalFrame[ return } - if (hand == EquipmentSlot.OFF_HAND || !(action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK)) { + if ( + hand == EquipmentSlot.OFF_HAND || !(action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK) + ) { return } - //設置をキャンセル + // 設置をキャンセル event.setCancelled(true) - effectEnvironment.unsafeRunEffectAsync( - "ポケットインベントリを開く", - api.openPocketInventory(player) - ) + effectEnvironment.unsafeRunEffectAsync("ポケットインベントリを開く", api.openPocketInventory(player)) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/domain/PocketSize.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/domain/PocketSize.scala index c6d4029562..9ea1921ccb 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/domain/PocketSize.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/domain/PocketSize.scala @@ -1,10 +1,7 @@ package com.github.unchama.seichiassist.subsystems.fourdimensionalpocket.domain case class PocketSize(chestRows: Int) { - require( - 1 <= chestRows && chestRows <= 6, - "chestRows should be in [1, 6]" - ) + require(1 <= chestRows && chestRows <= 6, "chestRows should be in [1, 6]") lazy val totalStackCount: Int = chestRows * 9 } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/domain/actions/CreateInventory.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/domain/actions/CreateInventory.scala index ff966453c3..c734c68f80 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/domain/actions/CreateInventory.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/domain/actions/CreateInventory.scala @@ -13,6 +13,8 @@ trait CreateInventory[F[_], Inventory] { object CreateInventory { - def apply[F[_], Inventory](implicit ev: CreateInventory[F, Inventory]): CreateInventory[F, Inventory] = ev + def apply[F[_], Inventory]( + implicit ev: CreateInventory[F, Inventory] + ): CreateInventory[F, Inventory] = ev } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/domain/actions/InteractInventory.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/domain/actions/InteractInventory.scala index 2ce39ea4a5..89ab466879 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/domain/actions/InteractInventory.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/domain/actions/InteractInventory.scala @@ -13,15 +13,14 @@ trait InteractInventory[F[_], Player, Inventory] { * ポケットインベントリを最低[[PocketSize]]にまで拡張した結果として得られる新しいインベントリを作成する作用。 * * この作用は、以下の二つのうちどちらかを行う: - * - `inventory` 自体のサイズを拡張し、 `inventory` を結果として返す + * - `inventory` 自体のサイズを拡張し、 `inventory` を結果として返す * * または、マインクラフトのメインスレッド上にて * - * - `inventory` を開いているすべてのプレーヤーにインベントリを閉じさせる - * - 新しいインベントリ `i` を作成し、 `inventory` から `i` にアイテムをすべて移し替え、 `i` を結果として返す + * - `inventory` を開いているすべてのプレーヤーにインベントリを閉じさせる + * - 新しいインベントリ `i` を作成し、 `inventory` から `i` にアイテムをすべて移し替え、 `i` を結果として返す * - * よって、プレーヤーにインベントリを紐づけ、サイズを拡張する時に、 - * この作用により得たインベントリをすぐに紐づけなおすという操作をすることで + * よって、プレーヤーにインベントリを紐づけ、サイズを拡張する時に、 この作用により得たインベントリをすぐに紐づけなおすという操作をすることで * 紐づいていたインベントリをプレーヤーが開いていたとしてもアイテムの増殖や喪失が起こることは無い。 */ def extendSize(newSize: PocketSize)(inventory: Inventory): F[Inventory] @@ -30,6 +29,7 @@ trait InteractInventory[F[_], Player, Inventory] { object InteractInventory { - def apply[F[_], P, I](implicit ev: InteractInventory[F, P, I]): InteractInventory[F, P, I] = ev + def apply[F[_], P, I](implicit ev: InteractInventory[F, P, I]): InteractInventory[F, P, I] = + ev } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/infrastructure/JdbcBukkitPocketInventoryPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/infrastructure/JdbcBukkitPocketInventoryPersistence.scala index 20e0222882..30946eca13 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/infrastructure/JdbcBukkitPocketInventoryPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/fourdimensionalpocket/infrastructure/JdbcBukkitPocketInventoryPersistence.scala @@ -8,7 +8,8 @@ import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} import java.util.UUID -class JdbcBukkitPocketInventoryPersistence[F[_] : Sync] extends PocketInventoryPersistence[F, Inventory] { +class JdbcBukkitPocketInventoryPersistence[F[_]: Sync] + extends PocketInventoryPersistence[F, Inventory] { // TODO BukkitSerializationのロジックをこっちに持ってくる @@ -21,12 +22,13 @@ class JdbcBukkitPocketInventoryPersistence[F[_] : Sync] extends PocketInventoryP } } - override def write(key: UUID, value: Inventory): F[Unit] = Sync[F].delay { DB.localTx { implicit session => val encoded = BukkitSerialization.toBase64(value) - sql"update playerdata set inventory = $encoded where uuid = ${key.toString}".update().apply() + sql"update playerdata set inventory = $encoded where uuid = ${key.toString}" + .update() + .apply() } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/System.scala index df2a870de4..0252aeb34b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/System.scala @@ -31,11 +31,11 @@ object System { import cats.effect.implicits._ import cats.implicits._ - def wired[ - F[_] : ConcurrentEffect : Timer : GetConnectedPlayers[*[_], Player] : ErrorLogger, - G[_] : SyncEffect - ](breakCountReadAPI: BreakCountReadAPI[F, G, Player]) - (implicit ioOnMainThread: OnMinecraftServerThread[IO]): G[System[F, G, Player]] = { + def wired[F[_]: ConcurrentEffect: Timer: GetConnectedPlayers[*[_], Player]: ErrorLogger, G[ + _ + ]: SyncEffect]( + breakCountReadAPI: BreakCountReadAPI[F, G, Player] + )(implicit ioOnMainThread: OnMinecraftServerThread[IO]): G[System[F, G, Player]] = { import com.github.unchama.minecraft.bukkit.algebra.BukkitPlayerHasUuid.instance val gachaPointPersistence = new JdbcGachaPointPersistence[G] @@ -46,15 +46,19 @@ object System { for { gachaPointRepositoryControls <- BukkitRepositoryControls.createHandles( - GachaPointRepositoryDefinition.withContext[G, F, Player](gachaPointPersistence)(grantEffectFactory) + GachaPointRepositoryDefinition + .withContext[G, F, Player](gachaPointPersistence)(grantEffectFactory) ) _ <- { val gachaPointRepository = - gachaPointRepositoryControls.repository.map(_.pointRef.mapK[F](ContextCoercion.asFunctionK)) + gachaPointRepositoryControls + .repository + .map(_.pointRef.mapK[F](ContextCoercion.asFunctionK)) val streams: List[fs2.Stream[F, Unit]] = List( - AddSeichiExpAsGachaPoint.stream(gachaPointRepository)(breakCountReadAPI.seichiAmountIncreases), + AddSeichiExpAsGachaPoint + .stream(gachaPointRepository)(breakCountReadAPI.seichiAmountIncreases) ) EffectExtra.runAsyncAndForget[F, G, Unit] { @@ -75,20 +79,17 @@ object System { gachaPointRepositoryControls .repository .lift(player) - .traverse { value => - value.semaphore.tryBatchTransaction - } + .traverse { value => value.semaphore.tryBatchTransaction } .as(()) } - override def addGachaPoint(point: GachaPoint): Kleisli[G, Player, Unit] = Kleisli { player => - gachaPointRepositoryControls - .repository - .lift(player) - .traverse { value => - value.pointRef.update(_.add(point)) - } - .as(()) + override def addGachaPoint(point: GachaPoint): Kleisli[G, Player, Unit] = Kleisli { + player => + gachaPointRepositoryControls + .repository + .lift(player) + .traverse { value => value.pointRef.update(_.add(point)) } + .as(()) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/application/process/AddSeichiExpAsGachaPoint.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/application/process/AddSeichiExpAsGachaPoint.scala index d4aac87e75..8bf3ed299b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/application/process/AddSeichiExpAsGachaPoint.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/application/process/AddSeichiExpAsGachaPoint.scala @@ -11,17 +11,14 @@ object AddSeichiExpAsGachaPoint { import cats.implicits._ - def stream[ - F[_] : Applicative, Player: HasUuid - ](refRepository: KeyedDataRepository[Player, Ref[F, GachaPoint]]) - (seichiExpStream: fs2.Stream[F, (Player, SeichiExpAmount)]): fs2.Stream[F, Unit] = - seichiExpStream - .evalMap { case (player, amount) => + def stream[F[_]: Applicative, Player: HasUuid]( + refRepository: KeyedDataRepository[Player, Ref[F, GachaPoint]] + )(seichiExpStream: fs2.Stream[F, (Player, SeichiExpAmount)]): fs2.Stream[F, Unit] = + seichiExpStream.evalMap { + case (player, amount) => val point = GachaPoint(amount) - refRepository.lift(player).traverse(ref => - ref.update(_.add(point)) - ).as(()) - } + refRepository.lift(player).traverse(ref => ref.update(_.add(point))).as(()) + } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/application/repository/GachaPointRepositoryDefinition.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/application/repository/GachaPointRepositoryDefinition.scala index da1b0fa7b5..13f994019b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/application/repository/GachaPointRepositoryDefinition.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/application/repository/GachaPointRepositoryDefinition.scala @@ -7,25 +7,31 @@ import com.github.unchama.datarepository.definitions.RefDictBackedRepositoryDefi import com.github.unchama.datarepository.template._ import com.github.unchama.generic.ContextCoercion import com.github.unchama.minecraft.algebra.HasUuid -import com.github.unchama.seichiassist.subsystems.gachapoint.domain.gachapoint.{GachaPoint, GachaPointPersistence} -import com.github.unchama.seichiassist.subsystems.gachapoint.domain.{BatchUsageSemaphore, GrantGachaTicketToAPlayer} +import com.github.unchama.seichiassist.subsystems.gachapoint.domain.gachapoint.{ + GachaPoint, + GachaPointPersistence +} +import com.github.unchama.seichiassist.subsystems.gachapoint.domain.{ + BatchUsageSemaphore, + GrantGachaTicketToAPlayer +} object GachaPointRepositoryDefinition { type TemporaryValue[F[_]] = Ref[F, GachaPoint] - case class RepositoryValue[F[_], G[_]](pointRef: Ref[G, GachaPoint], - semaphore: BatchUsageSemaphore[F, G]) + case class RepositoryValue[F[_], G[_]]( + pointRef: Ref[G, GachaPoint], + semaphore: BatchUsageSemaphore[F, G] + ) import cats.implicits._ - def withContext[ - G[_] : Sync : ContextCoercion[*[_], F], - F[_] : Concurrent : Timer, - Player: HasUuid - ](persistence: GachaPointPersistence[G]) - (grantEffectFactory: Player => GrantGachaTicketToAPlayer[F]) - : RepositoryDefinition[G, Player, RepositoryValue[F, G]] = + def withContext[G[_]: Sync: ContextCoercion[*[_], F], F[ + _ + ]: Concurrent: Timer, Player: HasUuid](persistence: GachaPointPersistence[G])( + grantEffectFactory: Player => GrantGachaTicketToAPlayer[F] + ): RepositoryDefinition[G, Player, RepositoryValue[F, G]] = RefDictBackedRepositoryDefinition .usingUuidRefDict[G, Player, GachaPoint](persistence)(GachaPoint.initial) .toRefRepository diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/bukkit/GrantBukkitGachaTicketToAPlayer.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/bukkit/GrantBukkitGachaTicketToAPlayer.scala index c5689df649..61b2380bfd 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/bukkit/GrantBukkitGachaTicketToAPlayer.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/bukkit/GrantBukkitGachaTicketToAPlayer.scala @@ -13,10 +13,9 @@ import org.bukkit.ChatColor.{GOLD, WHITE} import org.bukkit.Sound import org.bukkit.entity.Player -case class GrantBukkitGachaTicketToAPlayer[ - F[_] : LiftIO -](player: Player) - (implicit ioOnMainThread: OnMinecraftServerThread[IO]) extends GrantGachaTicketToAPlayer[F] { +case class GrantBukkitGachaTicketToAPlayer[F[_]: LiftIO](player: Player)( + implicit ioOnMainThread: OnMinecraftServerThread[IO] +) extends GrantGachaTicketToAPlayer[F] { override def give(count: Int): F[Unit] = { val effect = diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/domain/BatchUsageSemaphore.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/domain/BatchUsageSemaphore.scala index 8e0ebeccdf..b34aba3816 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/domain/BatchUsageSemaphore.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/domain/BatchUsageSemaphore.scala @@ -10,24 +10,20 @@ import com.github.unchama.seichiassist.subsystems.gachapoint.domain.gachapoint.G /** * 特定のプレーヤーについてガチャポイント変換の制御を提供するオブジェクトのクラス。 */ -class BatchUsageSemaphore[ - F[_] : FlatMap, - G[_] : ContextCoercion[*[_], F] -](gachaPointRef: Ref[G, GachaPoint], grantAction: GrantGachaTicketToAPlayer[F]) - (recoveringSemaphore: RecoveringSemaphore[F]) { +class BatchUsageSemaphore[F[_]: FlatMap, G[_]: ContextCoercion[*[_], F]]( + gachaPointRef: Ref[G, GachaPoint], + grantAction: GrantGachaTicketToAPlayer[F] +)(recoveringSemaphore: RecoveringSemaphore[F]) { import cats.implicits._ /** - * バッチでのガチャポイント変換を行い、 - * [[BatchUsageSemaphore.usageInterval]]の間使用不可にする作用。 + * バッチでのガチャポイント変換を行い、 [[BatchUsageSemaphore.usageInterval]]の間使用不可にする作用。 */ def tryBatchTransaction: F[Unit] = recoveringSemaphore.tryUse { ContextCoercion { - gachaPointRef.modify { point => - point.useInBatch.asTuple - } + gachaPointRef.modify { point => point.useInBatch.asTuple } }.flatTap(grantAction.give) }(BatchUsageSemaphore.usageInterval) @@ -45,13 +41,12 @@ object BatchUsageSemaphore { final val usageInterval = 1.second /** - * プレーヤーが持つガチャポイントとプレーヤーへガチャ券を与える作用から - * [[BatchUsageSemaphore]]を作成する。 + * プレーヤーが持つガチャポイントとプレーヤーへガチャ券を与える作用から [[BatchUsageSemaphore]]を作成する。 */ - def newIn[ - G[_] : Sync : ContextCoercion[*[_], F], - F[_] : Concurrent : Timer - ](gachaPointRef: Ref[G, GachaPoint], grantAction: GrantGachaTicketToAPlayer[F]): G[BatchUsageSemaphore[F, G]] = + def newIn[G[_]: Sync: ContextCoercion[*[_], F], F[_]: Concurrent: Timer]( + gachaPointRef: Ref[G, GachaPoint], + grantAction: GrantGachaTicketToAPlayer[F] + ): G[BatchUsageSemaphore[F, G]] = RecoveringSemaphore .newIn[G, F] .map(rs => new BatchUsageSemaphore(gachaPointRef, grantAction)(rs)) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/domain/gachapoint/GachaPoint.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/domain/gachapoint/GachaPoint.scala index bc589bfe83..43a9ff8d5b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/domain/gachapoint/GachaPoint.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/domain/gachapoint/GachaPoint.scala @@ -12,7 +12,8 @@ case class GachaPoint(exp: SeichiExpAmount) { /** * このガチャポイント量をすべて消費して得られるチケット数。 */ - lazy val availableTickets: BigInt = (exp.amount /% GachaPoint.perGachaTicket.exp.amount)._1.toBigInt + lazy val availableTickets: BigInt = + (exp.amount /% GachaPoint.perGachaTicket.exp.amount)._1.toBigInt /** * ガチャポイントをバッチでガチャ券に変換した際のポイントの変化を計算する。 @@ -45,11 +46,12 @@ object GachaPoint { /** * ガチャポイントを使用してガチャ券へと変換した結果。 - * @param remainingGachaPoint 変換後に残っているガチャポイント - * @param gachaTicketCount 変換にて得られるガチャ券の総数 + * @param remainingGachaPoint + * 変換後に残っているガチャポイント + * @param gachaTicketCount + * 変換にて得られるガチャ券の総数 */ - case class Usage(remainingGachaPoint: GachaPoint, - gachaTicketCount: Int) { + case class Usage(remainingGachaPoint: GachaPoint, gachaTicketCount: Int) { require(gachaTicketCount <= GachaPoint.batchSize, "usage must not exceed batch size") def asTuple: (GachaPoint, Int) = (remainingGachaPoint, gachaTicketCount) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/infrastructure/JdbcGachaPointPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/infrastructure/JdbcGachaPointPersistence.scala index 9ae2cd82ba..405d324c9d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/infrastructure/JdbcGachaPointPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/infrastructure/JdbcGachaPointPersistence.scala @@ -1,12 +1,15 @@ package com.github.unchama.seichiassist.subsystems.gachapoint.infrastructure import cats.effect.Sync -import com.github.unchama.seichiassist.subsystems.gachapoint.domain.gachapoint.{GachaPoint, GachaPointPersistence} +import com.github.unchama.seichiassist.subsystems.gachapoint.domain.gachapoint.{ + GachaPoint, + GachaPointPersistence +} import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} import java.util.UUID -class JdbcGachaPointPersistence[F[_] : Sync] extends GachaPointPersistence[F] { +class JdbcGachaPointPersistence[F[_]: Sync] extends GachaPointPersistence[F] { private def encode(gachaPoint: GachaPoint): BigInt = gachaPoint.exp.amount.toBigInt @@ -25,7 +28,8 @@ class JdbcGachaPointPersistence[F[_] : Sync] extends GachaPointPersistence[F] { override def write(key: UUID, value: GachaPoint): F[Unit] = Sync[F].delay { DB.localTx { implicit session => sql"update playerdata set gachapoint = ${encode(value)} where uuid = ${key.toString}" - .update().apply() + .update() + .apply() } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/halfhourranking/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/halfhourranking/System.scala index 8805bca79a..514d80b0e8 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/halfhourranking/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/halfhourranking/System.scala @@ -4,7 +4,11 @@ import cats.effect.{Concurrent, Timer} import cats.{Applicative, Functor} import com.github.unchama.generic.ContextCoercion import com.github.unchama.generic.effect.stream.StreamExtra -import com.github.unchama.minecraft.actions.{BroadcastMinecraftMessage, OnMinecraftServerThread, SendMinecraftMessage} +import com.github.unchama.minecraft.actions.{ + BroadcastMinecraftMessage, + OnMinecraftServerThread, + SendMinecraftMessage +} import com.github.unchama.minecraft.bukkit.actions.{BroadcastBukkitMessage, SendBukkitMessage} import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountReadAPI import com.github.unchama.seichiassist.subsystems.halfhourranking.application.AnnounceRankingRecord @@ -18,24 +22,24 @@ object System { import scala.concurrent.duration._ - def backgroundProcess[ - F[_] - : OnMinecraftServerThread - : Timer - : Concurrent - : ErrorLogger, - G[_] - : ContextCoercion[*[_], F] - : Functor - ](implicit breakCountReadAPI: BreakCountReadAPI[F, G, Player]): F[Nothing] = { + def backgroundProcess[F[_]: OnMinecraftServerThread: Timer: Concurrent: ErrorLogger, G[ + _ + ]: ContextCoercion[*[_], F]: Functor]( + implicit breakCountReadAPI: BreakCountReadAPI[F, G, Player] + ): F[Nothing] = { implicit val sendBukkitMessage: SendMinecraftMessage[F, Player] = SendBukkitMessage[F] - implicit val broadcastBukkitMessage: BroadcastMinecraftMessage[F] = BroadcastBukkitMessage[F] + implicit val broadcastBukkitMessage: BroadcastMinecraftMessage[F] = + BroadcastBukkitMessage[F] StreamExtra.compileToRestartingStream("[HalfHourRanking]") { breakCountReadAPI .batchedIncreases(30.minutes) .map(RankingRecord.apply) - .evalTap(AnnounceRankingRecord[F, G, Player](breakCountReadAPI)(p => Applicative[F].pure(p.getName))) + .evalTap( + AnnounceRankingRecord[F, G, Player](breakCountReadAPI)(p => + Applicative[F].pure(p.getName) + ) + ) } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/halfhourranking/application/AnnounceRankingRecord.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/halfhourranking/application/AnnounceRankingRecord.scala index f3a1a70dac..1d5f187117 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/halfhourranking/application/AnnounceRankingRecord.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/halfhourranking/application/AnnounceRankingRecord.scala @@ -14,34 +14,26 @@ object AnnounceRankingRecord { import cats.implicits._ - def apply[ - F[_] - : Monad - : SendMinecraftMessage[*[_], Player] - : BroadcastMinecraftMessage, - G[_] - : ContextCoercion[*[_], F] - : Functor, - Player: HasUuid - ](breakCountReadApi: BreakCountReadAPI[F, G, Player]) - (resolveName: Player => F[String]): RankingRecord[Player] => F[Unit] = { rankingRecord => + def apply[F[_]: Monad: SendMinecraftMessage[*[_], Player]: BroadcastMinecraftMessage, G[ + _ + ]: ContextCoercion[*[_], F]: Functor, Player: HasUuid]( + breakCountReadApi: BreakCountReadAPI[F, G, Player] + )(resolveName: Player => F[String]): RankingRecord[Player] => F[Unit] = { rankingRecord => val rankingPositionColor = List(LIGHT_PURPLE, YELLOW, AQUA) val sortedNonzeroRecords = rankingRecord.getSortedNonzeroRecords - val totalBreakCount = sortedNonzeroRecords.map(_._2).foldLeft(SeichiExpAmount.zero)(_.add(_)) + val totalBreakCount = + sortedNonzeroRecords.map(_._2).foldLeft(SeichiExpAmount.zero)(_.add(_)) val individualAnnouncements = - sortedNonzeroRecords.map { case (player, seichiExpAmount) => - SendMinecraftMessage[F, Player].string( - player, - s"あなたの整地量は $AQUA${seichiExpAmount.formatted}$WHITE でした" - ) + sortedNonzeroRecords.map { + case (player, seichiExpAmount) => + SendMinecraftMessage[F, Player] + .string(player, s"あなたの整地量は $AQUA${seichiExpAmount.formatted}$WHITE でした") } - val rankingAnnouncement = sortedNonzeroRecords - .zip(rankingPositionColor) - .zipWithIndex - .map { case (((player, seichiExpAmount), decorationColorCode), index) => + val rankingAnnouncement = sortedNonzeroRecords.zip(rankingPositionColor).zipWithIndex.map { + case (((player, seichiExpAmount), decorationColorCode), index) => val position = index + 1 val increaseAmountText = s"$AQUA${seichiExpAmount.formatted}$WHITE" @@ -49,7 +41,8 @@ object AnnounceRankingRecord { name <- resolveName(player) seichiExpAmountData <- ContextCoercion { - breakCountReadApi.seichiAmountDataRepository + breakCountReadApi + .seichiAmountDataRepository .lift(player) .map[G[Option[SeichiAmountData]]](_.read.map(Some(_))) .getOrElse { @@ -70,24 +63,18 @@ object AnnounceRankingRecord { case None => s"$decorationColorCode$name$WHITE" } - _ <- BroadcastMinecraftMessage[F].string( - s"整地量第${position}位は${playerNameText}で、整地量は${increaseAmountText}でした" - ) + _ <- BroadcastMinecraftMessage[F] + .string(s"整地量第${position}位は${playerNameText}で、整地量は${increaseAmountText}でした") } yield () - } + } val actions = List( - BroadcastMinecraftMessage[F].string( - "--------------30分間整地ランキング--------------" - ) + BroadcastMinecraftMessage[F].string("--------------30分間整地ランキング--------------") ) ++ individualAnnouncements ++ List( - BroadcastMinecraftMessage[F].string( - s"全体の整地量は $AQUA${totalBreakCount.formatted}$WHITE でした" - ) + BroadcastMinecraftMessage[F] + .string(s"全体の整地量は $AQUA${totalBreakCount.formatted}$WHITE でした") ) ++ rankingAnnouncement ++ List( - BroadcastMinecraftMessage[F].string( - "--------------------------------------------------" - ) + BroadcastMinecraftMessage[F].string("--------------------------------------------------") ) actions.sequence.as(()) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/halfhourranking/domain/RankingRecord.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/halfhourranking/domain/RankingRecord.scala index 1ab1545d4e..044563702d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/halfhourranking/domain/RankingRecord.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/halfhourranking/domain/RankingRecord.scala @@ -9,7 +9,9 @@ import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.Seichi */ case class RankingRecord[Player](batch: BatchedSeichiExpMap[Player]) { - def getSortedNonzeroRecords(implicit playerHasUuid: HasUuid[Player]): List[(Player, SeichiExpAmount)] = + def getSortedNonzeroRecords( + implicit playerHasUuid: HasUuid[Player] + ): List[(Player, SeichiExpAmount)] = batch .toUuidCollatedList .filter(_._2 != SeichiExpAmount.zero) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/EntryPoints.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/EntryPoints.scala index 3935b8225c..6feeb50470 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/EntryPoints.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/EntryPoints.scala @@ -4,7 +4,7 @@ import cats.effect.{IO, SyncEffect} trait EntryPoints { - def runDatabaseMigration[F[_] : SyncEffect]: F[Unit] + def runDatabaseMigration[F[_]: SyncEffect]: F[Unit] def runWorldMigration: IO[Unit] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/System.scala index 1125f96635..257ef27139 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/System.scala @@ -10,7 +10,10 @@ import com.github.unchama.itemmigration.bukkit.controllers.player.PlayerItemMigr import com.github.unchama.itemmigration.service.ItemMigrationService import com.github.unchama.seichiassist.SeichiAssist import com.github.unchama.seichiassist.meta.subsystem.Subsystem -import com.github.unchama.seichiassist.subsystems.itemmigration.controllers.{DatabaseMigrationController, WorldMigrationController} +import com.github.unchama.seichiassist.subsystems.itemmigration.controllers.{ + DatabaseMigrationController, + WorldMigrationController +} import com.github.unchama.seichiassist.subsystems.itemmigration.domain.minecraft.UuidRepository import com.github.unchama.seichiassist.subsystems.itemmigration.infrastructure.loggers.PlayerItemsMigrationSlf4jLogger import com.github.unchama.seichiassist.subsystems.itemmigration.infrastructure.minecraft.JdbcBackedUuidRepository @@ -29,15 +32,13 @@ object System { import cats.implicits._ - def wired[ - F[_] : ConcurrentEffect : ContextShift, - G[_] : SyncEffect : ContextCoercion[*[_], F] - ](implicit effectEnvironment: EffectEnvironment, logger: Logger): G[System[F]] = for { + def wired[F[_]: ConcurrentEffect: ContextShift, G[_]: SyncEffect: ContextCoercion[*[_], F]]( + implicit effectEnvironment: EffectEnvironment, + logger: Logger + ): G[System[F]] = for { migrations <- Sync[G].delay { - implicit val syncIOUuidRepository: UuidRepository[SyncIO] = JdbcBackedUuidRepository - .initializeStaticInstance[SyncIO] - .unsafeRunSync() - .apply[SyncIO] + implicit val syncIOUuidRepository: UuidRepository[SyncIO] = + JdbcBackedUuidRepository.initializeStaticInstance[SyncIO].unsafeRunSync().apply[SyncIO] SeichiAssistItemMigrations.seq } @@ -49,19 +50,24 @@ object System { repositoryControls <- BukkitRepositoryControls.createHandles( - RepositoryDefinition.Phased.SinglePhased.withoutTappingAction( - ItemMigrationStateRepositoryDefinitions.initialization[G], - ItemMigrationStateRepositoryDefinitions.finalization[G, UUID] - ) + RepositoryDefinition + .Phased + .SinglePhased + .withoutTappingAction( + ItemMigrationStateRepositoryDefinitions.initialization[G], + ItemMigrationStateRepositoryDefinitions.finalization[G, UUID] + ) ) } yield { val playerItemMigrationController = new PlayerItemMigrationController[F, G]( - repositoryControls.repository, migrations, service + repositoryControls.repository, + migrations, + service ) new System[F] { override val entryPoints: EntryPoints = new EntryPoints { - override def runDatabaseMigration[I[_] : SyncEffect]: I[Unit] = { + override def runDatabaseMigration[I[_]: SyncEffect]: I[Unit] = { DatabaseMigrationController[I](migrations).runDatabaseMigration } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/controllers/DatabaseMigrationController.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/controllers/DatabaseMigrationController.scala index 737f6046c2..db43855190 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/controllers/DatabaseMigrationController.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/controllers/DatabaseMigrationController.scala @@ -10,20 +10,24 @@ import com.github.unchama.seichiassist.subsystems.itemmigration.infrastructure.t import org.slf4j.Logger import scalikejdbc.DB -case class DatabaseMigrationController[F[_] : SyncEffect](migrations: ItemMigrations) - (implicit effectEnvironment: EffectEnvironment, logger: Logger) { +case class DatabaseMigrationController[F[_]: SyncEffect](migrations: ItemMigrations)( + implicit effectEnvironment: EffectEnvironment, + logger: Logger +) { lazy val runDatabaseMigration: F[Unit] = Sync[F].delay { DB.autoCommit { implicit session => import cats.effect.implicits._ // DB内アイテムのマイグレーション - ItemMigrationService.inContextOf[F]( - new PersistedItemsMigrationVersionRepository(), - new PersistedItemsMigrationSlf4jLogger(logger) - ) + ItemMigrationService + .inContextOf[F]( + new PersistedItemsMigrationVersionRepository(), + new PersistedItemsMigrationSlf4jLogger(logger) + ) .runMigration(migrations)(new SeichiAssistPersistedItems()) - .runSync[SyncIO].unsafeRunSync() + .runSync[SyncIO] + .unsafeRunSync() } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/controllers/WorldMigrationController.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/controllers/WorldMigrationController.scala index 6682db9229..b2b197363c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/controllers/WorldMigrationController.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/controllers/WorldMigrationController.scala @@ -11,16 +11,22 @@ import com.github.unchama.seichiassist.subsystems.itemmigration.infrastructure.r import com.github.unchama.seichiassist.subsystems.itemmigration.infrastructure.targets.SeichiAssistWorldLevelData import org.slf4j.Logger -case class WorldMigrationController(migrations: ItemMigrations) - (implicit effectEnvironment: EffectEnvironment, logger: Logger) { +case class WorldMigrationController(migrations: ItemMigrations)( + implicit effectEnvironment: EffectEnvironment, + logger: Logger +) { lazy val runWorldMigration: IO[Unit] = { // ワールド内アイテムのマイグレーション // TODO IOを剥がす - service.ItemMigrationService.inContextOf[IO]( - new WorldLevelItemsMigrationVersionRepository(SeichiAssist.seichiAssistConfig.getServerId), - new WorldLevelMigrationSlf4jLogger(logger) - ) + service + .ItemMigrationService + .inContextOf[IO]( + new WorldLevelItemsMigrationVersionRepository( + SeichiAssist.seichiAssistConfig.getServerId + ), + new WorldLevelMigrationSlf4jLogger(logger) + ) .runMigration(migrations) { import PluginExecutionContexts.asyncShift new SeichiAssistWorldLevelData() diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/loggers/PersistedItemsMigrationSlf4jLogger.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/loggers/PersistedItemsMigrationSlf4jLogger.scala index 5fef1a7dcd..b291a4b2e1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/loggers/PersistedItemsMigrationSlf4jLogger.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/loggers/PersistedItemsMigrationSlf4jLogger.scala @@ -6,10 +6,12 @@ import com.github.unchama.seichiassist.subsystems.itemmigration.infrastructure.t import org.slf4j.Logger class PersistedItemsMigrationSlf4jLogger[F[_]](logger: Logger)(implicit F: Sync[F]) - extends ItemMigrationLogger[F, SeichiAssistPersistedItems[F]] { + extends ItemMigrationLogger[F, SeichiAssistPersistedItems[F]] { - override def logMigrationVersionsToBeApplied(versions: IndexedSeq[ItemMigrationVersionNumber], - target: SeichiAssistPersistedItems[F]): F[Unit] = { + override def logMigrationVersionsToBeApplied( + versions: IndexedSeq[ItemMigrationVersionNumber], + target: SeichiAssistPersistedItems[F] + ): F[Unit] = { val concatenatedVersionString = versions.map(_.versionString).mkString(", ") F.delay { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/loggers/PlayerItemsMigrationSlf4jLogger.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/loggers/PlayerItemsMigrationSlf4jLogger.scala index 386e7c4ad8..03045c56f8 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/loggers/PlayerItemsMigrationSlf4jLogger.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/loggers/PlayerItemsMigrationSlf4jLogger.scala @@ -5,14 +5,19 @@ import com.github.unchama.itemmigration.bukkit.targets.PlayerInventoriesData import com.github.unchama.itemmigration.domain.{ItemMigrationLogger, ItemMigrationVersionNumber} import org.slf4j.Logger -class PlayerItemsMigrationSlf4jLogger[F[_] : Sync](logger: Logger) extends ItemMigrationLogger[F, PlayerInventoriesData[F]] { +class PlayerItemsMigrationSlf4jLogger[F[_]: Sync](logger: Logger) + extends ItemMigrationLogger[F, PlayerInventoriesData[F]] { - override def logMigrationVersionsToBeApplied(versions: IndexedSeq[ItemMigrationVersionNumber], - target: PlayerInventoriesData[F]): F[Unit] = { + override def logMigrationVersionsToBeApplied( + versions: IndexedSeq[ItemMigrationVersionNumber], + target: PlayerInventoriesData[F] + ): F[Unit] = { val concatenatedVersionString = versions.map(_.versionString).mkString(", ") Sync[F].delay { - logger.info(s"${target.player.getName} (UUID: ${target.player.getUniqueId})のインベントリ内データ変換を適用します…") + logger.info( + s"${target.player.getName} (UUID: ${target.player.getUniqueId})のインベントリ内データ変換を適用します…" + ) logger.info(s"適用するバージョン: $concatenatedVersionString") } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/loggers/WorldLevelMigrationSlf4jLogger.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/loggers/WorldLevelMigrationSlf4jLogger.scala index 528dbb9d92..899f141461 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/loggers/WorldLevelMigrationSlf4jLogger.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/loggers/WorldLevelMigrationSlf4jLogger.scala @@ -6,10 +6,12 @@ import com.github.unchama.itemmigration.domain.{ItemMigrationLogger, ItemMigrati import org.slf4j.Logger class WorldLevelMigrationSlf4jLogger[F[_]](logger: Logger)(implicit val F: Sync[F]) - extends ItemMigrationLogger[F, WorldLevelData[F]] { + extends ItemMigrationLogger[F, WorldLevelData[F]] { - override def logMigrationVersionsToBeApplied(versions: IndexedSeq[ItemMigrationVersionNumber], - target: WorldLevelData[F]): F[Unit] = { + override def logMigrationVersionsToBeApplied( + versions: IndexedSeq[ItemMigrationVersionNumber], + target: WorldLevelData[F] + ): F[Unit] = { val concatenatedVersionString = versions.map(_.versionString).mkString(", ") F.delay { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/minecraft/JdbcBackedUuidRepository.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/minecraft/JdbcBackedUuidRepository.scala index 6ccb76fe69..d27335ac00 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/minecraft/JdbcBackedUuidRepository.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/minecraft/JdbcBackedUuidRepository.scala @@ -1,31 +1,31 @@ package com.github.unchama.seichiassist.subsystems.itemmigration.infrastructure.minecraft -import java.util.UUID - import cats.Applicative import cats.effect.Sync import com.github.unchama.seichiassist.subsystems.itemmigration.domain.minecraft.UuidRepository import org.slf4j.Logger +import java.util.UUID + object JdbcBackedUuidRepository { trait ApplicativeUuidRepository { - def apply[F[_] : Applicative]: UuidRepository[F] + def apply[F[_]: Applicative]: UuidRepository[F] } /** * DBに入っているデータから `UuidRepository` を作成する。 * - * 初回問い合わせ時にのみ全データを読み込むため、 - * バッチ処理での使用を想定している。 + * 初回問い合わせ時にのみ全データを読み込むため、 バッチ処理での使用を想定している。 * * Minecraftのアカウントの仕様上、過去の名前のみから現在のUUIDを割り出すことは不可能であるため、 - * Mojangへの問い合わせは行っていない。具体的には、例えばプレーヤーがname1からname2に名前を変更した後、 - * 別のプレーヤーがname1の名前を使用することができる。 + * Mojangへの問い合わせは行っていない。具体的には、例えばプレーヤーがname1からname2に名前を変更した後、 別のプレーヤーがname1の名前を使用することができる。 * * このことから、最後にSeichiAssistが導入されていたサーバーで入った名前のみからUUIDを割り出すことにしている。 */ - def initializeStaticInstance[F[_] : Sync](implicit logger: Logger): F[ApplicativeUuidRepository] = Sync[F].delay { + def initializeStaticInstance[F[_]: Sync]( + implicit logger: Logger + ): F[ApplicativeUuidRepository] = Sync[F].delay { import scalikejdbc._ val databaseEntries = DB.readOnly { implicit session => @@ -40,13 +40,15 @@ object JdbcBackedUuidRepository { } new ApplicativeUuidRepository { - override def apply[G[_] : Applicative]: UuidRepository[G] = { - (playerName: String) => Applicative[G].pure(databaseEntries.get(playerName)) + override def apply[G[_]: Applicative]: UuidRepository[G] = { (playerName: String) => + Applicative[G].pure(databaseEntries.get(playerName)) } } } - def initializeInstanceIn[F[_] : Sync, G[_] : Applicative](implicit logger: Logger): F[UuidRepository[G]] = { + def initializeInstanceIn[F[_]: Sync, G[_]: Applicative]( + implicit logger: Logger + ): F[UuidRepository[G]] = { import cats.implicits._ for { @@ -56,5 +58,6 @@ object JdbcBackedUuidRepository { } } - def initializeInstance[F[_] : Sync](implicit logger: Logger): F[UuidRepository[F]] = initializeInstanceIn[F, F] -} \ No newline at end of file + def initializeInstance[F[_]: Sync](implicit logger: Logger): F[UuidRepository[F]] = + initializeInstanceIn[F, F] +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/repositories/PersistedItemsMigrationVersionRepository.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/repositories/PersistedItemsMigrationVersionRepository.scala index b331db363d..70907fb507 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/repositories/PersistedItemsMigrationVersionRepository.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/repositories/PersistedItemsMigrationVersionRepository.scala @@ -1,25 +1,32 @@ package com.github.unchama.seichiassist.subsystems.itemmigration.infrastructure.repositories import cats.effect.{ExitCase, Resource, Sync} -import com.github.unchama.itemmigration.domain.{ItemMigrationVersionNumber, ItemMigrationVersionRepository} +import com.github.unchama.itemmigration.domain.{ + ItemMigrationVersionNumber, + ItemMigrationVersionRepository +} import com.github.unchama.seichiassist.subsystems.itemmigration.infrastructure.targets.SeichiAssistPersistedItems import scalikejdbc._ class PersistedItemsMigrationVersionRepository[F[_]](implicit dbSession: DBSession, F: Sync[F]) - extends ItemMigrationVersionRepository[F, SeichiAssistPersistedItems[F]] { + extends ItemMigrationVersionRepository[F, SeichiAssistPersistedItems[F]] { private type PersistedItems = SeichiAssistPersistedItems[F] override type PersistenceLock[TInstance <: PersistedItems] = Unit - override def lockVersionPersistence(target: PersistedItems): Resource[F, PersistenceLock[PersistedItems]] = { + override def lockVersionPersistence( + target: PersistedItems + ): Resource[F, PersistenceLock[PersistedItems]] = { Resource.makeCase(F.delay { // トランザクション開始がここになる // https://dev.mysql.com/doc/refman/5.6/ja/lock-tables-and-transactions.html sql"set autocommit=0".update().apply() // ロックを取得するときは利用するテーブルすべてをロックしなければならない - sql"lock tables seichiassist.item_migration_on_database write, seichiassist.playerdata write".update().apply() + sql"lock tables seichiassist.item_migration_on_database write, seichiassist.playerdata write" + .update() + .apply() // このリソースを使用する際にはロックが取れているというのを保証すればよいため、リソースの実体は無くて良い () @@ -37,26 +44,31 @@ class PersistedItemsMigrationVersionRepository[F[_]](implicit dbSession: DBSessi } } - override def getVersionsAppliedTo(target: PersistedItems): PersistenceLock[target.type] => F[Set[ItemMigrationVersionNumber]] = - _ => F.delay { - sql""" + override def getVersionsAppliedTo( + target: PersistedItems + ): PersistenceLock[target.type] => F[Set[ItemMigrationVersionNumber]] = + _ => + F.delay { + sql""" select version_string from seichiassist.item_migration_on_database - """ - .map { rs => rs.string("version_string") } - .list.apply() - .flatMap(ItemMigrationVersionNumber.fromString).toSet - } + """.map { rs => rs.string("version_string") } + .list + .apply() + .flatMap(ItemMigrationVersionNumber.fromString) + .toSet + } - override def persistVersionsAppliedTo(target: PersistedItems, - versions: Iterable[ItemMigrationVersionNumber]): PersistenceLock[PersistedItems] => F[Unit] = - _ => F.delay { - val batchParams = versions.map(version => Seq(version.versionString)) + override def persistVersionsAppliedTo( + target: PersistedItems, + versions: Iterable[ItemMigrationVersionNumber] + ): PersistenceLock[PersistedItems] => F[Unit] = + _ => + F.delay { + val batchParams = versions.map(version => Seq(version.versionString)) - sql""" + sql""" insert into seichiassist.item_migration_on_database(version_string, completed_at) values (?, cast(now() as datetime)) - """ - .batch(batchParams.toSeq: _*) - .apply[List]() - } + """.batch(batchParams.toSeq: _*).apply[List]() + } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/repositories/PlayerItemsMigrationVersionRepository.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/repositories/PlayerItemsMigrationVersionRepository.scala index 8e137b6059..4c1e273b18 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/repositories/PlayerItemsMigrationVersionRepository.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/repositories/PlayerItemsMigrationVersionRepository.scala @@ -2,50 +2,60 @@ package com.github.unchama.seichiassist.subsystems.itemmigration.infrastructure. import cats.effect.{Resource, Sync} import com.github.unchama.itemmigration.bukkit.targets.PlayerInventoriesData -import com.github.unchama.itemmigration.domain.{ItemMigrationVersionNumber, ItemMigrationVersionRepository} +import com.github.unchama.itemmigration.domain.{ + ItemMigrationVersionNumber, + ItemMigrationVersionRepository +} import scalikejdbc._ class PlayerItemsMigrationVersionRepository[F[_]](serverId: String)(implicit F: Sync[F]) - extends ItemMigrationVersionRepository[F, PlayerInventoriesData[F]] { + extends ItemMigrationVersionRepository[F, PlayerInventoriesData[F]] { override type PersistenceLock[TInstance <: PlayerInventoriesData[F]] = Unit - override def lockVersionPersistence(target: PlayerInventoriesData[F]): Resource[F, PersistenceLock[target.type]] = { + override def lockVersionPersistence( + target: PlayerInventoriesData[F] + ): Resource[F, PersistenceLock[target.type]] = { + /** * プレーヤーは単一サーバーに1人しか存在しないためロックは不要 */ Resource.pure[F, Unit](()) } - override def getVersionsAppliedTo(target: PlayerInventoriesData[F]): PersistenceLock[target.type] => F[Set[ItemMigrationVersionNumber]] = - _ => F.delay { - DB.localTx { implicit session => - sql""" + override def getVersionsAppliedTo( + target: PlayerInventoriesData[F] + ): PersistenceLock[target.type] => F[Set[ItemMigrationVersionNumber]] = + _ => + F.delay { + DB.localTx { implicit session => + sql""" select version_string from seichiassist.player_in_server_item_migration where server_id = $serverId and player_uuid = ${target.player.getUniqueId.toString} - """ - .map { rs => rs.string("version_string") } - .list.apply() - .flatMap(ItemMigrationVersionNumber.fromString).toSet + """.map { rs => rs.string("version_string") } + .list + .apply() + .flatMap(ItemMigrationVersionNumber.fromString) + .toSet + } } - } - override def persistVersionsAppliedTo(target: PlayerInventoriesData[F], - versions: Iterable[ItemMigrationVersionNumber]): PersistenceLock[target.type] => F[Unit] = - _ => F.delay { - val batchParams = versions.map { version => - Seq(target.player.getUniqueId.toString, serverId, version.versionString) - } - - DB.localTx { implicit session => - sql""" + override def persistVersionsAppliedTo( + target: PlayerInventoriesData[F], + versions: Iterable[ItemMigrationVersionNumber] + ): PersistenceLock[target.type] => F[Unit] = + _ => + F.delay { + val batchParams = versions.map { version => + Seq(target.player.getUniqueId.toString, serverId, version.versionString) + } + + DB.localTx { implicit session => + sql""" insert into seichiassist.player_in_server_item_migration(player_uuid, server_id, version_string, completed_at) values (?, ?, ?, cast(now() as datetime)) - """ - .batch(batchParams.toSeq: _*) - .apply[List]() + """.batch(batchParams.toSeq: _*).apply[List]() + } } - } } - diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/repositories/WorldLevelItemsMigrationVersionRepository.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/repositories/WorldLevelItemsMigrationVersionRepository.scala index 47a3806977..d925e4c87a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/repositories/WorldLevelItemsMigrationVersionRepository.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/repositories/WorldLevelItemsMigrationVersionRepository.scala @@ -2,48 +2,56 @@ package com.github.unchama.seichiassist.subsystems.itemmigration.infrastructure. import cats.effect.{Resource, Sync} import com.github.unchama.itemmigration.bukkit.targets.WorldLevelData -import com.github.unchama.itemmigration.domain.{ItemMigrationVersionNumber, ItemMigrationVersionRepository} +import com.github.unchama.itemmigration.domain.{ + ItemMigrationVersionNumber, + ItemMigrationVersionRepository +} import scalikejdbc._ class WorldLevelItemsMigrationVersionRepository[F[_]](serverId: String)(implicit F: Sync[F]) - extends ItemMigrationVersionRepository[F, WorldLevelData[F]] { + extends ItemMigrationVersionRepository[F, WorldLevelData[F]] { override type PersistenceLock[TInstance <: WorldLevelData[F]] = Unit - override def lockVersionPersistence(target: WorldLevelData[F]): Resource[F, PersistenceLock[target.type]] = { + override def lockVersionPersistence( + target: WorldLevelData[F] + ): Resource[F, PersistenceLock[target.type]] = { + /** * サーバーIDが一意なら更新が一サーバーIDに対して一個しか走らないためロックは不要 */ Resource.pure[F, Unit](()) } - override def getVersionsAppliedTo(target: WorldLevelData[F]): PersistenceLock[target.type] => F[Set[ItemMigrationVersionNumber]] = - _ => F.delay { - DB.localTx { implicit session => - sql""" + override def getVersionsAppliedTo( + target: WorldLevelData[F] + ): PersistenceLock[target.type] => F[Set[ItemMigrationVersionNumber]] = + _ => + F.delay { + DB.localTx { implicit session => + sql""" select version_string from seichiassist.item_migration_in_server_world_levels where server_id = $serverId - """ - .map { rs => rs.string("version_string") } - .list.apply() - .flatMap(ItemMigrationVersionNumber.fromString).toSet + """.map { rs => rs.string("version_string") } + .list + .apply() + .flatMap(ItemMigrationVersionNumber.fromString) + .toSet + } } - } - override def persistVersionsAppliedTo(target: WorldLevelData[F], - versions: Iterable[ItemMigrationVersionNumber]): PersistenceLock[target.type] => F[Unit] = + override def persistVersionsAppliedTo( + target: WorldLevelData[F], + versions: Iterable[ItemMigrationVersionNumber] + ): PersistenceLock[target.type] => F[Unit] = _ => { - val batchParams = versions.map { version => - Seq(serverId, version.versionString) - }.toSeq + val batchParams = versions.map { version => Seq(serverId, version.versionString) }.toSeq F.delay { DB.localTx { implicit session => sql""" insert into seichiassist.item_migration_in_server_world_levels(server_id, version_string, completed_at) values (?, ?, cast(now() as datetime)) - """ - .batch(batchParams: _*) - .apply[List]() + """.batch(batchParams: _*).apply[List]() } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/targets/SeichiAssistPersistedItems.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/targets/SeichiAssistPersistedItems.scala index 3ac433074c..f02354f219 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/targets/SeichiAssistPersistedItems.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/targets/SeichiAssistPersistedItems.scala @@ -7,11 +7,14 @@ import com.github.unchama.seichiassist.util.{BukkitSerialization, ItemListSerial import org.bukkit.Material import scalikejdbc._ -class SeichiAssistPersistedItems[F[_]](implicit dBSession: DBSession, F: Sync[F]) extends ItemMigrationTarget[F] { +class SeichiAssistPersistedItems[F[_]](implicit dBSession: DBSession, F: Sync[F]) + extends ItemMigrationTarget[F] { import scala.jdk.CollectionConverters._ - private def convertSharedInventory(persistedSharedInventory: String)(conversion: ItemStackConversion): String = { + private def convertSharedInventory( + persistedSharedInventory: String + )(conversion: ItemStackConversion): String = { ItemListSerialization.serializeToBase64 { ItemListSerialization .deserializeFromBase64(persistedSharedInventory) @@ -27,7 +30,9 @@ class SeichiAssistPersistedItems[F[_]](implicit dBSession: DBSession, F: Sync[F] } } - private def convertPocketInventory(persistedPocketInventory: String)(conversion: ItemStackConversion): String = { + private def convertPocketInventory( + persistedPocketInventory: String + )(conversion: ItemStackConversion): String = { BukkitSerialization.toBase64 { val pocketInventory = BukkitSerialization.fromBase64forPocket(persistedPocketInventory) @@ -39,25 +44,25 @@ class SeichiAssistPersistedItems[F[_]](implicit dBSession: DBSession, F: Sync[F] override def runMigration(conversion: ItemStackConversion): F[Unit] = F.delay { val triples = sql"select uuid, shareinv, inventory from seichiassist.playerdata" - .map { rs => - (rs.string("uuid"), rs.stringOpt("shareinv"), rs.stringOpt("inventory")) - } - .list().apply() + .map { rs => (rs.string("uuid"), rs.stringOpt("shareinv"), rs.stringOpt("inventory")) } + .list() + .apply() - val batchParam: Seq[Seq[String]] = triples.map { case (uuid, shareinv, inventory) => - val newSharedInventory = shareinv.filter(_.nonEmpty).map(convertSharedInventory(_)(conversion)) - val newPocketInventory = inventory.filter(_.nonEmpty).map(convertPocketInventory(_)(conversion)) + val batchParam: Seq[Seq[String]] = triples.map { + case (uuid, shareinv, inventory) => + val newSharedInventory = + shareinv.filter(_.nonEmpty).map(convertSharedInventory(_)(conversion)) + val newPocketInventory = + inventory.filter(_.nonEmpty).map(convertPocketInventory(_)(conversion)) - Seq(newSharedInventory.getOrElse(""), newPocketInventory.getOrElse(""), uuid) + Seq(newSharedInventory.getOrElse(""), newPocketInventory.getOrElse(""), uuid) } sql""" update seichiassist.playerdata set shareinv = ?, inventory = ? where uuid = ? - """ - .batch(batchParam: _*) - .apply[List]() + """.batch(batchParam: _*).apply[List]() } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/targets/SeichiAssistWorldLevelData.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/targets/SeichiAssistWorldLevelData.scala index c91ecb9dfd..d7e6150b55 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/targets/SeichiAssistWorldLevelData.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/infrastructure/targets/SeichiAssistWorldLevelData.scala @@ -8,21 +8,23 @@ import org.bukkit.World import org.slf4j.Logger private object DelegatedImpls { - def getWorlds[F[_] : Sync]: F[IndexedSeq[World]] = { + def getWorlds[F[_]: Sync]: F[IndexedSeq[World]] = { val multiverseCore = ExternalPlugins.getMultiverseCore import scala.jdk.CollectionConverters._ Sync[F].delay { - multiverseCore.getMVWorldManager - .getMVWorlds.asScala - .map(_.getCBWorld).toIndexedSeq + multiverseCore.getMVWorldManager.getMVWorlds.asScala.map(_.getCBWorld).toIndexedSeq } } - def getWorldChunkCoordinates[F[_] : Sync](implicit logger: Logger): World => F[Seq[(Int, Int)]] = - ExternalServices.getChunkCoordinates[F](SeichiAssist.seichiAssistConfig.chunkSearchCommandBase) + def getWorldChunkCoordinates[F[_]: Sync]( + implicit logger: Logger + ): World => F[Seq[(Int, Int)]] = + ExternalServices.getChunkCoordinates[F]( + SeichiAssist.seichiAssistConfig.chunkSearchCommandBase + ) } class SeichiAssistWorldLevelData[F[_]](implicit metricsLogger: Logger, F: Concurrent[F]) - extends WorldLevelData[F](DelegatedImpls.getWorlds, DelegatedImpls.getWorldChunkCoordinates) + extends WorldLevelData[F](DelegatedImpls.getWorlds, DelegatedImpls.getWorldChunkCoordinates) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/SeichiAssistItemMigrations.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/SeichiAssistItemMigrations.scala index 1643aa25e2..09fa04c3b7 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/SeichiAssistItemMigrations.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/SeichiAssistItemMigrations.scala @@ -10,15 +10,17 @@ object SeichiAssistItemMigrations { /** * SeichiAssistが実施するアイテム変換の列。 * - * 注意:エラー等で不完全なマイグレーションが走った時、同じ版のマイグレーションを複数回行いたいケースがある。 - * これに備え、マイグレーションはできるだけ冪等な処理として記述すべきである。 + * 注意:エラー等で不完全なマイグレーションが走った時、同じ版のマイグレーションを複数回行いたいケースがある。 これに備え、マイグレーションはできるだけ冪等な処理として記述すべきである。 */ - def seq(implicit uuidRepository: UuidRepository[SyncIO], logger: Logger): ItemMigrations = ItemMigrations(IndexedSeq( - V1_0_0_MigrateMebiusToNewCodec.migration, - V1_1_0_AddUnbreakableToNarutoRemake.migration, - V1_2_0_FixTypoOf4thAnniversaryGT.migration, - V1_3_0_RemoveUnnecessaryLoreOfHalloweenItem.migration, - V1_4_0_AddEnchantsTo2_1billionRewardItems.migration - )) + def seq(implicit uuidRepository: UuidRepository[SyncIO], logger: Logger): ItemMigrations = + ItemMigrations( + IndexedSeq( + V1_0_0_MigrateMebiusToNewCodec.migration, + V1_1_0_AddUnbreakableToNarutoRemake.migration, + V1_2_0_FixTypoOf4thAnniversaryGT.migration, + V1_3_0_RemoveUnnecessaryLoreOfHalloweenItem.migration, + V1_4_0_AddEnchantsTo2_1billionRewardItems.migration + ) + ) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_0_0_MigrateMebiusToNewCodec.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_0_0_MigrateMebiusToNewCodec.scala index 772d1a875b..c58bcd791a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_0_0_MigrateMebiusToNewCodec.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_0_0_MigrateMebiusToNewCodec.scala @@ -21,18 +21,14 @@ object V1_0_0_MigrateMebiusToNewCodec { val ownerResolutionError = s"$RESET${DARK_RED}エラー:所有者が見つかりません。" - //noinspection DuplicatedCode + // noinspection DuplicatedCode object OldBukkitMebiusItemStackCodec { import de.tr7zw.itemnbtapi.NBTItem import org.bukkit.inventory.ItemStack - private val mebiusLoreHead = List( - s"$RESET", - s"$RESET${AQUA}初心者をサポートする不思議なヘルメット。", - s"$RESET${AQUA}整地により成長する。", - "" - ) + private val mebiusLoreHead = + List(s"$RESET", s"$RESET${AQUA}初心者をサポートする不思議なヘルメット。", s"$RESET${AQUA}整地により成長する。", "") private val ownerLoreRowPrefix = s"$RESET${DARK_GREEN}所有者:" private val levelLoreRowPrefix = s"$RESET$RED${BOLD}アイテムLv. " def isNewMebius(itemStack: ItemStack): Boolean = { @@ -75,18 +71,20 @@ object V1_0_0_MigrateMebiusToNewCodec { Some(OldMebiusRawProperty(ownerName, mebiusLevel, nickname, mebiusName)) } - case class OldMebiusRawProperty(ownerPlayerId: String, - level: Int, - ownerNicknameOverride: Option[String] = None, - mebiusName: String) + case class OldMebiusRawProperty( + ownerPlayerId: String, + level: Int, + ownerNicknameOverride: Option[String] = None, + mebiusName: String + ) } import eu.timepit.refined.auto._ - def migrationFunction(itemStack: ItemStack)(implicit - repository: UuidRepository[SyncIO], - logger: Logger): ItemStack = { + def migrationFunction( + itemStack: ItemStack + )(implicit repository: UuidRepository[SyncIO], logger: Logger): ItemStack = { val OldMebiusRawProperty(ownerPlayerId, level, ownerNicknameOverride, mebiusName) = OldBukkitMebiusItemStackCodec .decodeOldMebiusProperty(itemStack) @@ -133,7 +131,10 @@ object V1_0_0_MigrateMebiusToNewCodec { nbtItem.getItem } - def migration(implicit uuidRepository: UuidRepository[SyncIO], logger: Logger): ItemMigration = ItemMigration( + def migration( + implicit uuidRepository: UuidRepository[SyncIO], + logger: Logger + ): ItemMigration = ItemMigration( ItemMigrationVersionNumber(1, 0, 0), MigrationHelper.delegateConversionForContainers(migrationFunction) ) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_1_0_AddUnbreakableToNarutoRemake.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_1_0_AddUnbreakableToNarutoRemake.scala index fb9fbac321..f4d1bced70 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_1_0_AddUnbreakableToNarutoRemake.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_1_0_AddUnbreakableToNarutoRemake.scala @@ -21,7 +21,8 @@ object V1_1_0_AddUnbreakableToNarutoRemake { private val narutoRemake2Lore = s"${GRAY}2020ハロウィン討伐イベント特別賞" def isNarutoRemake(itemStack: ItemStack): Boolean = { - if (itemStack == null || !itemStack.hasItemMeta || !itemStack.getItemMeta.hasLore) return false + if (itemStack == null || !itemStack.hasItemMeta || !itemStack.getItemMeta.hasLore) + return false val lore = itemStack.getItemMeta.getLore.asScala lore.contains(narutoRemake1Lore) || lore.contains(narutoRemake2Lore) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_2_0_FixTypoOf4thAnniversaryGT.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_2_0_FixTypoOf4thAnniversaryGT.scala index afb12e919d..b86d659906 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_2_0_FixTypoOf4thAnniversaryGT.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_2_0_FixTypoOf4thAnniversaryGT.scala @@ -13,7 +13,8 @@ object V1_2_0_FixTypoOf4thAnniversaryGT { private val gt4thName = s"$WHITE$BOLD${ITALIC}4thAniv." def is4thGiganticItem(itemStack: ItemStack): Boolean = { - if (itemStack == null || !itemStack.hasItemMeta || !itemStack.getItemMeta.hasDisplayName) return false + if (itemStack == null || !itemStack.hasItemMeta || !itemStack.getItemMeta.hasDisplayName) + return false val name = itemStack.getItemMeta.getDisplayName name.contains(gt4thName) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_3_0_RemoveUnnecessaryLoreOfHalloweenItem.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_3_0_RemoveUnnecessaryLoreOfHalloweenItem.scala index 623dfe891a..e5e31a0ab8 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_3_0_RemoveUnnecessaryLoreOfHalloweenItem.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_3_0_RemoveUnnecessaryLoreOfHalloweenItem.scala @@ -17,7 +17,8 @@ object V1_3_0_RemoveUnnecessaryLoreOfHalloweenItem { private val halloweenSpecialPrizeLore = s"${GRAY}2020ハロウィン討伐イベント特別賞" def isHalloweenPrize(itemStack: ItemStack): Boolean = { - if (itemStack == null || !itemStack.hasItemMeta || !itemStack.getItemMeta.hasLore) return false + if (itemStack == null || !itemStack.hasItemMeta || !itemStack.getItemMeta.hasLore) + return false val lore = itemStack.getItemMeta.getLore.asScala lore.contains(halloweenClearPrizeLore) || lore.contains(halloweenSpecialPrizeLore) } @@ -30,7 +31,7 @@ object V1_3_0_RemoveUnnecessaryLoreOfHalloweenItem { import scala.util.chaining._ val clone = itemStack.clone() - val meta = clone.getItemMeta.tap {meta => + val meta = clone.getItemMeta.tap { meta => import meta._ setLore { if (isUnbreakable) getLore.asScala.filter(_ != s"$RESET${DARK_RED}耐久無限").asJava diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_4_0_AddEnchantsTo2_1billionRewardItems.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_4_0_AddEnchantsTo2_1billionRewardItems.scala index fdebc5d4f2..7d332ea896 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_4_0_AddEnchantsTo2_1billionRewardItems.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/itemmigration/migrations/V1_4_0_AddEnchantsTo2_1billionRewardItems.scala @@ -59,17 +59,25 @@ object V1_4_0_AddEnchantsTo2_1billionRewardItems { } def is21billionRewardItems(itemStack: ItemStack): Boolean = { - if (itemStack == null || !itemStack.hasItemMeta || !itemStack.getItemMeta.hasDisplayName || !itemStack.getItemMeta.hasLore) return false + if ( + itemStack == null || !itemStack.hasItemMeta || !itemStack + .getItemMeta + .hasDisplayName || !itemStack.getItemMeta.hasLore + ) return false isRewardedGaeaReplica(itemStack) || isRewardedTitanReplica(itemStack) } def isRewardedTitanReplica(item: ItemStack): Boolean = { val lores = item.getItemMeta.getLore.asScala - item.getItemMeta.getDisplayName == titanReplicaName && lores.contains(titanReplicaLore) && lores.contains(commonLore) + item.getItemMeta.getDisplayName == titanReplicaName && lores.contains( + titanReplicaLore + ) && lores.contains(commonLore) } def isRewardedGaeaReplica(item: ItemStack): Boolean = { val lores = item.getItemMeta.getLore.asScala - item.getItemMeta.getDisplayName == gaeaReplicaName && lores.contains(gaeaReplicaLore) && lores.contains(commonLore) + item.getItemMeta.getDisplayName == gaeaReplicaName && lores.contains( + gaeaReplicaLore + ) && lores.contains(commonLore) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/ManaApi.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/ManaApi.scala index f0780d508b..c26b56c6c3 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/ManaApi.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/ManaApi.scala @@ -1,7 +1,11 @@ package com.github.unchama.seichiassist.subsystems.mana import com.github.unchama.datarepository.KeyedDataRepository -import com.github.unchama.seichiassist.subsystems.mana.domain.{LevelCappedManaAmount, ManaManipulation, ManaMultiplier} +import com.github.unchama.seichiassist.subsystems.mana.domain.{ + LevelCappedManaAmount, + ManaManipulation, + ManaMultiplier +} trait ManaReadApi[F[_], G[_], Player] { @@ -24,6 +28,6 @@ trait ManaMultiplierApi[F[_]] { } trait ManaApi[F[_], G[_], Player] - extends ManaReadApi[F, G, Player] + extends ManaReadApi[F, G, Player] with ManaWriteApi[G, Player] with ManaMultiplierApi[G] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/System.scala index 4ca7dea8ef..bc04359e52 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/System.scala @@ -10,8 +10,16 @@ import com.github.unchama.generic.effect.stream.StreamExtra import com.github.unchama.seichiassist.meta.subsystem.Subsystem import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountReadAPI import com.github.unchama.seichiassist.subsystems.mana.application.ManaRepositoryDefinition -import com.github.unchama.seichiassist.subsystems.mana.application.process.{RefillToCap, UpdateManaCaps} -import com.github.unchama.seichiassist.subsystems.mana.domain.{LevelCappedManaAmount, ManaAmountPersistence, ManaManipulation, ManaMultiplier} +import com.github.unchama.seichiassist.subsystems.mana.application.process.{ + RefillToCap, + UpdateManaCaps +} +import com.github.unchama.seichiassist.subsystems.mana.domain.{ + LevelCappedManaAmount, + ManaAmountPersistence, + ManaManipulation, + ManaMultiplier +} import com.github.unchama.seichiassist.subsystems.mana.infrastructure.JdbcManaAmountPersistence import io.chrisdavenport.log4cats.ErrorLogger import org.bukkit.entity.Player @@ -27,10 +35,9 @@ object System { import cats.effect.implicits._ import cats.implicits._ - def wired[ - F[_] : ConcurrentEffect : ErrorLogger, - G[_] : SyncEffect : ContextCoercion[*[_], F] - ](implicit breakCountReadAPI: BreakCountReadAPI[F, G, Player]): F[System[F, G, Player]] = { + def wired[F[_]: ConcurrentEffect: ErrorLogger, G[_]: SyncEffect: ContextCoercion[*[_], F]]( + implicit breakCountReadAPI: BreakCountReadAPI[F, G, Player] + ): F[System[F, G, Player]] = { import com.github.unchama.minecraft.bukkit.algebra.BukkitPlayerHasUuid.instance val manaPersistence: ManaAmountPersistence[G] = new JdbcManaAmountPersistence[G] @@ -59,7 +66,9 @@ object System { topic.subscribe(1).mapFilter(identity) override val manaAmount: KeyedDataRepository[Player, ManaManipulation[G]] = - handles.repository.map(ManaManipulation.fromLevelCappedAmountRef[G](globalMultiplierRef)) + handles + .repository + .map(ManaManipulation.fromLevelCappedAmountRef[G](globalMultiplierRef)) override def setGlobalManaMultiplier(manaMultiplier: ManaMultiplier): G[Unit] = globalMultiplierRef.set(manaMultiplier) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/application/ManaRepositoryDefinition.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/application/ManaRepositoryDefinition.scala index 2cd0a5b79d..cc4c40532e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/application/ManaRepositoryDefinition.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/application/ManaRepositoryDefinition.scala @@ -3,39 +3,50 @@ package com.github.unchama.seichiassist.subsystems.mana.application import cats.Monad import cats.effect.concurrent.Ref import cats.effect.{ConcurrentEffect, Sync} -import com.github.unchama.datarepository.definitions.{RefDictBackedRepositoryDefinition, SignallingRepositoryDefinition} +import com.github.unchama.datarepository.definitions.{ + RefDictBackedRepositoryDefinition, + SignallingRepositoryDefinition +} import com.github.unchama.datarepository.template.RepositoryDefinition import com.github.unchama.generic.ContextCoercion import com.github.unchama.minecraft.algebra.HasUuid import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountReadAPI -import com.github.unchama.seichiassist.subsystems.mana.domain.{LevelCappedManaAmount, ManaAmount, ManaAmountPersistence} +import com.github.unchama.seichiassist.subsystems.mana.domain.{ + LevelCappedManaAmount, + ManaAmount, + ManaAmountPersistence +} import io.chrisdavenport.log4cats.ErrorLogger object ManaRepositoryDefinition { import cats.implicits._ - def withContext[ - F[_] : ConcurrentEffect : ErrorLogger, - G[_] : Sync : ContextCoercion[*[_], F], - Player: HasUuid - ](publishChanges: fs2.Pipe[F, (Player, LevelCappedManaAmount), Unit], - persistence: ManaAmountPersistence[G]) - (implicit breakCountReadAPI: BreakCountReadAPI[F, G, Player]) - : RepositoryDefinition[G, Player, Ref[G, LevelCappedManaAmount]] = { + def withContext[F[_]: ConcurrentEffect: ErrorLogger, G[_]: Sync: ContextCoercion[ + *[_], + F + ], Player: HasUuid]( + publishChanges: fs2.Pipe[F, (Player, LevelCappedManaAmount), Unit], + persistence: ManaAmountPersistence[G] + )( + implicit breakCountReadAPI: BreakCountReadAPI[F, G, Player] + ): RepositoryDefinition[G, Player, Ref[G, LevelCappedManaAmount]] = { - val valueRepository: RepositoryDefinition.Phased.TwoPhased[G, Player, LevelCappedManaAmount] = + val valueRepository + : RepositoryDefinition.Phased.TwoPhased[G, Player, LevelCappedManaAmount] = RefDictBackedRepositoryDefinition .usingUuidRefDict[G, Player, ManaAmount](persistence)(ManaAmount(0)) .toTwoPhased - .flatXmapWithPlayer(player => manaAmount => - breakCountReadAPI.seichiAmountDataRepository(player).read.map { data => - LevelCappedManaAmount.capping(manaAmount, data.levelCorrespondingToExp) - } + .flatXmapWithPlayer(player => + manaAmount => + breakCountReadAPI.seichiAmountDataRepository(player).read.map { data => + LevelCappedManaAmount.capping(manaAmount, data.levelCorrespondingToExp) + } )(cappedMana => Monad[G].pure(cappedMana.manaAmount)) - SignallingRepositoryDefinition - .withPublishSinkHidden[G, F, Player, LevelCappedManaAmount](publishChanges)(valueRepository) + SignallingRepositoryDefinition.withPublishSinkHidden[G, F, Player, LevelCappedManaAmount]( + publishChanges + )(valueRepository) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/application/process/RefillToCap.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/application/process/RefillToCap.scala index 6bc4db7e52..d56585e8a0 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/application/process/RefillToCap.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/application/process/RefillToCap.scala @@ -1,8 +1,8 @@ package com.github.unchama.seichiassist.subsystems.mana.application.process -import cats.{Functor, Monad} import cats.effect.concurrent.Ref import cats.implicits._ +import cats.{Functor, Monad} import com.github.unchama.datarepository.KeyedDataRepository import com.github.unchama.generic.{ContextCoercion, Diff} import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountReadAPI @@ -13,23 +13,20 @@ import com.github.unchama.seichiassist.subsystems.mana.domain.LevelCappedManaAmo */ object RefillToCap { - def using[ - F[_] : Functor, - G[_] : Monad : ContextCoercion[*[_], F], - Player - ](repository: KeyedDataRepository[Player, Ref[G, LevelCappedManaAmount]]) - (implicit breakCountReadAPI: BreakCountReadAPI[F, G, Player]): fs2.Stream[F, Unit] = { - breakCountReadAPI - .seichiStarLevelUpdates - .evalMap { case (player, Diff(_, _)) => + def using[F[_]: Functor, G[_]: Monad: ContextCoercion[*[_], F], Player]( + repository: KeyedDataRepository[Player, Ref[G, LevelCappedManaAmount]] + )(implicit breakCountReadAPI: BreakCountReadAPI[F, G, Player]): fs2.Stream[F, Unit] = { + breakCountReadAPI.seichiStarLevelUpdates.evalMap { + case (player, Diff(_, _)) => ContextCoercion { - repository.lift(player) + repository + .lift(player) .traverse { _.update(_.fillToCap) } .as(()) } - } + } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/application/process/UpdateManaCaps.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/application/process/UpdateManaCaps.scala index e15432188c..4329e87385 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/application/process/UpdateManaCaps.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/application/process/UpdateManaCaps.scala @@ -11,22 +11,17 @@ object UpdateManaCaps { import cats.implicits._ - def using[ - F[_] : Functor, - G[_] : Monad : ContextCoercion[*[_], F], - Player - ](repository: KeyedDataRepository[Player, Ref[G, LevelCappedManaAmount]]) - (implicit breakCountReadAPI: BreakCountReadAPI[F, G, Player]): fs2.Stream[F, Unit] = - breakCountReadAPI - .seichiLevelUpdates - .evalMap { case (player, Diff(_, newLevel)) => + def using[F[_]: Functor, G[_]: Monad: ContextCoercion[*[_], F], Player]( + repository: KeyedDataRepository[Player, Ref[G, LevelCappedManaAmount]] + )(implicit breakCountReadAPI: BreakCountReadAPI[F, G, Player]): fs2.Stream[F, Unit] = + breakCountReadAPI.seichiLevelUpdates.evalMap { + case (player, Diff(_, newLevel)) => ContextCoercion { - repository.lift(player) - .traverse { ref => - ref.updateMaybe(_.withHigherLevelOption(newLevel)) - } + repository + .lift(player) + .traverse { ref => ref.updateMaybe(_.withHigherLevelOption(newLevel)) } .as(()) } - } + } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/LevelCappedManaAmount.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/LevelCappedManaAmount.scala index 645f6b93b7..7c75a89e4e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/LevelCappedManaAmount.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/LevelCappedManaAmount.scala @@ -2,16 +2,13 @@ package com.github.unchama.seichiassist.subsystems.mana.domain import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.SeichiLevel -case class LevelCappedManaAmount private(manaAmount: ManaAmount, level: SeichiLevel) { +case class LevelCappedManaAmount private (manaAmount: ManaAmount, level: SeichiLevel) { import cats.implicits._ val cap: ManaAmount = ManaAmountCap.at(level) - assert( - manaAmount <= cap, - "LevelCappedManaAmountはマナのキャップ制約を満たす必要があります" - ) + assert(manaAmount <= cap, "LevelCappedManaAmountはマナのキャップ制約を満たす必要があります") val isFull: Boolean = manaAmount == cap @@ -19,7 +16,9 @@ case class LevelCappedManaAmount private(manaAmount: ManaAmount, level: SeichiLe LevelCappedManaAmount.capping(manaAmount.add(amount), level) } - def tryUse(amount: ManaAmount)(manaMultiplier: ManaMultiplier): Option[LevelCappedManaAmount] = { + def tryUse( + amount: ManaAmount + )(manaMultiplier: ManaMultiplier): Option[LevelCappedManaAmount] = { manaAmount.tryUse(amount)(manaMultiplier).map(LevelCappedManaAmount(_, level)) } @@ -32,8 +31,7 @@ case class LevelCappedManaAmount private(manaAmount: ManaAmount, level: SeichiLe lazy val fillToCap: LevelCappedManaAmount = LevelCappedManaAmount(cap, level) /** - * マナ最大値に対する `manaAmount` の割合を示す0以上1未満の数値。 - * マナ最大値が0だった場合には `None` となる。 + * マナ最大値に対する `manaAmount` の割合を示す0以上1未満の数値。 マナ最大値が0だった場合には `None` となる。 */ lazy val ratioToCap: Option[Double] = Option.when(cap.value != 0.0)(manaAmount.value / cap.value) @@ -47,5 +45,6 @@ object LevelCappedManaAmount { LevelCappedManaAmount(manaAmount min ManaAmountCap.at(level), level) } - val initialValue: LevelCappedManaAmount = LevelCappedManaAmount(ManaAmount(0), SeichiLevel.ofPositive(1)) + val initialValue: LevelCappedManaAmount = + LevelCappedManaAmount(ManaAmount(0), SeichiLevel.ofPositive(1)) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/ManaAmountCap.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/ManaAmountCap.scala index 93ebe1f944..1fc50c6991 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/ManaAmountCap.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/ManaAmountCap.scala @@ -1,7 +1,10 @@ package com.github.unchama.seichiassist.subsystems.mana.domain import com.github.unchama.generic.CachedFunction -import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.{SeichiLevel, SeichiLevelTable} +import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.{ + SeichiLevel, + SeichiLevelTable +} object ManaAmountCap { @@ -11,24 +14,19 @@ object ManaAmountCap { * 整地レベルと、その整地レベルでのマナ上限の対応を与える関数。 */ val at: SeichiLevel => ManaAmount = { + /** - * 整地レベル `level` を引数に取って、 `level` からその次のレベルに上がった際の - * マナ上限の増加値を返す関数。 + * 整地レベル `level` を引数に取って、 `level` からその次のレベルに上がった際の マナ上限の増加値を返す関数。 * - * - レベル9未満は増加なし - * - レベル9からのレベルアップで初めて100追加され、 - * - レベル10以降のレベルアップでは、 - * - n := 通り過ぎた10の倍数のレベルの数 - * - a := (n - 1) min 10 - * - x := (2のa乗 - 1) * 2 - * としたときに 10 + x ずつ追加される。 + * - レベル9未満は増加なし + * - レベル9からのレベルアップで初めて100追加され、 + * - レベル10以降のレベルアップでは、 + * - n := 通り過ぎた10の倍数のレベルの数 + * - a := (n - 1) min 10 + * - x := (2のa乗 - 1) * 2 としたときに 10 + x ずつ追加される。 * - * 例えば、 - * レベル10からレベル19までは10ずつ増加し、 - * レベル20からレベル29までは12ずつ、 - * レベル30からレベル39までは16ずつ、 - * レベル40からレベル49までは24ずつ…といった具合に、 - * レベル110までのレベル10ごとに増加幅が2冪で増える。 + * 例えば、 レベル10からレベル19までは10ずつ増加し、 レベル20からレベル29までは12ずつ、 レベル30からレベル39までは16ずつ、 + * レベル40からレベル49までは24ずつ…といった具合に、 レベル110までのレベル10ごとに増加幅が2冪で増える。 */ val increaseFrom: SeichiLevel => ManaAmount = CachedFunction { seichiLevel => if (seichiLevel < SeichiLevel.ofPositive(9)) { @@ -46,9 +44,7 @@ object ManaAmountCap { val levelsToConsider = SeichiLevelTable.table.levelRange.toVector // ManaAmount(0) からの累積和を取ったものをテーブルとする - val table = levelsToConsider - .map(increaseFrom) - .scanLeft(ManaAmount(0))(_.add(_)) + val table = levelsToConsider.map(increaseFrom).scanLeft(ManaAmount(0))(_.add(_)) level => table(level.level - 1) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/ManaManipulation.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/ManaManipulation.scala index bc54ce0158..8539b6687b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/ManaManipulation.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/ManaManipulation.scala @@ -24,8 +24,7 @@ trait ManaManipulation[F[_]] { def restoreCompletely: F[Unit] = restoreFraction(1) /** - * マナが足りない場合はマナを消費をせずに `None` を返し、 - * 足りていた場合は消費して `Some(amount)` を返すような作用。 + * マナが足りない場合はマナを消費をせずに `None` を返し、 足りていた場合は消費して `Some(amount)` を返すような作用。 */ def tryAcquire(amount: ManaAmount): F[Option[ManaAmount]] @@ -35,8 +34,9 @@ object ManaManipulation { import cats.implicits._ - def fromLevelCappedAmountRef[F[_] : FlatMap](multiplierRef: Ref[F, ManaMultiplier]) - (ref: Ref[F, LevelCappedManaAmount]): ManaManipulation[F] = + def fromLevelCappedAmountRef[F[_]: FlatMap]( + multiplierRef: Ref[F, ManaMultiplier] + )(ref: Ref[F, LevelCappedManaAmount]): ManaManipulation[F] = new ManaManipulation[F] { override def restoreAbsolute(amount: ManaAmount): F[Unit] = ref.update(_.add(amount)) @@ -49,7 +49,7 @@ object ManaManipulation { ref.modify { original => original.tryUse(amount)(multiplier) match { case Some(reduced) => (reduced, Some(amount)) - case None => (original, None) + case None => (original, None) } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/infrastructure/JdbcManaAmountPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/infrastructure/JdbcManaAmountPersistence.scala index 3bba3fce4e..2c27080658 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/infrastructure/JdbcManaAmountPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/infrastructure/JdbcManaAmountPersistence.scala @@ -1,7 +1,10 @@ package com.github.unchama.seichiassist.subsystems.mana.infrastructure import cats.effect.Sync -import com.github.unchama.seichiassist.subsystems.mana.domain.{ManaAmount, ManaAmountPersistence} +import com.github.unchama.seichiassist.subsystems.mana.domain.{ + ManaAmount, + ManaAmountPersistence +} import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} import java.util.UUID @@ -12,7 +15,8 @@ class JdbcManaAmountPersistence[F[_]](implicit F: Sync[F]) extends ManaAmountPer DB.localTx { implicit session => sql"select mana from playerdata where uuid = ${key.toString}" .map { rs => ManaAmount(rs.double("mana")) } - .first().apply() + .first() + .apply() } } @@ -20,7 +24,8 @@ class JdbcManaAmountPersistence[F[_]](implicit F: Sync[F]) extends ManaAmountPer F.delay { DB.localTx { implicit session => sql"update playerdata set mana = ${value.value} where uuid = ${key.toString}" - .update().apply() + .update() + .apply() } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/manabar/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/manabar/System.scala index afbfecd0cb..6738bc16e6 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/manabar/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/manabar/System.scala @@ -13,10 +13,9 @@ object System { import cats.implicits._ - def wired[ - F[_] : ConcurrentEffect : ErrorLogger, - G[_] : SyncEffect - ](implicit manaApi: ManaReadApi[F, G, Player]): G[Subsystem[F]] = { + def wired[F[_]: ConcurrentEffect: ErrorLogger, G[_]: SyncEffect]( + implicit manaApi: ManaReadApi[F, G, Player] + ): G[Subsystem[F]] = { import com.github.unchama.minecraft.bukkit.algebra.BukkitPlayerHasUuid.instance val definition = @@ -24,9 +23,8 @@ object System { BukkitRepositoryControls.createHandles(definition).map { control => new Subsystem[F] { - override val managedRepositoryControls: Seq[BukkitRepositoryControls[F, _]] = List( - control.coerceFinalizationContextTo[F] - ) + override val managedRepositoryControls: Seq[BukkitRepositoryControls[F, _]] = + List(control.coerceFinalizationContextTo[F]) } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/manabar/application/ManaBarManipulation.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/manabar/application/ManaBarManipulation.scala index fafcae7d92..8a78b21fb0 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/manabar/application/ManaBarManipulation.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/manabar/application/ManaBarManipulation.scala @@ -2,7 +2,10 @@ package com.github.unchama.seichiassist.subsystems.manabar.application import cats.Applicative import com.github.unchama.minecraft.objects.MinecraftBossBar -import com.github.unchama.seichiassist.subsystems.mana.domain.{LevelCappedManaAmount, ManaAmount} +import com.github.unchama.seichiassist.subsystems.mana.domain.{ + LevelCappedManaAmount, + ManaAmount +} import org.bukkit.ChatColor.{AQUA, BOLD} import java.text.DecimalFormat @@ -17,13 +20,20 @@ object ManaBarManipulation { decimalFormat.format(manaAmount.value) } - def write[F[_] : Applicative](amount: LevelCappedManaAmount, bossBar: MinecraftBossBar[F]): F[Unit] = { + def write[F[_]: Applicative]( + amount: LevelCappedManaAmount, + bossBar: MinecraftBossBar[F] + ): F[Unit] = { amount.ratioToCap match { case Some(fraction) => List( bossBar.visibility.write(true), bossBar.progress.write(fraction), - bossBar.title.write(s"$AQUA${BOLD}マナ(${formatAmount(amount.manaAmount)}/${formatAmount(amount.cap)})") + bossBar + .title + .write( + s"$AQUA${BOLD}マナ(${formatAmount(amount.manaAmount)}/${formatAmount(amount.cap)})" + ) ).sequence.as(()) case None => bossBar.visibility.write(false) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/manabar/application/ManaBarSynchronizationRepository.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/manabar/application/ManaBarSynchronizationRepository.scala index a0650b8e0d..b8649ce4be 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/manabar/application/ManaBarSynchronizationRepository.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/manabar/application/ManaBarSynchronizationRepository.scala @@ -13,36 +13,45 @@ import io.chrisdavenport.log4cats.ErrorLogger object ManaBarSynchronizationRepository { - type BossBarWithPlayer[F[_], P] = MinecraftBossBar[F] {type Player = P} + type BossBarWithPlayer[F[_], P] = MinecraftBossBar[F] { type Player = P } import cats.implicits._ - def withContext[ - G[_] : Sync, - F[_] : ConcurrentEffect : ContextCoercion[G, *[_]] : ErrorLogger, - Player: HasUuid - ](manaApi: ManaReadApi[F, G, Player]) - (createFreshBossBar: G[BossBarWithPlayer[F, Player]]): RepositoryDefinition[G, Player, _] = { - FiberAdjoinedRepositoryDefinition.extending { - RepositoryDefinition.Phased.SinglePhased - .withSupplierAndTrivialFinalization[G, Player, BossBarWithPlayer[F, Player]](createFreshBossBar) - }.withAnotherTappingAction { (player, pair) => - val (bossBar, promise) = pair - - val synchronization = - fs2.Stream - .eval(manaApi.readManaAmount(player)) - .translate(ContextCoercion.asFunctionK[G, F]) - .append(manaApi.manaAmountUpdates.through(StreamExtra.valuesWithKeyOfSameUuidAs(player))) - .evalTap(ManaBarManipulation.write[F](_, bossBar)) - - val programToRunAsync = - bossBar.players.add(player) >> - Concurrent[F].start[Nothing] { - StreamExtra.compileToRestartingStream("[ManaBarSynchronization]")(synchronization) - } >>= promise.complete - - EffectExtra.runAsyncAndForget[F, G, Unit](programToRunAsync) - } + def withContext[G[_]: Sync, F[_]: ConcurrentEffect: ContextCoercion[ + G, + *[_] + ]: ErrorLogger, Player: HasUuid]( + manaApi: ManaReadApi[F, G, Player] + )(createFreshBossBar: G[BossBarWithPlayer[F, Player]]): RepositoryDefinition[G, Player, _] = { + FiberAdjoinedRepositoryDefinition + .extending { + RepositoryDefinition + .Phased + .SinglePhased + .withSupplierAndTrivialFinalization[G, Player, BossBarWithPlayer[F, Player]]( + createFreshBossBar + ) + } + .withAnotherTappingAction { (player, pair) => + val (bossBar, promise) = pair + + val synchronization = + fs2 + .Stream + .eval(manaApi.readManaAmount(player)) + .translate(ContextCoercion.asFunctionK[G, F]) + .append( + manaApi.manaAmountUpdates.through(StreamExtra.valuesWithKeyOfSameUuidAs(player)) + ) + .evalTap(ManaBarManipulation.write[F](_, bossBar)) + + val programToRunAsync = + bossBar.players.add(player) >> + Concurrent[F].start[Nothing] { + StreamExtra.compileToRestartingStream("[ManaBarSynchronization]")(synchronization) + } >>= promise.complete + + EffectExtra.runAsyncAndForget[F, G, Unit](programToRunAsync) + } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/manabar/bukkit/CreateFreshBossBar.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/manabar/bukkit/CreateFreshBossBar.scala index 5b683a975e..ec736a298b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/manabar/bukkit/CreateFreshBossBar.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/manabar/bukkit/CreateFreshBossBar.scala @@ -10,7 +10,7 @@ object CreateFreshBossBar { import cats.implicits._ - def in[G[_] : Sync, F[_] : Sync]: G[MinecraftBossBar[F] {type Player = BukkitPlayer}] = + def in[G[_]: Sync, F[_]: Sync]: G[MinecraftBossBar[F] { type Player = BukkitPlayer }] = BukkitBossBar.in[G, F]("", BarColor.BLUE, BarStyle.SOLID).widen } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/System.scala index a642686ded..3c588c2ac2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/System.scala @@ -3,7 +3,10 @@ package com.github.unchama.seichiassist.subsystems.managedfly import cats.data.Kleisli import cats.effect.{ConcurrentEffect, SyncEffect, Timer} import com.github.unchama.datarepository.KeyedDataRepository -import com.github.unchama.datarepository.bukkit.player.{BukkitRepositoryControls, PlayerDataRepository} +import com.github.unchama.datarepository.bukkit.player.{ + BukkitRepositoryControls, + PlayerDataRepository +} import com.github.unchama.generic.ContextCoercion import com.github.unchama.generic.effect.concurrent.ReadOnlyRef import com.github.unchama.generic.effect.unsafe.EffectEnvironment @@ -20,8 +23,7 @@ import org.bukkit.entity.Player /** * NOTE: このサブシステム(managedfly)は本来BuildAssist側に属するが、 - * BuildAssistがSeichiAssistのサブシステムとして完全に整理されなおすまでは、 - * SeichiAssist直属のサブシステムとして扱う。 + * BuildAssistがSeichiAssistのサブシステムとして完全に整理されなおすまでは、 SeichiAssist直属のサブシステムとして扱う。 */ trait System[G[_], H[_]] extends Subsystem[H] { val api: ManagedFlyApi[G, Player] @@ -31,17 +33,18 @@ object System { import cats.implicits._ - def wired[ - AsyncContext[_] : ConcurrentEffect : OnMinecraftServerThread : Timer, - SyncContext[_] : SyncEffect : ContextCoercion[*[_], AsyncContext] - ](configuration: SystemConfiguration)(implicit effectEnvironment: EffectEnvironment) - : SyncContext[System[SyncContext, AsyncContext]] = { + def wired[AsyncContext[_]: ConcurrentEffect: OnMinecraftServerThread: Timer, SyncContext[ + _ + ]: SyncEffect: ContextCoercion[*[_], AsyncContext]](configuration: SystemConfiguration)( + implicit effectEnvironment: EffectEnvironment + ): SyncContext[System[SyncContext, AsyncContext]] = { implicit val _configuration: SystemConfiguration = configuration implicit val _jdbcRepository: FlyDurationPersistenceRepository[SyncContext] = new JdbcFlyDurationPersistenceRepository[SyncContext] - implicit val _playerKleisliManipulation: PlayerFlyStatusManipulation[Kleisli[AsyncContext, Player, *]] = + implicit val _playerKleisliManipulation + : PlayerFlyStatusManipulation[Kleisli[AsyncContext, Player, *]] = new BukkitPlayerFlyStatusManipulation[AsyncContext] implicit val _factory: ActiveSessionFactory[AsyncContext, Player] = @@ -49,28 +52,32 @@ object System { import com.github.unchama.minecraft.bukkit.algebra.BukkitPlayerHasUuid._ - BukkitRepositoryControls.createHandles( - ActiveSessionReferenceRepositoryDefinition.withContext(_factory, _jdbcRepository), - ).map { controls => - implicit val _repository: PlayerDataRepository[ActiveSessionReference[AsyncContext, SyncContext]] = - controls.repository + BukkitRepositoryControls + .createHandles( + ActiveSessionReferenceRepositoryDefinition.withContext(_factory, _jdbcRepository) + ) + .map { controls => + implicit val _repository + : PlayerDataRepository[ActiveSessionReference[AsyncContext, SyncContext]] = + controls.repository - new System[SyncContext, AsyncContext] { - override val api: ManagedFlyApi[SyncContext, Player] = new ManagedFlyApi[SyncContext, Player] { - override val playerFlyDurations: KeyedDataRepository[Player, ReadOnlyRef[SyncContext, PlayerFlyStatus]] = - controls.repository.map { sessionRef => - ReadOnlyRef.fromAnySource(sessionRef.getLatestFlyStatus) + new System[SyncContext, AsyncContext] { + override val api: ManagedFlyApi[SyncContext, Player] = + new ManagedFlyApi[SyncContext, Player] { + override val playerFlyDurations + : KeyedDataRepository[Player, ReadOnlyRef[SyncContext, PlayerFlyStatus]] = + controls.repository.map { sessionRef => + ReadOnlyRef.fromAnySource(sessionRef.getLatestFlyStatus) + } } - } - override val managedRepositoryControls: Seq[BukkitRepositoryControls[AsyncContext, _]] = Seq( - controls.coerceFinalizationContextTo[AsyncContext] - ) + override val managedRepositoryControls + : Seq[BukkitRepositoryControls[AsyncContext, _]] = + Seq(controls.coerceFinalizationContextTo[AsyncContext]) - override val commands: Map[String, TabExecutor] = Map( - "fly" -> BukkitFlyCommand.executor[AsyncContext, SyncContext] - ) + override val commands: Map[String, TabExecutor] = + Map("fly" -> BukkitFlyCommand.executor[AsyncContext, SyncContext]) + } } - } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSession.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSession.scala index 1b1414c1f5..59a47355f4 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSession.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSession.scala @@ -2,13 +2,17 @@ package com.github.unchama.seichiassist.subsystems.managedfly.application import cats.effect.Sync import com.github.unchama.generic.effect.concurrent.{AsymmetricTryableFiber, ReadOnlyRef} -import com.github.unchama.seichiassist.subsystems.managedfly.domain.{Flying, NotFlying, PlayerFlyStatus, RemainingFlyDuration} +import com.github.unchama.seichiassist.subsystems.managedfly.domain.{ + Flying, + NotFlying, + PlayerFlyStatus, + RemainingFlyDuration +} -class ActiveSession[ - AsyncContext[_] : Sync, - SyncContext[_] : Sync -](sessionFiber: AsymmetricTryableFiber[AsyncContext, Nothing], - latestRemainingDurationRef: ReadOnlyRef[SyncContext, RemainingFlyDuration]) { +class ActiveSession[AsyncContext[_]: Sync, SyncContext[_]: Sync]( + sessionFiber: AsymmetricTryableFiber[AsyncContext, Nothing], + latestRemainingDurationRef: ReadOnlyRef[SyncContext, RemainingFlyDuration] +) { import cats.implicits._ @@ -23,9 +27,7 @@ class ActiveSession[ sessionIsActive <- isActive latestFlyStatus <- if (sessionIsActive) - latestRemainingDurationRef.read.map { duration => - Flying(duration) - } + latestRemainingDurationRef.read.map { duration => Flying(duration) } else Sync[SyncContext].pure(NotFlying) } yield latestFlyStatus diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSessionFactory.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSessionFactory.scala index f704a5dfb4..312ac01c60 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSessionFactory.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSessionFactory.scala @@ -11,22 +11,25 @@ import com.github.unchama.seichiassist.subsystems.managedfly.domain._ /** * プレーヤーに紐づいたFlyセッションを作成できるオブジェクト。 */ -class ActiveSessionFactory[ - AsyncContext[_] : Timer : Concurrent, - Player -](implicit KleisliAsyncContext: PlayerFlyStatusManipulation[Kleisli[AsyncContext, Player, *]]) { +class ActiveSessionFactory[AsyncContext[_]: Timer: Concurrent, Player]( + implicit KleisliAsyncContext: PlayerFlyStatusManipulation[Kleisli[AsyncContext, Player, *]] +) { type KleisliAsyncContext[A] = Kleisli[AsyncContext, Player, A] - private def tickDuration[F[_]](duration: RemainingFlyDuration)(implicit F: Sync[F]): F[RemainingFlyDuration] = + private def tickDuration[F[_]]( + duration: RemainingFlyDuration + )(implicit F: Sync[F]): F[RemainingFlyDuration] = duration.tickOneMinute match { case Some(tickedDuration) => F.pure(tickedDuration) - case None => F.raiseError(FlyDurationExpired) + case None => F.raiseError(FlyDurationExpired) } import KleisliAsyncContext._ - private def doOneMinuteCycle(duration: RemainingFlyDuration): KleisliAsyncContext[RemainingFlyDuration] = { + private def doOneMinuteCycle( + duration: RemainingFlyDuration + ): KleisliAsyncContext[RemainingFlyDuration] = { import cats.implicits.catsSyntaxFlatMapOps import scala.concurrent.duration._ @@ -44,9 +47,9 @@ class ActiveSessionFactory[ } yield newDuration } - def start[ - SyncContext[_] : Sync : ContextCoercion[*[_], AsyncContext] - ](totalDuration: RemainingFlyDuration): KleisliAsyncContext[ActiveSession[AsyncContext, SyncContext]] = { + def start[SyncContext[_]: Sync: ContextCoercion[*[_], AsyncContext]]( + totalDuration: RemainingFlyDuration + ): KleisliAsyncContext[ActiveSession[AsyncContext, SyncContext]] = { import cats.effect.implicits._ import cats.implicits._ import com.github.unchama.generic.ContextCoercion._ @@ -71,7 +74,7 @@ class ActiveSessionFactory[ } }.guaranteeCase { case ExitCase.Error(e: InternalInterruption) => sendNotificationsOnInterruption(e) - case _ => Monad[KleisliAsyncContext].unit + case _ => Monad[KleisliAsyncContext].unit }.guarantee { synchronizeFlyStatus(NotFlying) }.run(player) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSessionReference.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSessionReference.scala index 91677129c6..720fc0ea2d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSessionReference.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSessionReference.scala @@ -8,24 +8,31 @@ import com.github.unchama.seichiassist.subsystems.managedfly.domain.{NotFlying, /** * プレーヤーの飛行セッションの参照 */ -class ActiveSessionReference[ - AsyncContext[_] : ConcurrentEffect, - SyncContext[_] : Sync : ContextCoercion[*[_], AsyncContext] -](private val sessionMutexRef: Mutex[AsyncContext, SyncContext, Option[ActiveSession[AsyncContext, SyncContext]]]) { +class ActiveSessionReference[AsyncContext[_]: ConcurrentEffect, SyncContext[ + _ +]: Sync: ContextCoercion[*[_], AsyncContext]]( + private val sessionMutexRef: Mutex[AsyncContext, SyncContext, Option[ + ActiveSession[AsyncContext, SyncContext] + ]] +) { import cats.implicits._ - private def finishSessionIfPresent(sessionOption: Option[ActiveSession[AsyncContext, SyncContext]]): AsyncContext[Boolean] = { + private def finishSessionIfPresent( + sessionOption: Option[ActiveSession[AsyncContext, SyncContext]] + ): AsyncContext[Boolean] = { sessionOption match { case Some(session) => session.finish - case None => Concurrent[AsyncContext].pure(false) + case None => Concurrent[AsyncContext].pure(false) } } def stopAnyRunningSession: AsyncContext[Boolean] = sessionMutexRef.lockAndModify(finishSessionIfPresent(_).map(finished => (None, finished))) - def replaceSession(createSession: AsyncContext[ActiveSession[AsyncContext, SyncContext]]): AsyncContext[Unit] = + def replaceSession( + createSession: AsyncContext[ActiveSession[AsyncContext, SyncContext]] + ): AsyncContext[Unit] = sessionMutexRef.lockAndModify { sessionOption => for { session <- finishSessionIfPresent(sessionOption) >> createSession @@ -37,7 +44,7 @@ class ActiveSessionReference[ sessionOption <- sessionMutexRef.readLatest status <- sessionOption match { case Some(session) => session.latestFlyStatus - case None => Sync[SyncContext].pure(NotFlying) + case None => Sync[SyncContext].pure(NotFlying) } } yield status } @@ -46,12 +53,12 @@ object ActiveSessionReference { import cats.implicits._ - def createNew[ - AsyncContext[_] : ConcurrentEffect, - SyncContext[_] : Sync : ContextCoercion[*[_], AsyncContext] - ]: SyncContext[ActiveSessionReference[AsyncContext, SyncContext]] = { + def createNew[AsyncContext[_]: ConcurrentEffect, SyncContext[_]: Sync: ContextCoercion[*[ + _ + ], AsyncContext]]: SyncContext[ActiveSessionReference[AsyncContext, SyncContext]] = { for { - mutex <- Mutex.of[AsyncContext, SyncContext, Option[ActiveSession[AsyncContext, SyncContext]]](None) + mutex <- Mutex + .of[AsyncContext, SyncContext, Option[ActiveSession[AsyncContext, SyncContext]]](None) } yield new ActiveSessionReference(mutex) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/FlyDurationPersistenceRepository.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/FlyDurationPersistenceRepository.scala index 6183381a3a..eae097136a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/FlyDurationPersistenceRepository.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/FlyDurationPersistenceRepository.scala @@ -5,4 +5,5 @@ import com.github.unchama.seichiassist.subsystems.managedfly.domain.RemainingFly import java.util.UUID -trait FlyDurationPersistenceRepository[F[_]] extends RefDict[F, UUID, Option[RemainingFlyDuration]] +trait FlyDurationPersistenceRepository[F[_]] + extends RefDict[F, UUID, Option[RemainingFlyDuration]] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/PlayerFlyStatusManipulation.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/PlayerFlyStatusManipulation.scala index 35ea00b52c..5208649b5e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/PlayerFlyStatusManipulation.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/PlayerFlyStatusManipulation.scala @@ -1,6 +1,10 @@ package com.github.unchama.seichiassist.subsystems.managedfly.application -import com.github.unchama.seichiassist.subsystems.managedfly.domain.{IdleStatus, PlayerFlyStatus, RemainingFlyDuration} +import com.github.unchama.seichiassist.subsystems.managedfly.domain.{ + IdleStatus, + PlayerFlyStatus, + RemainingFlyDuration +} /** * プレーヤーの飛行状態に`F`の文脈で干渉する手段を与える型クラスインスタンスのtrait。 @@ -8,15 +12,14 @@ import com.github.unchama.seichiassist.subsystems.managedfly.domain.{IdleStatus, * `F` は `Kleisli[G, Player, *]` の形をしていることを想定している。 */ trait PlayerFlyStatusManipulation[F[_]] extends AnyRef { + /** - * 飛行に必要な経験値をプレーヤーが持っていることを保証するアクション。 - * このアクションは [[PlayerExpNotEnough]] を `raiseError` してよい。 + * 飛行に必要な経験値をプレーヤーが持っていることを保証するアクション。 このアクションは [[PlayerExpNotEnough]] を `raiseError` してよい。 */ val ensurePlayerExp: F[Unit] /** - * 飛行に必要な経験値をプレーヤーに消費させるアクション。 - * このアクションは [[PlayerExpNotEnough]] を `raiseError` してよい。 + * 飛行に必要な経験値をプレーヤーに消費させるアクション。 このアクションは [[PlayerExpNotEnough]] を `raiseError` してよい。 */ val consumePlayerExp: F[Unit] @@ -43,4 +46,4 @@ trait PlayerFlyStatusManipulation[F[_]] extends AnyRef { object PlayerFlyStatusManipulation { def apply[F[_]: PlayerFlyStatusManipulation]: PlayerFlyStatusManipulation[F] = implicitly -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/repository/ActiveSessionReferenceRepositoryDefinition.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/repository/ActiveSessionReferenceRepositoryDefinition.scala index 5462559630..600fa74ba7 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/repository/ActiveSessionReferenceRepositoryDefinition.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/repository/ActiveSessionReferenceRepositoryDefinition.scala @@ -5,37 +5,40 @@ import com.github.unchama.datarepository.definitions.RefDictBackedRepositoryDefi import com.github.unchama.datarepository.template.RepositoryDefinition import com.github.unchama.generic.effect.EffectExtra import com.github.unchama.minecraft.algebra.HasUuid -import com.github.unchama.seichiassist.subsystems.managedfly.application.{ActiveSessionFactory, ActiveSessionReference, FlyDurationPersistenceRepository} -import com.github.unchama.seichiassist.subsystems.managedfly.domain.{PlayerFlyStatus, RemainingFlyDuration} +import com.github.unchama.seichiassist.subsystems.managedfly.application.{ + ActiveSessionFactory, + ActiveSessionReference, + FlyDurationPersistenceRepository +} +import com.github.unchama.seichiassist.subsystems.managedfly.domain.{ + PlayerFlyStatus, + RemainingFlyDuration +} object ActiveSessionReferenceRepositoryDefinition { import cats.implicits._ - def withContext[ - F[_] : ConcurrentEffect, - G[_] : SyncEffect, - Player: HasUuid - ](factory: ActiveSessionFactory[F, Player], - persistence: FlyDurationPersistenceRepository[G]): RepositoryDefinition[G, Player, ActiveSessionReference[F, G]] = + def withContext[F[_]: ConcurrentEffect, G[_]: SyncEffect, Player: HasUuid]( + factory: ActiveSessionFactory[F, Player], + persistence: FlyDurationPersistenceRepository[G] + ): RepositoryDefinition[G, Player, ActiveSessionReference[F, G]] = RefDictBackedRepositoryDefinition .usingUuidRefDict[G, Player, Option[RemainingFlyDuration]](persistence)(None) .toTwoPhased - .flatXmapWithPlayerAndIntermediateEffects(player => data => - ActiveSessionReference - .createNew[F, G] - .flatTap { reference => + .flatXmapWithPlayerAndIntermediateEffects(player => + data => + ActiveSessionReference.createNew[F, G].flatTap { reference => EffectExtra.runAsyncAndForget[F, G, Option[Unit]] { data.traverse { duration => reference.replaceSession(factory.start[G](duration).run(player)) } } } - )(sessionRef => - sessionRef.getLatestFlyStatus.map(PlayerFlyStatus.toDurationOption) - )(sessionRef => - EffectExtra.runAsyncAndForget[F, G, Unit] { - sessionRef.stopAnyRunningSession.as(()) - } >> sessionRef.getLatestFlyStatus.map(PlayerFlyStatus.toDurationOption) + )(sessionRef => sessionRef.getLatestFlyStatus.map(PlayerFlyStatus.toDurationOption))( + sessionRef => + EffectExtra.runAsyncAndForget[F, G, Unit] { + sessionRef.stopAnyRunningSession.as(()) + } >> sessionRef.getLatestFlyStatus.map(PlayerFlyStatus.toDurationOption) ) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/bukkit/BukkitPlayerFlyStatusManipulation.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/bukkit/BukkitPlayerFlyStatusManipulation.scala index 548fb267e4..a7e26b50d0 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/bukkit/BukkitPlayerFlyStatusManipulation.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/bukkit/BukkitPlayerFlyStatusManipulation.scala @@ -10,16 +10,15 @@ import com.github.unchama.seichiassist.util.exp.ExperienceManager import org.bukkit.ChatColor.{GRAY, GREEN, RED} import org.bukkit.entity.Player -class BukkitPlayerFlyStatusManipulation[ - AsyncContext[_] : Timer : Concurrent : OnMinecraftServerThread -](implicit configuration: SystemConfiguration) - extends PlayerFlyStatusManipulation[Kleisli[AsyncContext, Player, *]] { +class BukkitPlayerFlyStatusManipulation[AsyncContext[ + _ +]: Timer: Concurrent: OnMinecraftServerThread](implicit configuration: SystemConfiguration) + extends PlayerFlyStatusManipulation[Kleisli[AsyncContext, Player, *]] { import cats.implicits._ /** - * 飛行に必要な経験値をプレーヤーが持っていることを保証するアクション。 - * このアクションは [[PlayerExpNotEnough]] を `raiseError` してよい。 + * 飛行に必要な経験値をプレーヤーが持っていることを保証するアクション。 このアクションは [[PlayerExpNotEnough]] を `raiseError` してよい。 */ override val ensurePlayerExp: Kleisli[AsyncContext, Player, Unit] = Kleisli { player => val expManager = new ExperienceManager(player) @@ -28,12 +27,14 @@ class BukkitPlayerFlyStatusManipulation[ hasExp <- OnMinecraftServerThread[AsyncContext].runAction(SyncIO { expManager.hasExp(configuration.expConsumptionAmount) }) - _ <- if (hasExp) Sync[AsyncContext].unit else Sync[AsyncContext].raiseError(PlayerExpNotEnough) + _ <- + if (hasExp) Sync[AsyncContext].unit + else Sync[AsyncContext].raiseError(PlayerExpNotEnough) } yield () } + /** - * 飛行に必要な経験値をプレーヤーに消費させるアクション。 - * このアクションは [[PlayerExpNotEnough]] を `raiseError` してよい。 + * 飛行に必要な経験値をプレーヤーに消費させるアクション。 このアクションは [[PlayerExpNotEnough]] を `raiseError` してよい。 */ override val consumePlayerExp: Kleisli[AsyncContext, Player, Unit] = Kleisli { player => val expManager = new ExperienceManager(player) @@ -47,39 +48,44 @@ class BukkitPlayerFlyStatusManipulation[ false } }) - _ <- if (consumed) - Sync[AsyncContext].unit - else - Sync[AsyncContext].raiseError(PlayerExpNotEnough) + _ <- + if (consumed) + Sync[AsyncContext].unit + else + Sync[AsyncContext].raiseError(PlayerExpNotEnough) } yield () } /** * プレーヤーがアイドル状態であるかを判定するアクション。 */ - override val isPlayerIdle: Kleisli[AsyncContext, Player, IdleStatus] = Kleisli { player: Player => - Sync[AsyncContext].delay { - SeichiAssist.playermap(player.getUniqueId).idleMinute >= 10 - }.map { - if (_) Idle else HasMovedRecently - } + override val isPlayerIdle: Kleisli[AsyncContext, Player, IdleStatus] = Kleisli { + player: Player => + Sync[AsyncContext] + .delay { + SeichiAssist.playermap(player.getUniqueId).idleMinute >= 10 + } + .map { + if (_) Idle else HasMovedRecently + } } /** * プレーヤーの飛行状態を[[PlayerFlyStatus]]に基づいてセットするアクション。 */ - override val synchronizeFlyStatus: PlayerFlyStatus => Kleisli[AsyncContext, Player, Unit] = { status => - Kleisli { player => - val shouldBeFlying = status match { - case Flying(_) => true - case NotFlying => false - } + override val synchronizeFlyStatus: PlayerFlyStatus => Kleisli[AsyncContext, Player, Unit] = { + status => + Kleisli { player => + val shouldBeFlying = status match { + case Flying(_) => true + case NotFlying => false + } - OnMinecraftServerThread[AsyncContext].runAction(SyncIO { - player.setAllowFlight(shouldBeFlying) - player.setFlying(shouldBeFlying) - }) - } + OnMinecraftServerThread[AsyncContext].runAction(SyncIO { + player.setAllowFlight(shouldBeFlying) + player.setFlying(shouldBeFlying) + }) + } } private val sendMessages: List[String] => Kleisli[AsyncContext, Player, Unit] = { messages => @@ -93,16 +99,18 @@ class BukkitPlayerFlyStatusManipulation[ /** * 放置状態も考慮して、プレーヤーに残飛行時間の通知を送るアクション。 */ - override val notifyRemainingDuration: (IdleStatus, RemainingFlyDuration) => Kleisli[AsyncContext, Player, Unit] = + override val notifyRemainingDuration + : (IdleStatus, RemainingFlyDuration) => Kleisli[AsyncContext, Player, Unit] = (status, duration) => { val message = status match { case Idle => s"${GRAY}放置時間中のflyは無期限で継続中です(経験値は消費しません)" - case HasMovedRecently => duration match { - case RemainingFlyDuration.Infinity => - s"${GREEN}fly効果は無期限で継続中です" - case RemainingFlyDuration.PositiveMinutes(minutes) => - s"${GREEN}fly効果はあと${minutes}分です" - } + case HasMovedRecently => + duration match { + case RemainingFlyDuration.Infinity => + s"${GREEN}fly効果は無期限で継続中です" + case RemainingFlyDuration.PositiveMinutes(minutes) => + s"${GREEN}fly効果はあと${minutes}分です" + } } sendMessages(List(message)) @@ -111,16 +119,13 @@ class BukkitPlayerFlyStatusManipulation[ /** * [[InternalInterruption]] に対応して、プレーヤーへセッションが終了することを通知するアクション。 */ - override val sendNotificationsOnInterruption: InternalInterruption => Kleisli[AsyncContext, Player, Unit] = + override val sendNotificationsOnInterruption + : InternalInterruption => Kleisli[AsyncContext, Player, Unit] = exception => { val messages = exception match { - case PlayerExpNotEnough => List( - s"${RED}fly効果の発動に必要な経験値が不足しているため、", - s"${RED}fly効果を終了しました" - ) - case FlyDurationExpired => List( - s"${GREEN}fly効果が終了しました" - ) + case PlayerExpNotEnough => + List(s"${RED}fly効果の発動に必要な経験値が不足しているため、", s"${RED}fly効果を終了しました") + case FlyDurationExpired => List(s"${GREEN}fly効果が終了しました") } sendMessages(messages) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/bukkit/controllers/BukkitFlyCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/bukkit/controllers/BukkitFlyCommand.scala index 7b574ea790..c13bae93ca 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/bukkit/controllers/BukkitFlyCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/bukkit/controllers/BukkitFlyCommand.scala @@ -6,8 +6,15 @@ import com.github.unchama.contextualexecutor.builder.Parsers import com.github.unchama.contextualexecutor.executors.BranchedExecutor import com.github.unchama.datarepository.KeyedDataRepository import com.github.unchama.seichiassist.commands.contextual.builder.BuilderTemplates -import com.github.unchama.seichiassist.subsystems.managedfly.application.{ActiveSessionFactory, ActiveSessionReference} -import com.github.unchama.seichiassist.subsystems.managedfly.domain.{Flying, NotFlying, RemainingFlyDuration} +import com.github.unchama.seichiassist.subsystems.managedfly.application.{ + ActiveSessionFactory, + ActiveSessionReference +} +import com.github.unchama.seichiassist.subsystems.managedfly.domain.{ + Flying, + NotFlying, + RemainingFlyDuration +} import com.github.unchama.targetedeffect.TargetedEffect import com.github.unchama.targetedeffect.commandsender.MessageEffect import org.bukkit.ChatColor._ @@ -26,52 +33,58 @@ object BukkitFlyCommand { s"${GREEN}時間指定は「1」以上の整数を入力してください。" private val printUsageExecutor = - BuilderTemplates.playerCommandBuilder + BuilderTemplates + .playerCommandBuilder .execution(_ => IO.pure(MessageEffect(commandHelpMessage))) .build() - private val durationParser = Parsers - .closedRangeInt(1, Int.MaxValue, MessageEffect(durationParseFailedMessage)) - .andThen { parseResult => - parseResult.map { - case r: Int => RemainingFlyDuration.PositiveMinutes.fromPositive(r) - } + private val durationParser = + Parsers.closedRangeInt(1, Int.MaxValue, MessageEffect(durationParseFailedMessage)).andThen { + parseResult => + parseResult.map { case r: Int => RemainingFlyDuration.PositiveMinutes.fromPositive(r) } } import cats.effect.implicits._ import cats.implicits._ - def startEndlessCommand[ - F[_] : ConcurrentEffect : Timer, - G[_] : SyncEffect - ](implicit sessionReferenceRepository: KeyedDataRepository[Player, ActiveSessionReference[F, G]], - factory: ActiveSessionFactory[F, Player]): ContextualExecutor = - BuilderTemplates.playerCommandBuilder + def startEndlessCommand[F[_]: ConcurrentEffect: Timer, G[_]: SyncEffect]( + implicit + sessionReferenceRepository: KeyedDataRepository[Player, ActiveSessionReference[F, G]], + factory: ActiveSessionFactory[F, Player] + ): ContextualExecutor = + BuilderTemplates + .playerCommandBuilder .execution { context => for { _ <- sessionReferenceRepository(context.sender) - .replaceSession(factory.start[G](RemainingFlyDuration.Infinity).run(context.sender)) + .replaceSession( + factory.start[G](RemainingFlyDuration.Infinity).run(context.sender) + ) .toIO } yield TargetedEffect.emptyEffect } .build() - def addCommand[ - F[_] : ConcurrentEffect : Timer, - G[_] : SyncEffect - ](implicit sessionReferenceRepository: KeyedDataRepository[Player, ActiveSessionReference[F, G]], - factory: ActiveSessionFactory[F, Player]): ContextualExecutor = - BuilderTemplates.playerCommandBuilder + def addCommand[F[_]: ConcurrentEffect: Timer, G[_]: SyncEffect]( + implicit + sessionReferenceRepository: KeyedDataRepository[Player, ActiveSessionReference[F, G]], + factory: ActiveSessionFactory[F, Player] + ): ContextualExecutor = + BuilderTemplates + .playerCommandBuilder .argumentsParsers(List(durationParser)) .execution { context => val List(duration: RemainingFlyDuration) = context.args.parsed for { - currentStatus <- sessionReferenceRepository(context.sender).getLatestFlyStatus.runSync[SyncIO].toIO + currentStatus <- sessionReferenceRepository(context.sender) + .getLatestFlyStatus + .runSync[SyncIO] + .toIO newTotalDuration = currentStatus match { case Flying(remainingDuration) => remainingDuration.combine(duration) - case NotFlying => duration + case NotFlying => duration } _ <- sessionReferenceRepository(context.sender) @@ -81,16 +94,16 @@ object BukkitFlyCommand { } .build() - def finishCommand[ - F[_] : ConcurrentEffect, G[_] - ](implicit sessionReferenceRepository: KeyedDataRepository[Player, ActiveSessionReference[F, G]]): ContextualExecutor = - BuilderTemplates.playerCommandBuilder + def finishCommand[F[_]: ConcurrentEffect, G[_]]( + implicit + sessionReferenceRepository: KeyedDataRepository[Player, ActiveSessionReference[F, G]] + ): ContextualExecutor = + BuilderTemplates + .playerCommandBuilder .execution { context => for { sessionStopped <- - sessionReferenceRepository(context.sender) - .stopAnyRunningSession - .toIO + sessionReferenceRepository(context.sender).stopAnyRunningSession.toIO } yield { if (sessionStopped) { MessageEffect(s"${GREEN}fly効果を停止しました。") @@ -101,11 +114,11 @@ object BukkitFlyCommand { } .build() - def executor[ - F[_] : ConcurrentEffect : Timer, - G[_] : SyncEffect - ](implicit sessionReferenceRepository: KeyedDataRepository[Player, ActiveSessionReference[F, G]], - factory: ActiveSessionFactory[F, Player]): TabExecutor = + def executor[F[_]: ConcurrentEffect: Timer, G[_]: SyncEffect]( + implicit + sessionReferenceRepository: KeyedDataRepository[Player, ActiveSessionReference[F, G]], + factory: ActiveSessionFactory[F, Player] + ): TabExecutor = BranchedExecutor( Map( "endless" -> startEndlessCommand[F, G], @@ -113,6 +126,7 @@ object BukkitFlyCommand { "finish" -> finishCommand[F, G], "end" -> finishCommand[F, G] ), - whenArgInsufficient = Some(printUsageExecutor), whenBranchNotFound = Some(printUsageExecutor) + whenArgInsufficient = Some(printUsageExecutor), + whenBranchNotFound = Some(printUsageExecutor) ).asNonBlockingTabExecutor() } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/domain/PlayerFlyStatus.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/domain/PlayerFlyStatus.scala index dcdfe987da..b85bcf4ea5 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/domain/PlayerFlyStatus.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/domain/PlayerFlyStatus.scala @@ -9,6 +9,6 @@ case object NotFlying extends PlayerFlyStatus object PlayerFlyStatus { def toDurationOption(status: PlayerFlyStatus): Option[RemainingFlyDuration] = status match { case Flying(remainingDuration) => Some(remainingDuration) - case NotFlying => None + case NotFlying => None } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/domain/RemainingFlyDuration.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/domain/RemainingFlyDuration.scala index 58c7d974be..ef0beec399 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/domain/RemainingFlyDuration.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/domain/RemainingFlyDuration.scala @@ -19,7 +19,7 @@ object RemainingFlyDuration { override val tickOneMinute: Option[RemainingFlyDuration] = Some(this) } - case class PositiveMinutes private(value: Int) extends RemainingFlyDuration { + case class PositiveMinutes private (value: Int) extends RemainingFlyDuration { override lazy val tickOneMinute: Option[RemainingFlyDuration] = { import PositiveMinutes.fromPositive diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/infrastructure/JdbcFlyDurationPersistenceRepository.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/infrastructure/JdbcFlyDurationPersistenceRepository.scala index 59f4a98fe9..bdb1d8bedc 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/infrastructure/JdbcFlyDurationPersistenceRepository.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/infrastructure/JdbcFlyDurationPersistenceRepository.scala @@ -1,45 +1,44 @@ package com.github.unchama.seichiassist.subsystems.managedfly.infrastructure -import java.util.UUID - import cats.effect.Sync import com.github.unchama.seichiassist.subsystems.managedfly.application.FlyDurationPersistenceRepository import com.github.unchama.seichiassist.subsystems.managedfly.domain.RemainingFlyDuration import scalikejdbc._ -class JdbcFlyDurationPersistenceRepository[SyncContext[_]](implicit SyncContext: Sync[SyncContext]) - extends FlyDurationPersistenceRepository[SyncContext] { +import java.util.UUID - override def write(key: UUID, duration: Option[RemainingFlyDuration]): SyncContext[Unit] = SyncContext.delay { - DB.localTx { implicit session => - val serializedDuration = duration match { - case Some(RemainingFlyDuration.PositiveMinutes(n)) => n - case Some(RemainingFlyDuration.Infinity) => -1 - case None => 0 - } +class JdbcFlyDurationPersistenceRepository[SyncContext[_]]( + implicit SyncContext: Sync[SyncContext] +) extends FlyDurationPersistenceRepository[SyncContext] { - sql""" + override def write(key: UUID, duration: Option[RemainingFlyDuration]): SyncContext[Unit] = + SyncContext.delay { + DB.localTx { implicit session => + val serializedDuration = duration match { + case Some(RemainingFlyDuration.PositiveMinutes(n)) => n + case Some(RemainingFlyDuration.Infinity) => -1 + case None => 0 + } + + sql""" insert into fly_status_cache values (${key.toString}, $serializedDuration) on duplicate key update remaining_fly_minutes = $serializedDuration - """.stripMargin - .update().apply() + """.stripMargin.update().apply() + } } - } - override def read(key: UUID): SyncContext[Option[Option[RemainingFlyDuration]]] = SyncContext.delay { - DB.localTx { implicit session => - sql""" + override def read(key: UUID): SyncContext[Option[Option[RemainingFlyDuration]]] = + SyncContext.delay { + DB.localTx { implicit session => + sql""" select remaining_fly_minutes from fly_status_cache where player_uuid = ${key.toString} - """ - .map { rs => rs.int("remaining_fly_minutes") } - .headOption().apply() - .map { - case 0 => None - case -1 => Some(RemainingFlyDuration.Infinity) + """.map { rs => rs.int("remaining_fly_minutes") }.headOption().apply().map { + case 0 => None + case -1 => Some(RemainingFlyDuration.Infinity) case n if n > 0 => Some(RemainingFlyDuration.PositiveMinutes.fromPositive(n)) - case _ => throw new IllegalStateException("DB contains out-of-range value") + case _ => throw new IllegalStateException("DB contains out-of-range value") } + } } - } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/System.scala index fb9b68bffd..2a7d6e684e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/System.scala @@ -2,18 +2,27 @@ package com.github.unchama.seichiassist.subsystems.mebius import cats.effect.{ContextShift, IO, Sync, SyncEffect, SyncIO, Timer} import com.github.unchama.concurrent.RepeatingTaskContext -import com.github.unchama.datarepository.bukkit.player.{BukkitRepositoryControls, PlayerDataRepository} +import com.github.unchama.datarepository.bukkit.player.{ + BukkitRepositoryControls, + PlayerDataRepository +} import com.github.unchama.datarepository.template.RepositoryDefinition import com.github.unchama.generic.effect.unsafe.EffectEnvironment import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.meta.subsystem.Subsystem -import com.github.unchama.seichiassist.subsystems.mebius.application.repository.{MebiusSpeechRoutineFiberRepositoryDefinitions, SpeechServiceRepositoryDefinitions} +import com.github.unchama.seichiassist.subsystems.mebius.application.repository.{ + MebiusSpeechRoutineFiberRepositoryDefinitions, + SpeechServiceRepositoryDefinitions +} import com.github.unchama.seichiassist.subsystems.mebius.bukkit.PropertyModificationBukkitMessages import com.github.unchama.seichiassist.subsystems.mebius.bukkit.command.MebiusCommandExecutorProvider import com.github.unchama.seichiassist.subsystems.mebius.bukkit.gateway.BukkitMebiusSpeechGateway import com.github.unchama.seichiassist.subsystems.mebius.bukkit.listeners._ import com.github.unchama.seichiassist.subsystems.mebius.domain.message.PropertyModificationMessages -import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{MebiusSpeechBlockageState, MebiusSpeechGateway} +import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{ + MebiusSpeechBlockageState, + MebiusSpeechGateway +} import com.github.unchama.seichiassist.subsystems.mebius.service.MebiusSpeechService import com.github.unchama.seichiassist.subsystems.seasonalevents.api.SeasonalEventsAPI import com.github.unchama.util.RandomEffect @@ -24,57 +33,66 @@ import org.bukkit.event.Listener import scala.util.Random object System { - def wired[ - F[_] : Sync, - G[_] : SeasonalEventsAPI : SyncEffect - ](implicit effectEnvironment: EffectEnvironment, + def wired[F[_]: Sync, G[_]: SeasonalEventsAPI: SyncEffect]( + implicit effectEnvironment: EffectEnvironment, timer: Timer[IO], repeatingTaskContext: RepeatingTaskContext, onMainThread: OnMinecraftServerThread[IO], - ioShift: ContextShift[IO]): SyncIO[Subsystem[F]] = { + ioShift: ContextShift[IO] + ): SyncIO[Subsystem[F]] = { implicit val messages: PropertyModificationMessages = PropertyModificationBukkitMessages - implicit val gatewayProvider: Player => MebiusSpeechGateway[SyncIO] = new BukkitMebiusSpeechGateway(_) - implicit val getFreshSpeechBlockageState: SyncIO[MebiusSpeechBlockageState[SyncIO]] = SyncIO(new MebiusSpeechBlockageState[SyncIO]) + implicit val gatewayProvider: Player => MebiusSpeechGateway[SyncIO] = + new BukkitMebiusSpeechGateway(_) + implicit val getFreshSpeechBlockageState: SyncIO[MebiusSpeechBlockageState[SyncIO]] = + SyncIO(new MebiusSpeechBlockageState[SyncIO]) val seasonalEventsAPI = SeasonalEventsAPI[G] import seasonalEventsAPI.christmasEventsAPI implicit val randomEffect: RandomEffect[G] = RandomEffect.createFromRandom(Random) - BukkitRepositoryControls.createHandles( - RepositoryDefinition.Phased.TwoPhased( - SpeechServiceRepositoryDefinitions.initialization[SyncIO, Player], - SpeechServiceRepositoryDefinitions.finalization[SyncIO, Player] + BukkitRepositoryControls + .createHandles( + RepositoryDefinition + .Phased + .TwoPhased( + SpeechServiceRepositoryDefinitions.initialization[SyncIO, Player], + SpeechServiceRepositoryDefinitions.finalization[SyncIO, Player] + ) ) - ).flatMap { speechServiceRepositoryControls => - implicit val speechServiceRepository: PlayerDataRepository[MebiusSpeechService[SyncIO]] = - speechServiceRepositoryControls.repository + .flatMap { speechServiceRepositoryControls => + implicit val speechServiceRepository + : PlayerDataRepository[MebiusSpeechService[SyncIO]] = + speechServiceRepositoryControls.repository - BukkitRepositoryControls.createHandles( - RepositoryDefinition.Phased.TwoPhased( - MebiusSpeechRoutineFiberRepositoryDefinitions.initialization[SyncIO], - MebiusSpeechRoutineFiberRepositoryDefinitions.finalization[SyncIO, Player] - ) - ).map { speechRoutineFiberRepositoryControls => - new Subsystem[F] { - override val listeners: Seq[Listener] = Seq( - new MebiusDropTrialListener[G], - new MebiusInteractionResponder, - new MebiusLevelUpTrialListener, - new MebiusPlayerJoinGreeter[IO], - new MebiusRenamePreventionListener + BukkitRepositoryControls + .createHandles( + RepositoryDefinition + .Phased + .TwoPhased( + MebiusSpeechRoutineFiberRepositoryDefinitions.initialization[SyncIO], + MebiusSpeechRoutineFiberRepositoryDefinitions.finalization[SyncIO, Player] + ) ) + .map { speechRoutineFiberRepositoryControls => + new Subsystem[F] { + override val listeners: Seq[Listener] = Seq( + new MebiusDropTrialListener[G], + new MebiusInteractionResponder, + new MebiusLevelUpTrialListener, + new MebiusPlayerJoinGreeter[IO], + new MebiusRenamePreventionListener + ) - override val managedRepositoryControls: Seq[BukkitRepositoryControls[F, _]] = Seq( - speechServiceRepositoryControls, - speechRoutineFiberRepositoryControls - ).map(_.coerceFinalizationContextTo[F]) + override val managedRepositoryControls: Seq[BukkitRepositoryControls[F, _]] = + Seq(speechServiceRepositoryControls, speechRoutineFiberRepositoryControls).map( + _.coerceFinalizationContextTo[F] + ) - override val commands: Map[String, TabExecutor] = Map( - "mebius" -> new MebiusCommandExecutorProvider().executor - ) - } + override val commands: Map[String, TabExecutor] = + Map("mebius" -> new MebiusCommandExecutorProvider().executor) + } + } } - } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/application/repository/MebiusSpeechRoutineFiberRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/application/repository/MebiusSpeechRoutineFiberRepositoryDefinitions.scala index d00ce634a8..ab331e6089 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/application/repository/MebiusSpeechRoutineFiberRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/application/repository/MebiusSpeechRoutineFiberRepositoryDefinitions.scala @@ -19,33 +19,31 @@ object MebiusSpeechRoutineFiberRepositoryDefinitions { import cats.implicits._ // TODO PeriodicMebiusSpeechRoutineはbukkitに依存しているため、抽象化層をもう一層置くべき - def initialization[ - G[_] : Sync - ](implicit - serviceRepository: KeyedDataRepository[Player, MebiusSpeechService[SyncIO]], + def initialization[G[_]: Sync]( + implicit serviceRepository: KeyedDataRepository[Player, MebiusSpeechService[SyncIO]], repeatingTaskContext: RepeatingTaskContext, onMainThread: OnMinecraftServerThread[IO], - ioConcurrent: ConcurrentEffect[IO]): TwoPhasedRepositoryInitialization[G, Player, RepositoryValue[IO]] = - TwoPhasedRepositoryInitialization.withoutPrefetching[G, Player, RepositoryValue[IO]] { player => - for { - promise <- Deferred.in[G, IO, Fiber[IO, Nothing]] - _ <- EffectExtra.runAsyncAndForget[IO, G, Unit] { - PeriodicMebiusSpeechRoutine - .start(player) - .start(IO.contextShift(repeatingTaskContext)) - .flatMap(fiber => promise.complete(fiber)) - } - } yield promise + ioConcurrent: ConcurrentEffect[IO] + ): TwoPhasedRepositoryInitialization[G, Player, RepositoryValue[IO]] = + TwoPhasedRepositoryInitialization.withoutPrefetching[G, Player, RepositoryValue[IO]] { + player => + for { + promise <- Deferred.in[G, IO, Fiber[IO, Nothing]] + _ <- EffectExtra.runAsyncAndForget[IO, G, Unit] { + PeriodicMebiusSpeechRoutine + .start(player) + .start(IO.contextShift(repeatingTaskContext)) + .flatMap(fiber => promise.complete(fiber)) + } + } yield promise } - def finalization[ - G[_] : Sync, - Player - ]: RepositoryFinalization[G, Player, RepositoryValue[IO]] = - RepositoryFinalization.withoutAnyPersistence[G, Player, RepositoryValue[IO]] { (_, promise) => - EffectExtra.runAsyncAndForget[IO, G, Unit] { - promise.get.flatMap(_.cancel) - } + def finalization[G[_]: Sync, Player]: RepositoryFinalization[G, Player, RepositoryValue[IO]] = + RepositoryFinalization.withoutAnyPersistence[G, Player, RepositoryValue[IO]] { + (_, promise) => + EffectExtra.runAsyncAndForget[IO, G, Unit] { + promise.get.flatMap(_.cancel) + } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/application/repository/SpeechServiceRepositoryDefinitions.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/application/repository/SpeechServiceRepositoryDefinitions.scala index c0d2419795..6f746173f1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/application/repository/SpeechServiceRepositoryDefinitions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/application/repository/SpeechServiceRepositoryDefinitions.scala @@ -3,28 +3,28 @@ package com.github.unchama.seichiassist.subsystems.mebius.application.repository import cats.{Applicative, Monad} import com.github.unchama.datarepository.template.finalization.RepositoryFinalization import com.github.unchama.datarepository.template.initialization.TwoPhasedRepositoryInitialization -import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{MebiusSpeechBlockageState, MebiusSpeechGateway} +import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{ + MebiusSpeechBlockageState, + MebiusSpeechGateway +} import com.github.unchama.seichiassist.subsystems.mebius.service.MebiusSpeechService object SpeechServiceRepositoryDefinitions { import cats.implicits._ - def initialization[ - F[_] : Monad, - Player - ](implicit - getFreshBlockageState: F[MebiusSpeechBlockageState[F]], - gatewayProvider: Player => MebiusSpeechGateway[F]): TwoPhasedRepositoryInitialization[F, Player, MebiusSpeechService[F]] = - TwoPhasedRepositoryInitialization.withoutPrefetching[F, Player, MebiusSpeechService[F]] { player => - getFreshBlockageState.map { blockageState => - new MebiusSpeechService[F](gatewayProvider(player), blockageState) - } + def initialization[F[_]: Monad, Player]( + implicit getFreshBlockageState: F[MebiusSpeechBlockageState[F]], + gatewayProvider: Player => MebiusSpeechGateway[F] + ): TwoPhasedRepositoryInitialization[F, Player, MebiusSpeechService[F]] = + TwoPhasedRepositoryInitialization.withoutPrefetching[F, Player, MebiusSpeechService[F]] { + player => + getFreshBlockageState.map { blockageState => + new MebiusSpeechService[F](gatewayProvider(player), blockageState) + } } - def finalization[ - F[_] : Applicative, - Player - ]: RepositoryFinalization[F, Player, MebiusSpeechService[F]] = RepositoryFinalization.trivial + def finalization[F[_]: Applicative, Player] + : RepositoryFinalization[F, Player, MebiusSpeechService[F]] = RepositoryFinalization.trivial } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/PropertyModificationBukkitMessages.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/PropertyModificationBukkitMessages.scala index 1400ff30d5..62927d2aa7 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/PropertyModificationBukkitMessages.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/PropertyModificationBukkitMessages.scala @@ -1,13 +1,23 @@ package com.github.unchama.seichiassist.subsystems.mebius.bukkit -import com.github.unchama.seichiassist.subsystems.mebius.bukkit.codec.{BukkitMebiusAppearanceMaterialCodec, BukkitMebiusItemStackCodec} +import com.github.unchama.seichiassist.subsystems.mebius.bukkit.codec.{ + BukkitMebiusAppearanceMaterialCodec, + BukkitMebiusItemStackCodec +} import com.github.unchama.seichiassist.subsystems.mebius.domain.message.PropertyModificationMessages -import com.github.unchama.seichiassist.subsystems.mebius.domain.property.{MebiusEnchantment, MebiusProperty} +import com.github.unchama.seichiassist.subsystems.mebius.domain.property.{ + MebiusEnchantment, + MebiusProperty +} import org.bukkit.ChatColor._ object PropertyModificationBukkitMessages extends PropertyModificationMessages { - override def onLevelUp(oldMebiusProperty: MebiusProperty, newMebiusProperty: MebiusProperty): List[String] = { - val mebiusDisplayName = BukkitMebiusItemStackCodec.displayNameOfMaterializedItem(newMebiusProperty) + override def onLevelUp( + oldMebiusProperty: MebiusProperty, + newMebiusProperty: MebiusProperty + ): List[String] = { + val mebiusDisplayName = + BukkitMebiusItemStackCodec.displayNameOfMaterializedItem(newMebiusProperty) // レベルアップ通知 val levelUpMessage = @@ -19,10 +29,13 @@ object PropertyModificationBukkitMessages extends PropertyModificationMessages { // 進化通知 val materialChangeMessage = - if (BukkitMebiusAppearanceMaterialCodec.appearanceMaterialAt(oldMebiusProperty.level) != - BukkitMebiusAppearanceMaterialCodec.appearanceMaterialAt(newMebiusProperty.level)) List { + if ( + BukkitMebiusAppearanceMaterialCodec.appearanceMaterialAt(oldMebiusProperty.level) != + BukkitMebiusAppearanceMaterialCodec.appearanceMaterialAt(newMebiusProperty.level) + ) List { s"$mebiusDisplayName${RESET}の見た目が進化しました。" - } else Nil + } + else Nil // エンチャント効果変更通知 val givenEnchantments = { @@ -30,15 +43,31 @@ object PropertyModificationBukkitMessages extends PropertyModificationMessages { } val enchantmentChangeMessages = givenEnchantments.toList.map { givenEnchantment => - if (givenEnchantment == MebiusEnchantment.Unbreakable) { s"$RESET${AQUA}耐久無限${RESET}が付与されました。" } else { val romanSuffix = List( - "", "", " II", " III", " IV", " V", - " VI", " VII", " VIII", " IX", " X", - " XI", " XII", " XIII", " XIV", " XV", - " XVI", " XVII", " XVIII", " XIX", " XX" + "", + "", + " II", + " III", + " IV", + " V", + " VI", + " VII", + " VIII", + " IX", + " X", + " XI", + " XII", + " XIII", + " XIV", + " XV", + " XVI", + " XVII", + " XVIII", + " XIX", + " XX" ) oldMebiusProperty.enchantmentLevels.of(givenEnchantment) match { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/codec/BukkitMebiusAppearanceMaterialCodec.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/codec/BukkitMebiusAppearanceMaterialCodec.scala index db4ff50fb0..937eb6606a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/codec/BukkitMebiusAppearanceMaterialCodec.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/codec/BukkitMebiusAppearanceMaterialCodec.scala @@ -21,10 +21,7 @@ object BukkitMebiusAppearanceMaterialCodec { assert(appearanceThresholds.head._1 == 1) def appearanceMaterialAt(level: MebiusLevel): Material = { - appearanceThresholds - .findLast { case (threshold, _) => threshold <= level.value } - .get - ._2 + appearanceThresholds.findLast { case (threshold, _) => threshold <= level.value }.get._2 } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/codec/BukkitMebiusEnchantmentCodec.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/codec/BukkitMebiusEnchantmentCodec.scala index 4dad353746..92118229ff 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/codec/BukkitMebiusEnchantmentCodec.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/codec/BukkitMebiusEnchantmentCodec.scala @@ -10,7 +10,9 @@ object BukkitMebiusEnchantmentCodec { /** * アイテムスタックに対して破壊的にエンチャントを付与する */ - def applyEnchantment(enchantment: MebiusEnchantment, level: Int)(itemStack: ItemStack): ItemStack = { + def applyEnchantment(enchantment: MebiusEnchantment, level: Int)( + itemStack: ItemStack + ): ItemStack = { import scala.util.chaining._ def unsafeEnchantmentAdded(bukkitEnchantment: Enchantment): ItemStack = @@ -19,20 +21,18 @@ object BukkitMebiusEnchantmentCodec { } enchantment match { - case Protection => unsafeEnchantmentAdded(Enchantment.PROTECTION_ENVIRONMENTAL) - case Durability => unsafeEnchantmentAdded(Enchantment.DURABILITY) - case Mending => unsafeEnchantmentAdded(Enchantment.MENDING) - case FireProtection => unsafeEnchantmentAdded(Enchantment.PROTECTION_FIRE) + case Protection => unsafeEnchantmentAdded(Enchantment.PROTECTION_ENVIRONMENTAL) + case Durability => unsafeEnchantmentAdded(Enchantment.DURABILITY) + case Mending => unsafeEnchantmentAdded(Enchantment.MENDING) + case FireProtection => unsafeEnchantmentAdded(Enchantment.PROTECTION_FIRE) case ProjectileProtection => unsafeEnchantmentAdded(Enchantment.PROTECTION_PROJECTILE) - case ExplosionProtection => unsafeEnchantmentAdded(Enchantment.PROTECTION_EXPLOSIONS) - case Respiration => unsafeEnchantmentAdded(Enchantment.OXYGEN) - case WaterAffinity => unsafeEnchantmentAdded(Enchantment.WATER_WORKER) + case ExplosionProtection => unsafeEnchantmentAdded(Enchantment.PROTECTION_EXPLOSIONS) + case Respiration => unsafeEnchantmentAdded(Enchantment.OXYGEN) + case WaterAffinity => unsafeEnchantmentAdded(Enchantment.WATER_WORKER) case Unbreakable => itemStack.tap { _ => itemStack.setItemMeta { - itemStack.getItemMeta.tap { meta => - meta.setUnbreakable(true) - } + itemStack.getItemMeta.tap { meta => meta.setUnbreakable(true) } } } } @@ -40,15 +40,17 @@ object BukkitMebiusEnchantmentCodec { def getLevelOf(enchantment: MebiusEnchantment)(itemStack: ItemStack): Int = { enchantment match { - case Protection => itemStack.getEnchantmentLevel(Enchantment.PROTECTION_ENVIRONMENTAL) - case Durability => itemStack.getEnchantmentLevel(Enchantment.DURABILITY) - case Mending => itemStack.getEnchantmentLevel(Enchantment.MENDING) + case Protection => itemStack.getEnchantmentLevel(Enchantment.PROTECTION_ENVIRONMENTAL) + case Durability => itemStack.getEnchantmentLevel(Enchantment.DURABILITY) + case Mending => itemStack.getEnchantmentLevel(Enchantment.MENDING) case FireProtection => itemStack.getEnchantmentLevel(Enchantment.PROTECTION_FIRE) - case ProjectileProtection => itemStack.getEnchantmentLevel(Enchantment.PROTECTION_PROJECTILE) - case ExplosionProtection => itemStack.getEnchantmentLevel(Enchantment.PROTECTION_EXPLOSIONS) - case Respiration => itemStack.getEnchantmentLevel(Enchantment.OXYGEN) + case ProjectileProtection => + itemStack.getEnchantmentLevel(Enchantment.PROTECTION_PROJECTILE) + case ExplosionProtection => + itemStack.getEnchantmentLevel(Enchantment.PROTECTION_EXPLOSIONS) + case Respiration => itemStack.getEnchantmentLevel(Enchantment.OXYGEN) case WaterAffinity => itemStack.getEnchantmentLevel(Enchantment.WATER_WORKER) - case Unbreakable => if (itemStack.getItemMeta.isUnbreakable) 1 else 0 + case Unbreakable => if (itemStack.getItemMeta.isUnbreakable) 1 else 0 } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/codec/BukkitMebiusItemStackCodec.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/codec/BukkitMebiusItemStackCodec.scala index c8bd0e331d..3a10d9bd02 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/codec/BukkitMebiusItemStackCodec.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/codec/BukkitMebiusItemStackCodec.scala @@ -13,14 +13,10 @@ object BukkitMebiusItemStackCodec { import scala.jdk.CollectionConverters._ - //noinspection DuplicatedCode + // noinspection DuplicatedCode object LoreConstants { - val mebiusLoreHead = List( - s"$RESET", - s"$RESET${AQUA}初心者をサポートする不思議なヘルメット。", - s"$RESET${AQUA}整地により成長する。", - "" - ) + val mebiusLoreHead = + List(s"$RESET", s"$RESET${AQUA}初心者をサポートする不思議なヘルメット。", s"$RESET${AQUA}整地により成長する。", "") val unbreakableLoreRow = s"$RESET${AQUA}耐久無限" @@ -46,7 +42,7 @@ object BukkitMebiusItemStackCodec { def encodeTypeId(mebiusType: MebiusType): Int = mebiusType match { - case NormalMebius => 1 + case NormalMebius => 1 case ChristmasMebius => 2 } @@ -58,15 +54,16 @@ object BukkitMebiusItemStackCodec { } def encodeForcedMaterial(forcedMaterial: MebiusForcedMaterial): Byte = forcedMaterial match { - case MebiusForcedMaterial.None => 0 + case MebiusForcedMaterial.None => 0 case MebiusForcedMaterial.Leather => 1 } - def decodeForcedMaterial(forcedMaterialByte: Byte): MebiusForcedMaterial = forcedMaterialByte match { - case 0 => MebiusForcedMaterial.None - case 1 => MebiusForcedMaterial.Leather - case _ => MebiusForcedMaterial.None - } + def decodeForcedMaterial(forcedMaterialByte: Byte): MebiusForcedMaterial = + forcedMaterialByte match { + case 0 => MebiusForcedMaterial.None + case 1 => MebiusForcedMaterial.Leather + case _ => MebiusForcedMaterial.None + } def isMebius(itemStack: ItemStack): Boolean = itemStack != null && itemStack.getType != Material.AIR && { @@ -95,16 +92,18 @@ object BukkitMebiusItemStackCodec { val ownerNickname = Some(nbtItem.getString(ownerNicknameTag)).filter(_.nonEmpty) val mebiusName = nbtItem.getString(nameTag) - Some(MebiusProperty( - mebiusType, - ownerName, - ownerUuid, - enchantments, - mebiusForcedMaterial, - mebiusLevel, - ownerNickname, - mebiusName - )) + Some( + MebiusProperty( + mebiusType, + ownerName, + ownerUuid, + enchantments, + mebiusForcedMaterial, + mebiusLevel, + ownerNickname, + mebiusName + ) + ) } /** @@ -116,7 +115,8 @@ object BukkitMebiusItemStackCodec { */ def materialize(property: MebiusProperty, damageValue: Short): ItemStack = { val material = property.forcedMaterial match { - case MebiusForcedMaterial.None => BukkitMebiusAppearanceMaterialCodec.appearanceMaterialAt(property.level) + case MebiusForcedMaterial.None => + BukkitMebiusAppearanceMaterialCodec.appearanceMaterialAt(property.level) case MebiusForcedMaterial.Leather => Material.LEATHER_HELMET } @@ -137,20 +137,22 @@ object BukkitMebiusItemStackCodec { import LoreConstants._ mebiusLoreHead - .concat(List( - s"$levelLoreRowPrefix${property.level.value}", - s"$levelUpMebiusMessageLoreRowPrefix「${talk.mebiusMessage}」", - s"$levelUpPlayerMessageLoreRowPrefix${talk.playerMessage}", - s"", - s"$ownerLoreRowPrefix${property.ownerPlayerId}" - )) + .concat( + List( + s"$levelLoreRowPrefix${property.level.value}", + s"$levelUpMebiusMessageLoreRowPrefix「${talk.mebiusMessage}」", + s"$levelUpPlayerMessageLoreRowPrefix${talk.playerMessage}", + s"", + s"$ownerLoreRowPrefix${property.ownerPlayerId}" + ) + ) .concat { if (property.level.isMaximum) List(unbreakableLoreRow) else Nil } .concat { property.mebiusType match { case ChristmasMebius => ChristmasItemData.christmasMebiusLore - case _ => Nil + case _ => Nil } } .asJava @@ -158,8 +160,9 @@ object BukkitMebiusItemStackCodec { } } - property.enchantmentLevels.mapping.foreach { case (enchantment, level) => - BukkitMebiusEnchantmentCodec.applyEnchantment(enchantment, level)(item) + property.enchantmentLevels.mapping.foreach { + case (enchantment, level) => + BukkitMebiusEnchantmentCodec.applyEnchantment(enchantment, level)(item) } { @@ -184,14 +187,16 @@ object BukkitMebiusItemStackCodec { property.mebiusType match { case ChristmasMebius => s"$christmasMebiusNameStyle${property.mebiusName} Christmas Ver." - case _ => s"$mebiusNameStyle${property.mebiusName}" + case _ => s"$mebiusNameStyle${property.mebiusName}" } } def ownershipMatches(player: Player)(property: MebiusProperty): Boolean = property.ownerUuid == player.getUniqueId.toString - def decodePropertyOfOwnedMebius(player: Player)(itemStack: ItemStack): Option[MebiusProperty] = + def decodePropertyOfOwnedMebius(player: Player)( + itemStack: ItemStack + ): Option[MebiusProperty] = decodeMebiusProperty(itemStack).filter(ownershipMatches(player)) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/command/MebiusCommandExecutorProvider.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/command/MebiusCommandExecutorProvider.scala index bca516f703..08e5ad679e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/command/MebiusCommandExecutorProvider.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/command/MebiusCommandExecutorProvider.scala @@ -9,17 +9,24 @@ import com.github.unchama.datarepository.bukkit.player.PlayerDataRepository import com.github.unchama.seichiassist.commands.contextual.builder.BuilderTemplates.playerCommandBuilder import com.github.unchama.seichiassist.subsystems.mebius.bukkit.codec.BukkitMebiusItemStackCodec import com.github.unchama.seichiassist.subsystems.mebius.bukkit.command.MebiusCommandExecutorProvider.Messages -import com.github.unchama.seichiassist.subsystems.mebius.domain.property.{MebiusForcedMaterial, MebiusProperty} -import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{MebiusSpeech, MebiusSpeechStrength} +import com.github.unchama.seichiassist.subsystems.mebius.domain.property.{ + MebiusForcedMaterial, + MebiusProperty +} +import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{ + MebiusSpeech, + MebiusSpeechStrength +} import com.github.unchama.seichiassist.subsystems.mebius.service.MebiusSpeechService import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.{SequentialEffect, TargetedEffect, UnfocusedEffect} import org.bukkit.ChatColor._ -import org.bukkit.Material import org.bukkit.command.{CommandSender, TabExecutor} import org.bukkit.entity.Player -class MebiusCommandExecutorProvider(implicit serviceRepository: PlayerDataRepository[MebiusSpeechService[SyncIO]]) { +class MebiusCommandExecutorProvider( + implicit serviceRepository: PlayerDataRepository[MebiusSpeechService[SyncIO]] +) { import ChildExecutors._ @@ -30,14 +37,18 @@ class MebiusCommandExecutorProvider(implicit serviceRepository: PlayerDataReposi "nickname" -> ChildExecutors.NicknameCommand.executor, "naming" -> namingExecutor, "convert" -> convertExecutor - ), whenArgInsufficient = Some(printDescriptionExecutor), whenBranchNotFound = Some(printDescriptionExecutor) + ), + whenArgInsufficient = Some(printDescriptionExecutor), + whenBranchNotFound = Some(printDescriptionExecutor) ).asNonBlockingTabExecutor() } object ChildExecutors { - private case class MebiusInteractionTemplate(effectIfMebiusIsNotWorn: TargetedEffect[Player], - propertyModifier: MebiusProperty => MebiusProperty, - additionalEffectsOnModification: MebiusProperty => TargetedEffect[Player]) { + private case class MebiusInteractionTemplate( + effectIfMebiusIsNotWorn: TargetedEffect[Player], + propertyModifier: MebiusProperty => MebiusProperty, + additionalEffectsOnModification: MebiusProperty => TargetedEffect[Player] + ) { def effectOn(player: Player): IO[TargetedEffect[Player]] = for { @@ -45,12 +56,15 @@ class MebiusCommandExecutorProvider(implicit serviceRepository: PlayerDataReposi player.getInventory.getHelmet } effect <- IO.pure { - BukkitMebiusItemStackCodec.decodePropertyOfOwnedMebius(player)(helmet).map(propertyModifier) match { + BukkitMebiusItemStackCodec + .decodePropertyOfOwnedMebius(player)(helmet) + .map(propertyModifier) match { case Some(newProperty) => SequentialEffect( UnfocusedEffect { player.getInventory.setHelmet { - BukkitMebiusItemStackCodec.materialize(newProperty, damageValue = helmet.getDurability) + BukkitMebiusItemStackCodec + .materialize(newProperty, damageValue = helmet.getDurability) } }, additionalEffectsOnModification(newProperty) @@ -61,7 +75,8 @@ class MebiusCommandExecutorProvider(implicit serviceRepository: PlayerDataReposi } yield effect } - val printDescriptionExecutor: ContextualExecutor = ContextualExecutorBuilder.beginConfiguration() + val printDescriptionExecutor: ContextualExecutor = ContextualExecutorBuilder + .beginConfiguration() .execution { _ => IO(Messages.commandDescription) } .build() @@ -75,17 +90,20 @@ class MebiusCommandExecutorProvider(implicit serviceRepository: PlayerDataReposi MessageEffect(s"${RED}命名はMEBIUSを装着して行ってください."), _.copy(mebiusName = newName), newProperty => { - val newDisplayName = BukkitMebiusItemStackCodec.displayNameOfMaterializedItem(newProperty) + val newDisplayName = + BukkitMebiusItemStackCodec.displayNameOfMaterializedItem(newProperty) SequentialEffect( MessageEffect(s"$newDisplayName${RESET}に命名しました。"), Kleisli.liftF { - serviceRepository(player).makeSpeechIgnoringBlockage( - newProperty, - MebiusSpeech( - s"わーい、ありがとう!今日から僕は$newDisplayName${RESET}だ!", - MebiusSpeechStrength.Loud + serviceRepository(player) + .makeSpeechIgnoringBlockage( + newProperty, + MebiusSpeech( + s"わーい、ありがとう!今日から僕は$newDisplayName${RESET}だ!", + MebiusSpeechStrength.Loud + ) ) - ).toIO + .toIO } ) } @@ -102,10 +120,11 @@ class MebiusCommandExecutorProvider(implicit serviceRepository: PlayerDataReposi case Some(property) => if (property.level.isMaximum) { val newProperty = property.toggleForcedMaterial - val newItem = BukkitMebiusItemStackCodec.materialize(newProperty, mainHand.getDurability) + val newItem = + BukkitMebiusItemStackCodec.materialize(newProperty, mainHand.getDurability) val newMaterialName = newProperty.forcedMaterial match { - case MebiusForcedMaterial.None => "ダイヤモンド" + case MebiusForcedMaterial.None => "ダイヤモンド" case MebiusForcedMaterial.Leather => "革" } @@ -155,47 +174,54 @@ class MebiusCommandExecutorProvider(implicit serviceRepository: PlayerDataReposi } .build() - private def setNicknameOverrideOnMebiusOn(player: Player, - name: String, - successMessage: String => String, - errorMessage: String): IO[TargetedEffect[Player]] = { + private def setNicknameOverrideOnMebiusOn( + player: Player, + name: String, + successMessage: String => String, + errorMessage: String + ): IO[TargetedEffect[Player]] = { MebiusInteractionTemplate( MessageEffect(errorMessage), _.copy(ownerNicknameOverride = Some(name)), - newProperty => SequentialEffect( - MessageEffect(successMessage(name)), - Kleisli.liftF { - serviceRepository(player).makeSpeechIgnoringBlockage( - newProperty, - MebiusSpeech( - s"わーい、ありがとう!今日から君のこと$GREEN$name${RESET}って呼ぶね!", - MebiusSpeechStrength.Loud - ) - ).toIO - } - ) + newProperty => + SequentialEffect( + MessageEffect(successMessage(name)), + Kleisli.liftF { + serviceRepository(player) + .makeSpeechIgnoringBlockage( + newProperty, + MebiusSpeech( + s"わーい、ありがとう!今日から君のこと$GREEN$name${RESET}って呼ぶね!", + MebiusSpeechStrength.Loud + ) + ) + .toIO + } + ) ).effectOn(player) } private val checkNicknameExecutor = playerCommandBuilder .execution { context => IO(MessageEffect { - BukkitMebiusItemStackCodec.decodePropertyOfOwnedMebius(context.sender)(context.sender.getInventory.getHelmet) + BukkitMebiusItemStackCodec + .decodePropertyOfOwnedMebius(context.sender)( + context.sender.getInventory.getHelmet + ) .map(_.ownerNickname) .fold { s"${RED}呼び名の確認はMEBIUSを装着して行ってください." - } { name => - s"${GREEN}現在のメビウスからの呼び名 : $name" - } + } { name => s"${GREEN}現在のメビウスからの呼び名 : $name" } }) } .build() - val executor: BranchedExecutor = BranchedExecutor(Map( - "reset" -> resetNicknameExecutor, - "set" -> setNicknameExecutor - ), whenArgInsufficient = Some(checkNicknameExecutor), whenBranchNotFound = Some(checkNicknameExecutor)) + val executor: BranchedExecutor = BranchedExecutor( + Map("reset" -> resetNicknameExecutor, "set" -> setNicknameExecutor), + whenArgInsufficient = Some(checkNicknameExecutor), + whenBranchNotFound = Some(checkNicknameExecutor) + ) } } @@ -222,7 +248,7 @@ object MebiusCommandExecutorProvider { s"$RED MEBIUSからの呼び名をプレイヤー名(初期設定)に戻します", "", s"$RED/mebius convert", - s"$RED MEBIUSの材質を変換します", + s"$RED MEBIUSの材質を変換します" ) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/gateway/BukkitMebiusSpeechGateway.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/gateway/BukkitMebiusSpeechGateway.scala index 5745d41ed3..8298ce99c5 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/gateway/BukkitMebiusSpeechGateway.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/gateway/BukkitMebiusSpeechGateway.scala @@ -1,29 +1,40 @@ package com.github.unchama.seichiassist.subsystems.mebius.bukkit.gateway -import java.util.concurrent.TimeUnit - import cats.effect.{IO, SyncIO, Timer} import com.github.unchama.seichiassist.subsystems.mebius.domain.property.MebiusProperty -import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{MebiusSpeechGateway, MebiusSpeechStrength} +import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{ + MebiusSpeechGateway, + MebiusSpeechStrength +} import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.player.FocusedSoundEffect -import com.github.unchama.targetedeffect.{DelayEffect, RepeatedEffect, SequentialEffect, TargetedEffect} +import com.github.unchama.targetedeffect.{ + DelayEffect, + RepeatedEffect, + SequentialEffect, + TargetedEffect +} import org.bukkit.ChatColor._ import org.bukkit.Sound import org.bukkit.entity.Player +import java.util.concurrent.TimeUnit import scala.concurrent.duration.FiniteDuration -class BukkitMebiusSpeechGateway(player: Player)(implicit timer: Timer[IO]) extends MebiusSpeechGateway[SyncIO] { +class BukkitMebiusSpeechGateway(player: Player)(implicit timer: Timer[IO]) + extends MebiusSpeechGateway[SyncIO] { override def sendMessage(property: MebiusProperty, message: String): SyncIO[Unit] = { - MessageEffect( - s"$RESET$GRAY<$GOLD$BOLD${property.mebiusName}$RESET$GRAY>$RESET $message" - ).run(player).runAsync(_ => IO.unit) + MessageEffect(s"$RESET$GRAY<$GOLD$BOLD${property.mebiusName}$RESET$GRAY>$RESET $message") + .run(player) + .runAsync(_ => IO.unit) } override def playSpeechSound(strength: MebiusSpeechStrength): SyncIO[Unit] = { - def playSoundsInSequence(firstSound: TargetedEffect[Player], secondSound: TargetedEffect[Player]): TargetedEffect[Player] = + def playSoundsInSequence( + firstSound: TargetedEffect[Player], + secondSound: TargetedEffect[Player] + ): TargetedEffect[Player] = SequentialEffect( firstSound, DelayEffect(FiniteDuration(100, TimeUnit.MILLISECONDS)), @@ -39,7 +50,7 @@ class BukkitMebiusSpeechGateway(player: Player)(implicit timer: Timer[IO]) exten case MebiusSpeechStrength.Loud => playSoundsInSequence( RepeatedEffect(5)(FocusedSoundEffect(Sound.BLOCK_NOTE_HARP, 2.0f, 1.5f)), - RepeatedEffect(5)(FocusedSoundEffect(Sound.BLOCK_NOTE_HARP, 2.0f, 2.0f)), + RepeatedEffect(5)(FocusedSoundEffect(Sound.BLOCK_NOTE_HARP, 2.0f, 2.0f)) ) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusDropTrialListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusDropTrialListener.scala index 4a3ac7cc0b..44e4229971 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusDropTrialListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusDropTrialListener.scala @@ -7,7 +7,10 @@ import com.github.unchama.seichiassist.ManagedWorld._ import com.github.unchama.seichiassist.MaterialSets import com.github.unchama.seichiassist.subsystems.mebius.bukkit.codec.BukkitMebiusItemStackCodec import com.github.unchama.seichiassist.subsystems.mebius.domain.MebiusDrop -import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{MebiusSpeech, MebiusSpeechStrength} +import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{ + MebiusSpeech, + MebiusSpeechStrength +} import com.github.unchama.seichiassist.subsystems.mebius.service.MebiusSpeechService import com.github.unchama.seichiassist.subsystems.seasonalevents.api.ChristmasEventsAPI import com.github.unchama.seichiassist.util.Util @@ -22,10 +25,11 @@ import org.bukkit.event.{EventHandler, EventPriority, Listener} import java.util.concurrent.TimeUnit import scala.concurrent.duration.FiniteDuration -class MebiusDropTrialListener[ - G[_] : ChristmasEventsAPI : RandomEffect : SyncEffect -](implicit serviceRepository: PlayerDataRepository[MebiusSpeechService[SyncIO]], - effectEnvironment: EffectEnvironment, timer: Timer[IO]) extends Listener { +class MebiusDropTrialListener[G[_]: ChristmasEventsAPI: RandomEffect: SyncEffect]( + implicit serviceRepository: PlayerDataRepository[MebiusSpeechService[SyncIO]], + effectEnvironment: EffectEnvironment, + timer: Timer[IO] +) extends Listener { import cats.effect.implicits._ import cats.implicits._ @@ -40,10 +44,14 @@ class MebiusDropTrialListener[ val droppedMebiusProperty = MebiusDrop .tryOnce[G](player.getName, player.getUniqueId.toString) - .runSync[SyncIO].unsafeRunSync() - .getOrElse(return) + .runSync[SyncIO] + .unsafeRunSync() + .getOrElse( + return + ) - val mebius = BukkitMebiusItemStackCodec.materialize(droppedMebiusProperty, damageValue = 0.toShort) + val mebius = + BukkitMebiusItemStackCodec.materialize(droppedMebiusProperty, damageValue = 0.toShort) player.sendMessage(s"$RESET$YELLOW${BOLD}おめでとうございます。採掘中にMEBIUSを発見しました。") player.sendMessage(s"$RESET$YELLOW${BOLD}MEBIUSはプレイヤーと共に成長するヘルメットです。") @@ -51,15 +59,17 @@ class MebiusDropTrialListener[ effectEnvironment.unsafeRunEffectAsync( "Mebiusのドロップ時メッセージを再生する", - serviceRepository(player).makeSpeechIgnoringBlockage( - droppedMebiusProperty, - MebiusSpeech( - s"こんにちは、${player.getName}$RESET。" + - s"僕は${BukkitMebiusItemStackCodec.displayNameOfMaterializedItem(droppedMebiusProperty)}" + - s"$RESET!これからよろしくね!", - MebiusSpeechStrength.Loud + serviceRepository(player) + .makeSpeechIgnoringBlockage( + droppedMebiusProperty, + MebiusSpeech( + s"こんにちは、${player.getName}$RESET。" + + s"僕は${BukkitMebiusItemStackCodec.displayNameOfMaterializedItem(droppedMebiusProperty)}" + + s"$RESET!これからよろしくね!", + MebiusSpeechStrength.Loud + ) ) - ).toIO >> SequentialEffect( + .toIO >> SequentialEffect( FocusedSoundEffect(Sound.BLOCK_ANVIL_PLACE, 1.0f, 1.0f), DelayEffect(FiniteDuration(500, TimeUnit.MILLISECONDS)) ).run(player) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusInteractionResponder.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusInteractionResponder.scala index 13f22e1bd6..1fdff31a1e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusInteractionResponder.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusInteractionResponder.scala @@ -6,7 +6,10 @@ import com.github.unchama.generic.effect.unsafe.EffectEnvironment import com.github.unchama.seichiassist.MaterialSets import com.github.unchama.seichiassist.subsystems.mebius.bukkit.codec.BukkitMebiusItemStackCodec import com.github.unchama.seichiassist.subsystems.mebius.domain.resources.MebiusMessages -import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{MebiusSpeech, MebiusSpeechStrength} +import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{ + MebiusSpeech, + MebiusSpeechStrength +} import com.github.unchama.seichiassist.subsystems.mebius.service.MebiusSpeechService import com.github.unchama.targetedeffect.SequentialEffect import com.github.unchama.targetedeffect.commandsender.MessageEffect @@ -19,16 +22,21 @@ import org.bukkit.event.entity.{EntityDamageByEntityEvent, EntityDeathEvent} import org.bukkit.event.player.PlayerItemBreakEvent import org.bukkit.event.{EventHandler, EventPriority, Listener} -class MebiusInteractionResponder(implicit serviceRepository: PlayerDataRepository[MebiusSpeechService[SyncIO]], - effectEnvironment: EffectEnvironment) - extends Listener { +class MebiusInteractionResponder( + implicit serviceRepository: PlayerDataRepository[MebiusSpeechService[SyncIO]], + effectEnvironment: EffectEnvironment +) extends Listener { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) def onDamage(event: EntityDamageByEntityEvent): Unit = { // プレイヤーがダメージを受けた場合 event.getEntity match { case player: Player => val helmet = player.getInventory.getHelmet - val mebiusProperty = BukkitMebiusItemStackCodec.decodePropertyOfOwnedMebius(player)(helmet).getOrElse(return) + val mebiusProperty = BukkitMebiusItemStackCodec + .decodePropertyOfOwnedMebius(player)(helmet) + .getOrElse( + return + ) val speechService = serviceRepository(player) @@ -37,22 +45,27 @@ class MebiusInteractionResponder(implicit serviceRepository: PlayerDataRepositor // 耐久閾値を超えていたら破損警告 speechService.tryMakingSpeech( mebiusProperty, - MebiusSpeech(message.interpolate(mebiusProperty.ownerNickname), MebiusSpeechStrength.Medium) + MebiusSpeech( + message.interpolate(mebiusProperty.ownerNickname), + MebiusSpeechStrength.Medium + ) ) } - } else event.getDamager match { - case monster: Monster => - // モンスターからダメージを受けた場合の対モンスターメッセージ - MebiusMessages.onDamageWarnEnemy.pickOne[SyncIO].flatMap { message => - speechService.tryMakingSpeech( - mebiusProperty, - MebiusSpeech( - message.interpolate(mebiusProperty.ownerNickname, monster.getName), MebiusSpeechStrength.Medium + } else + event.getDamager match { + case monster: Monster => + // モンスターからダメージを受けた場合の対モンスターメッセージ + MebiusMessages.onDamageWarnEnemy.pickOne[SyncIO].flatMap { message => + speechService.tryMakingSpeech( + mebiusProperty, + MebiusSpeech( + message.interpolate(mebiusProperty.ownerNickname, monster.getName), + MebiusSpeechStrength.Medium + ) ) - ) - } - case _ => SyncIO.unit - } + } + case _ => SyncIO.unit + } messageProgram.unsafeRunSync() case _ => @@ -63,7 +76,8 @@ class MebiusInteractionResponder(implicit serviceRepository: PlayerDataRepositor def onBreak(event: PlayerItemBreakEvent): Unit = { val player = event.getPlayer - BukkitMebiusItemStackCodec.decodePropertyOfOwnedMebius(player)(event.getBrokenItem) + BukkitMebiusItemStackCodec + .decodePropertyOfOwnedMebius(player)(event.getBrokenItem) .foreach { property => val speechService = serviceRepository(player) @@ -72,11 +86,18 @@ class MebiusInteractionResponder(implicit serviceRepository: PlayerDataRepositor effectEnvironment.unsafeRunEffectAsync( "Mebius破壊時のエフェクトを再生する", MebiusMessages.onMebiusBreak.pickOne[SyncIO].toIO.flatMap { message => - speechService.makeSpeechIgnoringBlockage( - property, - MebiusSpeech(message.interpolate(property.ownerNickname), MebiusSpeechStrength.Medium) - ).toIO >> SequentialEffect( - MessageEffect(s"${BukkitMebiusItemStackCodec.displayNameOfMaterializedItem(property)}${RESET}が旅立ちました。"), + speechService + .makeSpeechIgnoringBlockage( + property, + MebiusSpeech( + message.interpolate(property.ownerNickname), + MebiusSpeechStrength.Medium + ) + ) + .toIO >> SequentialEffect( + MessageEffect( + s"${BukkitMebiusItemStackCodec.displayNameOfMaterializedItem(property)}${RESET}が旅立ちました。" + ), FocusedSoundEffect(Sound.ENTITY_ENDERDRAGON_DEATH, 1.0f, 0.1f) ).run(player) } @@ -91,23 +112,32 @@ class MebiusInteractionResponder(implicit serviceRepository: PlayerDataRepositor val player = killedMonster.getKiller if (killedMonster == null || player == null) return - //もしモンスター名が取れなければ除外 + // もしモンスター名が取れなければ除外 val killedMonsterName = killedMonster.getName if (killedMonsterName == "") return val mebiusProperty = - BukkitMebiusItemStackCodec.decodePropertyOfOwnedMebius(player)(player.getInventory.getHelmet).getOrElse(return) + BukkitMebiusItemStackCodec + .decodePropertyOfOwnedMebius(player)(player.getInventory.getHelmet) + .getOrElse( + return + ) val speechService = serviceRepository(player) - MebiusMessages.onMonsterKill.pickOne[SyncIO].flatMap { message => - speechService.tryMakingSpeech( - mebiusProperty, - MebiusSpeech( - message.interpolate(mebiusProperty.ownerNickname, killedMonsterName), MebiusSpeechStrength.Medium + MebiusMessages + .onMonsterKill + .pickOne[SyncIO] + .flatMap { message => + speechService.tryMakingSpeech( + mebiusProperty, + MebiusSpeech( + message.interpolate(mebiusProperty.ownerNickname, killedMonsterName), + MebiusSpeechStrength.Medium + ) ) - ) - }.unsafeRunSync() + } + .unsafeRunSync() } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = false) @@ -117,15 +147,26 @@ class MebiusInteractionResponder(implicit serviceRepository: PlayerDataRepositor val player = event.getPlayer val mebiusProperty = - BukkitMebiusItemStackCodec.decodePropertyOfOwnedMebius(player)(player.getInventory.getHelmet).getOrElse(return) + BukkitMebiusItemStackCodec + .decodePropertyOfOwnedMebius(player)(player.getInventory.getHelmet) + .getOrElse( + return + ) val speechService = serviceRepository(player) - MebiusMessages.onBlockBreak.pickOne[SyncIO].flatMap { message => - speechService.tryMakingSpeech( - mebiusProperty, - MebiusSpeech(message.interpolate(mebiusProperty.ownerNickname), MebiusSpeechStrength.Medium) - ) - }.unsafeRunSync() + MebiusMessages + .onBlockBreak + .pickOne[SyncIO] + .flatMap { message => + speechService.tryMakingSpeech( + mebiusProperty, + MebiusSpeech( + message.interpolate(mebiusProperty.ownerNickname), + MebiusSpeechStrength.Medium + ) + ) + } + .unsafeRunSync() } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusLevelUpTrialListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusLevelUpTrialListener.scala index b539e747ed..1505585cb9 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusLevelUpTrialListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusLevelUpTrialListener.scala @@ -7,15 +7,20 @@ import com.github.unchama.seichiassist.ManagedWorld._ import com.github.unchama.seichiassist.subsystems.mebius.bukkit.codec.BukkitMebiusItemStackCodec import com.github.unchama.seichiassist.subsystems.mebius.domain.message.PropertyModificationMessages import com.github.unchama.seichiassist.subsystems.mebius.domain.resources.MebiusTalks -import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{MebiusSpeech, MebiusSpeechStrength} +import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{ + MebiusSpeech, + MebiusSpeechStrength +} import com.github.unchama.seichiassist.subsystems.mebius.service.MebiusSpeechService import com.github.unchama.targetedeffect.commandsender.MessageEffect import org.bukkit.event.block.BlockBreakEvent import org.bukkit.event.{EventHandler, EventPriority, Listener} -class MebiusLevelUpTrialListener(implicit serviceRepository: PlayerDataRepository[MebiusSpeechService[SyncIO]], - effectEnvironment: EffectEnvironment, - messages: PropertyModificationMessages) extends Listener { +class MebiusLevelUpTrialListener( + implicit serviceRepository: PlayerDataRepository[MebiusSpeechService[SyncIO]], + effectEnvironment: EffectEnvironment, + messages: PropertyModificationMessages +) extends Listener { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) def tryMebiusLevelUpOn(event: BlockBreakEvent): Unit = { @@ -26,7 +31,9 @@ class MebiusLevelUpTrialListener(implicit serviceRepository: PlayerDataRepositor val oldMebiusProperty = BukkitMebiusItemStackCodec .decodePropertyOfOwnedMebius(player)(player.getInventory.getHelmet) - .getOrElse(return) + .getOrElse( + return + ) val newMebiusProperty = oldMebiusProperty.tryUpgradeByOneLevel[SyncIO].unsafeRunSync() @@ -38,13 +45,16 @@ class MebiusLevelUpTrialListener(implicit serviceRepository: PlayerDataRepositor import cats.implicits._ effectEnvironment.unsafeRunEffectAsync( "Mebiusのレベルアップ時の通知を行う", - serviceRepository(player).makeSpeechIgnoringBlockage( - newMebiusProperty, - MebiusSpeech( - MebiusTalks.at(newMebiusProperty.level).mebiusMessage, - MebiusSpeechStrength.Loud + serviceRepository(player) + .makeSpeechIgnoringBlockage( + newMebiusProperty, + MebiusSpeech( + MebiusTalks.at(newMebiusProperty.level).mebiusMessage, + MebiusSpeechStrength.Loud + ) ) - ).toIO >> MessageEffect(messages.onLevelUp(oldMebiusProperty, newMebiusProperty)).run(player) + .toIO >> MessageEffect(messages.onLevelUp(oldMebiusProperty, newMebiusProperty)) + .run(player) ) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusPlayerJoinGreeter.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusPlayerJoinGreeter.scala index cc6bcd44dc..83f684de10 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusPlayerJoinGreeter.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusPlayerJoinGreeter.scala @@ -4,7 +4,10 @@ import cats.effect.{Effect, IO, SyncIO, Timer} import com.github.unchama.datarepository.bukkit.player.PlayerDataRepository import com.github.unchama.generic.effect.unsafe.EffectEnvironment import com.github.unchama.seichiassist.subsystems.mebius.bukkit.codec.BukkitMebiusItemStackCodec -import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{MebiusSpeech, MebiusSpeechStrength} +import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{ + MebiusSpeech, + MebiusSpeechStrength +} import com.github.unchama.seichiassist.subsystems.mebius.service.MebiusSpeechService import com.github.unchama.targetedeffect.DelayEffect import org.bukkit.event.player.PlayerJoinEvent @@ -13,9 +16,11 @@ import org.bukkit.event.{EventHandler, EventPriority, Listener} import java.util.concurrent.TimeUnit import scala.concurrent.duration.FiniteDuration -class MebiusPlayerJoinGreeter[F[_] : Effect](implicit effectEnvironment: EffectEnvironment, - speechServiceRepository: PlayerDataRepository[MebiusSpeechService[SyncIO]], - timer: Timer[IO]) extends Listener { +class MebiusPlayerJoinGreeter[F[_]: Effect]( + implicit effectEnvironment: EffectEnvironment, + speechServiceRepository: PlayerDataRepository[MebiusSpeechService[SyncIO]], + timer: Timer[IO] +) extends Listener { @EventHandler(priority = EventPriority.MONITOR) def onJoin(event: PlayerJoinEvent): Unit = { @@ -32,8 +37,12 @@ class MebiusPlayerJoinGreeter[F[_] : Effect](implicit effectEnvironment: EffectE speechServiceRepository(player) .makeSpeechIgnoringBlockage( property, - MebiusSpeech(s"おかえり${property.ownerNickname}!待ってたよ!", MebiusSpeechStrength.Medium) - ).toIO + MebiusSpeech( + s"おかえり${property.ownerNickname}!待ってたよ!", + MebiusSpeechStrength.Medium + ) + ) + .toIO ) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusRenamePreventionListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusRenamePreventionListener.scala index fb17f9e761..05f615586f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusRenamePreventionListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/listeners/MebiusRenamePreventionListener.scala @@ -2,7 +2,12 @@ package com.github.unchama.seichiassist.subsystems.mebius.bukkit.listeners import com.github.unchama.seichiassist.subsystems.mebius.bukkit.codec.BukkitMebiusItemStackCodec import org.bukkit.ChatColor._ -import org.bukkit.event.inventory.{InventoryAction, InventoryClickEvent, InventoryDragEvent, InventoryInteractEvent} +import org.bukkit.event.inventory.{ + InventoryAction, + InventoryClickEvent, + InventoryDragEvent, + InventoryInteractEvent +} import org.bukkit.event.{EventHandler, Listener} import org.bukkit.inventory.AnvilInventory @@ -16,10 +21,16 @@ class MebiusRenamePreventionListener extends Listener { val clickedInventory = event.getClickedInventory if (clickedInventory.isInstanceOf[AnvilInventory]) { // mebiusを選択中、左枠に置いた場合はcancel - if (BukkitMebiusItemStackCodec.isMebius(event.getCursor) && event.getView.convertSlot(0) == 0 && event.getRawSlot == 0) { + if ( + BukkitMebiusItemStackCodec.isMebius(event.getCursor) && event + .getView + .convertSlot(0) == 0 && event.getRawSlot == 0 + ) { cancelEventAndNotifyTheAlternative(event) } - } else if (event.getClick.isShiftClick && BukkitMebiusItemStackCodec.isMebius(event.getCurrentItem)) { + } else if ( + event.getClick.isShiftClick && BukkitMebiusItemStackCodec.isMebius(event.getCurrentItem) + ) { // mebiusをShiftクリックした場合 // 金床の左枠が空いている場合はcancel if (event.getView.getTopInventory.getItem(0) == null) { @@ -30,7 +41,9 @@ class MebiusRenamePreventionListener extends Listener { private def cancelEventAndNotifyTheAlternative(event: InventoryInteractEvent): Unit = { event.setCancelled(true) - event.getWhoClicked.sendMessage(s"${RED}MEBIUSへの命名は$RESET/mebius naming ${RED}で行ってください。") + event + .getWhoClicked + .sendMessage(s"${RED}MEBIUSへの命名は$RESET/mebius naming ${RED}で行ってください。") } // 金床配置時(ドラッグ) @@ -53,7 +66,9 @@ class MebiusRenamePreventionListener extends Listener { val action = event.getAction // event.getActionが数字キーによるアイテムの移動ならば処理を続行 - if (action == null || (action != InventoryAction.HOTBAR_SWAP && action != InventoryAction.HOTBAR_MOVE_AND_READD)) return + if ( + action == null || (action != InventoryAction.HOTBAR_SWAP && action != InventoryAction.HOTBAR_MOVE_AND_READD) + ) return // 金床の一番左のスロットにアイテムを移動させた場合以外return if (event.getView.convertSlot(0) != 0 || event.getRawSlot != 0) return diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/routines/PeriodicMebiusSpeechRoutine.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/routines/PeriodicMebiusSpeechRoutine.scala index e0dc1904ff..738a66c6c3 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/routines/PeriodicMebiusSpeechRoutine.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/routines/PeriodicMebiusSpeechRoutine.scala @@ -6,8 +6,14 @@ import com.github.unchama.concurrent.{RepeatingRoutine, RepeatingTaskContext} import com.github.unchama.datarepository.KeyedDataRepository import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.subsystems.mebius.bukkit.codec.BukkitMebiusItemStackCodec -import com.github.unchama.seichiassist.subsystems.mebius.domain.resources.{MebiusMessages, MebiusTalks} -import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{MebiusSpeech, MebiusSpeechStrength} +import com.github.unchama.seichiassist.subsystems.mebius.domain.resources.{ + MebiusMessages, + MebiusTalks +} +import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{ + MebiusSpeech, + MebiusSpeechStrength +} import com.github.unchama.seichiassist.subsystems.mebius.service.MebiusSpeechService import com.github.unchama.util.collection.RandomizedCollection import org.bukkit.entity.Player @@ -22,9 +28,9 @@ object PeriodicMebiusSpeechRoutine { 1.minute } - def unblockAndSpeakTipsOrMessageRandomly(player: Player) - (implicit - serviceRepository: KeyedDataRepository[Player, MebiusSpeechService[SyncIO]]): SyncIO[Unit] = { + def unblockAndSpeakTipsOrMessageRandomly(player: Player)( + implicit serviceRepository: KeyedDataRepository[Player, MebiusSpeechService[SyncIO]] + ): SyncIO[Unit] = { val service = serviceRepository(player) for { @@ -36,24 +42,23 @@ object PeriodicMebiusSpeechRoutine { .decodePropertyOfOwnedMebius(player)(helmet) .map { property => val messageCandidates = new RandomizedCollection[String]( - NonEmptyList( - MebiusTalks.at(property.level).mebiusMessage, - MebiusMessages.tips - ) + NonEmptyList(MebiusTalks.at(property.level).mebiusMessage, MebiusMessages.tips) ) messageCandidates.pickOne[SyncIO].flatMap { message => - service.tryMakingSpeech(property, MebiusSpeech(message, MebiusSpeechStrength.Medium)) + service + .tryMakingSpeech(property, MebiusSpeech(message, MebiusSpeechStrength.Medium)) } } .getOrElse(SyncIO.unit) } yield () } - def start(player: Player)(implicit - serviceRepository: KeyedDataRepository[Player, MebiusSpeechService[SyncIO]], - context: RepeatingTaskContext, - onMainThread: OnMinecraftServerThread[IO]): IO[Nothing] = { + def start(player: Player)( + implicit serviceRepository: KeyedDataRepository[Player, MebiusSpeechService[SyncIO]], + context: RepeatingTaskContext, + onMainThread: OnMinecraftServerThread[IO] + ): IO[Nothing] = { implicit val timer: Timer[IO] = IO.timer(context) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/MebiusDrop.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/MebiusDrop.scala index d400a51958..486aaeff1c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/MebiusDrop.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/MebiusDrop.scala @@ -1,8 +1,11 @@ package com.github.unchama.seichiassist.subsystems.mebius.domain import cats.Apply -import com.github.unchama.seichiassist.subsystems.seasonalevents.christmas.Christmas -import com.github.unchama.seichiassist.subsystems.mebius.domain.property.{ChristmasMebius, MebiusProperty, NormalMebius} +import com.github.unchama.seichiassist.subsystems.mebius.domain.property.{ + ChristmasMebius, + MebiusProperty, + NormalMebius +} import com.github.unchama.seichiassist.subsystems.seasonalevents.api.ChristmasEventsAPI import com.github.unchama.util.RandomEffect @@ -14,20 +17,21 @@ object MebiusDrop { // 平均 averageBlocksToBeBrokenPerMebiusDrop 回の試行でドロップすることになる。 private val averageBlocksToBeBrokenPerMebiusDrop = 50000 - import cats.implicits._ - - def tryOnce[F[_] : RandomEffect : ChristmasEventsAPI : Apply](ownerName: String, - ownerUuid: String): F[Option[MebiusProperty]] = + def tryOnce[F[_]: RandomEffect: ChristmasEventsAPI: Apply]( + ownerName: String, + ownerUuid: String + ): F[Option[MebiusProperty]] = Apply[F].map2( RandomEffect[F].tryForOneIn(averageBlocksToBeBrokenPerMebiusDrop), ChristmasEventsAPI[F].isInEvent - ) { case (dropping, isChristmas) => - if (dropping) { - val mebiusType = if (isChristmas) ChristmasMebius else NormalMebius - Some(MebiusProperty.initialProperty(mebiusType, ownerName, ownerUuid)) - } else { - None - } + ) { + case (dropping, isChristmas) => + if (dropping) { + val mebiusType = if (isChristmas) ChristmasMebius else NormalMebius + Some(MebiusProperty.initialProperty(mebiusType, ownerName, ownerUuid)) + } else { + None + } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/message/MebiusCombatMessage.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/message/MebiusCombatMessage.scala index 13522f475b..124806b056 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/message/MebiusCombatMessage.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/message/MebiusCombatMessage.scala @@ -11,8 +11,6 @@ case class MebiusCombatMessage(rawMessage: String) { * `ownerNickname` と `enemyName` を `rawMessage` に補完してメッセージを生成する。 */ def interpolate(ownerNickname: String, enemyName: String): String = - rawMessage - .replace("[str1]", ownerNickname) - .replace("[str2]", enemyName) + rawMessage.replace("[str1]", ownerNickname).replace("[str2]", enemyName) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/message/MebiusDialogue.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/message/MebiusDialogue.scala index 53854ad61c..b295e7c255 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/message/MebiusDialogue.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/message/MebiusDialogue.scala @@ -3,7 +3,6 @@ package com.github.unchama.seichiassist.subsystems.mebius.domain.message /** * Mebiusとプレーヤー間のやり取り。 * - * アイテムに書き込まれたり、レベルアップ時にMebiusとのやり取りが - * チャットに表示されたりすることを想定している。 + * アイテムに書き込まれたり、レベルアップ時にMebiusとのやり取りが チャットに表示されたりすることを想定している。 */ case class MebiusDialogue(mebiusMessage: String, playerMessage: String) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/message/MebiusPlayerMessage.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/message/MebiusPlayerMessage.scala index fda3e18285..7cb6b4b071 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/message/MebiusPlayerMessage.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/message/MebiusPlayerMessage.scala @@ -11,7 +11,6 @@ case class MebiusPlayerMessage(rawMessage: String) { * `ownerNickname` を `rawMessage` に補完してメッセージを生成する。 */ def interpolate(ownerNickname: String): String = - rawMessage - .replace("[str1]", ownerNickname) + rawMessage.replace("[str1]", ownerNickname) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusEnchantment.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusEnchantment.scala index 8506996da0..cfa8286a14 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusEnchantment.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusEnchantment.scala @@ -5,9 +5,11 @@ import enumeratum._ /** * Mebiusに付与できるエンチャントのクラス */ -sealed abstract class MebiusEnchantment(val unlockLevel: MebiusLevel, - val maxLevel: Int, - val displayName: String) extends EnumEntry { +sealed abstract class MebiusEnchantment( + val unlockLevel: MebiusLevel, + val maxLevel: Int, + val displayName: String +) extends EnumEntry { import cats.implicits._ require { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusEnchantmentLevels.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusEnchantmentLevels.scala index 0b69242489..19ccb7d25b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusEnchantmentLevels.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusEnchantmentLevels.scala @@ -4,15 +4,16 @@ import cats.effect.Sync import scala.util.Random -class MebiusEnchantmentLevels private(val mapping: Map[MebiusEnchantment, Int]) { +class MebiusEnchantmentLevels private (val mapping: Map[MebiusEnchantment, Int]) { import cats.implicits._ - mapping.foreach { case m@(MebiusEnchantment(_, maxLevel, _), enchantmentLevel) => - require( - 1 <= enchantmentLevel && enchantmentLevel <= maxLevel, - s"$enchantmentLevel is in [1, $maxLevel] for $m" - ) + mapping.foreach { + case m @ (MebiusEnchantment(_, maxLevel, _), enchantmentLevel) => + require( + 1 <= enchantmentLevel && enchantmentLevel <= maxLevel, + s"$enchantmentLevel is in [1, $maxLevel] for $m" + ) } def of(enchantment: MebiusEnchantment): Int = mapping.getOrElse(enchantment, 0) @@ -46,8 +47,9 @@ class MebiusEnchantmentLevels private(val mapping: Map[MebiusEnchantment, Int]) .toSet } - def randomlyUpgradeAt[F[_]](mebiusLevel: MebiusLevel) - (implicit F: Sync[F]): F[MebiusEnchantmentLevels] = { + def randomlyUpgradeAt[F[_]]( + mebiusLevel: MebiusLevel + )(implicit F: Sync[F]): F[MebiusEnchantmentLevels] = { val upgradableEnchantments = upgradableEnchantmentsAt(mebiusLevel).toSeq F.delay { @@ -58,19 +60,22 @@ class MebiusEnchantmentLevels private(val mapping: Map[MebiusEnchantment, Int]) } def differenceFrom(another: MebiusEnchantmentLevels): Set[MebiusEnchantment] = { - this.mapping.keySet - .union(another.mapping.keySet) - .filter { e => this.of(e) != another.of(e) } + this.mapping.keySet.union(another.mapping.keySet).filter { e => + this.of(e) != another.of(e) + } } } object MebiusEnchantmentLevels { - def apply(levelMapping: (MebiusEnchantment, Int)*) = new MebiusEnchantmentLevels(Map(levelMapping: _*)) + def apply(levelMapping: (MebiusEnchantment, Int)*) = new MebiusEnchantmentLevels( + Map(levelMapping: _*) + ) def fromUnsafeCounts(counter: MebiusEnchantment => Int): MebiusEnchantmentLevels = { new MebiusEnchantmentLevels( - MebiusEnchantment.values + MebiusEnchantment + .values .map { e => e -> counter(e) } .filter { case (e, l) => 1 <= l && l <= e.maxLevel } .toMap diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusLevel.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusLevel.scala index 30b0645197..e2a59b7d1b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusLevel.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusLevel.scala @@ -5,16 +5,17 @@ import cats.effect.Sync import scala.util.Random -case class MebiusLevel private(value: Int) extends AnyVal { +case class MebiusLevel private (value: Int) extends AnyVal { def attemptLevelUp[F[_]](implicit F: Sync[F]): F[Boolean] = if (isMaximum) F.pure(false) - else F.delay { - // パラメータpの幾何分布の平均は1/pであるから、 - // 1ブロック壊すごとに 1 / averageAttemptsToLevelUp の確率でレベルアップが起これば - // 平均 averageAttemptsToLevelUp 回の試行でレベルアップすることになる。 - Random.nextInt(MebiusLevel.averageAttemptsToLevelUp(value - 1)) == 0 - } + else + F.delay { + // パラメータpの幾何分布の平均は1/pであるから、 + // 1ブロック壊すごとに 1 / averageAttemptsToLevelUp の確率でレベルアップが起これば + // 平均 averageAttemptsToLevelUp 回の試行でレベルアップすることになる。 + Random.nextInt(MebiusLevel.averageAttemptsToLevelUp(value - 1)) == 0 + } def isMaximum: Boolean = this == MebiusLevel.max @@ -31,8 +32,7 @@ object MebiusLevel { val max: MebiusLevel = new MebiusLevel(30) - private val averageAttemptsToLevelUp = List( - 500, 500, 500, 500, // 5 + private val averageAttemptsToLevelUp = List(500, 500, 500, 500, // 5 800, 800, 800, 800, 800, // 10 1700, 1700, 1700, 1700, 1700, // 15 1800, 1800, 1800, 1800, 1800, // 20 @@ -41,10 +41,7 @@ object MebiusLevel { ) def apply(level: Int): MebiusLevel = { - require( - 1 <= level && level <= max.value, - s"$level in [1, ${max.value}]" - ) + require(1 <= level && level <= max.value, s"$level in [1, ${max.value}]") new MebiusLevel(level) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusProperty.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusProperty.scala index e355a8a8a2..d1ecad6d5a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusProperty.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusProperty.scala @@ -5,23 +5,33 @@ import cats.effect.Sync import scala.annotation.tailrec /** - * @param mebiusType メビウスの種類 - * @param forcedMaterial メビウスの素材の強制書き換え設定 - * @param ownerPlayerId オーナーのプレーヤーID - * @param ownerUuid オーナーのUUID文字列 - * @param enchantmentLevels 付与されるエンチャントのレベル - * @param level Mebiusのレベル - * @param ownerNicknameOverride オーナーをMebiusがどう呼ぶか - * @param mebiusName Mebius自体の名前 + * @param mebiusType + * メビウスの種類 + * @param forcedMaterial + * メビウスの素材の強制書き換え設定 + * @param ownerPlayerId + * オーナーのプレーヤーID + * @param ownerUuid + * オーナーのUUID文字列 + * @param enchantmentLevels + * 付与されるエンチャントのレベル + * @param level + * Mebiusのレベル + * @param ownerNicknameOverride + * オーナーをMebiusがどう呼ぶか + * @param mebiusName + * Mebius自体の名前 */ -case class MebiusProperty private(mebiusType: MebiusType, - ownerPlayerId: String, - ownerUuid: String, - enchantmentLevels: MebiusEnchantmentLevels, - forcedMaterial: MebiusForcedMaterial = MebiusForcedMaterial.None, - level: MebiusLevel = MebiusLevel(1), - ownerNicknameOverride: Option[String] = None, - mebiusName: String = "MEBIUS") { +case class MebiusProperty private ( + mebiusType: MebiusType, + ownerPlayerId: String, + ownerUuid: String, + enchantmentLevels: MebiusEnchantmentLevels, + forcedMaterial: MebiusForcedMaterial = MebiusForcedMaterial.None, + level: MebiusLevel = MebiusLevel(1), + ownerNicknameOverride: Option[String] = None, + mebiusName: String = "MEBIUS" +) { require(enchantmentLevels.isValidAt(level)) require(forcedMaterial.allowedAt(level)) @@ -34,22 +44,20 @@ case class MebiusProperty private(mebiusType: MebiusType, val upgradeEnchantmentLevels = if (newMebiusLevel.isMaximum) F.pure { enchantmentLevels.addNew(MebiusEnchantment.Unbreakable) - } else { + } + else { enchantmentLevels.randomlyUpgradeAt[F](newMebiusLevel) } upgradeEnchantmentLevels.map { upgradedEnchantmentLevels => - this.copy( - level = newMebiusLevel, - enchantmentLevels = upgradedEnchantmentLevels - ) + this.copy(level = newMebiusLevel, enchantmentLevels = upgradedEnchantmentLevels) } case None => F.raiseError(new IllegalStateException("Level cannot be upgraded from maximum")) } } - def tryUpgradeByOneLevel[F[_] : Sync]: F[MebiusProperty] = { + def tryUpgradeByOneLevel[F[_]: Sync]: F[MebiusProperty] = { for { levelUpHappened <- level.attemptLevelUp[F] updatedProperty <- { @@ -76,7 +84,11 @@ case class MebiusProperty private(mebiusType: MebiusType, } object MebiusProperty { - def initialProperty(mebiusType: MebiusType, ownerPlayerId: String, ownerUuid: String): MebiusProperty = { + def initialProperty( + mebiusType: MebiusType, + ownerPlayerId: String, + ownerUuid: String + ): MebiusProperty = { MebiusProperty( mebiusType, ownerPlayerId, diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusType.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusType.scala index ddc6b7da31..c9a2d0ffab 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusType.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusType.scala @@ -10,4 +10,3 @@ sealed trait MebiusType case object NormalMebius extends MebiusType case object ChristmasMebius extends MebiusType - diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/resources/MebiusMessages.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/resources/MebiusMessages.scala index 705cff3343..420ae693c1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/resources/MebiusMessages.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/resources/MebiusMessages.scala @@ -1,74 +1,87 @@ package com.github.unchama.seichiassist.subsystems.mebius.domain.resources import cats.data.NonEmptyList -import com.github.unchama.seichiassist.subsystems.mebius.domain.message.{MebiusCombatMessage, MebiusPlayerMessage} +import com.github.unchama.seichiassist.subsystems.mebius.domain.message.{ + MebiusCombatMessage, + MebiusPlayerMessage +} import com.github.unchama.util.collection.RandomizedCollection object MebiusMessages { val onBlockBreak: RandomizedCollection[MebiusPlayerMessage] = new RandomizedCollection( - NonEmptyList.of( - "ポコポコポコポコ…整地の音って、落ち着くねえ。", - "頑張れー!頑張れー!そこをまっすぐ!左にも石があるよー!…うるさい?", - "一生懸命掘ってると、いつの間にか無心になっちゃうよねえ…!", - "なんだか眠たくなってきちゃったー、[str1]は平気ー?", - "今日はどこまで掘るのかなー?", - "[str1]と一緒に整地するの、楽しいねえ!", - "ブロックが1つも浮いていないと、ちょうど空が見えて綺麗だよね!", - "ねえ、いま僕の兄弟の声がしなかった?気のせいかなあ", - "ポコポコ…ザクザク…音が気持ちいいよね!", - "整地♪整地♪ せ・い・ちー♪", - "あとちょっと掘ろうよ!", - "僕もスキルでバーっと掘ってみたいなー" - ).map(MebiusPlayerMessage) + NonEmptyList + .of( + "ポコポコポコポコ…整地の音って、落ち着くねえ。", + "頑張れー!頑張れー!そこをまっすぐ!左にも石があるよー!…うるさい?", + "一生懸命掘ってると、いつの間にか無心になっちゃうよねえ…!", + "なんだか眠たくなってきちゃったー、[str1]は平気ー?", + "今日はどこまで掘るのかなー?", + "[str1]と一緒に整地するの、楽しいねえ!", + "ブロックが1つも浮いていないと、ちょうど空が見えて綺麗だよね!", + "ねえ、いま僕の兄弟の声がしなかった?気のせいかなあ", + "ポコポコ…ザクザク…音が気持ちいいよね!", + "整地♪整地♪ せ・い・ちー♪", + "あとちょっと掘ろうよ!", + "僕もスキルでバーっと掘ってみたいなー" + ) + .map(MebiusPlayerMessage) ) val onMebiusBreak: RandomizedCollection[MebiusPlayerMessage] = new RandomizedCollection( - NonEmptyList.of( - "ここまでかぁっ…[str1]と一緒に旅したこと、すごく楽しかったなぁ…", - "この先[str1]のこと、守ってあげられなくなっちゃった…ごめんね…", - "僕、少しは[str1]の役に立てたかなぁ…もしそうだったら、嬉しいなぁ", - "[str1]のおかげで最期まで防具としていられたんだぁ…使ってくれて、ありがとう。", - "最期まで[str1]の頭にいれたことって、すごく幸せなことだよ", - "もし生まれ変わっても、また[str1]と…", - "ごめんね…[str1]とずっと一緒でいたかったけど、僕がついていけなかったよ…" - ).map(MebiusPlayerMessage) + NonEmptyList + .of( + "ここまでかぁっ…[str1]と一緒に旅したこと、すごく楽しかったなぁ…", + "この先[str1]のこと、守ってあげられなくなっちゃった…ごめんね…", + "僕、少しは[str1]の役に立てたかなぁ…もしそうだったら、嬉しいなぁ", + "[str1]のおかげで最期まで防具としていられたんだぁ…使ってくれて、ありがとう。", + "最期まで[str1]の頭にいれたことって、すごく幸せなことだよ", + "もし生まれ変わっても、また[str1]と…", + "ごめんね…[str1]とずっと一緒でいたかったけど、僕がついていけなかったよ…" + ) + .map(MebiusPlayerMessage) ) val onDamageBreaking: RandomizedCollection[MebiusPlayerMessage] = new RandomizedCollection( - NonEmptyList.of( - "いたた…もうすぐ壊れちゃいそうだ…", - "もうダメかも…こんなところで、悔しいなぁ", - "お願い、修繕して欲しいよ…", - "ごめんね…これ以上は[str1]のこと、守ってあげられそうにないよ…", - "もっと[str1]と、旅したかったなぁ", - "まだ平気…壊れるまでは、[str1]のことを守るんだ…", - "僕のこと、大事にしてね?" - ).map(MebiusPlayerMessage) + NonEmptyList + .of( + "いたた…もうすぐ壊れちゃいそうだ…", + "もうダメかも…こんなところで、悔しいなぁ", + "お願い、修繕して欲しいよ…", + "ごめんね…これ以上は[str1]のこと、守ってあげられそうにないよ…", + "もっと[str1]と、旅したかったなぁ", + "まだ平気…壊れるまでは、[str1]のことを守るんだ…", + "僕のこと、大事にしてね?" + ) + .map(MebiusPlayerMessage) ) val onDamageWarnEnemy: RandomizedCollection[MebiusCombatMessage] = new RandomizedCollection( - NonEmptyList.of( - "[str2]からの攻撃だ!気を付けて!", - "お前なんか余裕なんだからなー!さあ[str1]、やっちゃえ!", - "びっくりしたなー!人が休んでるときにー!", - "もーなんで今攻撃してくるのさあああ!", - "いったーいっ、今僕の小指踏んだなー!?", - "いてっ!やめろよー!僕を怒らせたら怖いぞー!", - "うわぁっ!飲み物がこぼれちゃったじゃないかー!" - ).map(MebiusCombatMessage) + NonEmptyList + .of( + "[str2]からの攻撃だ!気を付けて!", + "お前なんか余裕なんだからなー!さあ[str1]、やっちゃえ!", + "びっくりしたなー!人が休んでるときにー!", + "もーなんで今攻撃してくるのさあああ!", + "いったーいっ、今僕の小指踏んだなー!?", + "いてっ!やめろよー!僕を怒らせたら怖いぞー!", + "うわぁっ!飲み物がこぼれちゃったじゃないかー!" + ) + .map(MebiusCombatMessage) ) val onMonsterKill: RandomizedCollection[MebiusCombatMessage] = new RandomizedCollection( - NonEmptyList.of( - "さすが[str1]![str2]なんて敵じゃないね!", - "僕にかかれば[str2]なんてこんなもんだよー!", - "モンスターってなんで人間を襲うんだろう…?", - "ねえ[str1]、今の僕のおかげだよね!ね?", - "たまにはやられてみたいもんだねー、ふふん!", - "[str2]なんて僕の力を出すまでもなかったね!", - "やるね!僕も負けてらんないぞー!" - ).map(MebiusCombatMessage) + NonEmptyList + .of( + "さすが[str1]![str2]なんて敵じゃないね!", + "僕にかかれば[str2]なんてこんなもんだよー!", + "モンスターってなんで人間を襲うんだろう…?", + "ねえ[str1]、今の僕のおかげだよね!ね?", + "たまにはやられてみたいもんだねー、ふふん!", + "[str2]なんて僕の力を出すまでもなかったね!", + "やるね!僕も負けてらんないぞー!" + ) + .map(MebiusCombatMessage) ) val tips: List[String] = List( diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/speech/MebiusSpeechBlockageState.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/speech/MebiusSpeechBlockageState.scala index ca114a9953..82a816f64c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/speech/MebiusSpeechBlockageState.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/speech/MebiusSpeechBlockageState.scala @@ -8,7 +8,7 @@ import scala.util.Random /** * Mebiusの発話を阻止するかどうかを決定するオブジェクトのクラス。 */ -final class MebiusSpeechBlockageState[F[_] : Sync] { +final class MebiusSpeechBlockageState[F[_]: Sync] { private val willBlockSpeech: Ref[F, Boolean] = Ref.unsafe[F, Boolean](false) @@ -35,6 +35,7 @@ final class MebiusSpeechBlockageState[F[_] : Sync] { } object MebiusSpeechBlockageState { + /** * [[MebiusSpeechBlockageState.shouldBlock()]]が、内部状態がfalseの時にも発話を不許可とする確率 */ diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/service/MebiusSpeechService.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/service/MebiusSpeechService.scala index 3525ac9575..48b75136c3 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/service/MebiusSpeechService.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mebius/service/MebiusSpeechService.scala @@ -2,18 +2,24 @@ package com.github.unchama.seichiassist.subsystems.mebius.service import cats.Monad import com.github.unchama.seichiassist.subsystems.mebius.domain.property.MebiusProperty -import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{MebiusSpeech, MebiusSpeechBlockageState, MebiusSpeechGateway} +import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.{ + MebiusSpeech, + MebiusSpeechBlockageState, + MebiusSpeechGateway +} -class MebiusSpeechService[F[_] : Monad](gateway: MebiusSpeechGateway[F], - blockageState: MebiusSpeechBlockageState[F]) { +class MebiusSpeechService[F[_]: Monad]( + gateway: MebiusSpeechGateway[F], + blockageState: MebiusSpeechBlockageState[F] +) { def unblockSpeech(): F[Unit] = blockageState.unblock /** * `property` をプロパティとして持つMebiusに発話させる。 * - * 一度このアクションにてMebiusが発話された場合、 `unblockSpeech` が行われるまで - * 次の `tryMakingSpeech` は `Monad[F].unit` と等価になる。 + * 一度このアクションにてMebiusが発話された場合、 `unblockSpeech` が行われるまで 次の `tryMakingSpeech` は `Monad[F].unit` + * と等価になる。 * * また、 `unblockSpeech` が事前に呼ばれていたとしても、 * [[MebiusSpeechBlockageState.speechBlockProbability]]の確率で発話は行われない。 diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/System.scala index 8274e070c9..f63133a04e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/System.scala @@ -11,11 +11,11 @@ import com.github.unchama.seichiassist.subsystems.present.infrastructure.JdbcBac import org.bukkit.command.TabExecutor object System { - def wired[ - ConcurrentContext[_] : ConcurrentEffect : NonServerThreadContextShift - ](implicit environment: EffectEnvironment, + def wired[ConcurrentContext[_]: ConcurrentEffect: NonServerThreadContextShift]( + implicit environment: EffectEnvironment, uuidToLastSeenName: UuidToLastSeenName[ConcurrentContext], - ioOnMainThread: OnMinecraftServerThread[IO]): Subsystem[ConcurrentContext] = { + ioOnMainThread: OnMinecraftServerThread[IO] + ): Subsystem[ConcurrentContext] = { implicit val repo: JdbcBackedPresentPersistence[ConcurrentContext] = new JdbcBackedPresentPersistence[ConcurrentContext] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala index 9d66e395f3..23a956878f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala @@ -7,12 +7,16 @@ import cats.implicits._ import com.github.unchama.concurrent.NonServerThreadContextShift import com.github.unchama.contextualexecutor.ContextualExecutor import com.github.unchama.contextualexecutor.builder.Parsers -import com.github.unchama.contextualexecutor.executors.{BranchedExecutor, EchoExecutor, TraverseExecutor} +import com.github.unchama.contextualexecutor.executors.{ + BranchedExecutor, + EchoExecutor, + TraverseExecutor +} import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.commands.contextual.builder.BuilderTemplates.playerCommandBuilder import com.github.unchama.seichiassist.domain.actions.UuidToLastSeenName import com.github.unchama.seichiassist.subsystems.present.domain.OperationResult.DeleteResult -import com.github.unchama.seichiassist.subsystems.present.domain.{GrantRejectReason, PaginationRejectReason, PresentClaimingState, PresentPersistence, RevokeWarning} +import com.github.unchama.seichiassist.subsystems.present.domain._ import com.github.unchama.seichiassist.util.Util import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.{SequentialEffect, TargetedEffect} @@ -35,35 +39,37 @@ import org.bukkit.{ChatColor, Material} * - 操作が成功しなかったときは適切なメッセージを表示する。 */ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { - private val presentIdParser = Parsers.integer( - MessageEffect("presentコマンドに与えるプレゼントIDは整数である必要があります。") - ) + private val presentIdParser = + Parsers.integer(MessageEffect("presentコマンドに与えるプレゼントIDは整数である必要があります。")) - private val presentScopeModeParser = Parsers.fromOptionParser({ arg1: String => - arg1 match { - // enum match - case "player" | "all" => Some(arg1) - case _ => None - } - }, MessageEffect("presentコマンドで対象を指定する際のモードは、playerまたはallを指定してください。")) + private val presentScopeModeParser = Parsers.fromOptionParser( + { arg1: String => + arg1 match { + // enum match + case "player" | "all" => Some(arg1) + case _ => None + } + }, + MessageEffect("presentコマンドで対象を指定する際のモードは、playerまたはallを指定してください。") + ) private val noPermissionMessage = MessageEffect("You don't have the permission.") private object SubCommands { object State { - val help: EchoExecutor = EchoExecutor(MessageEffect(List( - "/present state", - " 対象となっている全てのプレゼントを表示します" - ))) + val help: EchoExecutor = EchoExecutor( + MessageEffect(List("/present state", " 対象となっている全てのプレゼントを表示します")) + ) /** - * 概要: 全てのプレゼントのうち、実行プレイヤーが対象となっているプレゼントの受け取り状況を表示する。 - * 実行プレイヤーが対象ではないプレゼントは表示されない。 + * 概要: 全てのプレゼントのうち、実行プレイヤーが対象となっているプレゼントの受け取り状況を表示する。 実行プレイヤーが対象ではないプレゼントは表示されない。 * * 構文: * - /present state */ - def executor[F[_] : ConcurrentEffect : NonServerThreadContextShift](implicit persistence: PresentPersistence[F, ItemStack]): ContextualExecutor = playerCommandBuilder + def executor[F[_]: ConcurrentEffect: NonServerThreadContextShift]( + implicit persistence: PresentPersistence[F, ItemStack] + ): ContextualExecutor = playerCommandBuilder .execution { context => val eff = for { // off-main-thread @@ -74,8 +80,9 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { .toList // 配布対象外のプレゼントを除外 .filter { case (_, state) => state != PresentClaimingState.Unavailable } - .map { case (id, state) => - s"ID=$id: ${decoratePresentState(state)}" + .map { + case (id, state) => + s"ID=$id: ${decoratePresentState(state)}" } .filter(_.nonEmpty) @@ -83,7 +90,7 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { List("対象のプレゼントが存在しません") } else { List( - s"${ChatColor.GRAY}${ChatColor.UNDERLINE}対象のプレゼント一覧:${ChatColor.RESET}", + s"${ChatColor.GRAY}${ChatColor.UNDERLINE}対象のプレゼント一覧:${ChatColor.RESET}" ) ::: presents } @@ -96,10 +103,9 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { } object ListSubCommand { - val help: EchoExecutor = EchoExecutor(MessageEffect(List( - "/present list <ページ数>", - " 全てのプレゼントをページに分けて表示します", - ))) + val help: EchoExecutor = EchoExecutor( + MessageEffect(List("/present list <ページ数>", " 全てのプレゼントをページに分けて表示します")) + ) /** * 概要: 実行プレイヤーと全てのプレゼントの受け取り状況をページネーションと共に表示する @@ -108,7 +114,9 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { * * - /present list <page: PositiveInt> */ - def executor[F[_] : ConcurrentEffect : NonServerThreadContextShift](implicit persistence: PresentPersistence[F, ItemStack]): ContextualExecutor = + def executor[F[_]: ConcurrentEffect: NonServerThreadContextShift]( + implicit persistence: PresentPersistence[F, ItemStack] + ): ContextualExecutor = playerCommandBuilder .argumentsParsers( List( @@ -120,21 +128,26 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { val perPage: Int Refined Positive = 10 val page = refineV[Positive](context.args.parsed.head.asInstanceOf[Int]) match { // argumentsParsersで1以上を指定しているのでここでコケることはないはず - case Left(l) => throw new AssertionError(s"positive int: failed. message: $l") + case Left(l) => throw new AssertionError(s"positive int: failed. message: $l") case Right(v) => v } val player = context.sender.getUniqueId val eff = for { _ <- NonServerThreadContextShift[F].shift states <- persistence.fetchStateWithPagination(player, perPage, page) - messageLine = states.fold({ - case PaginationRejectReason.TooLargePage(max) => - List(s"ページ数が大きすぎます。${max}ページ以下にしてください") - case PaginationRejectReason.Empty => - List(s"プレゼントが定義されていません。プレゼントを定義するには/present defineを使用してください。") - }, b => b.map { case (id, state) => - s"ID=$id: ${decoratePresentState(state)}" - }.toList) + messageLine = states.fold( + { + case PaginationRejectReason.TooLargePage(max) => + List(s"ページ数が大きすぎます。${max}ページ以下にしてください") + case PaginationRejectReason.Empty => + List(s"プレゼントが定義されていません。プレゼントを定義するには/present defineを使用してください。") + }, + b => + b.map { + case (id, state) => + s"ID=$id: ${decoratePresentState(state)}" + }.toList + ) } yield { MessageEffect(messageLine) } @@ -144,14 +157,12 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { } object Claim { - val help: EchoExecutor = EchoExecutor(MessageEffect(List( - "/present claim <プレゼントID>", - " プレゼントを受け取ります", - ))) + val help: EchoExecutor = EchoExecutor( + MessageEffect(List("/present claim <プレゼントID>", " プレゼントを受け取ります")) + ) /** - * 概要: 指定されたIDのプレゼントを受け取れるかどうかテストする。 - * 受け取れる場合は、プレイヤーのインベントリにアイテムを追加する。 + * 概要: 指定されたIDのプレゼントを受け取れるかどうかテストする。 受け取れる場合は、プレイヤーのインベントリにアイテムを追加する。 * 受け取れない場合は、エラーメッセージを表示する。 * * 構文: @@ -159,7 +170,9 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { * * 出力: 受け取った場合は、その旨表示する。失敗した場合は、適切なエラーメッセージを表示する。 */ - def executor[F[_] : ConcurrentEffect : NonServerThreadContextShift](implicit persistence: PresentPersistence[F, ItemStack]): ContextualExecutor = + def executor[F[_]: ConcurrentEffect: NonServerThreadContextShift]( + implicit persistence: PresentPersistence[F, ItemStack] + ): ContextualExecutor = playerCommandBuilder .argumentsParsers(List(presentIdParser), onMissingArguments = help) .execution { context => @@ -191,7 +204,9 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { } } case PresentClaimingState.Unavailable => - Monad[F].pure(MessageEffect(s"ID: ${presentId}のプレゼントは存在しないか、あるいは配布対象ではありません。")) + Monad[F].pure( + MessageEffect(s"ID: ${presentId}のプレゼントは存在しないか、あるいは配布対象ではありません。") + ) } } yield effect @@ -201,14 +216,12 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { } object Define { - val help: EchoExecutor = EchoExecutor(MessageEffect(List( - "/present define", - " プレゼントを手に持っているアイテムで定義します", - ))) + val help: EchoExecutor = EchoExecutor( + MessageEffect(List("/present define", " プレゼントを手に持っているアイテムで定義します")) + ) /** - * 概要: メインハンドに持っているアイテムをプレゼントとして定義する。 - * プレイヤーがプレゼントを受け取ることができるようになるには、必ずプレゼントを定義しなければならない。 + * 概要: メインハンドに持っているアイテムをプレゼントとして定義する。 プレイヤーがプレゼントを受け取ることができるようになるには、必ずプレゼントを定義しなければならない。 * * 権限ノード: `seichiassist.present.define` * @@ -217,7 +230,9 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { * * 出力: 定義が成功した場合は、割り振られたアイテムのIDを表示する。失敗した場合は、適切なエラーメッセージを表示する。 */ - def executor[F[_] : ConcurrentEffect : NonServerThreadContextShift](implicit persistence: PresentPersistence[F, ItemStack]): ContextualExecutor = + def executor[F[_]: ConcurrentEffect: NonServerThreadContextShift]( + implicit persistence: PresentPersistence[F, ItemStack] + ): ContextualExecutor = playerCommandBuilder .execution { context => val player = context.sender @@ -243,20 +258,20 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { } object Delete { - val help: EchoExecutor = EchoExecutor(MessageEffect(List( - "/present delete <プレゼントID>", - " プレゼントを削除します", - ))) + val help: EchoExecutor = EchoExecutor( + MessageEffect(List("/present delete <プレゼントID>", " プレゼントを削除します")) + ) /** - * 概要: 指定したプレゼントを消去する。 - * 対応が失われるため、このコマンドの実行が完了した後、プレイヤーはそのプレゼントを受け取ることができなくなる。 + * 概要: 指定したプレゼントを消去する。 対応が失われるため、このコマンドの実行が完了した後、プレイヤーはそのプレゼントを受け取ることができなくなる。 * * 権限ノード: `seichiassist.present.delete` * * 出力: 操作の結果とそれに伴うメッセージ。 */ - def executor[F[_] : ConcurrentEffect : NonServerThreadContextShift](implicit persistence: PresentPersistence[F, ItemStack]): ContextualExecutor = + def executor[F[_]: ConcurrentEffect: NonServerThreadContextShift]( + implicit persistence: PresentPersistence[F, ItemStack] + ): ContextualExecutor = playerCommandBuilder .argumentsParsers(List(presentIdParser), onMissingArguments = help) .execution { context => @@ -281,10 +296,14 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { } object Grant { - val help: EchoExecutor = EchoExecutor(MessageEffect(List( - "/present grant <プレゼントID> all|(player <...プレーヤー名>)", - " プレゼントを受け取れるプレイヤーを追加します", - ))) + val help: EchoExecutor = EchoExecutor( + MessageEffect( + List( + "/present grant <プレゼントID> all|(player <...プレーヤー名>)", + " プレゼントを受け取れるプレイヤーを追加します" + ) + ) + ) /** * 概要: プレイヤーが指定されたプレゼントを受け取れるようにする。 @@ -295,15 +314,22 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { * * コマンド構文: * - * - /present grant <presentId: PresentID> player <...players^†^: PlayerName> + * - /present grant <presentId: PresentID> player <...players^†^: + * PlayerName> * - /present grant <presentId: PresentID> all * * 備考: * - †: スペース区切り。 */ - def executor[F[_] : ConcurrentEffect : NonServerThreadContextShift](implicit persistence: PresentPersistence[F, ItemStack], globalPlayerAccessor: UuidToLastSeenName[F]): ContextualExecutor = + def executor[F[_]: ConcurrentEffect: NonServerThreadContextShift]( + implicit persistence: PresentPersistence[F, ItemStack], + globalPlayerAccessor: UuidToLastSeenName[F] + ): ContextualExecutor = playerCommandBuilder - .argumentsParsers(List(presentIdParser, presentScopeModeParser), onMissingArguments = help) + .argumentsParsers( + List(presentIdParser, presentScopeModeParser), + onMissingArguments = help + ) .execution { context => if (context.sender.hasPermission("seichiassist.present.grant")) { // Parserを通した段階でargs[0]は "player" | "all" になっているのでこれでOK @@ -315,24 +341,29 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { // TODO: 以下の処理は多分共通化できるがうまい方法が思いつかない globalUUID2Name <- globalPlayerAccessor.entries // 可変長引数には対応していないので`yetToBeParsed`を使う - restArg = context.args + restArg = context + .args // プレイヤー名は /[A-Za-z0-9_]{,16}/であるため空白が誤って解釈されることはない .yetToBeParsed // 連続した空白を消去 .filter(_.nonEmpty) - target = if (isGlobal) - globalUUID2Name.keys - else - globalUUID2Name.filter { case (_, name) => restArg.contains(name) }.keys - errorIfNobody = Option.when(target.isEmpty) { MessageEffect("対象のプレイヤーが存在しません!") } + target = + if (isGlobal) + globalUUID2Name.keys + else + globalUUID2Name.filter { case (_, name) => restArg.contains(name) }.keys + errorIfNobody = Option.when(target.isEmpty) { + MessageEffect("対象のプレイヤーが存在しません!") + } grantError <- persistence.grant(presentId, target.toSet) - } yield - errorIfNobody.getOrElse(grantError.map { - case GrantRejectReason.NoSuchPresentID => - MessageEffect("指定されたプレゼントIDは存在しません!") - }.getOrElse(MessageEffect( - s"プレゼント(id: $presentId)を受け取れるプレイヤーを追加しました。" - ))) + } yield errorIfNobody.getOrElse( + grantError + .map { + case GrantRejectReason.NoSuchPresentID => + MessageEffect("指定されたプレゼントIDは存在しません!") + } + .getOrElse(MessageEffect(s"プレゼント(id: $presentId)を受け取れるプレイヤーを追加しました。")) + ) eff.toIO } else { @@ -343,10 +374,14 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { } object Revoke { - val help: EchoExecutor = EchoExecutor(MessageEffect(List( - "/present revoke <プレゼントID> all|(player <...プレーヤー名>)", - " プレゼントを受け取れるプレイヤーを削除します" - ))) + val help: EchoExecutor = EchoExecutor( + MessageEffect( + List( + "/present revoke <プレゼントID> all|(player <...プレーヤー名>)", + " プレゼントを受け取れるプレイヤーを削除します" + ) + ) + ) /** * 概要: プレイヤーがプレゼントを受け取れないようにする。 @@ -354,7 +389,8 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { * 権限ノード: `seichiassist.present.revoke` * * 構文: - * - /present revoke <presentId: PresentID> player <...players^✝^: PlayerName> + * - /present revoke <presentId: PresentID> player <...players^✝^: + * PlayerName> * - /present revoke <presentId: PresentID> all * * 出力: 操作の結果とそれに伴うメッセージ。 @@ -362,9 +398,15 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { * 備考: * - ✝: スペース区切り。 */ - def executor[F[_] : ConcurrentEffect : NonServerThreadContextShift](implicit persistence: PresentPersistence[F, ItemStack], globalPlayerAccessor: UuidToLastSeenName[F]): ContextualExecutor = + def executor[F[_]: ConcurrentEffect: NonServerThreadContextShift]( + implicit persistence: PresentPersistence[F, ItemStack], + globalPlayerAccessor: UuidToLastSeenName[F] + ): ContextualExecutor = playerCommandBuilder - .argumentsParsers(List(presentIdParser, presentScopeModeParser), onMissingArguments = help) + .argumentsParsers( + List(presentIdParser, presentScopeModeParser), + onMissingArguments = help + ) .execution { context => if (context.sender.hasPermission("seichiassist.present.revoke")) { val args = context.args @@ -379,22 +421,24 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { .yetToBeParsed // 連続した空白を消去 .filter(_.nonEmpty) - target = if (isGlobal) - globalUUID2Name.keys - else - globalUUID2Name.filter { case (_, name) => restArg.contains(name) }.keys - errorIfNobody = if (target.isEmpty) Some(MessageEffect("対象のプレイヤーが存在しません!")) else None + target = + if (isGlobal) + globalUUID2Name.keys + else + globalUUID2Name.filter { case (_, name) => restArg.contains(name) }.keys + errorIfNobody = + if (target.isEmpty) Some(MessageEffect("対象のプレイヤーが存在しません!")) else None warning <- persistence.revoke(presentId, target.toSet) } yield { errorIfNobody.getOrElse { - warning.map { - case RevokeWarning.NoSuchPresentID => MessageEffect("そのようなプレゼントIDはありません!") - case RevokeWarning.NoPlayers => MessageEffect("対象となるプレイヤーが存在しません!") - }.getOrElse { - MessageEffect( - s"プレゼント(id: $presentId)を受け取れるプレイヤーを削除しました。" - ) - } + warning + .map { + case RevokeWarning.NoSuchPresentID => MessageEffect("そのようなプレゼントIDはありません!") + case RevokeWarning.NoPlayers => MessageEffect("対象となるプレイヤーが存在しません!") + } + .getOrElse { + MessageEffect(s"プレゼント(id: $presentId)を受け取れるプレイヤーを削除しました。") + } } } eff.toIO @@ -406,6 +450,7 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { } object Help { + /** * 概要: マニュアルを表示する。 * @@ -413,27 +458,34 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { * - /present help */ def executor: ContextualExecutor = { - TraverseExecutor(List( - SubCommands.State.help, - SubCommands.ListSubCommand.help, - SubCommands.Claim.help, - EchoExecutor(MessageEffect(List( - "/present help", - " このメッセージを表示します", - s"${ChatColor.GRAY}==== [管理者用コマンド] ====", - ))), - SubCommands.Define.help, - SubCommands.Delete.help, - SubCommands.Grant.help, - SubCommands.Revoke.help, - )) + TraverseExecutor( + List( + SubCommands.State.help, + SubCommands.ListSubCommand.help, + SubCommands.Claim.help, + EchoExecutor( + MessageEffect( + List( + "/present help", + " このメッセージを表示します", + s"${ChatColor.GRAY}==== [管理者用コマンド] ====" + ) + ) + ), + SubCommands.Define.help, + SubCommands.Delete.help, + SubCommands.Grant.help, + SubCommands.Revoke.help + ) + ) } } } - def executor[ - F[_] : ConcurrentEffect : NonServerThreadContextShift - ](implicit persistence: PresentPersistence[F, ItemStack], globalPlayerAccessor: UuidToLastSeenName[F]): TabExecutor = BranchedExecutor( + def executor[F[_]: ConcurrentEffect: NonServerThreadContextShift]( + implicit persistence: PresentPersistence[F, ItemStack], + globalPlayerAccessor: UuidToLastSeenName[F] + ): TabExecutor = BranchedExecutor( Map( "define" -> SubCommands.Define.executor, "delete" -> SubCommands.Delete.executor, @@ -442,15 +494,15 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { "claim" -> SubCommands.Claim.executor, "list" -> SubCommands.ListSubCommand.executor, "state" -> SubCommands.State.executor, - "help" -> SubCommands.Help.executor, + "help" -> SubCommands.Help.executor ), Some(SubCommands.Help.executor), Some(SubCommands.Help.executor) ).asNonBlockingTabExecutor() private def decoratePresentState(state: PresentClaimingState): String = state match { - case PresentClaimingState.Claimed => s"${ChatColor.GOLD}受け取り済み" - case PresentClaimingState.NotClaimed => s"${ChatColor.GREEN}受け取り可能" + case PresentClaimingState.Claimed => s"${ChatColor.GOLD}受け取り済み" + case PresentClaimingState.NotClaimed => s"${ChatColor.GREEN}受け取り可能" case PresentClaimingState.Unavailable => s"${ChatColor.GRAY}配布対象外" } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/GrantRejectReason.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/GrantRejectReason.scala index 97a6dc14f1..e5b0c399c1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/GrantRejectReason.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/GrantRejectReason.scala @@ -4,4 +4,4 @@ sealed trait GrantRejectReason object GrantRejectReason { case object NoSuchPresentID extends GrantRejectReason -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentClaimingState.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentClaimingState.scala index 004c2c1460..1f771c7a86 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentClaimingState.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentClaimingState.scala @@ -18,4 +18,3 @@ object PresentClaimingState { */ case object Unavailable extends PresentClaimingState } - diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala index 5d05d0f985..e2c73821a1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala @@ -7,8 +7,7 @@ import eu.timepit.refined.numeric.Positive import java.util.UUID /** - * プレゼントシステムに関する永続化のインターフェースを規定するトレイト。 - * 他で指定がない限り、以下の条件および制約を満たす。違反した時の動作は未定義である: + * プレゼントシステムに関する永続化のインターフェースを規定するトレイト。 他で指定がない限り、以下の条件および制約を満たす。違反した時の動作は未定義である: * - 引数で渡される`PresentID`は対応するプレゼントが存在し、その多重度は1対1である * - 返り値としての`PresentID`は対応するプレゼントが存在する */ @@ -18,15 +17,18 @@ trait PresentPersistence[F[_], ItemStack] { /** * 指定した[[ItemStack]]に対応するプレゼントを新しく定義する。 * - * @param itemstack プレゼントの中身 - * @return 定義を行った後、新しく割り振られたPresentIDを返し、かつ定義に失敗した場合例外を投げる作用 + * @param itemstack + * プレゼントの中身 + * @return + * 定義を行った後、新しく割り振られたPresentIDを返し、かつ定義に失敗した場合例外を投げる作用 */ def define(itemstack: ItemStack): F[PresentID] /** * 指定したPresentIDに対応するプレゼントを消去する。 * - * @param presentID プレゼントID + * @param presentID + * プレゼントID */ def delete(presentID: PresentID): F[DeleteResult] @@ -34,34 +36,44 @@ trait PresentPersistence[F[_], ItemStack] { * 指定したUUIDを持つプレイヤー群に対して`presentID`で指定されたプレゼントを受け取ることができるようにする。 * このメソッドは同じプレイヤーとプレゼントIDで呼び出された場合はべき等である。また、すでに受取可能なプレイヤーが * `players`の中に入っていた場合は、そのプレイヤーについての受取可能にする処理をスキップする。 - * @param presentID 対象のプレゼントID - * @param players 受け取ることができるようにするプレイヤーのUUID - * @return 永続化層への書き込みを行う作用 + * @param presentID + * 対象のプレゼントID + * @param players + * 受け取ることができるようにするプレイヤーのUUID + * @return + * 永続化層への書き込みを行う作用 */ def grant(presentID: PresentID, players: Set[UUID]): F[Option[GrantRejectReason]] /** * 指定したUUIDを持つプレイヤー群が`presentID`で指定されたプレゼントを受け取ることができないようにする。 * - * @param presentID 対象のプレゼントID - * @param players 受け取ることができないようにするプレイヤーのUUID - * @return 永続化層への書き込みを行う作用 + * @param presentID + * 対象のプレゼントID + * @param players + * 受け取ることができないようにするプレイヤーのUUID + * @return + * 永続化層への書き込みを行う作用 */ def revoke(presentID: PresentID, players: Set[UUID]): F[Option[RevokeWarning]] /** * 永続化層でプレゼントを受け取ったことにする。 * - * @param player プレイヤーのUUID - * @param presentID プレゼントID - * @return 永続化層への書き込みを行う作用 + * @param player + * プレイヤーのUUID + * @param presentID + * プレゼントID + * @return + * 永続化層への書き込みを行う作用 */ def markAsClaimed(presentID: PresentID, player: UUID): F[Unit] /** * 全ての有効な[[PresentID]]とそれに紐付けられた[[ItemStack]]を列挙する。 * - * @return 全てのプレゼントを列挙する作用 + * @return + * 全てのプレゼントを列挙する作用 */ def mapping: F[Map[PresentID, ItemStack]] @@ -71,7 +83,8 @@ trait PresentPersistence[F[_], ItemStack] { * また、ページネーションされたListの中での出現順序も、[[PresentID]]が最も若いエントリから先に出現する。 * * 例として以下のような状況を仮定する: - * - 既知のPresentIDとItemStackのエントリ: `List((1, aaa), (3, ccc), (6, fff), (4, ddd), (5, eee), (2, bbb))` + * - 既知のPresentIDとItemStackのエントリ: `List((1, aaa), (3, ccc), (6, fff), (4, ddd), (5, eee), + * (2, bbb))` * - PresentPersistenceのインスタンス `pp` * - 調査対象のプレイヤー `A` * - `A` が対象となっているプレゼントのPresentID: `Set(1, 2, 4, 6)` @@ -87,35 +100,42 @@ trait PresentPersistence[F[_], ItemStack] { * - 最終インデックスが有効なプレゼントの総数を超えるとき、作用はLeftを返さなければならない。 * - 最終インデックスが有効なプレゼントの総数を超えないとき、作用はRightを返さなければならない。 * - * @param player 調べる対象のプレイヤー - * @param perPage ページごとのエントリの数 - * @param page ページ、1オリジン - * @return ページネーションを計算して返す作用 + * @param player + * 調べる対象のプレイヤー + * @param perPage + * ページごとのエントリの数 + * @param page + * ページ、1オリジン + * @return + * ページネーションを計算して返す作用 */ def fetchStateWithPagination( - player: UUID, - perPage: Int Refined Positive, - page: Int Refined Positive - ): F[Either[PaginationRejectReason, List[(PresentID, PresentClaimingState)]]] + player: UUID, + perPage: Int Refined Positive, + page: Int Refined Positive + ): F[Either[PaginationRejectReason, List[(PresentID, PresentClaimingState)]]] /** * プレイヤーがプレゼントを受け取ることができるかどうか列挙する。このとき、計算されるMapは次の性質を満たす: * - * - すでに受け取ったプレゼントに対応するPresentIDに対して[[PresentClaimingState.Claimed]]がマッピングされる - * - 受け取ることができるが、まだ受け取っていないプレゼントに対応するPresentIDに対して[[PresentClaimingState.NotClaimed]]がマッピングされる - * - 有効かつ、プレイヤーが受け取ることができないPresentIDに対して[[PresentClaimingState.Unavailable]]がマッピングされる + * - すでに受け取ったプレゼントに対応するPresentIDに対して[[PresentClaimingState.Claimed]]がマッピングされる + * - 受け取ることができるが、まだ受け取っていないプレゼントに対応するPresentIDに対して[[PresentClaimingState.NotClaimed]]がマッピングされる + * - 有効かつ、プレイヤーが受け取ることができないPresentIDに対して[[PresentClaimingState.Unavailable]]がマッピングされる * - * @param player チェックするプレイヤー - * @return [[PresentID]]とそれに紐付けられたプレゼントを受け取ることができるかを - * [[PresentClaimingState]]で記述する[[Map]]を計算する作用 + * @param player + * チェックするプレイヤー + * @return + * [[PresentID]]とそれに紐付けられたプレゼントを受け取ることができるかを [[PresentClaimingState]]で記述する[[Map]]を計算する作用 */ def fetchState(player: UUID): F[Map[PresentID, PresentClaimingState]] /** * 指定したプレゼントIDでプレゼントを検索する。 * - * @param presentID プレゼントID - * @return 存在する場合は`Some[ItemStack]`、存在しない場合は`None`を返す作用 + * @param presentID + * プレゼントID + * @return + * 存在する場合は`Some[ItemStack]`、存在しない場合は`None`を返す作用 */ def lookup(presentID: PresentID): F[Option[ItemStack]] } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/RevokeWarning.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/RevokeWarning.scala index a724c97ac1..0cacfb2b72 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/RevokeWarning.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/RevokeWarning.scala @@ -5,4 +5,4 @@ sealed trait RevokeWarning object RevokeWarning { case object NoSuchPresentID extends RevokeWarning case object NoPlayers extends RevokeWarning -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/GlobalPlayerAccessor.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/GlobalPlayerAccessor.scala index c2d8849e54..228b6814be 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/GlobalPlayerAccessor.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/GlobalPlayerAccessor.scala @@ -10,19 +10,15 @@ import java.util.UUID * Bukkitの[[org.bukkit.Server]]の枠組みを超えて、全てのプレイヤーの情報についてアクセスするオブジェクト */ class GlobalPlayerAccessor[F[_]: Sync] extends UuidToLastSeenName[F] { + /** - * - * @return 全てのUUIDとそれに紐付けられた最終的な名前 + * @return + * 全てのUUIDとそれに紐付けられた最終的な名前 */ override def entries: F[Map[UUID, String]] = Sync[F].delay { DB.readOnly { implicit session => sql"""SELECT name, uuid from seichiassist.playerdata""" - .map { rs => - ( - UUID.fromString(rs.string("uuid")), - rs.string("name") - ) - } + .map { rs => (UUID.fromString(rs.string("uuid")), rs.string("name")) } .list() .apply() .toMap diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/ItemStackBlobProxy.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/ItemStackBlobProxy.scala index 83db7e3e1a..e007c83dcc 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/ItemStackBlobProxy.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/ItemStackBlobProxy.scala @@ -14,9 +14,7 @@ object ItemStackBlobProxy { type Base64 = String def itemStackToBlob(stack: ItemStack): Base64 = { Using.resource(new ByteArrayOutputStream()) { baos => - Using.resource(new BukkitObjectOutputStream(baos)) { bos => - bos.writeObject(stack) - } + Using.resource(new BukkitObjectOutputStream(baos)) { bos => bos.writeObject(stack) } Base64Coder.encodeLines(baos.toByteArray) } } @@ -29,4 +27,3 @@ object ItemStackBlobProxy { } } } - diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala index 059b87aaca..197c85ee08 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala @@ -4,7 +4,7 @@ import cats.Applicative import cats.effect.Sync import com.github.unchama.generic.MapExtra import com.github.unchama.seichiassist.subsystems.present.domain.OperationResult.DeleteResult -import com.github.unchama.seichiassist.subsystems.present.domain.{GrantRejectReason, PaginationRejectReason, PresentClaimingState, PresentPersistence, RevokeWarning} +import com.github.unchama.seichiassist.subsystems.present.domain._ import eu.timepit.refined.api.Refined import eu.timepit.refined.auto._ import eu.timepit.refined.numeric.Positive @@ -16,7 +16,7 @@ import java.util.UUID /** * [[PresentPersistence]]のJDBC実装。この実装は[[PresentPersistence]]の制約を引き継ぐ。 */ -class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, ItemStack] { +class JdbcBackedPresentPersistence[F[_]: Sync] extends PresentPersistence[F, ItemStack] { override def define(itemstack: ItemStack): F[PresentID] = Sync[F].delay { val stackAsBlob = ItemStackBlobProxy.itemStackToBlob(itemstack) DB.localTx { implicit session => @@ -30,19 +30,16 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It /** * 指定したPresentIDに対応するプレゼントを物理消去する。 * - * @param presentId プレゼントID + * @param presentId + * プレゼントID */ override def delete(presentId: PresentID): F[DeleteResult] = Sync[F].delay { DB.localTx { implicit session => // 制約をかけているのでpresent_stateの方から先に消さないと整合性エラーを吐く - sql"""DELETE FROM present_state WHERE present_id = $presentId""" - .execute() - .apply() + sql"""DELETE FROM present_state WHERE present_id = $presentId""".execute().apply() val deletedRows = - sql"""DELETE FROM present WHERE present_id = $presentId""" - .update() - .apply() + sql"""DELETE FROM present WHERE present_id = $presentId""".update().apply() if (deletedRows == 1) DeleteResult.Done else DeleteResult.NotFound } @@ -53,36 +50,22 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It val program = for { exists <- Sync[F].delay { DB.readOnly { implicit session => - sql"""SELECT present_id FROM present""" - .map(x => x.long("present_id")) - .list() - .apply() + sql"""SELECT present_id FROM present""".map(x => x.long("present_id")).list().apply() }.contains(presentID) } } yield { if (exists) { Sync[F].delay { - val alreadyAddedPlayers = DB.readOnly { implicit session => - sql"""SELECT uuid FROM present_state WHERE present_id = $presentID""" - .map(x => UUID.fromString(x.string("uuid"))) - .list() - .apply() - } - import scala.collection.Seq.iterableFactory - val initialValues = players - .map { uuid => Seq(presentID, uuid.toString, false) } - .toSeq + val initialValues = players.map { uuid => Seq(presentID, uuid.toString, false) }.toSeq DB.localTx { implicit session => // upsert - これによってfilterなしで整合性違反を起こすことはなくなる sql""" INSERT INTO present_state VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE present_id=present_id, uuid=uuid - """ - .batch(initialValues: _*) - .apply() + """.batch(initialValues: _*).apply() } // 型推論 @@ -90,7 +73,9 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It } } else { // 型推論 - Applicative[F].pure(Some(GrantRejectReason.NoSuchPresentID: GrantRejectReason): Option[GrantRejectReason]) + Applicative[F].pure( + Some(GrantRejectReason.NoSuchPresentID: GrantRejectReason): Option[GrantRejectReason] + ) } } @@ -127,12 +112,7 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It override def mapping: F[Map[PresentID, ItemStack]] = Sync[F].delay { DB.readOnly { implicit session => sql"""SELECT present_id, itemstack FROM present""" - .map { rs => - ( - rs.long("present_id"), - unwrapItemStack(rs) - ) - } + .map { rs => (rs.long("present_id"), unwrapItemStack(rs)) } .list() .apply() .toMap @@ -140,10 +120,10 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It } override def fetchStateWithPagination( - player: UUID, - perPage: Int Refined Positive, - page: Int Refined Positive - ): F[Either[PaginationRejectReason, List[(PresentID, PresentClaimingState)]]] = { + player: UUID, + perPage: Int Refined Positive, + page: Int Refined Positive + ): F[Either[PaginationRejectReason, List[(PresentID, PresentClaimingState)]]] = { import cats.implicits._ for { idSliceWithPagination <- idSliceWithPagination(perPage, page) @@ -161,14 +141,18 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It |FROM present_state |WHERE uuid = ${player.toString} AND present_id IN ($idSliceWithPagination) |ORDER BY present_id - """ - .stripMargin - .map(wrapResultForState) - .toList() - .apply() + """.stripMargin.map(wrapResultForState).toList().apply() } - Right(MapExtra.fillOnBaseSet(associatedEntries.toMap, idSliceWithPagination, PresentClaimingState.Unavailable).toList) + Right( + MapExtra + .fillOnBaseSet( + associatedEntries.toMap, + idSliceWithPagination, + PresentClaimingState.Unavailable + ) + .toList + ) } } } @@ -186,7 +170,11 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It .apply() } - MapExtra.fillOnBaseSet(associatedEntries.toMap, validPresentIDs.toSet, PresentClaimingState.Unavailable) + MapExtra.fillOnBaseSet( + associatedEntries.toMap, + validPresentIDs.toSet, + PresentClaimingState.Unavailable + ) } } @@ -199,7 +187,10 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It } } - private def idSliceWithPagination(perPage: Int Refined Positive, page: Int Refined Positive): F[Set[PresentID]] = + private def idSliceWithPagination( + perPage: Int Refined Positive, + page: Int Refined Positive + ): F[Set[PresentID]] = Sync[F].delay { val offset = (page - 1) * perPage DB.readOnly { implicit session => @@ -212,10 +203,11 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It } private def wrapResultForState(rs: WrappedResultSet): (Long, PresentClaimingState) = { - val claimState = if (rs.boolean("claimed")) - PresentClaimingState.Claimed - else - PresentClaimingState.NotClaimed + val claimState = + if (rs.boolean("claimed")) + PresentClaimingState.Claimed + else + PresentClaimingState.NotClaimed (rs.long("present_id"), claimState) } @@ -226,11 +218,7 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It private def computeValidPresentCount: F[Long] = Sync[F].delay { DB.readOnly { implicit session => - sql"""SELECT COUNT(*) AS c FROM present""" - .map(_.long("c")) - .first() - .apply() - .get // safe + sql"""SELECT COUNT(*) AS c FROM present""".map(_.long("c")).first().apply().get // safe } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/System.scala index c40ecb0f76..425c46cfdf 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/System.scala @@ -3,7 +3,10 @@ package com.github.unchama.seichiassist.subsystems.ranking import cats.effect.{Concurrent, Timer} import com.github.unchama.seichiassist.subsystems.breakcount.domain.SeichiAmountData import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountData -import com.github.unchama.seichiassist.subsystems.ranking.api.{AssortedRankingApi, RankingProvider} +import com.github.unchama.seichiassist.subsystems.ranking.api.{ + AssortedRankingApi, + RankingProvider +} import com.github.unchama.seichiassist.subsystems.ranking.application.GenericRefreshingRankingCache import com.github.unchama.seichiassist.subsystems.ranking.domain.values.{LoginTime, VoteCount} import com.github.unchama.seichiassist.subsystems.ranking.infrastructure._ @@ -13,21 +16,33 @@ object System { import cats.implicits._ - def wired[ - F[_] : Timer : Concurrent : ErrorLogger, - H[_] - ]: F[AssortedRankingApi[F]] = + def wired[F[_]: Timer: Concurrent: ErrorLogger, H[_]]: F[AssortedRankingApi[F]] = for { - seichiRanking <- GenericRefreshingRankingCache.withPersistence(new JdbcSeichiRankingRecordPersistence[F]) - buildRanking <- GenericRefreshingRankingCache.withPersistence(new JdbcBuildRankingRecordPersistence[F]) - loginRanking <- GenericRefreshingRankingCache.withPersistence(new JdbcLoginRankingRecordPersistence[F]) - voteRanking <- GenericRefreshingRankingCache.withPersistence(new JdbcVoteRankingRecordPersistence[F]) + seichiRanking <- GenericRefreshingRankingCache.withPersistence( + new JdbcSeichiRankingRecordPersistence[F] + ) + buildRanking <- GenericRefreshingRankingCache.withPersistence( + new JdbcBuildRankingRecordPersistence[F] + ) + loginRanking <- GenericRefreshingRankingCache.withPersistence( + new JdbcLoginRankingRecordPersistence[F] + ) + voteRanking <- GenericRefreshingRankingCache.withPersistence( + new JdbcVoteRankingRecordPersistence[F] + ) } yield { new AssortedRankingApi[F] { - override val seichiAmountRanking: RankingProvider[F, SeichiAmountData] = RankingProvider(seichiRanking) - override val buildAmountRanking: RankingProvider[F, BuildAmountData] = RankingProvider(buildRanking) - override val loginTimeRanking: RankingProvider[F, LoginTime] = RankingProvider(loginRanking) - override val voteCountRanking: RankingProvider[F, VoteCount] = RankingProvider(voteRanking) + override val seichiAmountRanking: RankingProvider[F, SeichiAmountData] = + RankingProvider(seichiRanking) + override val buildAmountRanking: RankingProvider[F, BuildAmountData] = RankingProvider( + buildRanking + ) + override val loginTimeRanking: RankingProvider[F, LoginTime] = RankingProvider( + loginRanking + ) + override val voteCountRanking: RankingProvider[F, VoteCount] = RankingProvider( + voteRanking + ) } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/api/AssortedRankingApi.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/api/AssortedRankingApi.scala index 3853cf423c..86cd5f2107 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/api/AssortedRankingApi.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/api/AssortedRankingApi.scala @@ -9,8 +9,10 @@ import com.github.unchama.seichiassist.subsystems.ranking.domain.values.{LoginTi /** * 定期的に更新されるランキングデータを提供するオブジェクトのtrait。 * - * @tparam F ランキングを取得する作用の文脈 - * @tparam R ランキングのレコードが保持するデータ型 + * @tparam F + * ランキングを取得する作用の文脈 + * @tparam R + * ランキングのレコードが保持するデータ型 */ case class RankingProvider[F[_], R](ranking: ReadOnlyRef[F, Ranking[R]]) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/application/GenericRefreshingRankingCache.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/application/GenericRefreshingRankingCache.scala index e5271f9e5c..c1649c7ec3 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/application/GenericRefreshingRankingCache.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/application/GenericRefreshingRankingCache.scala @@ -15,19 +15,22 @@ import scala.concurrent.duration.DurationInt object GenericRefreshingRankingCache { - def withPersistence[ - F[_] : Concurrent : Timer : ErrorLogger, R: Order: Monoid - ](persistence: RankingRecordPersistence[F, R]): F[ReadOnlyRef[F, Ranking[R]]] = + def withPersistence[F[_]: Concurrent: Timer: ErrorLogger, R: Order: Monoid]( + persistence: RankingRecordPersistence[F, R] + ): F[ReadOnlyRef[F, Ranking[R]]] = for { initialRankingRecords <- persistence.getAllRankingRecords rankingRef <- Ref.of(new Ranking(initialRankingRecords)) _ <- - StreamExtra.compileToRestartingStream[F, Unit]("[GenericRefreshingRankingCache]") { - fs2.Stream - .awakeEvery[F](30.seconds) - .evalMap(_ => persistence.getAllRankingRecords) - .evalTap(newRecords => rankingRef.set(new Ranking(newRecords))) - }.start + StreamExtra + .compileToRestartingStream[F, Unit]("[GenericRefreshingRankingCache]") { + fs2 + .Stream + .awakeEvery[F](30.seconds) + .evalMap(_ => persistence.getAllRankingRecords) + .evalTap(newRecords => rankingRef.set(new Ranking(newRecords))) + } + .start } yield ReadOnlyRef.fromRef(rankingRef) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/Ranking.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/Ranking.scala index 9211ed5812..a23066af53 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/Ranking.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/Ranking.scala @@ -4,7 +4,8 @@ import cats.{Monoid, Order} /** * ある時点でのランキング全体の情報を持つオブジェクトのクラス。 - * @tparam R 各プレーヤーのレコードが持つデータ型 + * @tparam R + * 各プレーヤーのレコードが持つデータ型 */ class Ranking[R: Order: Monoid](records: Vector[RankingRecord[R]]) { import cats.implicits._ @@ -15,13 +16,58 @@ class Ranking[R: Order: Monoid](records: Vector[RankingRecord[R]]) { val total: R = Monoid[R].combineAll(records.map(_.value)) - val recordsWithPositions: Vector[(Int, RankingRecord[R])] = - sortedRecords.zipWithIndex.map { case (record, i) => (i + 1, record) } + /** + * ランキングのレコードと、そのレコードの順位の組の集まり。 + * + * この順位はタイを考慮する。 つまり、二つのレコード `r1` と `r2` があり、 `r1.value` と `r2.value` が[[Order]]により等しければ、 `r1` + * と `r2` の順位は同じになる。 + */ + val recordsWithPositions: Vector[RankingRecordWithPosition[R]] = Vector.from { + var positionOfPreviousRecord = 0 - def positionAndRecordOf(playerName: String): Option[(Int, RankingRecord[R])] = - recordsWithPositions - .find { case (_, record) => record.playerName == playerName } + for { + index <- sortedRecords.indices + } yield { + // より小さな値に出くわしたら、記録する順位をindex + 1に戻す + if (index == 0 || (sortedRecords(index).value < sortedRecords(index - 1).value)) { + positionOfPreviousRecord = index + 1 + } + + RankingRecordWithPosition(sortedRecords(index), positionOfPreviousRecord) + } + } + + /** + * `recordsWithPositions` のインデックスで、 `playerName` のレコードが格納されたもの。 もしそのようなレコードが存在しなければ `None` + * が返される。 + */ + private def indexOfRecordOf(playerName: String): Option[Int] = { + val index = recordsWithPositions.indexWhere(_.record.playerName == playerName) + + if (index == -1) None else Some(index) + } + + def positionAndRecordOf(playerName: String): Option[RankingRecordWithPosition[R]] = + indexOfRecordOf(playerName).map(recordsWithPositions) + + def bestRecordBelow(playerName: String): Option[RankingRecordWithPosition[R]] = + indexOfRecordOf(playerName).flatMap { recordIndex => + // recordsWithPositionは降順ソートされているので、該当レコードよりも奥を切り出し、 + // レコード値が異なるような一番手前のレコードを持ってくればよい + recordsWithPositions + .drop(recordIndex + 1) + .find(_.record.value neqv recordsWithPositions(recordIndex).record.value) + } + + def worstRecordAbove(playerName: String): Option[RankingRecordWithPosition[R]] = + indexOfRecordOf(playerName).flatMap { recordIndex => + // recordsWithPositionは降順ソートされているので、該当レコードよりも手前を切り出し、 + // レコード値が異なるような一番奥のレコードを持ってくればよい + recordsWithPositions + .take(recordIndex) + .findLast(_.record.value neqv recordsWithPositions(recordIndex).record.value) + } def positionOf(playerName: String): Option[Int] = - positionAndRecordOf(playerName).map(_._1) + positionAndRecordOf(playerName).map(_.positionInRanking) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/RankingRecord.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/RankingRecord.scala index 8603bacdd3..4efa97c7da 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/RankingRecord.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/RankingRecord.scala @@ -1,3 +1,5 @@ package com.github.unchama.seichiassist.subsystems.ranking.domain case class RankingRecord[V](playerName: String, value: V) + +case class RankingRecordWithPosition[V](record: RankingRecord[V], positionInRanking: Int) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/RankingRecordPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/RankingRecordPersistence.scala index 3c9f743d22..80d6331966 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/RankingRecordPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/RankingRecordPersistence.scala @@ -1,8 +1,10 @@ package com.github.unchama.seichiassist.subsystems.ranking.domain /** - * @tparam F ランキングレコードを取得する作用の文脈 - * @tparam R レコードが記録する値の型 + * @tparam F + * ランキングレコードを取得する作用の文脈 + * @tparam R + * レコードが記録する値の型 */ trait RankingRecordPersistence[F[_], R] { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/values/LoginTime.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/values/LoginTime.scala index 5f6836e566..3debb31cfb 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/values/LoginTime.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/values/LoginTime.scala @@ -10,7 +10,8 @@ case class LoginTime(inTick: Long) { } object LoginTime { - implicit val isMonoid: Monoid[LoginTime] = Monoid.instance(LoginTime(0), (a, b) => LoginTime(a.inTick + b.inTick)) + implicit val isMonoid: Monoid[LoginTime] = + Monoid.instance(LoginTime(0), (a, b) => LoginTime(a.inTick + b.inTick)) implicit val isOrdered: Order[LoginTime] = Order.by(_.inTick) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/values/VoteCount.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/values/VoteCount.scala index 20ae239fb9..23243c1fb7 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/values/VoteCount.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/domain/values/VoteCount.scala @@ -10,5 +10,6 @@ case class VoteCount(value: Int) extends AnyVal object VoteCount { implicit val order: Order[VoteCount] = Order.by(_.value) - implicit val monoid: Monoid[VoteCount] = Monoid.instance(VoteCount(0), (a, b) => VoteCount(a.value + b.value)) + implicit val monoid: Monoid[VoteCount] = + Monoid.instance(VoteCount(0), (a, b) => VoteCount(a.value + b.value)) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/infrastructure/JdbcBuildRankingRecordPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/infrastructure/JdbcBuildRankingRecordPersistence.scala index a23354fe4b..710c99798a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/infrastructure/JdbcBuildRankingRecordPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/infrastructure/JdbcBuildRankingRecordPersistence.scala @@ -3,10 +3,14 @@ package com.github.unchama.seichiassist.subsystems.ranking.infrastructure import cats.effect.Sync import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountData -import com.github.unchama.seichiassist.subsystems.ranking.domain.{RankingRecordPersistence, RankingRecord} +import com.github.unchama.seichiassist.subsystems.ranking.domain.{ + RankingRecord, + RankingRecordPersistence +} import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} -class JdbcBuildRankingRecordPersistence[F[_] : Sync] extends RankingRecordPersistence[F, BuildAmountData] { +class JdbcBuildRankingRecordPersistence[F[_]: Sync] + extends RankingRecordPersistence[F, BuildAmountData] { override def getAllRankingRecords: F[Vector[RankingRecord[BuildAmountData]]] = Sync[F].delay { DB.readOnly { implicit session => @@ -17,7 +21,9 @@ class JdbcBuildRankingRecordPersistence[F[_] : Sync] extends RankingRecordPersis BuildAmountData(BuildExpAmount(BigDecimal(rs.string("build_count")))) ) } - .list().apply().toVector + .list() + .apply() + .toVector } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/infrastructure/JdbcLoginRankingRecordPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/infrastructure/JdbcLoginRankingRecordPersistence.scala index a354611bb7..fd8b8cd225 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/infrastructure/JdbcLoginRankingRecordPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/infrastructure/JdbcLoginRankingRecordPersistence.scala @@ -2,20 +2,21 @@ package com.github.unchama.seichiassist.subsystems.ranking.infrastructure import cats.effect.Sync import com.github.unchama.seichiassist.subsystems.ranking.domain.values.LoginTime -import com.github.unchama.seichiassist.subsystems.ranking.domain.{RankingRecord, RankingRecordPersistence} +import com.github.unchama.seichiassist.subsystems.ranking.domain.{ + RankingRecord, + RankingRecordPersistence +} import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} -class JdbcLoginRankingRecordPersistence[F[_] : Sync] extends RankingRecordPersistence[F, LoginTime] { +class JdbcLoginRankingRecordPersistence[F[_]: Sync] + extends RankingRecordPersistence[F, LoginTime] { override def getAllRankingRecords: F[Vector[RankingRecord[LoginTime]]] = Sync[F].delay { DB.readOnly { implicit session => sql"SELECT name,playtick from playerdata" - .map { rs => - RankingRecord( - rs.string("name"), - LoginTime(rs.int("playtick")) - ) - } - .list().apply().toVector + .map { rs => RankingRecord(rs.string("name"), LoginTime(rs.int("playtick"))) } + .list() + .apply() + .toVector } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/infrastructure/JdbcSeichiRankingRecordPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/infrastructure/JdbcSeichiRankingRecordPersistence.scala index b47131d4d9..ea8c6ef7cc 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/infrastructure/JdbcSeichiRankingRecordPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/infrastructure/JdbcSeichiRankingRecordPersistence.scala @@ -3,21 +3,30 @@ package com.github.unchama.seichiassist.subsystems.ranking.infrastructure import cats.effect.Sync import com.github.unchama.seichiassist.subsystems.breakcount.domain.SeichiAmountData import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.SeichiExpAmount -import com.github.unchama.seichiassist.subsystems.ranking.domain.{RankingRecordPersistence, RankingRecord} +import com.github.unchama.seichiassist.subsystems.ranking.domain.{ + RankingRecord, + RankingRecordPersistence +} import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} -class JdbcSeichiRankingRecordPersistence[F[_] : Sync] extends RankingRecordPersistence[F, SeichiAmountData] { +class JdbcSeichiRankingRecordPersistence[F[_]: Sync] + extends RankingRecordPersistence[F, SeichiAmountData] { - override def getAllRankingRecords: F[Vector[RankingRecord[SeichiAmountData]]] = Sync[F].delay { - DB.localTx { implicit session => - sql"select name, totalbreaknum from playerdata" - .map { rs => - RankingRecord( - rs.string("name"), - SeichiAmountData(SeichiExpAmount.ofNonNegative(rs.bigInt("totalbreaknum").longValueExact())) - ) - } - .list().apply().toVector + override def getAllRankingRecords: F[Vector[RankingRecord[SeichiAmountData]]] = + Sync[F].delay { + DB.localTx { implicit session => + sql"select name, totalbreaknum from playerdata" + .map { rs => + RankingRecord( + rs.string("name"), + SeichiAmountData( + SeichiExpAmount.ofNonNegative(rs.bigInt("totalbreaknum").longValueExact()) + ) + ) + } + .list() + .apply() + .toVector + } } - } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/infrastructure/JdbcVoteRankingRecordPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/infrastructure/JdbcVoteRankingRecordPersistence.scala index 9f994f0142..3012680e3c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/infrastructure/JdbcVoteRankingRecordPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/ranking/infrastructure/JdbcVoteRankingRecordPersistence.scala @@ -2,20 +2,21 @@ package com.github.unchama.seichiassist.subsystems.ranking.infrastructure import cats.effect.Sync import com.github.unchama.seichiassist.subsystems.ranking.domain.values.VoteCount -import com.github.unchama.seichiassist.subsystems.ranking.domain.{RankingRecord, RankingRecordPersistence} +import com.github.unchama.seichiassist.subsystems.ranking.domain.{ + RankingRecord, + RankingRecordPersistence +} import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} -class JdbcVoteRankingRecordPersistence[F[_] : Sync] extends RankingRecordPersistence[F, VoteCount] { +class JdbcVoteRankingRecordPersistence[F[_]: Sync] + extends RankingRecordPersistence[F, VoteCount] { override def getAllRankingRecords: F[Vector[RankingRecord[VoteCount]]] = Sync[F].delay { DB.readOnly { implicit session => sql"SELECT name,p_vote from playerdata" - .map { rs => - RankingRecord( - rs.string("name"), - VoteCount(rs.int("p_vote")) - ) - } - .list().apply().toVector + .map { rs => RankingRecord(rs.string("name"), VoteCount(rs.int("p_vote"))) } + .list() + .apply() + .toVector } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/rescueplayer/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/rescueplayer/System.scala index 70f770370a..90acf74b13 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/rescueplayer/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/rescueplayer/System.scala @@ -7,8 +7,6 @@ import org.bukkit.event.Listener object System { def wired[F[_]]: Subsystem[F] = new Subsystem[F] { - override val listeners: Seq[Listener] = Seq( - new RescuePlayerListener() - ) + override val listeners: Seq[Listener] = Seq(new RescuePlayerListener()) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/System.scala index d8e3088c02..0d38ececc7 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/System.scala @@ -25,22 +25,23 @@ import org.bukkit.plugin.java.JavaPlugin import java.util.UUID -class System[F[_]](override val listeners: Seq[Listener], - override val commands: Map[String, TabExecutor]) extends Subsystem[F] { +class System[F[_]]( + override val listeners: Seq[Listener], + override val commands: Map[String, TabExecutor] +) extends Subsystem[F] { - def api[G[_] : Clock : Functor]: SeasonalEventsAPI[G] = SeasonalEventsAPI.withF[G] + def api[G[_]: Clock: Functor]: SeasonalEventsAPI[G] = SeasonalEventsAPI.withF[G] } object System { - def wired[ - F[_] : ConcurrentEffect : NonServerThreadContextShift, - G[_] : SyncEffect, - H[_] - ](instance: JavaPlugin) - (implicit manaWriteApi: ManaWriteApi[G, Player], + def wired[F[_]: ConcurrentEffect: NonServerThreadContextShift, G[_]: SyncEffect, H[_]]( + instance: JavaPlugin + )( + implicit manaWriteApi: ManaWriteApi[G, Player], effectEnvironment: EffectEnvironment, - ioOnMainThread: OnMinecraftServerThread[IO]): System[H] = { + ioOnMainThread: OnMinecraftServerThread[IO] + ): System[H] = { implicit val repository: LastQuitPersistenceRepository[F, UUID] = new JdbcLastQuitPersistenceRepository[F] @@ -53,11 +54,9 @@ object System { new LimitedLoginBonusGifter, new SeizonsikiListener, new ValentineListener(), - new NewYearListener(), + new NewYearListener() ), - commands = Map( - "event" -> new EventCommand().executor - ) + commands = Map("event" -> new EventCommand().executor) ) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/Util.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/Util.scala index 7a7f7f949b..387f1e22ec 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/Util.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/Util.scala @@ -8,11 +8,14 @@ import java.time.temporal.ChronoUnit import java.util.Random object Util { + /** * 指定されたEntityがいるLocationに、指定されたitemをドロップする * - * @param entity 対象のエンティティ - * @param item ドロップさせるItemStack + * @param entity + * 対象のエンティティ + * @param item + * ドロップさせるItemStack */ def randomlyDropItemAt(entity: Entity, item: ItemStack, rate: Double): Unit = { val rand = new Random().nextDouble() @@ -22,9 +25,12 @@ object Util { /** * 引数で指定されたDoubleがドロップ率として適当な範囲(0.0以上1.0以下の小数)にあるかどうか検証して返す * - * @param rate ドロップ率 - * @return 適当な値であれば`rate`、適当な値でなければ`IllegalArgumentException` - * @throws IllegalArgumentException 指定されたドロップ率が適切ではない + * @param rate + * ドロップ率 + * @return + * 適当な値であれば`rate`、適当な値でなければ`IllegalArgumentException` + * @throws IllegalArgumentException + * 指定されたドロップ率が適切ではない */ def validateItemDropRate(rate: Double): Double = if (0.0 <= rate && rate <= 1.0) rate @@ -33,9 +39,12 @@ object Util { /** * 引数で指定されたStringが告知のブログ記事として適切なものかどうかを検証し、Stringを返す * - * @param url URL - * @return 適切であれば指定された`url`をそのまま返し、適切でなければ`IllegalArgumentException`を出す - * @throws IllegalArgumentException 指定されたURLが適切ではない + * @param url + * URL + * @return + * 適切であれば指定された`url`をそのまま返し、適切でなければ`IllegalArgumentException`を出す + * @throws IllegalArgumentException + * 指定されたURLが適切ではない */ def validateUrl(url: String): String = if (url.startsWith("https://www.seichi.network/post/")) url @@ -44,11 +53,16 @@ object Util { /** * 指定された期間に含まれるすべての日付を返す * - * @param from 期間の開始日 - * @param to 期間の終了日 - * @return 期間に含まれるすべてのLocalDateをもつSeq - * @see [[https://qiita.com/pictiny/items/357630e48043185da223 Qiita: Scalaで日付の範囲を指定してリストを作る]] - * @throws IllegalArgumentException `from`に`to`より後の日付が指定された時 + * @param from + * 期間の開始日 + * @param to + * 期間の終了日 + * @return + * 期間に含まれるすべてのLocalDateをもつSeq + * @see + * [[https://qiita.com/pictiny/items/357630e48043185da223 Qiita: Scalaで日付の範囲を指定してリストを作る]] + * @throws IllegalArgumentException + * `from`に`to`より後の日付が指定された時 */ def dateRangeAsSequence(from: LocalDate, to: LocalDate): Seq[LocalDate] = if (from.isAfter(to)) throw new IllegalArgumentException("適切ではない期間が指定されました。") diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/anniversary/Anniversary.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/anniversary/Anniversary.scala index 1e142688a0..0a318fccf9 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/anniversary/Anniversary.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/anniversary/Anniversary.scala @@ -1,6 +1,9 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.anniversary -import com.github.unchama.seichiassist.subsystems.seasonalevents.Util.{dateRangeAsSequence, validateUrl} +import com.github.unchama.seichiassist.subsystems.seasonalevents.Util.{ + dateRangeAsSequence, + validateUrl +} import java.time.LocalDate @@ -9,7 +12,9 @@ object Anniversary { val ANNIVERSARY_COUNT: Int = EVENT_YEAR - 2016 val START_DATE: LocalDate = LocalDate.of(EVENT_YEAR, 6, 29) val END_DATE: LocalDate = LocalDate.of(EVENT_YEAR, 7, 5) - val blogArticleUrl: String = validateUrl(s"https://www.seichi.network/post/${ANNIVERSARY_COUNT}th_anniv") + val blogArticleUrl: String = validateUrl( + s"https://www.seichi.network/post/${ANNIVERSARY_COUNT}th_anniv" + ) def isInEvent: Boolean = dateRangeAsSequence(START_DATE, END_DATE).contains(LocalDate.now()) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/anniversary/AnniversaryItemData.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/anniversary/AnniversaryItemData.scala index b4bcc7f985..7cbd7ff707 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/anniversary/AnniversaryItemData.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/anniversary/AnniversaryItemData.scala @@ -17,28 +17,23 @@ import scala.util.chaining._ object AnniversaryItemData { - //region まいんちゃんの記念頭 + // region まいんちゃんの記念頭 - private val mineChan = SkullOwnerTextureValue("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDhmNTQ0OGI0ZDg4ZTQwYjE0YzgyOGM2ZjFiNTliMzg1NDVkZGE5MzNlNzNkZmYzZjY5NWU2ZmI0Mjc4MSJ9fX0=") + private val mineChan = SkullOwnerTextureValue( + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDhmNTQ0OGI0ZDg4ZTQwYjE0YzgyOGM2ZjFiNTliMzg1NDVkZGE5MzNlNzNkZmYzZjY5NWU2ZmI0Mjc4MSJ9fX0=" + ) val mineHead: ItemStack = new SkullItemStackBuilder(mineChan) .title("まいんちゃん") - .lore( - "", - s"${YELLOW}ギガンティック☆整地鯖${ANNIVERSARY_COUNT}周年記念だよ!" - ) + .lore("", s"${YELLOW}ギガンティック☆整地鯖${ANNIVERSARY_COUNT}周年記念だよ!") .build() - //endregion + // endregion - //region 「気になる木」の苗 + // region 「気になる木」の苗 val strangeSapling: ItemStack = { - val loreList = List( - "", - "植えるとすぐ成長する。", - "先端のブロックがランダムで変化する。", - "極稀にあの「林檎」も...?" - ).map(lore => s"$RESET$GRAY$lore") + val loreList = List("", "植えるとすぐ成長する。", "先端のブロックがランダムで変化する。", "極稀にあの「林檎」も...?") + .map(lore => s"$RESET$GRAY$lore") .asJava val itemMeta = Bukkit.getItemFactory.getItemMeta(SAPLING).tap { meta => @@ -50,17 +45,38 @@ object AnniversaryItemData { val itemStack = new ItemStack(SAPLING, 1) itemStack.setItemMeta(itemMeta) - new NBTItem(itemStack).tap { item => - import item._ - setByte(NBTTagConstants.typeIdTag, 1.toByte) - } + new NBTItem(itemStack) + .tap { item => + import item._ + setByte(NBTTagConstants.typeIdTag, 1.toByte) + } .pipe(_.getItem) } val strangeSaplingBlockSet = Set( - ANVIL, BEACON, BONE_BLOCK, BOOKSHELF, BRICK, CAKE, CAULDRON, COAL_BLOCK, DIAMOND_BLOCK, - DRAGON_EGG, FENCE, FLOWER_POT, GLOWSTONE, GOLD_BLOCK, GRASS, ICE, IRON_BLOCK, MELON_BLOCK, - NETHER_BRICK, QUARTZ_BLOCK, SAND, SPONGE, WORKBENCH + ANVIL, + BEACON, + BONE_BLOCK, + BOOKSHELF, + BRICK, + CAKE, + CAULDRON, + COAL_BLOCK, + DIAMOND_BLOCK, + DRAGON_EGG, + FENCE, + FLOWER_POT, + GLOWSTONE, + GOLD_BLOCK, + GRASS, + ICE, + IRON_BLOCK, + MELON_BLOCK, + NETHER_BRICK, + QUARTZ_BLOCK, + SAND, + SPONGE, + WORKBENCH ) val strangeSaplingSiinaRate = 0.0008 @@ -70,16 +86,13 @@ object AnniversaryItemData { new NBTItem(item).getByte(NBTTagConstants.typeIdTag) == 1 } - //endregion + // endregion - //region 修繕の書 + // region 修繕の書 val mendingBook: ItemStack = { - val loreList = List( - "", - "手に持って右クリックすると", - "オフハンドにあるアイテムの耐久値を全回復する" - ).map(lore => s"$RESET$GRAY$lore") + val loreList = List("", "手に持って右クリックすると", "オフハンドにあるアイテムの耐久値を全回復する") + .map(lore => s"$RESET$GRAY$lore") .asJava val itemMeta = Bukkit.getItemFactory.getItemMeta(WRITTEN_BOOK).tap { meta => @@ -91,17 +104,19 @@ object AnniversaryItemData { val itemStack = new ItemStack(WRITTEN_BOOK, 1) itemStack.setItemMeta(itemMeta) - new NBTItem(itemStack).tap { item => - import item._ - setByte(NBTTagConstants.typeIdTag, 2.toByte) - } + new NBTItem(itemStack) + .tap { item => + import item._ + setByte(NBTTagConstants.typeIdTag, 2.toByte) + } .pipe(_.getItem) } def isMendingBook(item: ItemStack): Boolean = { def isOriginal(meta: ItemMeta) = meta match { - case bookMeta: BookMeta => bookMeta.hasGeneration && bookMeta.getGeneration == Generation.ORIGINAL + case bookMeta: BookMeta => + bookMeta.hasGeneration && bookMeta.getGeneration == Generation.ORIGINAL case _ => false } @@ -110,29 +125,22 @@ object AnniversaryItemData { } && item.hasItemMeta && isOriginal(item.getItemMeta) } - //endregion + // endregion - //region 記念限定シャベル + // region 記念限定シャベル val anniversaryShovel: ItemStack = { - val enchantments = Set( - (Enchantment.DIG_SPEED, 3), - (Enchantment.DURABILITY, 4), - (Enchantment.MENDING, 1) - ) + val enchantments = + Set((Enchantment.DIG_SPEED, 3), (Enchantment.DURABILITY, 4), (Enchantment.MENDING, 1)) val loreList = { - val enchDescription = enchantments - .map { case (ench, lvl) => s"$GRAY${Util.getEnchantName(ench.getName, lvl)}" } - .toList - val lore = List( - "", - "特殊なエンチャントが付与されています", - ).map(lore => s"$YELLOW$lore") + val enchDescription = enchantments.map { + case (ench, lvl) => s"$GRAY${Util.getEnchantName(ench.getName, lvl)}" + }.toList + val lore = List("", "特殊なエンチャントが付与されています").map(lore => s"$YELLOW$lore") enchDescription ::: lore - }.map(lore => s"$RESET$lore") - .asJava + }.map(lore => s"$RESET$lore").asJava val itemMeta = Bukkit.getItemFactory.getItemMeta(DIAMOND_SPADE).tap { meta => import meta._ @@ -145,10 +153,11 @@ object AnniversaryItemData { val itemStack = new ItemStack(DIAMOND_SPADE, 1) itemStack.setItemMeta(itemMeta) - new NBTItem(itemStack).tap { item => - import item._ - setByte(NBTTagConstants.typeIdTag, 3.toByte) - } + new NBTItem(itemStack) + .tap { item => + import item._ + setByte(NBTTagConstants.typeIdTag, 3.toByte) + } .pipe(_.getItem) } @@ -157,7 +166,7 @@ object AnniversaryItemData { new NBTItem(item).getByte(NBTTagConstants.typeIdTag) == 3 } - //endregion + // endregion // SeichiAssistで呼ばれてるだけ def anniversaryPlayerHead(head: SkullMeta): SkullMeta = { @@ -165,8 +174,7 @@ object AnniversaryItemData { "", s"$GREEN${ITALIC}大切なあなたへ感謝を。", s"$YELLOW$UNDERLINE$ITALIC${ANNIVERSARY_COUNT}th Anniversary" - ).map(str => s"$RESET$str") - .asJava + ).map(str => s"$RESET$str").asJava head.setLore(lore) head } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/anniversary/AnniversaryListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/anniversary/AnniversaryListener.scala index e21f628f70..28d39ad492 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/anniversary/AnniversaryListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/anniversary/AnniversaryListener.scala @@ -4,11 +4,18 @@ import cats.effect.IO import com.github.unchama.generic.effect.unsafe.EffectEnvironment import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.SeichiAssist -import com.github.unchama.seichiassist.data.player.PlayerData -import com.github.unchama.seichiassist.subsystems.seasonalevents.anniversary.Anniversary.{ANNIVERSARY_COUNT, blogArticleUrl, isInEvent} +import com.github.unchama.seichiassist.subsystems.seasonalevents.anniversary.Anniversary.{ + ANNIVERSARY_COUNT, + blogArticleUrl, + isInEvent +} import com.github.unchama.seichiassist.subsystems.seasonalevents.anniversary.AnniversaryItemData._ import com.github.unchama.seichiassist.util.StaticGachaPrizeFactory.getMaxRingo -import com.github.unchama.seichiassist.util.Util.{grantItemStacksEffect, isEnemy, removeItemfromPlayerInventory} +import com.github.unchama.seichiassist.util.Util.{ + grantItemStacksEffect, + isEnemy, + removeItemfromPlayerInventory +} import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.player.FocusedSoundEffect import com.github.unchama.targetedeffect.{SequentialEffect, UnfocusedEffect} @@ -25,8 +32,10 @@ import org.bukkit.{Material, Sound, TreeType} import scala.jdk.CollectionConverters._ import scala.util.Random -class AnniversaryListener(implicit effectEnvironment: EffectEnvironment, - ioOnMainThread: OnMinecraftServerThread[IO]) extends Listener { +class AnniversaryListener( + implicit effectEnvironment: EffectEnvironment, + ioOnMainThread: OnMinecraftServerThread[IO] +) extends Listener { @EventHandler def onPlayerJoin(event: PlayerJoinEvent): Unit = { @@ -57,7 +66,10 @@ class AnniversaryListener(implicit effectEnvironment: EffectEnvironment, MessageEffect(s"${BLUE}ギガンティック☆整地鯖${ANNIVERSARY_COUNT}周年の記念品を入手しました。"), FocusedSoundEffect(Sound.BLOCK_ANVIL_PLACE, 1.0f, 1.0f), UnfocusedEffect { - SeichiAssist.databaseGateway.playerDataManipulator.setAnniversary(false, Some(playerUuid)) + SeichiAssist + .databaseGateway + .playerDataManipulator + .setAnniversary(false, Some(playerUuid)) } ), s"${ANNIVERSARY_COUNT}周年記念ヘッドを付与する" @@ -105,10 +117,14 @@ class AnniversaryListener(implicit effectEnvironment: EffectEnvironment, val player = event.getPlayer if (!isAnniversaryShovel(player.getInventory.getItemInMainHand)) return - player.getNearbyEntities(20.0, 20.0, 20.0).asScala.filter(mob => isEnemy(mob.getType)).foreach { - case enemy: LivingEntity => enemy.damage(10.0) - case _ => - } + player + .getNearbyEntities(20.0, 20.0, 20.0) + .asScala + .filter(mob => isEnemy(mob.getType)) + .foreach { + case enemy: LivingEntity => enemy.damage(10.0) + case _ => + } } /** @@ -125,4 +141,4 @@ class AnniversaryListener(implicit effectEnvironment: EffectEnvironment, block.setType(strangeSaplingBlockSet.toVector(random)) } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/api/SeasonalEventsAPI.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/api/SeasonalEventsAPI.scala index 6da563b0a3..0672be2862 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/api/SeasonalEventsAPI.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/api/SeasonalEventsAPI.scala @@ -17,14 +17,15 @@ object ChristmasEventsAPI { import cats.implicits._ - def withF[F[_] : Clock : Functor]: ChristmasEventsAPI[F] = new ChristmasEventsAPI[F] { + def withF[F[_]: Clock: Functor]: ChristmasEventsAPI[F] = new ChristmasEventsAPI[F] { override val isInEvent: F[Boolean] = - JavaTime.fromClock[F] + JavaTime + .fromClock[F] .getLocalDate(ZoneId.of("JST", ZoneId.SHORT_IDS)) .map(Christmas.isInEvent) } - def apply[F[_] : ChristmasEventsAPI]: ChristmasEventsAPI[F] = implicitly + def apply[F[_]: ChristmasEventsAPI]: ChristmasEventsAPI[F] = implicitly } trait SeasonalEventsAPI[F[_]] extends AnyRef { @@ -34,9 +35,10 @@ trait SeasonalEventsAPI[F[_]] extends AnyRef { } object SeasonalEventsAPI { - def withF[F[_] : Clock : Functor]: SeasonalEventsAPI[F] = new SeasonalEventsAPI[F] { - override implicit val christmasEventsAPI: ChristmasEventsAPI[F] = ChristmasEventsAPI.withF[F] + def withF[F[_]: Clock: Functor]: SeasonalEventsAPI[F] = new SeasonalEventsAPI[F] { + override implicit val christmasEventsAPI: ChristmasEventsAPI[F] = + ChristmasEventsAPI.withF[F] } - def apply[F[_] : SeasonalEventsAPI]: SeasonalEventsAPI[F] = implicitly + def apply[F[_]: SeasonalEventsAPI]: SeasonalEventsAPI[F] = implicitly } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/christmas/Christmas.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/christmas/Christmas.scala index c399cbfaff..646d062b78 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/christmas/Christmas.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/christmas/Christmas.scala @@ -1,8 +1,12 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.christmas -import java.time.LocalDate +import com.github.unchama.seichiassist.subsystems.seasonalevents.Util.{ + dateRangeAsSequence, + validateItemDropRate, + validateUrl +} -import com.github.unchama.seichiassist.subsystems.seasonalevents.Util.{dateRangeAsSequence, validateItemDropRate, validateUrl} +import java.time.LocalDate object Christmas { val itemDropRate: Double = validateItemDropRate(0.006) @@ -10,9 +14,12 @@ object Christmas { val EVENT_YEAR: Int = 2020 val START_DATE: LocalDate = LocalDate.of(EVENT_YEAR, 12, 15) val END_DATE: LocalDate = LocalDate.of(EVENT_YEAR, 12, 31) - val blogArticleUrl: String = validateUrl(s"https://www.seichi.network/post/christmas$EVENT_YEAR") + val blogArticleUrl: String = validateUrl( + s"https://www.seichi.network/post/christmas$EVENT_YEAR" + ) - def isInEvent(date: LocalDate): Boolean = dateRangeAsSequence(START_DATE, END_DATE).contains(date) + def isInEvent(date: LocalDate): Boolean = + dateRangeAsSequence(START_DATE, END_DATE).contains(date) // side-effectful def isInEventNow: Boolean = isInEvent(LocalDate.now()) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/christmas/ChristmasItemData.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/christmas/ChristmasItemData.scala index 3af720892b..1b30678665 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/christmas/ChristmasItemData.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/christmas/ChristmasItemData.scala @@ -1,6 +1,6 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.christmas -import com.github.unchama.seichiassist.subsystems.seasonalevents.christmas.Christmas.{END_DATE, EVENT_YEAR} +import com.github.unchama.seichiassist.subsystems.seasonalevents.christmas.Christmas.EVENT_YEAR import com.github.unchama.seichiassist.util.Util import de.tr7zw.itemnbtapi.NBTItem import org.bukkit.ChatColor._ @@ -18,13 +18,9 @@ import scala.util.chaining._ object ChristmasItemData { - private val christmasItemBaseLore = List( - "", - s"$RESET$GRAY${EVENT_YEAR}クリスマスイベント限定品", - "", - ) + private val christmasItemBaseLore = List("", s"$RESET$GRAY${EVENT_YEAR}クリスマスイベント限定品", "") - //region ChristmasCake + // region ChristmasCake private def christmasCakePieceLore(remainingPieces: Int) = List(s"$RESET${GRAY}残り摂食可能回数: $remainingPieces/${christmasCakeDefaultPieces}回") @@ -32,14 +28,10 @@ object ChristmasItemData { val christmasCakeDefaultPieces = 7 def christmasCake(pieces: Int): ItemStack = { - val itemFlags = Set( - ItemFlag.HIDE_ENCHANTS - ) + val itemFlags = Set(ItemFlag.HIDE_ENCHANTS) val loreList = { - val cakeBaseLore: List[String] = List( - "置かずに食べられます", - "食べると不運か幸運がランダムで付与されます" - ).map(str => s"$RESET$YELLOW$str") + val cakeBaseLore: List[String] = + List("置かずに食べられます", "食べると不運か幸運がランダムで付与されます").map(str => s"$RESET$YELLOW$str") christmasItemBaseLore ::: cakeBaseLore ::: christmasCakePieceLore(pieces) }.asJava @@ -55,32 +47,28 @@ object ChristmasItemData { val cake = new ItemStack(Material.CAKE, 1) cake.setItemMeta(itemMeta) - new NBTItem(cake).tap { nbtItem => - import nbtItem._ - setByte(NBTTagConstants.typeIdTag, 1.toByte) - setByte(NBTTagConstants.cakePieceTag, pieces.toByte) - } + new NBTItem(cake) + .tap { nbtItem => + import nbtItem._ + setByte(NBTTagConstants.typeIdTag, 1.toByte) + setByte(NBTTagConstants.cakePieceTag, pieces.toByte) + } .pipe(_.getItem) } def isChristmasCake(itemStack: ItemStack): Boolean = itemStack != null && itemStack.getType == Material.CAKE && { - new NBTItem(itemStack) - .getByte(NBTTagConstants.typeIdTag) == 1 + new NBTItem(itemStack).getByte(NBTTagConstants.typeIdTag) == 1 } - //endregion + // endregion - //region ChristmasTurkey + // region ChristmasTurkey val christmasTurkey: ItemStack = { - val itemFlags = Set( - ItemFlag.HIDE_ENCHANTS - ) + val itemFlags = Set(ItemFlag.HIDE_ENCHANTS) val loreList = { - val lore = List( - s"$RESET${YELLOW}食べると移動速度上昇か低下がランダムで付与されます" - ) + val lore = List(s"$RESET${YELLOW}食べると移動速度上昇か低下がランダムで付与されます") christmasItemBaseLore ::: lore }.asJava @@ -96,79 +84,66 @@ object ChristmasItemData { val turkey = new ItemStack(Material.COOKED_CHICKEN, 1) turkey.setItemMeta(itemMeta) - new NBTItem(turkey).tap { nbtItem => - import nbtItem._ - setByte(NBTTagConstants.typeIdTag, 2.toByte) - } + new NBTItem(turkey) + .tap { nbtItem => + import nbtItem._ + setByte(NBTTagConstants.typeIdTag, 2.toByte) + } .pipe(_.getItem) } def isChristmasTurkey(itemStack: ItemStack): Boolean = itemStack != null && itemStack.getType == Material.COOKED_CHICKEN && { - new NBTItem(itemStack) - .getByte(NBTTagConstants.typeIdTag) == 2 + new NBTItem(itemStack).getByte(NBTTagConstants.typeIdTag) == 2 } - //endregion + // endregion - //region ChristmasPotion + // region ChristmasPotion val christmasPotion: ItemStack = { - val itemFlags = Set( - ItemFlag.HIDE_ENCHANTS, - ItemFlag.HIDE_POTION_EFFECTS - ) + val itemFlags = Set(ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_POTION_EFFECTS) val potionEffects = Set( new PotionEffect(PotionEffectType.INCREASE_DAMAGE, 20 * 30, 0), new PotionEffect(PotionEffectType.REGENERATION, 20 * 20, 0) ) val loreList = { - val lore = List( - s"$RESET${YELLOW}クリスマスを一人で過ごす鯖民たちの涙(血涙)を集めた瓶" - ) + val lore = List(s"$RESET${YELLOW}クリスマスを一人で過ごす鯖民たちの涙(血涙)を集めた瓶") christmasItemBaseLore ::: lore }.asJava - val potionMeta = Bukkit.getItemFactory.getItemMeta(Material.POTION).asInstanceOf[PotionMeta].tap { meta => - import meta._ - setDisplayName(s"$AQUA${ITALIC}みんなの涙") - setColor(fromRGB(215, 0, 58)) - addEnchant(Enchantment.MENDING, 1, true) - setLore(loreList) - itemFlags.foreach(flg => addItemFlags(flg)) - potionEffects.foreach(effect => addCustomEffect(effect, true)) - } + val potionMeta = + Bukkit.getItemFactory.getItemMeta(Material.POTION).asInstanceOf[PotionMeta].tap { meta => + import meta._ + setDisplayName(s"$AQUA${ITALIC}みんなの涙") + setColor(fromRGB(215, 0, 58)) + addEnchant(Enchantment.MENDING, 1, true) + setLore(loreList) + itemFlags.foreach(flg => addItemFlags(flg)) + potionEffects.foreach(effect => addCustomEffect(effect, true)) + } val potion = new ItemStack(Material.POTION, 1) potion.setItemMeta(potionMeta) - new NBTItem(potion) - .tap(_.setByte(NBTTagConstants.typeIdTag, 3.toByte)) - .pipe(_.getItem) + new NBTItem(potion).tap(_.setByte(NBTTagConstants.typeIdTag, 3.toByte)).pipe(_.getItem) } def isChristmasPotion(itemStack: ItemStack): Boolean = itemStack != null && itemStack.getType == Material.POTION && { - new NBTItem(itemStack) - .getByte(NBTTagConstants.typeIdTag) == 3 + new NBTItem(itemStack).getByte(NBTTagConstants.typeIdTag) == 3 } - //endregion + // endregion - //region ChristmasChestPlate + // region ChristmasChestPlate val christmasChestPlate: ItemStack = { - val enchants = Set( - Enchantment.MENDING, - Enchantment.DURABILITY - ) + val enchants = Set(Enchantment.MENDING, Enchantment.DURABILITY) val loreList = { - val lore = List( - "特殊エンチャント:迷彩 I", - "敵から気づかれにくくなります", - "「鮮やかに、キメろ。」" - ).map(str => s"$RESET$WHITE$str") + val lore = + List("特殊エンチャント:迷彩 I", "敵から気づかれにくくなります", "「鮮やかに、キメろ。」").map(str => s"$RESET$WHITE$str") christmasItemBaseLore ::: lore }.asJava @@ -183,18 +158,18 @@ object ChristmasItemData { val chestPlate = new ItemStack(Material.DIAMOND_CHESTPLATE, 1) chestPlate.setItemMeta(itemMeta) - new NBTItem(chestPlate).tap { nbtItem => - import nbtItem._ - setByte(NBTTagConstants.typeIdTag, 4.toByte) - setByte(NBTTagConstants.camouflageEnchLevelTag, 1.toByte) - } + new NBTItem(chestPlate) + .tap { nbtItem => + import nbtItem._ + setByte(NBTTagConstants.typeIdTag, 4.toByte) + setByte(NBTTagConstants.camouflageEnchLevelTag, 1.toByte) + } .pipe(_.getItem) } def isChristmasChestPlate(itemStack: ItemStack): Boolean = itemStack != null && itemStack.getType == Material.DIAMOND_CHESTPLATE && { - new NBTItem(itemStack) - .getByte(NBTTagConstants.typeIdTag) == 4 + new NBTItem(itemStack).getByte(NBTTagConstants.typeIdTag) == 4 } def calculateStandardDistance(enchLevel: Int, enemyType: EntityType): Double = { @@ -211,9 +186,9 @@ object ChristmasItemData { (if (isZombie) 40 else 20) * rate } - //endregion + // endregion - //region THANATOSレプリカ + // region THANATOSレプリカ val christmasPickaxe: ItemStack = { val displayName = Seq( @@ -226,19 +201,13 @@ object ChristmasItemData { "O" -> LIGHT_PURPLE, "S" -> RED, " Replica" -> WHITE - ).map { case (c, color) => s"$color$BOLD$ITALIC$c" } - .mkString - val enchants = Set( - (Enchantment.DIG_SPEED, 3), - (Enchantment.LOOT_BONUS_BLOCKS, 1) - ) + ).map { case (c, color) => s"$color$BOLD$ITALIC$c" }.mkString + val enchants = Set((Enchantment.DIG_SPEED, 3), (Enchantment.LOOT_BONUS_BLOCKS, 1)) val loreList = { - val enchDescription = enchants - .map { case (ench, lvl) => s"$RESET$GRAY${Util.getEnchantName(ench.getName, lvl)}" } - .toList - val lore = List( - s"$RESET${AQUA}耐久無限" - ) + val enchDescription = enchants.map { + case (ench, lvl) => s"$RESET$GRAY${Util.getEnchantName(ench.getName, lvl)}" + }.toList + val lore = List(s"$RESET${AQUA}耐久無限") enchDescription ::: christmasItemBaseLore ::: lore }.asJava @@ -255,16 +224,17 @@ object ChristmasItemData { val pickaxe = new ItemStack(Material.DIAMOND_PICKAXE, 1) pickaxe.setItemMeta(itemMeta) - new NBTItem(pickaxe).tap { nbtItem => - import nbtItem._ - setByte(NBTTagConstants.typeIdTag, 5.toByte) - } + new NBTItem(pickaxe) + .tap { nbtItem => + import nbtItem._ + setByte(NBTTagConstants.typeIdTag, 5.toByte) + } .pipe(_.getItem) } - //endregion + // endregion - //region ChristmasSock + // region ChristmasSock val christmasSock: ItemStack = { val loreList = List( @@ -272,8 +242,7 @@ object ChristmasItemData { s"クリスマスをお祝いして$RED${UNDERLINE}靴下${RESET}をどうぞ!", s"$RED${UNDERLINE}アルカディア、エデン、ヴァルハラサーバー メインワールドの", s"$RED${UNDERLINE}スポーン地点にいる村人に欲しい物を詰めてもらおう!" - ).map(str => s"$RESET$str") - .asJava + ).map(str => s"$RESET$str").asJava val itemMeta = Bukkit.getItemFactory.getItemMeta(Material.INK_SACK).tap { meta => import meta._ @@ -293,12 +262,10 @@ object ChristmasItemData { itemStack } - //endregion + // endregion - val christmasMebiusLore: List[String] = List( - "", - s"$RESET${WHITE}Merry Christmas! あなたに特別なMebiusを" - ) + val christmasMebiusLore: List[String] = + List("", s"$RESET${WHITE}Merry Christmas! あなたに特別なMebiusを") // SeichiAssistで呼ばれてるだけ def christmasPlayerHead(head: SkullMeta): SkullMeta = { @@ -306,8 +273,7 @@ object ChristmasItemData { "", s"$GREEN${ITALIC}大切なあなたへ。", s"$YELLOW$UNDERLINE${ITALIC}Merry Christmas $EVENT_YEAR" - ).map(str => s"$RESET$str") - .asJava + ).map(str => s"$RESET$str").asJava head.setLore(lore) head } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/christmas/ChristmasItemListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/christmas/ChristmasItemListener.scala index 7cedaeafce..1c89536522 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/christmas/ChristmasItemListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/christmas/ChristmasItemListener.scala @@ -7,7 +7,12 @@ import com.github.unchama.seichiassist.subsystems.mana.ManaWriteApi import com.github.unchama.seichiassist.subsystems.seasonalevents.Util import com.github.unchama.seichiassist.subsystems.seasonalevents.christmas.Christmas._ import com.github.unchama.seichiassist.subsystems.seasonalevents.christmas.ChristmasItemData._ -import com.github.unchama.seichiassist.util.Util.{addItem, dropItem, isPlayerInventoryFull, removeItemfromPlayerInventory} +import com.github.unchama.seichiassist.util.Util.{ + addItem, + dropItem, + isPlayerInventoryFull, + removeItemfromPlayerInventory +} import de.tr7zw.itemnbtapi.NBTItem import org.bukkit.ChatColor._ import org.bukkit.entity.EntityType._ @@ -23,11 +28,9 @@ import org.bukkit.{Bukkit, Sound} import java.util.Random -class ChristmasItemListener[ - F[_], - G[_] : SyncEffect -](instance: JavaPlugin) - (implicit manaApi: ManaWriteApi[G, Player]) extends Listener { +class ChristmasItemListener[F[_], G[_]: SyncEffect](instance: JavaPlugin)( + implicit manaApi: ManaWriteApi[G, Player] +) extends Listener { @EventHandler def onPlayerJoin(event: PlayerJoinEvent): Unit = { if (isInEventNow) { @@ -35,9 +38,7 @@ class ChristmasItemListener[ s"$LIGHT_PURPLE${END_DATE}までの期間限定で、クリスマスイベントを開催しています。", "詳しくは下記URLのサイトをご覧ください。", s"$DARK_GREEN$UNDERLINE$blogArticleUrl" - ).foreach( - event.getPlayer.sendMessage(_) - ) + ).foreach(event.getPlayer.sendMessage(_)) } } @@ -47,7 +48,8 @@ class ChristmasItemListener[ if (!isChristmasCake(item)) return if (event.getHand == EquipmentSlot.OFF_HAND) return - if (event.getAction != Action.RIGHT_CLICK_AIR && event.getAction != Action.LEFT_CLICK_BLOCK) return + if (event.getAction != Action.RIGHT_CLICK_AIR && event.getAction != Action.LEFT_CLICK_BLOCK) + return event.setCancelled(true) @@ -85,21 +87,49 @@ class ChristmasItemListener[ // 1分おきに計5回マナを一定量回復する for (i <- 1 to 5) { - Bukkit.getServer.getScheduler.runTaskLater(instance, new Runnable { - override def run(): Unit = { - // マナを15%回復する - manaApi.manaAmount(player).restoreFraction(0.15).runSync[SyncIO].unsafeRunSync() - player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0F, 1.2F) - } - }, (20 * 60 * i).toLong) + Bukkit + .getServer + .getScheduler + .runTaskLater( + instance, + new Runnable { + override def run(): Unit = { + // マナを15%回復する + manaApi.manaAmount(player).restoreFraction(0.15).runSync[SyncIO].unsafeRunSync() + player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0f, 1.2f) + } + }, + (20 * 60 * i).toLong + ) } } @EventHandler def onEntityTarget(event: EntityTargetLivingEntityEvent): Unit = { val enemies = Set( - BLAZE, CREEPER, ELDER_GUARDIAN, ENDERMAN, ENDERMITE, EVOKER, GHAST, GUARDIAN, HUSK, MAGMA_CUBE, PIG_ZOMBIE, - SHULKER, SILVERFISH, SKELETON, SLIME, SPIDER, STRAY, VEX, VINDICATOR, WITCH, WITHER_SKELETON, ZOMBIE, ZOMBIE_VILLAGER + BLAZE, + CREEPER, + ELDER_GUARDIAN, + ENDERMAN, + ENDERMITE, + EVOKER, + GHAST, + GUARDIAN, + HUSK, + MAGMA_CUBE, + PIG_ZOMBIE, + SHULKER, + SILVERFISH, + SKELETON, + SLIME, + SPIDER, + STRAY, + VEX, + VINDICATOR, + WITCH, + WITHER_SKELETON, + ZOMBIE, + ZOMBIE_VILLAGER ) val entity = event.getEntity @@ -118,14 +148,20 @@ class ChristmasItemListener[ val playerLocation = player.getLocation val distance = entityLocation.distance(playerLocation) - val enchantLevel = new NBTItem(chestPlate).getByte(NBTTagConstants.camouflageEnchLevelTag).toInt + val enchantLevel = + new NBTItem(chestPlate).getByte(NBTTagConstants.camouflageEnchLevelTag).toInt // ここの数字に敵からの索敵距離を下げる try { val standard = calculateStandardDistance(enchantLevel, entity.getType) if (distance > standard) event.setCancelled(true) } catch { case err: IllegalArgumentException => - Bukkit.getServer.getLogger.info(s"${player.getName}によって、「迷彩」エンチャントのついたアイテムが使用されましたが、設定されているエンチャントレベルが不正なものでした。") + Bukkit + .getServer + .getLogger + .info( + s"${player.getName}によって、「迷彩」エンチャントのついたアイテムが使用されましたが、設定されているエンチャントレベルが不正なものでした。" + ) err.printStackTrace() } case _ => @@ -159,10 +195,11 @@ class ChristmasItemListener[ if (!isInEventNow) return event.getEntity match { - case entity: LivingEntity if entity.getType == EntityType.STRAY && entity.getKiller != null => + case entity: LivingEntity + if entity.getType == EntityType.STRAY && entity.getKiller != null => Util.randomlyDropItemAt(entity, christmasSock, itemDropRateFromStray) case _ => } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/commands/EventCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/commands/EventCommand.scala index 6bdfc81bec..aac2a58aa1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/commands/EventCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/commands/EventCommand.scala @@ -7,14 +7,11 @@ import com.github.unchama.seichiassist.subsystems.seasonalevents.anniversary.Ann import com.github.unchama.seichiassist.subsystems.seasonalevents.christmas.ChristmasItemData._ import com.github.unchama.seichiassist.subsystems.seasonalevents.halloween.HalloweenItemData._ import com.github.unchama.seichiassist.subsystems.seasonalevents.newyear.NewYearItemData._ -import com.github.unchama.seichiassist.subsystems.seasonalevents.valentine.ValentineItemData.cookieOf import com.github.unchama.seichiassist.util.Util import com.github.unchama.targetedeffect.TargetedEffect._ import org.bukkit.command.TabExecutor import org.bukkit.entity.Player -import java.util.UUID - class EventCommand(implicit ioOnMainThread: OnMinecraftServerThread[IO]) { import com.github.unchama.targetedeffect._ @@ -30,41 +27,26 @@ class EventCommand(implicit ioOnMainThread: OnMinecraftServerThread[IO]) { ) val newYearGrantEffect: TargetedEffect[Player] = - Util.grantItemStacksEffect( - newYearApple, - newYearBag - ) + Util.grantItemStacksEffect(newYearApple, newYearBag) val halloweenGrantEffect: TargetedEffect[Player] = - Util.grantItemStacksEffect( - halloweenPotion, - halloweenHoe - ) + Util.grantItemStacksEffect(halloweenPotion, halloweenHoe) val anniversaryGrantEffect: TargetedEffect[Player] = - Util.grantItemStacksEffect( - mineHead, - strangeSapling, - mendingBook, - anniversaryShovel - ) - - def valentineGrantEffect: TargetedEffect[Player] = - Util.grantItemStacksEffect(cookieOf("kinton", UUID.fromString("85dd5867-db09-4a2f-bae7-8d38d5a9c547"))) + Util.grantItemStacksEffect(mineHead, strangeSapling, mendingBook, anniversaryShovel) val executor: TabExecutor = playerCommandBuilder .execution { context => val effect = context.args.yetToBeParsed match { case "anniversary" :: _ => anniversaryGrantEffect - case "christmas" :: _ => christsmasGrantEffect - case "newyear" :: _ => newYearGrantEffect - case "halloween" :: _ => halloweenGrantEffect - case "valentine" :: _ => valentineGrantEffect - case _ => emptyEffect + case "christmas" :: _ => christsmasGrantEffect + case "newyear" :: _ => newYearGrantEffect + case "halloween" :: _ => halloweenGrantEffect + case _ => emptyEffect } IO.pure(effect) } .build() .asNonBlockingTabExecutor() -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/domain/DateTimeDuration.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/domain/DateTimeDuration.scala index 451e4c4699..ecdbd7061f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/domain/DateTimeDuration.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/domain/DateTimeDuration.scala @@ -2,20 +2,62 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.domain import java.time.{LocalDate, LocalDateTime, LocalTime} +/** + * 2つの[[LocalDateTime]]で表される期間を表す + * + * @param from + * 期間の開始日 + * @param to + * 期間の終了日 + * @see + * [[LocalTime]]を省略するならば、`DateTimeDuration.fromLocalDate`を使用する + * @throws IllegalArgumentException + * `from`が`to`より後である(等しいときはthrowされない) + */ case class DateTimeDuration(from: LocalDateTime, to: LocalDateTime) { require(from.isBefore(to) || from.isEqual(to), "期間の開始日が終了日よりも後に指定されています。") - def isInDuration(base: LocalDateTime): Boolean = { + /** + * 指定した[[LocalDateTime]]が`DateTimeDuration.from`と`DateTimeDuration.to`の期間内にあるかどうか + * + * @param base + * 比較する[[LocalDateTime]] + * @return + * 以下の条件をどちらも満たせば`true` + * - 指定した[[LocalDateTime]]が、`DateTimeDuration.from`より前にあるもしくは等しい + * - 指定した[[LocalDateTime]]が、`DateTimeDuration.from`より後にあるもしくは等しい + */ + def contains(base: LocalDateTime): Boolean = { val isAfterFrom = base.isEqual(from) || base.isAfter(from) val isBeforeTo = base.isEqual(to) || base.isBefore(to) isAfterFrom && isBeforeTo } + + /** + * 指定した[[LocalDateTime]]が`DateTimeDuration.from`より前にあるかどうか + * + * @param base + * 比較する[[LocalDateTime]] + * @return + * 指定した[[LocalDateTime]]が、`DateTimeDuration.from`より前にあるもしくは等しければ`true` + */ + def isEntirelyAfter(base: LocalDateTime): Boolean = base.isBefore(from) || base.isEqual(from) } object DateTimeDuration { private val REBOOT_TIME = LocalTime.of(4, 10) + /** + * [[LocalDate]]から[[DateTimeDuration]]を生成する + * + * @param from + * 期間の開始日 + * @param to + * 期間の終了日 + * @return + * 受け取った`from`と`to`に[[REBOOT_TIME]]の時刻をつけた2つの[[LocalDateTime]]を持つ[[DateTimeDuration]] + */ def fromLocalDate(from: LocalDate, to: LocalDate): DateTimeDuration = DateTimeDuration(LocalDateTime.of(from, REBOOT_TIME), LocalDateTime.of(to, REBOOT_TIME)) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/domain/LastQuitPersistenceRepository.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/domain/LastQuitPersistenceRepository.scala index cd8f2c94be..58c0785cd1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/domain/LastQuitPersistenceRepository.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/domain/LastQuitPersistenceRepository.scala @@ -1,8 +1,7 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.domain import java.time.LocalDateTime -import java.util.UUID trait LastQuitPersistenceRepository[F[_], Key] { def loadPlayerLastQuit(key: Key): F[Option[LocalDateTime]] -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/halloween/Halloween.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/halloween/Halloween.scala index 343fc8a07c..799b689c72 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/halloween/Halloween.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/halloween/Halloween.scala @@ -1,14 +1,19 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.halloween -import java.time.LocalDate +import com.github.unchama.seichiassist.subsystems.seasonalevents.Util.{ + dateRangeAsSequence, + validateUrl +} -import com.github.unchama.seichiassist.subsystems.seasonalevents.Util.{dateRangeAsSequence, validateUrl} +import java.time.LocalDate object Halloween { val EVENT_YEAR: Int = 2021 val START_DATE: LocalDate = LocalDate.of(EVENT_YEAR, 10, 18) val END_DATE: LocalDate = LocalDate.of(EVENT_YEAR, 11, 15) - val blogArticleUrl: String = validateUrl(s"https://www.seichi.network/post/halloween$EVENT_YEAR") + val blogArticleUrl: String = validateUrl( + s"https://www.seichi.network/post/halloween$EVENT_YEAR" + ) def isInEvent: Boolean = dateRangeAsSequence(START_DATE, END_DATE).contains(LocalDate.now()) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/halloween/HalloweenItemData.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/halloween/HalloweenItemData.scala index ab1c0bce46..10ba045782 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/halloween/HalloweenItemData.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/halloween/HalloweenItemData.scala @@ -17,13 +17,10 @@ import scala.util.chaining._ object HalloweenItemData { - //region HalloweenPotion + // region HalloweenPotion val halloweenPotion: ItemStack = { - val itemFlags = Set( - ItemFlag.HIDE_ENCHANTS, - ItemFlag.HIDE_POTION_EFFECTS - ) + val itemFlags = Set(ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_POTION_EFFECTS) val potionEffects = Set( new PotionEffect(PotionEffectType.REGENERATION, 20 * 10, 3), new PotionEffect(PotionEffectType.DAMAGE_RESISTANCE, 20 * 20, 2), @@ -42,33 +39,31 @@ object HalloweenItemData { ).map(str => s"$RESET$GRAY$str") }.asJava - val potionMeta = Bukkit.getItemFactory.getItemMeta(Material.POTION).asInstanceOf[PotionMeta].tap { meta => - import meta._ - setDisplayName(s"$AQUA${ITALIC}うんちゃまの汗") - setColor(fromRGB(1, 93, 178)) - addEnchant(Enchantment.MENDING, 1, true) - setLore(loreList) - itemFlags.foreach(flg => addItemFlags(flg)) - potionEffects.foreach(effect => addCustomEffect(effect, true)) - } + val potionMeta = + Bukkit.getItemFactory.getItemMeta(Material.POTION).asInstanceOf[PotionMeta].tap { meta => + import meta._ + setDisplayName(s"$AQUA${ITALIC}うんちゃまの汗") + setColor(fromRGB(1, 93, 178)) + addEnchant(Enchantment.MENDING, 1, true) + setLore(loreList) + itemFlags.foreach(flg => addItemFlags(flg)) + potionEffects.foreach(effect => addCustomEffect(effect, true)) + } val potion = new ItemStack(Material.POTION, 1) potion.setItemMeta(potionMeta) - new NBTItem(potion) - .tap(_.setByte(NBTTagConstants.typeIdTag, 1.toByte)) - .pipe(_.getItem) + new NBTItem(potion).tap(_.setByte(NBTTagConstants.typeIdTag, 1.toByte)).pipe(_.getItem) } def isHalloweenPotion(itemStack: ItemStack): Boolean = itemStack != null && itemStack.getType == Material.POTION && { - new NBTItem(itemStack) - .getByte(NBTTagConstants.typeIdTag) == 1 + new NBTItem(itemStack).getByte(NBTTagConstants.typeIdTag) == 1 } - //endregion + // endregion - //region HalloweenHoe + // region HalloweenHoe val halloweenHoe: ItemStack = { val displayName = Seq( @@ -80,18 +75,14 @@ object HalloweenItemData { "O" -> DARK_AQUA, "T" -> LIGHT_PURPLE, "L" -> RED - ).map { case (c, color) => s"$color$BOLD$ITALIC$c" } - .mkString - val enchantments = Set( - (Enchantment.DURABILITY, 7), - (Enchantment.DIG_SPEED, 7), - (Enchantment.MENDING, 1) - ) + ).map { case (c, color) => s"$color$BOLD$ITALIC$c" }.mkString + val enchantments = + Set((Enchantment.DURABILITY, 7), (Enchantment.DIG_SPEED, 7), (Enchantment.MENDING, 1)) val loreList = { val year = Calendar.getInstance().get(Calendar.YEAR) - val enchDescription = enchantments - .map { case (ench, lvl) => s"$RESET$GRAY${Util.getEnchantName(ench.getName, lvl)}" } - .toList + val enchDescription = enchantments.map { + case (ench, lvl) => s"$RESET$GRAY${Util.getEnchantName(ench.getName, lvl)}" + }.toList val lore = List( "", s"$GRAY${year}ハロウィンイベント限定品", @@ -115,18 +106,15 @@ object HalloweenItemData { val hoe = new ItemStack(Material.DIAMOND_HOE, 1) hoe.setItemMeta(itemMeta) - new NBTItem(hoe) - .tap(_.setByte(NBTTagConstants.typeIdTag, 2.toByte)) - .pipe(_.getItem) + new NBTItem(hoe).tap(_.setByte(NBTTagConstants.typeIdTag, 2.toByte)).pipe(_.getItem) } def isHalloweenHoe(itemStack: ItemStack): Boolean = itemStack != null && itemStack.getType == Material.DIAMOND_HOE && { - new NBTItem(itemStack) - .getByte(NBTTagConstants.typeIdTag) == 2 + new NBTItem(itemStack).getByte(NBTTagConstants.typeIdTag) == 2 } - //endregion + // endregion private object NBTTagConstants { val typeIdTag = "halloweenItemTypeId" diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/halloween/HalloweenItemListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/halloween/HalloweenItemListener.scala index 84b7005ffd..00c6368525 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/halloween/HalloweenItemListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/halloween/HalloweenItemListener.scala @@ -1,7 +1,14 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.halloween -import com.github.unchama.seichiassist.subsystems.seasonalevents.halloween.Halloween.{blogArticleUrl, END_DATE, isInEvent} -import com.github.unchama.seichiassist.subsystems.seasonalevents.halloween.HalloweenItemData.{isHalloweenHoe, isHalloweenPotion} +import com.github.unchama.seichiassist.subsystems.seasonalevents.halloween.Halloween.{ + END_DATE, + blogArticleUrl, + isInEvent +} +import com.github.unchama.seichiassist.subsystems.seasonalevents.halloween.HalloweenItemData.{ + isHalloweenHoe, + isHalloweenPotion +} import com.github.unchama.util.external.WorldGuardWrapper.isRegionMember import org.bukkit.ChatColor.{DARK_GREEN, LIGHT_PURPLE, UNDERLINE} import org.bukkit.Material @@ -22,9 +29,7 @@ object HalloweenItemListener extends Listener { s"$LIGHT_PURPLE${END_DATE}までの期間限定で、ハロウィンイベントを開催しています。", "詳しくは下記URLのサイトをご覧ください。", s"$DARK_GREEN$UNDERLINE$blogArticleUrl" - ).foreach( - event.getPlayer.sendMessage(_) - ) + ).foreach(event.getPlayer.sendMessage(_)) } } @@ -33,7 +38,9 @@ object HalloweenItemListener extends Listener { if (isHalloweenPotion(event.getItem)) { // 1.12.2では、Saturationのポーションは効果がないので、PotionEffectとして直接Playerに付与する // 10分 - event.getPlayer.addPotionEffect(new PotionEffect(PotionEffectType.SATURATION, 20 * 60 * 10, 0), true) + event + .getPlayer + .addPotionEffect(new PotionEffect(PotionEffectType.SATURATION, 20 * 60 * 10, 0), true) } } @@ -65,6 +72,9 @@ object HalloweenItemListener extends Listener { } private def canBeReplacedWithSoil(player: Player, block: Block) = { - (block.getType == Material.DIRT || block.getType == Material.GRASS) && isRegionMember(player, block.getLocation) + (block.getType == Material.DIRT || block.getType == Material.GRASS) && isRegionMember( + player, + block.getLocation + ) } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/infrastructure/JdbcLastQuitPersistenceRepository.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/infrastructure/JdbcLastQuitPersistenceRepository.scala index e4b54e3542..6b69e28bed 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/infrastructure/JdbcLastQuitPersistenceRepository.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/infrastructure/JdbcLastQuitPersistenceRepository.scala @@ -7,13 +7,17 @@ import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} import java.time.LocalDateTime import java.util.UUID -class JdbcLastQuitPersistenceRepository[F[_]](implicit SyncContext: Sync[F]) extends LastQuitPersistenceRepository[F, UUID] { +class JdbcLastQuitPersistenceRepository[F[_]](implicit SyncContext: Sync[F]) + extends LastQuitPersistenceRepository[F, UUID] { override def loadPlayerLastQuit(key: UUID): F[Option[LocalDateTime]] = { SyncContext.delay { DB.localTx { implicit session => - sql"select lastquit from playerdata where uuid = {uuid}".bindByName(Symbol("uuid") -> key.toString) + sql"select lastquit from playerdata where uuid = {uuid}" + .bindByName(Symbol("uuid") -> key.toString) .map(_.timestampOpt("lastquit").map(_.toLocalDateTime)) - .single().apply().flatten + .single() + .apply() + .flatten } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/limitedlogin/LimitedLoginBonusGifter.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/limitedlogin/LimitedLoginBonusGifter.scala index 2e7ea10538..df1048b1dd 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/limitedlogin/LimitedLoginBonusGifter.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/limitedlogin/LimitedLoginBonusGifter.scala @@ -3,8 +3,14 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.limitedlogin import cats.effect.IO import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.data.GachaSkullData -import com.github.unchama.seichiassist.subsystems.seasonalevents.limitedlogin.LimitedLoginEvent.{START_DATE, isInEvent} -import com.github.unchama.seichiassist.subsystems.seasonalevents.limitedlogin.LoginBonusDay.{Everyday, TotalDay} +import com.github.unchama.seichiassist.subsystems.seasonalevents.limitedlogin.LimitedLoginEvent.{ + START_DATE, + isInEvent +} +import com.github.unchama.seichiassist.subsystems.seasonalevents.limitedlogin.LoginBonusDay.{ + Everyday, + TotalDay +} import com.github.unchama.seichiassist.subsystems.seasonalevents.limitedlogin.LoginBonusItemList.bonusAt import com.github.unchama.seichiassist.util.Util.grantItemStacksEffect import com.github.unchama.seichiassist.{DefaultEffectEnvironment, SeichiAssist} @@ -16,7 +22,8 @@ import org.bukkit.inventory.ItemStack import java.time.LocalDate import java.time.format.DateTimeFormatter -class LimitedLoginBonusGifter(implicit ioOnMainThread: OnMinecraftServerThread[IO]) extends Listener { +class LimitedLoginBonusGifter(implicit ioOnMainThread: OnMinecraftServerThread[IO]) + extends Listener { @EventHandler def onPlayerJoin(event: PlayerJoinEvent): Unit = { if (!isInEvent) return @@ -45,13 +52,13 @@ class LimitedLoginBonusGifter(implicit ioOnMainThread: OnMinecraftServerThread[I } private def giveLoginBonus(day: LoginBonusDay)(implicit player: Player): Unit = { - val loginBonusSet = bonusAt(day) - .getOrElse(throw new NoSuchElementException("存在しないアイテムデータが指定されました。")) + val loginBonusSet = + bonusAt(day).getOrElse(throw new NoSuchElementException("存在しないアイテムデータが指定されました。")) loginBonusSet.foreach { loginBonus => val messageOfDay = day match { case TotalDay(count) => s"${count}日目" - case Everyday => "毎日" + case Everyday => "毎日" } loginBonus.itemId match { @@ -64,14 +71,14 @@ class LimitedLoginBonusGifter(implicit ioOnMainThread: OnMinecraftServerThread[I } } - private def giveItem(itemName: String, amount: Int, item: ItemStack)(implicit player: Player): Unit = { + private def giveItem(itemName: String, amount: Int, item: ItemStack)( + implicit player: Player + ): Unit = { import cats.implicits._ DefaultEffectEnvironment.unsafeRunEffectAsync( s"${itemName}を付与する", - List.fill(amount)( - grantItemStacksEffect(item) - ).sequence.run(player) + List.fill(amount)(grantItemStacksEffect(item)).sequence.run(player) ) } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/limitedlogin/LoginBonusItemId.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/limitedlogin/LoginBonusItemId.scala index bb712db627..55ee0b3327 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/limitedlogin/LoginBonusItemId.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/limitedlogin/LoginBonusItemId.scala @@ -8,4 +8,4 @@ case class LoginBonus(itemId: LoginBonusItemId, amount: Int) case object LoginBonusGachaTicket extends LoginBonusItemId -//endregion \ No newline at end of file +//endregion diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/limitedlogin/LoginBonusItemList.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/limitedlogin/LoginBonusItemList.scala index 62a7d3e52b..41d2065216 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/limitedlogin/LoginBonusItemList.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/limitedlogin/LoginBonusItemList.scala @@ -1,6 +1,9 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.limitedlogin -import com.github.unchama.seichiassist.subsystems.seasonalevents.limitedlogin.LoginBonusDay.{Everyday, TotalDay} +import com.github.unchama.seichiassist.subsystems.seasonalevents.limitedlogin.LoginBonusDay.{ + Everyday, + TotalDay +} object LoginBonusItemList { private val map = Map( @@ -12,6 +15,6 @@ object LoginBonusItemList { def bonusAt(day: LoginBonusDay): Option[Set[LoginBonus]] = day match { case TotalDay(count) => map.get(TotalDay(count)) - case Everyday => Some(dailyItem) + case Everyday => Some(dailyItem) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/newyear/NewYear.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/newyear/NewYear.scala index 55e9d874c3..4db5b69492 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/newyear/NewYear.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/newyear/NewYear.scala @@ -1,19 +1,28 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.newyear -import com.github.unchama.seichiassist.subsystems.seasonalevents.Util.{dateRangeAsSequence, validateItemDropRate, validateUrl} +import com.github.unchama.seichiassist.subsystems.seasonalevents.Util.{ + dateRangeAsSequence, + validateItemDropRate, + validateUrl +} +import com.github.unchama.seichiassist.subsystems.seasonalevents.domain.DateTimeDuration -import java.time.LocalDate +import java.time.{LocalDate, LocalDateTime} object NewYear { // 新年が何年かを西暦で入力しておくと、自動的に他の日付が設定される val EVENT_YEAR: Int = 2020 val START_DATE: LocalDate = LocalDate.of(EVENT_YEAR, 1, 1) val END_DATE: LocalDate = LocalDate.of(EVENT_YEAR, 1, 31) - val DISTRIBUTED_SOBA_DATE: LocalDate = START_DATE.minusDays(1) + // 年越しそばが配布されるのは、大晦日の4:10から元旦の4:10まで + val NEW_YEAR_EVE: DateTimeDuration = + DateTimeDuration.fromLocalDate(START_DATE.minusDays(1), START_DATE) val itemDropRate: Double = validateItemDropRate(0.002) - val blogArticleUrl: String = validateUrl(s"https://www.seichi.network/post/newyear$EVENT_YEAR") + val blogArticleUrl: String = validateUrl( + s"https://www.seichi.network/post/newyear$EVENT_YEAR" + ) - def sobaWillBeDistributed: Boolean = LocalDate.now().isEqual(DISTRIBUTED_SOBA_DATE) + def sobaWillBeDistributed: Boolean = NEW_YEAR_EVE.contains(LocalDateTime.now()) def isInEvent: Boolean = dateRangeAsSequence(START_DATE, END_DATE).contains(LocalDate.now()) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/newyear/NewYearItemData.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/newyear/NewYearItemData.scala index 9033282083..92ff0f8111 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/newyear/NewYearItemData.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/newyear/NewYearItemData.scala @@ -1,7 +1,11 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.newyear import com.github.unchama.itemstackbuilder.{SkullItemStackBuilder, SkullOwnerTextureValue} -import com.github.unchama.seichiassist.subsystems.seasonalevents.newyear.NewYear.{DISTRIBUTED_SOBA_DATE, END_DATE, EVENT_YEAR} +import com.github.unchama.seichiassist.subsystems.seasonalevents.newyear.NewYear.{ + END_DATE, + EVENT_YEAR, + NEW_YEAR_EVE +} import de.tr7zw.itemnbtapi.NBTItem import org.bukkit.ChatColor._ import org.bukkit.enchantments.Enchantment @@ -22,8 +26,7 @@ object NewYearItemData { "", s"${DARK_GREEN}消費期限:$END_DATE", s"${AQUA}マナ回復(10%)$GRAY (期限内)" - ).map(str => s"$RESET$str") - .asJava + ).map(str => s"$RESET$str").asJava val itemMeta = Bukkit.getItemFactory.getItemMeta(Material.GOLDEN_APPLE).tap { meta => import meta._ @@ -36,11 +39,12 @@ object NewYearItemData { val itemStack = new ItemStack(Material.GOLDEN_APPLE, 1) itemStack.setItemMeta(itemMeta) - new NBTItem(itemStack).tap { item => - import item._ - setByte(NBTTagConstants.typeIdTag, 1.toByte) - setObject(NBTTagConstants.expiryDateTag, END_DATE) - } + new NBTItem(itemStack) + .tap { item => + import item._ + setByte(NBTTagConstants.typeIdTag, 1.toByte) + setObject(NBTTagConstants.expiryDateTag, END_DATE) + } .pipe(_.getItem) } @@ -56,8 +60,7 @@ object NewYearItemData { s"新年をお祝いして$RED${UNDERLINE}お年玉袋${RESET}をプレゼント!", s"$RED${UNDERLINE}アルカディア、エデン、ヴァルハラサーバー メインワールドの", s"$RED${UNDERLINE}スポーン地点にいる村人で様々なアイテムに交換可能です。" - ).map(str => s"$RESET$str") - .asJava + ).map(str => s"$RESET$str").asJava val itemMeta = Bukkit.getItemFactory.getItemMeta(Material.PAPER).tap { meta => import meta._ @@ -74,14 +77,13 @@ object NewYearItemData { } // https://minecraft-heads.com/custom-heads/food-drinks/413-bowl-of-noodles - private val soba = SkullOwnerTextureValue("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMjY4MzRiNWIyNTQyNmRlNjM1MzhlYzgyY2E4ZmJlY2ZjYmIzZTY4MmQ4MDYzNjQzZDJlNjdhNzYyMWJkIn19fQ==") + private val soba = SkullOwnerTextureValue( + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMjY4MzRiNWIyNTQyNmRlNjM1MzhlYzgyY2E4ZmJlY2ZjYmIzZTY4MmQ4MDYzNjQzZDJlNjdhNzYyMWJkIn19fQ==" + ) val sobaHead: ItemStack = new SkullItemStackBuilder(soba) - .title(s"年越し蕎麦(${DISTRIBUTED_SOBA_DATE.getYear}年)") - .lore(List( - "", - s"${YELLOW}大晦日記念アイテムだよ!" - )) + .title(s"年越し蕎麦(${NEW_YEAR_EVE.from.getYear}年)") + .lore(List("", s"${YELLOW}大晦日記念アイテムだよ!")) .build() object NBTTagConstants { @@ -90,4 +92,4 @@ object NewYearItemData { val eventYearTag = "newYearEventYear" } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/newyear/NewYearListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/newyear/NewYearListener.scala index 201a2833df..56628f3d31 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/newyear/NewYearListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/newyear/NewYearListener.scala @@ -10,7 +10,13 @@ import com.github.unchama.seichiassist.subsystems.mana.ManaWriteApi import com.github.unchama.seichiassist.subsystems.seasonalevents.domain.LastQuitPersistenceRepository import com.github.unchama.seichiassist.subsystems.seasonalevents.newyear.NewYear._ import com.github.unchama.seichiassist.subsystems.seasonalevents.newyear.NewYearItemData._ -import com.github.unchama.seichiassist.util.Util.{addItem, dropItem, grantItemStacksEffect, isPlayerInventoryFull} +import com.github.unchama.seichiassist.util.Util.{ + addItem, + dropItem, + grantItemStacksEffect, + isPlayerInventoryFull +} +import com.github.unchama.targetedeffect.SequentialEffect import com.github.unchama.targetedeffect.TargetedEffect.emptyEffect import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.player.FocusedSoundEffect @@ -25,13 +31,12 @@ import org.bukkit.event.{EventHandler, EventPriority, Listener} import java.time.LocalDate import java.util.{Random, UUID} -class NewYearListener[ - F[_] : ConcurrentEffect : NonServerThreadContextShift, - G[_] : SyncEffect -](implicit effectEnvironment: EffectEnvironment, +class NewYearListener[F[_]: ConcurrentEffect: NonServerThreadContextShift, G[_]: SyncEffect]( + implicit effectEnvironment: EffectEnvironment, repository: LastQuitPersistenceRepository[F, UUID], manaApi: ManaWriteApi[G, Player], - ioOnMainThread: OnMinecraftServerThread[IO]) extends Listener { + ioOnMainThread: OnMinecraftServerThread[IO] +) extends Listener { import cats.implicits._ @@ -44,9 +49,7 @@ class NewYearListener[ s"$LIGHT_PURPLE${END_DATE}までの期間限定で、新年イベントを開催しています。", "詳しくは下記URLのサイトをご覧ください。", s"$DARK_GREEN$UNDERLINE$blogArticleUrl" - ).foreach( - player.sendMessage - ) + ).foreach(player.sendMessage) } } @@ -59,21 +62,20 @@ class NewYearListener[ val program = for { _ <- NonServerThreadContextShift[F].shift lastQuit <- repository.loadPlayerLastQuit(player.getUniqueId) - _ <- LiftIO[F].liftIO(IO{ - val hasNotJoinedInEventYet = lastQuit match { - case Some(dateTime) => dateTime.isBefore(START_DATE.atStartOfDay()) - case None => true - } + _ <- LiftIO[F].liftIO { + val hasNotJoinedInEventYet = lastQuit.forall(NEW_YEAR_EVE.isEntirelyAfter) val effects = - if (hasNotJoinedInEventYet) List( - grantItemStacksEffect(sobaHead), - MessageEffect(s"${BLUE}大晦日ログインボーナスとして記念品を入手しました。"), - FocusedSoundEffect(Sound.BLOCK_ANVIL_PLACE, 1.0f, 1.0f)) - else List(emptyEffect) - - effects.traverse(_.run(player)) - }) + if (hasNotJoinedInEventYet) + SequentialEffect( + grantItemStacksEffect(sobaHead), + MessageEffect(s"${BLUE}大晦日ログインボーナスとして記念品を入手しました。"), + FocusedSoundEffect(Sound.BLOCK_ANVIL_PLACE, 1.0f, 1.0f) + ) + else emptyEffect + + effects.run(player) + } } yield () effectEnvironment.unsafeRunEffectAsync("大晦日ログインボーナスヘッドを付与するかどうかを判定する", program) @@ -88,11 +90,12 @@ class NewYearListener[ val player = event.getPlayer val today = LocalDate.now() - val expiryDate = new NBTItem(item).getObject(NBTTagConstants.expiryDateTag, classOf[LocalDate]) + val expiryDate = + new NBTItem(item).getObject(NBTTagConstants.expiryDateTag, classOf[LocalDate]) if (today.isBefore(expiryDate) || today.isEqual(expiryDate)) { // マナを10%回復する manaApi.manaAmount(player).restoreFraction(0.1).runSync[SyncIO].unsafeRunSync() - player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0F, 1.2F) + player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0f, 1.2f) } } @@ -117,4 +120,4 @@ class NewYearListener[ player.playSound(player.getLocation, Sound.BLOCK_NOTE_HARP, 3.0f, 1.0f) } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/seizonsiki/Seizonsiki.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/seizonsiki/Seizonsiki.scala index d5e1ffa592..9e74853b8e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/seizonsiki/Seizonsiki.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/seizonsiki/Seizonsiki.scala @@ -1,6 +1,10 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.seizonsiki -import com.github.unchama.seichiassist.subsystems.seasonalevents.Util.{dateRangeAsSequence, validateItemDropRate, validateUrl} +import com.github.unchama.seichiassist.subsystems.seasonalevents.Util.{ + dateRangeAsSequence, + validateItemDropRate, + validateUrl +} import java.time.LocalDate @@ -10,7 +14,9 @@ object Seizonsiki { val END_DATE: LocalDate = LocalDate.of(EVENT_YEAR, 1, 22) val itemDropRate: Double = validateItemDropRate(0.3) // 2022年は新年イベントのブログ記事と同じ記事に記載 - val blogArticleUrl: String = validateUrl(s"https://www.seichi.network/post/newyear$EVENT_YEAR") + val blogArticleUrl: String = validateUrl( + s"https://www.seichi.network/post/newyear$EVENT_YEAR" + ) def isInEvent: Boolean = dateRangeAsSequence(START_DATE, END_DATE).contains(LocalDate.now()) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/seizonsiki/SeizonsikiItemData.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/seizonsiki/SeizonsikiItemData.scala index dde70afdce..d5a72532db 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/seizonsiki/SeizonsikiItemData.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/seizonsiki/SeizonsikiItemData.scala @@ -21,8 +21,7 @@ object SeizonsikiItemData { "", s"${DARK_GREEN}消費期限:$END_DATE", s"${AQUA}マナ回復(10%)$GRAY (期限内)" - ).map(str => s"$RESET$str") - .asJava + ).map(str => s"$RESET$str").asJava val itemMeta = Bukkit.getItemFactory.getItemMeta(Material.GOLDEN_APPLE).tap { meta => import meta._ @@ -33,11 +32,12 @@ object SeizonsikiItemData { val itemStack = new ItemStack(Material.GOLDEN_APPLE, 1) itemStack.setItemMeta(itemMeta) - new NBTItem(itemStack).tap { item => - import item._ - setByte(NBTTagConstants.typeIdTag, 1.toByte) - setObject(NBTTagConstants.expiryDateTag, END_DATE) - } + new NBTItem(itemStack) + .tap { item => + import item._ + setByte(NBTTagConstants.typeIdTag, 1.toByte) + setObject(NBTTagConstants.expiryDateTag, END_DATE) + } .pipe(_.getItem) } @@ -64,4 +64,4 @@ object SeizonsikiItemData { val expiryDateTag = "seizonsikiZongoExpiryDate" } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/seizonsiki/SeizonsikiListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/seizonsiki/SeizonsikiListener.scala index 429aeeb82b..2ec0824f60 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/seizonsiki/SeizonsikiListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/seizonsiki/SeizonsikiListener.scala @@ -6,7 +6,10 @@ import com.github.unchama.seichiassist.subsystems.mana.ManaWriteApi import com.github.unchama.seichiassist.subsystems.seasonalevents.Util.randomlyDropItemAt import com.github.unchama.seichiassist.subsystems.seasonalevents.seizonsiki.Seizonsiki._ import com.github.unchama.seichiassist.subsystems.seasonalevents.seizonsiki.SeizonsikiItemData._ -import com.github.unchama.seichiassist.util.Util.{isEntityKilledByThornsEnchant, sendMessageToEveryoneIgnoringPreference} +import com.github.unchama.seichiassist.util.Util.{ + isEntityKilledByThornsEnchant, + sendMessageToEveryoneIgnoringPreference +} import de.tr7zw.itemnbtapi.NBTItem import org.bukkit.ChatColor.{DARK_GREEN, LIGHT_PURPLE, UNDERLINE} import org.bukkit.Sound @@ -18,10 +21,8 @@ import org.bukkit.event.{EventHandler, Listener} import java.time.LocalDate import java.util.Random -class SeizonsikiListener[ - F[_], - G[_] : SyncEffect -](implicit manaApi: ManaWriteApi[G, Player]) extends Listener { +class SeizonsikiListener[F[_], G[_]: SyncEffect](implicit manaApi: ManaWriteApi[G, Player]) + extends Listener { import cats.effect.implicits._ @@ -33,7 +34,8 @@ class SeizonsikiListener[ if (entity.getType != EntityType.ZOMBIE || killer == null) return killer match { - case _: Player if isEntityKilledByThornsEnchant(entity) => randomlyDropItemAt(entity, seizonsikiZongo, itemDropRate) + case _: Player if isEntityKilledByThornsEnchant(entity) => + randomlyDropItemAt(entity, seizonsikiZongo, itemDropRate) case _ => } } @@ -45,9 +47,7 @@ class SeizonsikiListener[ s"$LIGHT_PURPLE${END_DATE}までの期間限定で、イベント『チャラゾンビたちの成ゾン式!』を開催しています。", "詳しくは下記URLのサイトをご覧ください。", s"$DARK_GREEN$UNDERLINE$blogArticleUrl" - ).foreach( - event.getPlayer.sendMessage(_) - ) + ).foreach(event.getPlayer.sendMessage(_)) } } @@ -62,7 +62,7 @@ class SeizonsikiListener[ if (today.isBefore(exp)) { // マナを10%回復する manaApi.manaAmount(player).restoreFraction(0.1).runSync[SyncIO].unsafeRunSync() - player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0F, 1.2F) + player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0f, 1.2f) } else { // END_DATEと同じ日かその翌日以降なら // 死ぬ @@ -72,4 +72,4 @@ class SeizonsikiListener[ sendMessageToEveryoneIgnoringPreference(messages(new Random().nextInt(messages.size))) } } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/Valentine.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/Valentine.scala index 125d1a184e..ee664cfee1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/Valentine.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/Valentine.scala @@ -1,10 +1,12 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.valentine -import com.github.unchama.seichiassist.subsystems.seasonalevents.Util.{validateItemDropRate, validateUrl} +import com.github.unchama.seichiassist.subsystems.seasonalevents.Util.{ + validateItemDropRate, + validateUrl +} import com.github.unchama.seichiassist.subsystems.seasonalevents.domain.DateTimeDuration import java.time.{LocalDate, LocalDateTime} -import java.util.UUID object Valentine { val EVENT_YEAR: Int = 2022 @@ -12,33 +14,10 @@ object Valentine { private val START_DATE = LocalDate.of(EVENT_YEAR, 2, 17) val END_DATE: LocalDate = LocalDate.of(EVENT_YEAR, 2, 27) val EVENT_DURATION: DateTimeDuration = DateTimeDuration.fromLocalDate(START_DATE, END_DATE) - - /** - * 2022バレンタインイベントにおいて - * - * - 0時で区切ってイベント日時を設定していたため、0時を超えてログインし続けていたプレイヤー - * - 配布処理はログイン時に行われ、最終ログアウト日時を参照しているため、0時を超えた時点でそこで一度ログアウトしていなければ配布済みと判断されてしまう - * - lastquitがnullableなのにそれを考慮しなかったために配布されなかった初見プレイヤー - * - * に正常に配布されなかったので、そのプレイヤーたちのリスト - */ - val cookieUnGivenPlayers: Set[UUID] = Set( - "4328d5fb-e4ed-461f-8349-d7df34910547", "57370d2e-0c1a-4a70-9e23-8e539f5daee2", "36402334-e319-4dc5-806a-2d61fd376351", - "59adc18e-e498-46fb-879f-058522cf1252", "54bcb3eb-a393-41c5-b87b-649c164f2bf7", "6632887b-fa8e-48c8-8510-1fc2b5e2a949", - "1e335e7c-bc2d-4136-a817-8cf5b69ed70c", "dc638e00-943f-42b7-95b7-235c851b6e26", "3431c15c-268c-47d1-ba82-6a691fb76c97", - "09f1067b-a393-43af-a9c8-289b42e77e34", "d19012e3-b6d4-4678-b339-be10a5f3e831", "f5167115-2136-46c2-8f6e-34b1b1cbc06b", - "58fcfaf8-41b1-4a7d-b506-1e322abbe1a7", "122fd5a3-86ab-4e19-af87-80f85a2966b6", "4279e953-ffe3-4971-ac6c-3bd50265d78b", - "91097db5-dc97-46ff-bc55-a92677bc8761", "252aaf75-fe66-42fe-8967-52d666ef2dbf", "41fb305b-b591-47a5-a467-1bb0ca333834", - "85dd5867-db09-4a2f-bae7-8d38d5a9c547", "424d2b92-94a3-4b57-8168-db1b001b8465", "ea6157be-28e2-4765-87c8-22f5a43567c9", - "3423c69b-a1cd-41cf-a896-992f9a6f5935", "6b5a0649-e89a-4087-aff2-f6e32a983582", "6d705d18-296a-47d9-b5cf-1b557c7a35e7", - "3d3c592b-ef28-4052-92b5-b0dcfac2a20c", "d42436db-9c85-4ac1-bef0-ec00ad201bfd", "de34061f-0f25-47f2-ad57-43510ae4060e", - "ce3b6eea-204f-4aaa-b8f7-cd5a72a38ad3", "3dc5d243-2c85-4abb-aada-237511b410b8", "d1d2b094-d0c2-41bd-be61-b6cf38600806", - "57da9e7b-6390-45f5-92e3-2b93596facb2", "c36e3251-e498-4558-ad5c-6099c0cc9707", "7ce8c94e-0dcc-46c9-9913-142b84fb325e", - "ab757137-3b3c-45ce-9dc2-49572833db89", "e55e5441-8d8b-4fb2-9573-d32c8a11cc46" - ).map(UUID.fromString) - val itemDropRate: Double = validateItemDropRate(0.3) - val blogArticleUrl: String = validateUrl(s"https://www.seichi.network/post/valentine$EVENT_YEAR") + val blogArticleUrl: String = validateUrl( + s"https://www.seichi.network/post/valentine$EVENT_YEAR" + ) - def isInEvent: Boolean = EVENT_DURATION.isInDuration(LocalDateTime.now()) + def isInEvent: Boolean = EVENT_DURATION.contains(LocalDateTime.now()) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineCookieEffects.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineCookieEffects.scala index 50405c1094..9f5f4be5a7 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineCookieEffects.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineCookieEffects.scala @@ -5,7 +5,7 @@ sealed trait ValentineCookieEffects extends enumeratum.EnumEntry object ValentineCookieEffects extends enumeratum.Enum[ValentineCookieEffects] { override val values: IndexedSeq[ValentineCookieEffects] = findValues - //region プラス効果 + // region プラス効果 case object Jump extends ValentineCookieEffects @@ -25,11 +25,11 @@ object ValentineCookieEffects extends enumeratum.Enum[ValentineCookieEffects] { case object DamageResistance extends ValentineCookieEffects - //endregion + // endregion - //region マイナス効果 + // region マイナス効果 case object Unluck extends ValentineCookieEffects - //endregion -} \ No newline at end of file + // endregion +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineCookieEffectsHandler.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineCookieEffectsHandler.scala index f32ce3a0f8..151179b5ed 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineCookieEffectsHandler.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineCookieEffectsHandler.scala @@ -10,21 +10,26 @@ object ValentineCookieEffectsHandler { ValentineCookieEffects.values(new Random().nextInt(ValentineCookieEffects.values.size)) def getEffect(effect: ValentineCookieEffects): (String, PotionEffect) = effect match { - case Jump => ("跳躍力上昇", new PotionEffect(PotionEffectType.JUMP, 20 * 60 * 10, 1)) - case Speed => ("移動速度上昇", new PotionEffect(PotionEffectType.SPEED, 20 * 60 * 10, 1)) - case Absorption => ("衝撃吸収", new PotionEffect(PotionEffectType.ABSORPTION, 20 * 60 * 10, 1)) + case Jump => ("跳躍力上昇", new PotionEffect(PotionEffectType.JUMP, 20 * 60 * 10, 1)) + case Speed => ("移動速度上昇", new PotionEffect(PotionEffectType.SPEED, 20 * 60 * 10, 1)) + case Absorption => ("衝撃吸収", new PotionEffect(PotionEffectType.ABSORPTION, 20 * 60 * 10, 1)) case NightVision => ("暗視", new PotionEffect(PotionEffectType.NIGHT_VISION, 20 * 60 * 10, 1)) - case Regeneration => ("再生能力", new PotionEffect(PotionEffectType.REGENERATION, 20 * 60 * 10, 1)) - case FireResistance => ("火炎耐性", new PotionEffect(PotionEffectType.FIRE_RESISTANCE, 20 * 60 * 10, 1)) - case WaterBreathing => ("水中呼吸", new PotionEffect(PotionEffectType.WATER_BREATHING, 20 * 60 * 10, 1)) - case IncreaseDamage => ("攻撃力上昇", new PotionEffect(PotionEffectType.INCREASE_DAMAGE, 20 * 60 * 10, 1)) - case DamageResistance => ("耐性", new PotionEffect(PotionEffectType.DAMAGE_RESISTANCE, 20 * 60 * 10, 1)) + case Regeneration => + ("再生能力", new PotionEffect(PotionEffectType.REGENERATION, 20 * 60 * 10, 1)) + case FireResistance => + ("火炎耐性", new PotionEffect(PotionEffectType.FIRE_RESISTANCE, 20 * 60 * 10, 1)) + case WaterBreathing => + ("水中呼吸", new PotionEffect(PotionEffectType.WATER_BREATHING, 20 * 60 * 10, 1)) + case IncreaseDamage => + ("攻撃力上昇", new PotionEffect(PotionEffectType.INCREASE_DAMAGE, 20 * 60 * 10, 1)) + case DamageResistance => + ("耐性", new PotionEffect(PotionEffectType.DAMAGE_RESISTANCE, 20 * 60 * 10, 1)) case Unluck => ("不運", new PotionEffect(PotionEffectType.UNLUCK, 20 * 60, 1)) } def getMessage(effect: ValentineCookieEffects): String = effect match { case Unluck => "不運IIを感じてしまった…はぁ…むなしいなぁ…" - case _ => s"${getEffect(effect)._1}IIを奪い取った!あぁ、おいしいなぁ!" + case _ => s"${getEffect(effect)._1}IIを奪い取った!あぁ、おいしいなぁ!" } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineItemData.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineItemData.scala index f9e33b3a4d..e81da19a01 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineItemData.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineItemData.scala @@ -1,6 +1,9 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.valentine -import com.github.unchama.seichiassist.subsystems.seasonalevents.valentine.Valentine.{END_DATE, EVENT_YEAR} +import com.github.unchama.seichiassist.subsystems.seasonalevents.valentine.Valentine.{ + END_DATE, + EVENT_YEAR +} import de.tr7zw.itemnbtapi.NBTItem import org.bukkit.ChatColor._ import org.bukkit.inventory.ItemStack @@ -29,14 +32,11 @@ object ValentineItemData { today.isBefore(exp) } - //region DroppedCookie -> 爆発したmobからドロップするやつ + // region DroppedCookie -> 爆発したmobからドロップするやつ val droppedCookie: ItemStack = { val loreList = { - List( - "", - s"$RESET${GRAY}リア充を爆発させて奪い取った。" - ) ++ baseLore + List("", s"$RESET${GRAY}リア充を爆発させて奪い取った。") ++ baseLore }.asJava val itemMeta = Bukkit.getItemFactory.getItemMeta(Material.COOKIE).tap { meta => @@ -48,11 +48,12 @@ object ValentineItemData { val itemStack = new ItemStack(Material.COOKIE, 1) itemStack.setItemMeta(itemMeta) - new NBTItem(itemStack).tap { item => - import item._ - setByte(NBTTagConstants.typeIdTag, 1.toByte) - setObject(NBTTagConstants.expiryDateTag, END_DATE) - } + new NBTItem(itemStack) + .tap { item => + import item._ + setByte(NBTTagConstants.typeIdTag, 1.toByte) + setObject(NBTTagConstants.expiryDateTag, END_DATE) + } .pipe(_.getItem) } @@ -61,15 +62,13 @@ object ValentineItemData { new NBTItem(item).getByte(NBTTagConstants.typeIdTag) == 1 } - //endregion + // endregion - //region GiftedCookie -> 棒メニューでもらえるやつ + // region GiftedCookie -> 棒メニューでもらえるやつ def cookieOf(playerName: String, playerUuid: UUID): ItemStack = { val loreList = { - val header = List( - "", - s"$RESET${GRAY}手作りのチョコチップクッキー。") + val header = List("", s"$RESET${GRAY}手作りのチョコチップクッキー。") val producer = List(s"$RESET${DARK_GREEN}製作者:$playerName") header ++ baseLore ++ producer @@ -84,13 +83,14 @@ object ValentineItemData { val itemStack = new ItemStack(Material.COOKIE, 64) itemStack.setItemMeta(itemMeta) - new NBTItem(itemStack).tap { item => - import item._ - setByte(NBTTagConstants.typeIdTag, 2.toByte) - setObject(NBTTagConstants.expiryDateTag, END_DATE) - setObject(NBTTagConstants.producerUuidTag, playerUuid) - setString(NBTTagConstants.producerNameTag, playerName) - } + new NBTItem(itemStack) + .tap { item => + import item._ + setByte(NBTTagConstants.typeIdTag, 2.toByte) + setObject(NBTTagConstants.expiryDateTag, END_DATE) + setObject(NBTTagConstants.producerUuidTag, playerUuid) + setString(NBTTagConstants.producerNameTag, playerName) + } .pipe(_.getItem) } @@ -118,7 +118,7 @@ object ValentineItemData { s"おい聞いたか!${cookieProducerName}が${playerName}にチョコチップクッキー送ったらしいぞー!" ) - //endregion + // endregion // SeichiAssistで呼ばれてるだけ def valentinePlayerHead(head: SkullMeta): SkullMeta = { @@ -126,8 +126,7 @@ object ValentineItemData { "", s"$GREEN${ITALIC}大切なあなたへ。", s"$YELLOW$UNDERLINE${ITALIC}Happy Valentine $EVENT_YEAR" - ).map(str => s"$RESET$str") - .asJava + ).map(str => s"$RESET$str").asJava head.setLore(lore) head } @@ -139,4 +138,4 @@ object ValentineItemData { val producerUuidTag = "valentineCookieProducerUuid" } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineListener.scala index ad366bbff5..7bff3dffa9 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/valentine/ValentineListener.scala @@ -9,7 +9,10 @@ import com.github.unchama.seichiassist.subsystems.seasonalevents.domain.LastQuit import com.github.unchama.seichiassist.subsystems.seasonalevents.valentine.Valentine._ import com.github.unchama.seichiassist.subsystems.seasonalevents.valentine.ValentineCookieEffectsHandler._ import com.github.unchama.seichiassist.subsystems.seasonalevents.valentine.ValentineItemData._ -import com.github.unchama.seichiassist.util.Util.{grantItemStacksEffect, sendMessageToEveryoneIgnoringPreference} +import com.github.unchama.seichiassist.util.Util.{ + grantItemStacksEffect, + sendMessageToEveryoneIgnoringPreference +} import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.player.FocusedSoundEffect import com.github.unchama.targetedeffect.{SequentialEffect, TargetedEffect} @@ -25,16 +28,14 @@ import org.bukkit.event.{EventHandler, Listener} import org.bukkit.inventory.ItemStack import org.bukkit.potion.{PotionEffect, PotionEffectType} -import java.time.LocalDateTime import java.util.{Random, UUID} import scala.util.chaining._ -class ValentineListener[ - F[_] : ConcurrentEffect : NonServerThreadContextShift -](implicit - effectEnvironment: EffectEnvironment, +class ValentineListener[F[_]: ConcurrentEffect: NonServerThreadContextShift]( + implicit effectEnvironment: EffectEnvironment, repository: LastQuitPersistenceRepository[F, UUID], - ioOnMainThread: OnMinecraftServerThread[IO]) extends Listener { + ioOnMainThread: OnMinecraftServerThread[IO] +) extends Listener { @EventHandler def onEntityExplode(event: EntityExplodeEvent): Unit = { @@ -54,7 +55,8 @@ class ValentineListener[ val damager = event.getDamager if (damager == null) return - if (event.getCause != DamageCause.ENTITY_EXPLOSION || damager.getType != EntityType.CREEPER) return + if (event.getCause != DamageCause.ENTITY_EXPLOSION || damager.getType != EntityType.CREEPER) + return event.getEntity match { case monster: Monster => @@ -73,9 +75,7 @@ class ValentineListener[ s"$LIGHT_PURPLE${END_DATE}までの期間限定で、イベント『<ブラックバレンタイン>リア充 vs 整地民!』を開催しています。", "詳しくは下記URLのサイトをご覧ください。", s"$DARK_GREEN$UNDERLINE$blogArticleUrl" - ).foreach( - event.getPlayer.sendMessage(_) - ) + ).foreach(event.getPlayer.sendMessage(_)) } } @@ -91,20 +91,15 @@ class ValentineListener[ _ <- NonServerThreadContextShift[F].shift lastQuit <- repository.loadPlayerLastQuit(playerUuid) _ <- LiftIO[F].liftIO { - val baseDateTime = - /** - * 2022: 0時を超えてログインし続けていた人と初見さんに対応するための条件分岐 - * 詳細は[[cookieUnGivenPlayers]] - */ - if (cookieUnGivenPlayers.contains(playerUuid)) LocalDateTime.of(2022, 2, 18, 4, 0) - else EVENT_DURATION.from - val hasNotJoinedBeforeYet = lastQuit.forall { quit => quit.isBefore(baseDateTime) || quit.isEqual(baseDateTime) } + val hasNotJoinedBeforeYet = lastQuit.forall(EVENT_DURATION.isEntirelyAfter) val effects = - if (hasNotJoinedBeforeYet) SequentialEffect( - grantItemStacksEffect(cookieOf(player.getName, playerUuid)), - MessageEffect(s"${AQUA}チョコチップクッキーを付与しました。"), - FocusedSoundEffect(Sound.BLOCK_ANVIL_PLACE, 1.0f, 1.0f)) + if (hasNotJoinedBeforeYet) + SequentialEffect( + grantItemStacksEffect(cookieOf(player.getName, playerUuid)), + MessageEffect(s"${AQUA}チョコチップクッキーを付与しました。"), + FocusedSoundEffect(Sound.BLOCK_ANVIL_PLACE, 1.0f, 1.0f) + ) else TargetedEffect.emptyEffect effects.run(player) @@ -128,7 +123,7 @@ class ValentineListener[ import player._ sendMessage(getMessage(effect)) addPotionEffect(getEffect(effect)._2) - playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0F, 1.2F) + playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0f, 1.2f) } } @@ -140,9 +135,12 @@ class ValentineListener[ // 死ぬ player.setHealth(0) - val messages = deathMessages(player.getName, new NBTItem(item).getString(NBTTagConstants.producerNameTag)) + val messages = deathMessages( + player.getName, + new NBTItem(item).getString(NBTTagConstants.producerNameTag) + ) sendMessageToEveryoneIgnoringPreference(messages(new Random().nextInt(messages.size))) } - player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0F, 1.2F) + player.playSound(player.getLocation, Sound.ENTITY_WITCH_DRINK, 1.0f, 1.2f) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/System.scala index 1b3bd6dc47..98c8db5bd5 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/System.scala @@ -7,36 +7,38 @@ import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.commands.legacy.GachaCommand import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountReadAPI import com.github.unchama.seichiassist.subsystems.seichilevelupgift.bukkit.GiftItemInterpreter -import com.github.unchama.seichiassist.subsystems.seichilevelupgift.domain.{Gift, GiftInterpreter} +import com.github.unchama.seichiassist.subsystems.seichilevelupgift.domain.{ + Gift, + GiftInterpreter +} import io.chrisdavenport.log4cats.ErrorLogger import org.bukkit.entity.Player object System { - def backGroundProcess[ - F[_] : OnMinecraftServerThread : ErrorLogger : Async, - G[_] - ](implicit breakCountReadApi: BreakCountReadAPI[F, G, Player]): F[Nothing] = { + def backGroundProcess[F[_]: OnMinecraftServerThread: ErrorLogger: Async, G[_]]( + implicit breakCountReadApi: BreakCountReadAPI[F, G, Player] + ): F[Nothing] = { val interpreter: GiftInterpreter[F, Player] = { val giftItemInterpreter = new GiftItemInterpreter[F] { case item: Gift.Item => giftItemInterpreter(item) - case Gift.AutomaticGachaRun => Kleisli { - player => + case Gift.AutomaticGachaRun => + Kleisli { player => Sync[F].delay { player.sendMessage("レベルアップ記念としてガチャを回しました。") GachaCommand.Gachagive(player, 1, player.getName) } - } + } } } StreamExtra.compileToRestartingStream("[SeichiLevelUpGift]") { - breakCountReadApi - .seichiLevelUpdates - .evalTap { case (player, diff) => interpreter.onLevelDiff(diff).run(player) } + breakCountReadApi.seichiLevelUpdates.evalTap { + case (player, diff) => interpreter.onLevelDiff(diff).run(player) + } } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/bukkit/GiftItemInterpreter.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/bukkit/GiftItemInterpreter.scala index 682d21ed36..4300ab89c2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/bukkit/GiftItemInterpreter.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/bukkit/GiftItemInterpreter.scala @@ -14,14 +14,15 @@ import org.bukkit.entity.Player /** * アイテムギフトの付与を実行するインタプリタ。 */ -class GiftItemInterpreter[F[_] : OnMinecraftServerThread : Sync] extends (Gift.Item => Kleisli[F, Player, Unit]) { +class GiftItemInterpreter[F[_]: OnMinecraftServerThread: Sync] + extends (Gift.Item => Kleisli[F, Player, Unit]) { override def apply(item: Gift.Item): Kleisli[F, Player, Unit] = { val itemStack = item match { - case Item.GachaTicket => GachaSkullData.gachaSkull + case Item.GachaTicket => GachaSkullData.gachaSkull case Item.SuperPickaxe => ItemData.getSuperPickaxe(1) - case Item.GachaApple => ItemData.getGachaApple(1) - case Item.Elsa => ItemData.getElsa(1) + case Item.GachaApple => ItemData.getGachaApple(1) + case Item.Elsa => ItemData.getElsa(1) } SequentialEffect( diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/domain/GiftBundle.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/domain/GiftBundle.scala index d4ddccc238..ccc789d1d1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/domain/GiftBundle.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/domain/GiftBundle.scala @@ -4,14 +4,12 @@ case class GiftBundle(map: Map[Gift, Int]) { def combinePair(gift: Gift, count: Int): GiftBundle = GiftBundle { map.updatedWith(gift) { case Some(value) => Some(value + count) - case None => Some(count) + case None => Some(count) } } def combine(bundle: GiftBundle): GiftBundle = - bundle - .map.toList - .foldLeft(this)((bundle, pair) => bundle.combinePair(pair._1, pair._2)) + bundle.map.toList.foldLeft(this)((bundle, pair) => bundle.combinePair(pair._1, pair._2)) } object GiftBundle { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/domain/GiftBundleTable.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/domain/GiftBundleTable.scala index d90e7fcb68..1f513a75e4 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/domain/GiftBundleTable.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/domain/GiftBundleTable.scala @@ -22,7 +22,8 @@ object GiftBundleTable { case 70 => GiftBundle.ofSinglePair(Gift.AutomaticGachaRun, 25) case 80 => GiftBundle.ofSinglePair(Gift.AutomaticGachaRun, 24) case 90 => GiftBundle.ofSinglePair(Gift.AutomaticGachaRun, 20) - case 100 => GiftBundle.ofSinglePair(Gift.AutomaticGachaRun, 21).combinePair(Gift.Item.Elsa, 1) + case 100 => + GiftBundle.ofSinglePair(Gift.AutomaticGachaRun, 21).combinePair(Gift.Item.Elsa, 1) case _ => GiftBundle.empty } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/domain/GiftInterpreter.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/domain/GiftInterpreter.scala index 3bf7e0922e..b60b769704 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/domain/GiftInterpreter.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/domain/GiftInterpreter.scala @@ -12,14 +12,14 @@ trait GiftInterpreter[F[_], Player] { def onGift(gift: Gift): Kleisli[F, Player, Unit] - final def onBundle(giftBundle: GiftBundle)(implicit F: Applicative[F]): Kleisli[F, Player, Unit] = - giftBundle - .map.toList - .traverse { case (gift, i) => onGift(gift).replicateA(i) } - .as(()) + final def onBundle(giftBundle: GiftBundle)( + implicit F: Applicative[F] + ): Kleisli[F, Player, Unit] = + giftBundle.map.toList.traverse { case (gift, i) => onGift(gift).replicateA(i) }.as(()) - final def onLevelDiff(levelDiff: Diff[SeichiLevel]) - (implicit F: Applicative[F]): Kleisli[F, Player, Unit] = { + final def onLevelDiff( + levelDiff: Diff[SeichiLevel] + )(implicit F: Applicative[F]): Kleisli[F, Player, Unit] = { HasSuccessor[SeichiLevel] .leftOpenRightClosedRange(levelDiff.left, levelDiff.right) .toList diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupmessage/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupmessage/System.scala index 475fa71e7f..2dff77e286 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupmessage/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupmessage/System.scala @@ -9,17 +9,14 @@ import io.chrisdavenport.log4cats.ErrorLogger object System { - def backgroundProcess[ - F[_] : Async : SendMinecraftMessage[*[_], Player] : ErrorLogger, - G[_], - Player - ](implicit breakCountReadAPI: BreakCountReadAPI[F, G, Player]): F[Nothing] = { + def backgroundProcess[F[_]: Async: SendMinecraftMessage[*[_], Player]: ErrorLogger, G[ + _ + ], Player](implicit breakCountReadAPI: BreakCountReadAPI[F, G, Player]): F[Nothing] = { StreamExtra.compileToRestartingStream("[SeichiLevelUpMessage]") { - breakCountReadAPI - .seichiLevelUpdates - .evalMap { case (player, diff) => + breakCountReadAPI.seichiLevelUpdates.evalMap { + case (player, diff) => SendMinecraftMessage[F, Player].list(player, MessageTable.messagesOnDiff(diff)) - } + } } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupmessage/domain/MessageTable.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupmessage/domain/MessageTable.scala index 667639e4c4..28428f6660 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupmessage/domain/MessageTable.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupmessage/domain/MessageTable.scala @@ -39,9 +39,7 @@ object MessageTable { ) def messageOnReaching(seichiLevel: SeichiLevel): Option[String] = - messages - .get(seichiLevel.level) - .map(ChatColor.AQUA + _) + messages.get(seichiLevel.level).map(ChatColor.AQUA + _) def messagesOnDiff(levelDiff: Diff[SeichiLevel]): List[String] = HasSuccessor[SeichiLevel] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/SubHomeAPI.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/SubHomeAPI.scala index ff4547d97e..3a42392fb9 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/SubHomeAPI.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/SubHomeAPI.scala @@ -1,7 +1,12 @@ package com.github.unchama.seichiassist.subsystems.subhome import cats.Functor -import com.github.unchama.seichiassist.subsystems.subhome.domain.{OperationResult, SubHome, SubHomeId, SubHomeLocation} +import com.github.unchama.seichiassist.subsystems.subhome.domain.{ + OperationResult, + SubHome, + SubHomeId, + SubHomeLocation +} import java.util.UUID diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/System.scala index 3c63edfc6c..ee4d3f6a46 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/System.scala @@ -7,7 +7,11 @@ import com.github.unchama.seichiassist.SeichiAssist.Scopes.globalChatInterceptio import com.github.unchama.seichiassist.meta.subsystem.Subsystem import com.github.unchama.seichiassist.subsystems.subhome.bukkit.command.SubHomeCommand import com.github.unchama.seichiassist.subsystems.subhome.domain.OperationResult.RenameResult -import com.github.unchama.seichiassist.subsystems.subhome.domain.{SubHome, SubHomeId, SubHomeLocation} +import com.github.unchama.seichiassist.subsystems.subhome.domain.{ + SubHome, + SubHomeId, + SubHomeLocation +} import com.github.unchama.seichiassist.subsystems.subhome.infrastructure.JdbcSubHomePersistence import org.bukkit.command.TabExecutor @@ -18,17 +22,15 @@ trait System[F[_]] extends Subsystem[F] { } object System { - def wired[ - F[_] - : OnMinecraftServerThread - : ConcurrentEffect - : NonServerThreadContextShift - ]: System[F] = { + def wired[F[_]: OnMinecraftServerThread: ConcurrentEffect: NonServerThreadContextShift] + : System[F] = { val persistence = new JdbcSubHomePersistence[F]() new System[F] { override implicit val api: SubHomeAPI[F] = new SubHomeAPI[F] { - override def upsertLocation(ownerUuid: UUID, id: SubHomeId)(location: SubHomeLocation): F[Unit] = + override def upsertLocation(ownerUuid: UUID, id: SubHomeId)( + location: SubHomeLocation + ): F[Unit] = persistence.upsertLocation(ownerUuid, id)(location) override def rename(ownerUuid: UUID, id: SubHomeId)(name: String): F[RenameResult] = persistence.rename(ownerUuid, id)(name) @@ -38,9 +40,7 @@ object System { persistence.list(ownerUuid) } override val commands: Map[String, TabExecutor] = - Map( - "subhome" -> SubHomeCommand.executor - ) + Map("subhome" -> SubHomeCommand.executor) } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/bukkit/LocationCodec.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/bukkit/LocationCodec.scala index 69fbc82467..7f2b33bb64 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/bukkit/LocationCodec.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/bukkit/LocationCodec.scala @@ -12,7 +12,12 @@ import org.bukkit.{Bukkit, Location} object LocationCodec { def fromBukkitLocation(location: Location): SubHomeLocation = { - SubHomeLocation(location.getWorld.getName, location.getBlockX, location.getBlockY, location.getBlockZ) + SubHomeLocation( + location.getWorld.getName, + location.getBlockX, + location.getBlockY, + location.getBlockZ + ) } def toBukkitLocation(location: SubHomeLocation): Option[Location] = { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/bukkit/command/SubHomeCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/bukkit/command/SubHomeCommand.scala index 18d94fbd01..5df615378f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/bukkit/command/SubHomeCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/bukkit/command/SubHomeCommand.scala @@ -14,7 +14,11 @@ import com.github.unchama.seichiassist.commands.contextual.builder.BuilderTempla import com.github.unchama.seichiassist.subsystems.subhome.bukkit.{LocationCodec, TeleportEffect} import com.github.unchama.seichiassist.subsystems.subhome.domain.OperationResult.RenameResult import com.github.unchama.seichiassist.subsystems.subhome.domain.{SubHome, SubHomeId} -import com.github.unchama.seichiassist.subsystems.subhome.{SubHomeAPI, SubHomeReadAPI, SubHomeWriteAPI} +import com.github.unchama.seichiassist.subsystems.subhome.{ + SubHomeAPI, + SubHomeReadAPI, + SubHomeWriteAPI +} import com.github.unchama.targetedeffect.TargetedEffect import com.github.unchama.targetedeffect.commandsender.MessageEffect import org.bukkit.ChatColor._ @@ -40,97 +44,84 @@ object SubHomeCommand { private val subHomeMax = SeichiAssist.seichiAssistConfig.getSubHomeMax - private val argsAndSenderConfiguredBuilder = playerCommandBuilder - .argumentsParsers( - List( - Parsers.closedRangeInt( - 1, subHomeMax, - failureMessage = MessageEffect(s"サブホームの番号を1~${subHomeMax}の間で入力してください")) - ), - onMissingArguments = printDescriptionExecutor - ) + private val argsAndSenderConfiguredBuilder = playerCommandBuilder.argumentsParsers( + List( + Parsers.closedRangeInt( + 1, + subHomeMax, + failureMessage = MessageEffect(s"サブホームの番号を1~${subHomeMax}の間で入力してください") + ) + ), + onMissingArguments = printDescriptionExecutor + ) private def subHomeNotSetMessage(id: SubHomeId): List[String] = List( s"${YELLOW}指定されたサブホームポイントが設定されていません。" ) - def executor[ - F[_] - : SubHomeAPI - : ConcurrentEffect - : NonServerThreadContextShift - : OnMinecraftServerThread - ](implicit scope: ChatInterceptionScope): TabExecutor = BranchedExecutor( - Map( - "warp" -> warpExecutor, - "set" -> setExecutor, - "name" -> nameExecutor - ), - whenArgInsufficient = Some(printDescriptionExecutor), whenBranchNotFound = Some(printDescriptionExecutor) + def executor[F[ + _ + ]: SubHomeAPI: ConcurrentEffect: NonServerThreadContextShift: OnMinecraftServerThread]( + implicit scope: ChatInterceptionScope + ): TabExecutor = BranchedExecutor( + Map("warp" -> warpExecutor, "set" -> setExecutor, "name" -> nameExecutor), + whenArgInsufficient = Some(printDescriptionExecutor), + whenBranchNotFound = Some(printDescriptionExecutor) ).asNonBlockingTabExecutor() - private def warpExecutor[ - F[_] - : ConcurrentEffect - : NonServerThreadContextShift - : OnMinecraftServerThread - : SubHomeReadAPI - ] = argsAndSenderConfiguredBuilder - .execution { context => - val subHomeId = SubHomeId(context.args.parsed.head.asInstanceOf[Int]) - val player = context.sender - - val eff = for { - _ <- NonServerThreadContextShift[F].shift - subHomeLocation <- SubHomeReadAPI[F].get(player.getUniqueId, subHomeId) - } yield { - subHomeLocation match { - case None => MessageEffect(s"サブホームポイント${subHomeId}が設定されてません") - case Some(SubHome(_, location)) => - LocationCodec.toBukkitLocation(location) match { - case Some(bukkitLocation) => - TeleportEffect.to[F](bukkitLocation).mapK(Effect.toIOK[F]) >> - MessageEffect(s"サブホームポイント${subHomeId}にワープしました") - case None => - MessageEffect(List( - s"${RED}サブホームポイントへのワープに失敗しました", - s"${RED}登録先のワールドが削除された可能性があります" - )) - } + private def warpExecutor[F[ + _ + ]: ConcurrentEffect: NonServerThreadContextShift: OnMinecraftServerThread: SubHomeReadAPI] = + argsAndSenderConfiguredBuilder + .execution { context => + val subHomeId = SubHomeId(context.args.parsed.head.asInstanceOf[Int]) + val player = context.sender + + val eff = for { + _ <- NonServerThreadContextShift[F].shift + subHomeLocation <- SubHomeReadAPI[F].get(player.getUniqueId, subHomeId) + } yield { + subHomeLocation match { + case None => MessageEffect(s"サブホームポイント${subHomeId}が設定されてません") + case Some(SubHome(_, location)) => + LocationCodec.toBukkitLocation(location) match { + case Some(bukkitLocation) => + TeleportEffect.to[F](bukkitLocation).mapK(Effect.toIOK[F]) >> + MessageEffect(s"サブホームポイント${subHomeId}にワープしました") + case None => + MessageEffect( + List(s"${RED}サブホームポイントへのワープに失敗しました", s"${RED}登録先のワールドが削除された可能性があります") + ) + } + } } - } - eff.toIO - } - .build() + eff.toIO + } + .build() - private def setExecutor[ - F[_] - : ConcurrentEffect - : NonServerThreadContextShift - : SubHomeWriteAPI - ] = argsAndSenderConfiguredBuilder - .execution { context => - val subHomeId = SubHomeId(context.args.parsed.head.asInstanceOf[Int]) - val player = context.sender + private def setExecutor[F[ + _ + ]: ConcurrentEffect: NonServerThreadContextShift: SubHomeWriteAPI] = + argsAndSenderConfiguredBuilder + .execution { context => + val subHomeId = SubHomeId(context.args.parsed.head.asInstanceOf[Int]) + val player = context.sender - val subHomeLocation = LocationCodec.fromBukkitLocation(player.getLocation) + val subHomeLocation = LocationCodec.fromBukkitLocation(player.getLocation) - val eff = for { - _ <- NonServerThreadContextShift[F].shift - _ <- SubHomeWriteAPI[F].upsertLocation(player.getUniqueId, subHomeId)(subHomeLocation) - } yield MessageEffect(s"現在位置をサブホームポイント${subHomeId}に設定しました") + val eff = for { + _ <- NonServerThreadContextShift[F].shift + _ <- SubHomeWriteAPI[F].upsertLocation(player.getUniqueId, subHomeId)(subHomeLocation) + } yield MessageEffect(s"現在位置をサブホームポイント${subHomeId}に設定しました") - eff.toIO - } - .build() + eff.toIO + } + .build() - private def nameExecutor[ - F[_] - : ConcurrentEffect - : NonServerThreadContextShift - : SubHomeAPI - ](implicit scope: ChatInterceptionScope) = argsAndSenderConfiguredBuilder + private def nameExecutor[F[_]: ConcurrentEffect: NonServerThreadContextShift: SubHomeAPI]( + implicit scope: ChatInterceptionScope + ) = argsAndSenderConfiguredBuilder .execution { context => val subHomeId = SubHomeId(context.args.parsed.head.asInstanceOf[Int]) @@ -142,14 +133,10 @@ object SubHomeCommand { s"$YELLOW※入力されたチャット内容は他のプレイヤーには見えません" ) - def doneMessage(inputName: String) = List( - s"${GREEN}サブホームポイント${subHomeId}の名前を", - s"$GREEN${inputName}に更新しました" - ) + def doneMessage(inputName: String) = + List(s"${GREEN}サブホームポイント${subHomeId}の名前を", s"$GREEN${inputName}に更新しました") - val cancelledInputMessage = List( - s"${YELLOW}入力がキャンセルされました。" - ) + val cancelledInputMessage = List(s"${YELLOW}入力がキャンセルされました。") for { _ <- Monad[IO].ifM(SubHomeReadAPI[F].configured(uuid, subHomeId).toIO)( @@ -163,11 +150,11 @@ object SubHomeCommand { MessageEffect(subHomeNotSetMessage(subHomeId))(player) } case Right(Overridden) => MessageEffect(cancelledInputMessage)(player) - case Right(_) => IO.unit + case Right(_) => IO.unit }, MessageEffect(subHomeNotSetMessage(subHomeId))(player) ) } yield TargetedEffect.emptyEffect } .build() -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/domain/SubHomePersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/domain/SubHomePersistence.scala index 6fe9150e75..d33668f833 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/domain/SubHomePersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/domain/SubHomePersistence.scala @@ -8,8 +8,8 @@ import java.util.UUID /** * サブホームの永続化された情報。 * - * この情報はサーバー間で共有されることを想定していない。 - * 例えばサーバー(s1, s2)、プレーヤーのUUID(u)があった時、s1でlist(u)をした結果とs2でlist(u)をした結果は一般に異なる。 + * この情報はサーバー間で共有されることを想定していない。 例えばサーバー(s1, + * s2)、プレーヤーのUUID(u)があった時、s1でlist(u)をした結果とs2でlist(u)をした結果は一般に異なる。 * これは、サブホームをサーバー間で共有しないという仕様に基づくものである。 */ trait SubHomePersistence[F[_]] { @@ -39,8 +39,9 @@ trait SubHomePersistence[F[_]] { * * サブホームがすでに存在した場合、古いサブホームの名前を新しいサブホームへと引き継ぐ。 */ - final def upsertLocation(ownerUuid: UUID, id: SubHomeId)(location: SubHomeLocation) - (implicit F: Monad[F]): F[Unit] = + final def upsertLocation(ownerUuid: UUID, id: SubHomeId)( + location: SubHomeLocation + )(implicit F: Monad[F]): F[Unit] = for { old <- get(ownerUuid, id) newSubHome = SubHome(old.flatMap(_.name), location) @@ -52,19 +53,23 @@ trait SubHomePersistence[F[_]] { * * 作用の結果として更新が行われたかどうかを示すBooleanを返す。 */ - final def alter(ownerUuid: UUID, id: SubHomeId)(f: SubHome => SubHome)(implicit F: Monad[F]): F[Boolean] = + final def alter(ownerUuid: UUID, id: SubHomeId)( + f: SubHome => SubHome + )(implicit F: Monad[F]): F[Boolean] = for { current <- get(ownerUuid, id) _ <- current match { case Some(currentSubHome) => upsert(ownerUuid, id)(f(currentSubHome)) - case None => F.unit + case None => F.unit } } yield current.nonEmpty /** * 指定されたサブホームをnon-atomicにリネームする。存在しないサブホームが指定された場合何も行わない。 */ - final def rename(ownerUuid: UUID, id: SubHomeId)(newName: String)(implicit F: Monad[F]): F[OperationResult.RenameResult] = + final def rename(ownerUuid: UUID, id: SubHomeId)( + newName: String + )(implicit F: Monad[F]): F[OperationResult.RenameResult] = alter(ownerUuid, id)(_.copy(name = Some(newName))).map { r => if (r) RenameResult.Done else RenameResult.NotFound } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/infrastructure/JdbcSubHomePersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/infrastructure/JdbcSubHomePersistence.scala index 336d942e42..e43d4118d2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/infrastructure/JdbcSubHomePersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/subhome/infrastructure/JdbcSubHomePersistence.scala @@ -3,12 +3,18 @@ package com.github.unchama.seichiassist.subsystems.subhome.infrastructure import cats.effect.Sync import com.github.unchama.concurrent.NonServerThreadContextShift import com.github.unchama.seichiassist.SeichiAssist -import com.github.unchama.seichiassist.subsystems.subhome.domain.{SubHome, SubHomeId, SubHomeLocation, SubHomePersistence} +import com.github.unchama.seichiassist.subsystems.subhome.domain.{ + SubHome, + SubHomeId, + SubHomeLocation, + SubHomePersistence +} import scalikejdbc._ import java.util.UUID -class JdbcSubHomePersistence[F[_]: Sync: NonServerThreadContextShift] extends SubHomePersistence[F] { +class JdbcSubHomePersistence[F[_]: Sync: NonServerThreadContextShift] + extends SubHomePersistence[F] { private val serverId = SeichiAssist.seichiAssistConfig.getServerNum import cats.implicits._ @@ -21,16 +27,15 @@ class JdbcSubHomePersistence[F[_]: Sync: NonServerThreadContextShift] extends Su // NOTE 2021/05/19: 何故かDB上のIDは1少ない。つまり、ID 1のサブホームはDB上ではid=0である。 sql"""insert into seichiassist.sub_home |(player_uuid, server_id, id, name, location_x, location_y, location_z, world_name) values - | (${ownerUuid.toString}, $serverId, ${id.value - 1}, ${subHome.name.orNull}, $x, $y, $z, $worldName) + | (${ownerUuid.toString}, $serverId, ${id.value - 1}, ${subHome + .name + .orNull}, $x, $y, $z, $worldName) | on duplicate key update | name = ${subHome.name.orNull}, | location_x = $x, | location_y = $y, | location_z = $z, - | world_name = $worldName""" - .stripMargin - .update() - .apply() + | world_name = $worldName""".stripMargin.update().apply() } } diff --git a/src/main/scala/com/github/unchama/seichiassist/task/GiganticBerserkTask.scala b/src/main/scala/com/github/unchama/seichiassist/task/GiganticBerserkTask.scala index dab4b76f82..26eb26457f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/task/GiganticBerserkTask.scala +++ b/src/main/scala/com/github/unchama/seichiassist/task/GiganticBerserkTask.scala @@ -15,43 +15,48 @@ import org.bukkit.entity.Player import scala.util.Random class GiganticBerserkTask { - def PlayerKillEnemy[ - F[_] - : ConcurrentEffect - : NonServerThreadContextShift - : DiscordNotificationAPI - ](p: Player)(implicit manaApi: ManaApi[IO, SyncIO, Player]): Unit = { + def PlayerKillEnemy[F[ + _ + ]: ConcurrentEffect: NonServerThreadContextShift: DiscordNotificationAPI]( + p: Player + )(implicit manaApi: ManaApi[IO, SyncIO, Player]): Unit = { val player = p val uuid = p.getUniqueId val playerdata = SeichiAssist.playermap(uuid) playerdata.GBcd = playerdata.giganticBerserk.cd + 1 - if (playerdata.giganticBerserk.cd >= SeichiAssist.seichiAssistConfig.getGiganticBerserkLimit) { + if ( + playerdata.giganticBerserk.cd >= SeichiAssist.seichiAssistConfig.getGiganticBerserkLimit + ) { if (SeichiAssist.DEBUG) player.sendMessage("上限到達") return } if (playerdata.idleMinute >= 3) return - //確率でマナを回復させる + // 確率でマナを回復させる val d = Math.random if (d < playerdata.giganticBerserk.manaRegenerationProbability) { - if(playerdata.giganticBerserk.reachedLimit()){ + if (playerdata.giganticBerserk.reachedLimit()) { manaApi.manaAmount(p).restoreCompletely.unsafeRunSync() - player.sendMessage(s"${YELLOW}${BOLD}${UNDERLINE}Gigantic${RED}${BOLD}${UNDERLINE}Berserk${WHITE}の効果でマナが完全回復しました") - }else { + player.sendMessage( + s"${YELLOW}${BOLD}${UNDERLINE}Gigantic${RED}${BOLD}${UNDERLINE}Berserk${WHITE}の効果でマナが完全回復しました" + ) + } else { val i = getRecoveryValue(playerdata) manaApi.manaAmount(p).restoreAbsolute(ManaAmount(i)).unsafeRunSync() - player.sendMessage(s"${YELLOW}${BOLD}${UNDERLINE}Gigantic${RED}${BOLD}${UNDERLINE}Berserk${WHITE}の効果でマナが${i}回復しました") + player.sendMessage( + s"${YELLOW}${BOLD}${UNDERLINE}Gigantic${RED}${BOLD}${UNDERLINE}Berserk${WHITE}の効果でマナが${i}回復しました" + ) } player.playSound(player.getLocation, Sound.ENTITY_WITHER_SHOOT, 1, 0.5f) } - //最大レベルの場合終了 + // 最大レベルの場合終了 if (playerdata.giganticBerserk.reachedLimit()) return - //進化待機状態の場合終了 + // 進化待機状態の場合終了 if (playerdata.giganticBerserk.canEvolve) return // stage * level @@ -60,15 +65,21 @@ class GiganticBerserkTask { playerdata.GBexp = playerdata.giganticBerserk.exp + 1 - //レベルアップするかどうか判定 - if (LevelThresholds.giganticBerserkLevelList(n).asInstanceOf[Integer] <= playerdata.giganticBerserk.exp) if (level <= 8) { + // レベルアップするかどうか判定 + if ( + LevelThresholds.giganticBerserkLevelList(n).asInstanceOf[Integer] <= playerdata + .giganticBerserk + .exp + ) if (level <= 8) { playerdata.giganticBerserkLevelUp() - //プレイヤーにメッセージ - player.sendMessage(s"${YELLOW}${BOLD}${UNDERLINE}Gigantic${RED}${BOLD}${UNDERLINE}Berserk${WHITE}のレベルがアップし、確率が上昇しました") + // プレイヤーにメッセージ + player.sendMessage( + s"${YELLOW}${BOLD}${UNDERLINE}Gigantic${RED}${BOLD}${UNDERLINE}Berserk${WHITE}のレベルがアップし、確率が上昇しました" + ) player.playSound(player.getLocation, Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1, 0.8f) - //最大レベルになった時の処理 + // 最大レベルになった時の処理 if (playerdata.giganticBerserk.reachedLimit()) { import cats.effect.implicits._ import cats.implicits._ @@ -90,10 +101,11 @@ class GiganticBerserkTask { program.unsafeRunAsyncAndForget() } - } - else { //レベルが10かつ段階が第2段階の木の剣未満の場合は進化待機状態へ + } else { // レベルが10かつ段階が第2段階の木の剣未満の場合は進化待機状態へ if (playerdata.giganticBerserk.stage <= 4) { - player.sendMessage(s"${GREEN}パッシブスキルメニューより${YELLOW}${BOLD}${UNDERLINE}Gigantic${RED}${BOLD}${UNDERLINE}Berserk${GREEN}スキルが進化可能です。") + player.sendMessage( + s"${GREEN}パッシブスキルメニューより${YELLOW}${BOLD}${UNDERLINE}Gigantic${RED}${BOLD}${UNDERLINE}Berserk${GREEN}スキルが進化可能です。" + ) player.playSound(player.getLocation, Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1, 0.8f) playerdata.isGBStageUp = true } @@ -268,4 +280,4 @@ class GiganticBerserkTask { i += rnd.nextInt(l.toInt + 1) i } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/task/PlayerDataLoading.scala b/src/main/scala/com/github/unchama/seichiassist/task/PlayerDataLoading.scala index 8b2f74fbfe..38e70c8152 100644 --- a/src/main/scala/com/github/unchama/seichiassist/task/PlayerDataLoading.scala +++ b/src/main/scala/com/github/unchama/seichiassist/task/PlayerDataLoading.scala @@ -6,12 +6,21 @@ import com.github.unchama.seichiassist.data.player.settings.BroadcastMutingSetti import com.github.unchama.seichiassist.database.DatabaseConstants import com.github.unchama.seichiassist.minestack.MineStackObj import com.github.unchama.seichiassist.seichiskill.effect.ActiveSkillEffect.NoEffect -import com.github.unchama.seichiassist.seichiskill.effect.{ActiveSkillNormalEffect, ActiveSkillPremiumEffect, UnlockableActiveSkillEffect} -import com.github.unchama.seichiassist.seichiskill.{ActiveSkill, AssaultSkill, SeichiSkill, SeichiSkillUsageMode} +import com.github.unchama.seichiassist.seichiskill.effect.{ + ActiveSkillNormalEffect, + ActiveSkillPremiumEffect, + UnlockableActiveSkillEffect +} +import com.github.unchama.seichiassist.seichiskill.{ + ActiveSkill, + AssaultSkill, + SeichiSkill, + SeichiSkillUsageMode +} import com.github.unchama.seichiassist.{MineStackObjectList, SeichiAssist} import com.github.unchama.util.MillisecondTimer +import org.bukkit.Bukkit import org.bukkit.ChatColor._ -import org.bukkit.{Bukkit, Location} import java.sql.{ResultSet, Statement} import java.text.{ParseException, SimpleDateFormat} @@ -26,12 +35,13 @@ object PlayerDataLoading { /** * プレイヤーデータロードを実施する処理(非同期で実行すること) * - * @deprecated Should be inlined. - * @author unchama + * @deprecated + * Should be inlined. + * @author + * unchama */ @Deprecated() def loadExistingPlayerData(playerUUID: UUID, playerName: String): PlayerData = { - val config = SeichiAssist.seichiAssistConfig val databaseGateway = SeichiAssist.databaseGateway val uuid: UUID = playerUUID @@ -56,10 +66,8 @@ object PlayerDataLoading { + "player_uuid = '" + stringUuid + "'") /** - * TODO:これはここにあるべきではない - * 格納可能なアイテムのリストはプラグインインスタンスの中に動的に持たれるべきで、 - * そのリストをラップするオブジェクトに同期された形でこのオブジェクトがもたれるべきであり、 - * ロードされるたびに再計算されるべきではない + * TODO:これはここにあるべきではない 格納可能なアイテムのリストはプラグインインスタンスの中に動的に持たれるべきで、 + * そのリストをラップするオブジェクトに同期された形でこのオブジェクトがもたれるべきであり、 ロードされるたびに再計算されるべきではない */ val nameObjectMappings: Map[String, MineStackObj] = MineStackObjectList.minestacklist.map(obj => obj.mineStackObjName -> obj).toMap @@ -114,33 +122,42 @@ object PlayerDataLoading { val unlockedSkillEffectQuery = s"select effect_name from $db.${DatabaseConstants.SKILL_EFFECT_TABLENAME} where player_uuid = '$stringUuid'" - stmt.executeQuery(unlockedSkillEffectQuery).recordIteration { resultSet: ResultSet => - val effectName = resultSet.getString("effect_name") - val effect = - ActiveSkillNormalEffect.withNameOption(effectName) - .orElse(ActiveSkillPremiumEffect.withNameOption(effectName)) + stmt + .executeQuery(unlockedSkillEffectQuery) + .recordIteration { resultSet: ResultSet => + val effectName = resultSet.getString("effect_name") + val effect = + ActiveSkillNormalEffect + .withNameOption(effectName) + .orElse(ActiveSkillPremiumEffect.withNameOption(effectName)) + + if (effect.isEmpty) { + Bukkit.getLogger.warning(s"${stringUuid}所有のスキルエフェクト${effectName}は未定義です") + } - if (effect.isEmpty) { - Bukkit.getLogger.warning(s"${stringUuid}所有のスキルエフェクト${effectName}は未定義です") + effect } - - effect - }.flatten.toSet + .flatten + .toSet } def loadSeichiSkillUnlockState(statement: Statement): Set[SeichiSkill] = { val unlockedSkillQuery = s"select skill_name from seichiassist.unlocked_seichi_skill where player_uuid = '$stringUuid'" - statement.executeQuery(unlockedSkillQuery).recordIteration { resultSet: ResultSet => - val skillName = resultSet.getString("skill_name") - val skill = SeichiSkill.withNameOption(skillName) - if (skill.isEmpty) { - Bukkit.getLogger.warning(s"${stringUuid}所有のスキル${skillName}は未定義です") - } + statement + .executeQuery(unlockedSkillQuery) + .recordIteration { resultSet: ResultSet => + val skillName = resultSet.getString("skill_name") + val skill = SeichiSkill.withNameOption(skillName) + if (skill.isEmpty) { + Bukkit.getLogger.warning(s"${stringUuid}所有のスキル${skillName}は未定義です") + } - skill - }.flatten.toSet + skill + } + .flatten + .toSet } // playerDataをDBから得られた値で更新する @@ -156,10 +173,12 @@ object PlayerDataLoading { playerData.settings.shouldDisplayDeathMessages = rs.getBoolean("killlogflag") playerData.settings.shouldDisplayWorldGuardLogs = rs.getBoolean("worldguardlogflag") - playerData.settings.multipleidbreakflag = rs.getBoolean("multipleidbreakflag") + playerData.settings.performMultipleIDBlockBreakWhenOutsideSeichiWorld = + rs.getBoolean("multipleidbreakflag") playerData.settings.pvpflag = rs.getBoolean("pvpflag") - playerData.settings.broadcastMutingSettings = BroadcastMutingSettings.fromBooleanSettings(rs.getBoolean("everymessage"), rs.getBoolean("everysound")) + playerData.settings.broadcastMutingSettings = BroadcastMutingSettings + .fromBooleanSettings(rs.getBoolean("everymessage"), rs.getBoolean("everysound")) playerData.settings.nickname = PlayerNickname( NicknameStyle.marshal(rs.getBoolean("displayTypeLv")), rs.getInt("displayTitle1No"), @@ -177,20 +196,23 @@ object PlayerDataLoading { PlayerSkillEffectState(obtainedEffects, selectedEffect.getOrElse(NoEffect)) } - playerData.skillState.set( - PlayerSkillState.fromUnsafeConfiguration( - obtainedSkills, - SeichiSkillUsageMode.withValue(rs.getInt("serialized_usage_mode")), - SeichiSkill.withNameOption(rs.getString("selected_active_skill")).flatMap { - case a: ActiveSkill => Some(a) - case _ => None - }, - SeichiSkill.withNameOption(rs.getString("selected_assault_skill")).flatMap { - case a: AssaultSkill => Some(a) - case _ => None - } + playerData + .skillState + .set( + PlayerSkillState.fromUnsafeConfiguration( + obtainedSkills, + SeichiSkillUsageMode.withValue(rs.getInt("serialized_usage_mode")), + SeichiSkill.withNameOption(rs.getString("selected_active_skill")).flatMap { + case a: ActiveSkill => Some(a) + case _ => None + }, + SeichiSkill.withNameOption(rs.getString("selected_assault_skill")).flatMap { + case a: AssaultSkill => Some(a) + case _ => None + } + ) ) - ).unsafeRunSync() + .unsafeRunSync() playerData.unclaimedApologyItems = rs.getInt("numofsorryforbug") playerData.regionCount = rs.getInt("rgnum") @@ -205,7 +227,7 @@ object PlayerDataLoading { serializedInventory != null && serializedInventory != "" } - //実績、二つ名の情報 + // 実績、二つ名の情報 playerData.p_vote_forT = rs.getInt("p_vote") playerData.giveachvNo = rs.getInt("giveachvNo") playerData.achievePoint = AchievementPoint( @@ -214,10 +236,10 @@ object PlayerDataLoading { rs.getInt("achvChangenum") ) - //期間限定ログインイベント専用の累計ログイン日数 + // 期間限定ログインイベント専用の累計ログイン日数 playerData.LimitedLoginCount = rs.getInt("LimitedLoginCount") - //連続・通算ログインの情報、およびその更新 + // 連続・通算ログインの情報、およびその更新 val cal = Calendar.getInstance() val sdf = new SimpleDateFormat("yyyy/MM/dd") val lastIn = rs.getString("lastcheckdate") @@ -227,18 +249,22 @@ object PlayerDataLoading { lastIn } val chain = rs.getInt("ChainJoin") - playerData.loginStatus = playerData.loginStatus.copy(consecutiveLoginDays = if (chain == 0) { - 1 - } else { - chain - }) + playerData.loginStatus = playerData + .loginStatus + .copy(consecutiveLoginDays = if (chain == 0) { + 1 + } else { + chain + }) val total = rs.getInt("TotalJoin") - playerData.loginStatus = playerData.loginStatus.copy(totalLoginDay = if (total == 0) { - 1 - } else { - total - }) + playerData.loginStatus = playerData + .loginStatus + .copy(totalLoginDay = if (total == 0) { + 1 + } else { + total + }) try { val TodayDate = sdf.parse(sdf.format(cal.getTime)) @@ -255,8 +281,12 @@ object PlayerDataLoading { else 1 - playerData.loginStatus = - playerData.loginStatus.copy(totalLoginDay = newTotalLoginDay, consecutiveLoginDays = newConsecutiveLoginDays) + playerData.loginStatus = playerData + .loginStatus + .copy( + totalLoginDay = newTotalLoginDay, + consecutiveLoginDays = newConsecutiveLoginDays + ) } } catch { case e: ParseException => e.printStackTrace() @@ -266,12 +296,15 @@ object PlayerDataLoading { playerData.ChainVote = rs.getInt("chainvote") - //実績解除フラグのBitSet型への復元処理 - //初回nullエラー回避のための分岐 + // 実績解除フラグのBitSet型への復元処理 + // 初回nullエラー回避のための分岐 try { - val Titlenums = rs.getString("TitleFlags").split(",").reverse.dropWhile(_.isEmpty).reverse + val Titlenums = + rs.getString("TitleFlags").split(",").reverse.dropWhile(_.isEmpty).reverse - val Titlearray = Titlenums.map { x: String => java.lang.Long.parseUnsignedLong(x, 16) } + val Titlearray = Titlenums.map { x: String => + java.lang.Long.parseUnsignedLong(x, 16) + } val TitleFlags = mutable.BitSet.fromBitMask(Titlearray) playerData.TitleFlags = TitleFlags } catch { @@ -280,7 +313,7 @@ object PlayerDataLoading { playerData.TitleFlags.addOne(1) } - //マナ妖精 + // マナ妖精 playerData.usingVotingFairy = rs.getBoolean("canVotingFairyUse") playerData.VotingFairyRecoveryValue = rs.getInt("VotingFairyRecoveryValue") playerData.hasVotingFairyMana = rs.getInt("hasVotingFairyMana") @@ -289,7 +322,6 @@ object PlayerDataLoading { playerData.setVotingFairyTime(rs.getString("newVotingFairyTime")) playerData.p_apple = rs.getLong("p_apple") - playerData.giganticBerserk = GiganticBerserk( rs.getInt("GBlevel"), rs.getInt("GBexp"), @@ -299,10 +331,10 @@ object PlayerDataLoading { playerData.anniversary = rs.getBoolean("anniversary") } } - //sqlコネクションチェック + // sqlコネクションチェック databaseGateway.ensureConnection() - //同ステートメントだとmysqlの処理がバッティングした時に止まってしまうので別ステートメントを作成する + // 同ステートメントだとmysqlの処理がバッティングした時に止まってしまうので別ステートメントを作成する Using(databaseGateway.con.createStatement()) { newStmt => loadPlayerData(newStmt) updateLoginInfo(newStmt) diff --git a/src/main/scala/com/github/unchama/seichiassist/task/PlayerDataSaveTask.scala b/src/main/scala/com/github/unchama/seichiassist/task/PlayerDataSaveTask.scala index a7897a3e73..9e7dbce272 100644 --- a/src/main/scala/com/github/unchama/seichiassist/task/PlayerDataSaveTask.scala +++ b/src/main/scala/com/github/unchama/seichiassist/task/PlayerDataSaveTask.scala @@ -3,24 +3,28 @@ package com.github.unchama.seichiassist.task import cats.Monad import cats.effect.Sync import com.github.unchama.seichiassist.data.player.{NicknameStyle, PlayerData} -import com.github.unchama.seichiassist.seichiskill.effect.{ActiveSkillEffect, UnlockableActiveSkillEffect} +import com.github.unchama.seichiassist.seichiskill.effect.{ + ActiveSkillEffect, + UnlockableActiveSkillEffect +} import com.github.unchama.seichiassist.{MineStackObjectList, SeichiAssist} import com.github.unchama.util.ActionStatus import org.bukkit.ChatColor._ import org.bukkit.entity.Player import java.sql.{SQLException, Statement} -import scala.util.Using object PlayerDataSaveTask { + /** - * プレイヤーデータをDBに同期的に保存する処理 - * DBにセーブしたい値が増えた/減った場合は更新すること + * プレイヤーデータをDBに同期的に保存する処理 DBにセーブしたい値が増えた/減った場合は更新すること * - * @param playerdata 保存するプレーヤーデータ - * @author unchama + * @param playerdata + * 保存するプレーヤーデータ + * @author + * unchama */ - def savePlayerData[F[_] : Sync](player: Player, playerdata: PlayerData): F[Unit] = { + def savePlayerData[F[_]: Sync](player: Player, playerdata: PlayerData): F[Unit] = { val databaseGateway = SeichiAssist.databaseGateway def updatePlayerMineStack(stmt: Statement): Unit = { @@ -42,19 +46,22 @@ object PlayerDataSaveTask { val playerUuid = playerdata.uuid.toString // 既存データをすべてクリアする - stmt.executeUpdate(s"delete from seichiassist.grid_template where designer_uuid = '$playerUuid'") + stmt.executeUpdate( + s"delete from seichiassist.grid_template where designer_uuid = '$playerUuid'" + ) // 各グリッドテンプレートについてデータを保存する - playerdata.templateMap.toList.map { case (gridTemplateId, gridTemplate) => - val updateCommand = "insert into seichiassist.grid_template set " + - "id = " + gridTemplateId + ", " + - "designer_uuid = '" + playerUuid + "', " + - "ahead_length = " + gridTemplate.getAheadAmount + ", " + - "behind_length = " + gridTemplate.getBehindAmount + ", " + - "right_length = " + gridTemplate.getRightAmount + ", " + - "left_length = " + gridTemplate.getLeftAmount - - stmt.executeUpdate(updateCommand) + playerdata.templateMap.toList.map { + case (gridTemplateId, gridTemplate) => + val updateCommand = "insert into seichiassist.grid_template set " + + "id = " + gridTemplateId + ", " + + "designer_uuid = '" + playerUuid + "', " + + "ahead_length = " + gridTemplate.getAheadAmount + ", " + + "behind_length = " + gridTemplate.getBehindAmount + ", " + + "right_length = " + gridTemplate.getRightAmount + ", " + + "left_length = " + gridTemplate.getLeftAmount + + stmt.executeUpdate(updateCommand) } } @@ -68,7 +75,8 @@ object PlayerDataSaveTask { if (effectsObtained.nonEmpty) { stmt.executeUpdate { - val data = effectsObtained.map(e => s"('$playerUuid', '${e.entryName}')").mkString(",") + val data = + effectsObtained.map(e => s"('$playerUuid', '${e.entryName}')").mkString(",") s"insert into seichiassist.unlocked_active_skill_effect(player_uuid, effect_name) values $data" } @@ -95,7 +103,7 @@ object PlayerDataSaveTask { def updatePlayerDataColumns(stmt: Statement): Unit = { val playerUuid = playerdata.uuid.toString - //実績のフラグ(BitSet)保存用変換処理 + // 実績のフラグ(BitSet)保存用変換処理 val flagString = playerdata.TitleFlags.toBitMask.map(_.toHexString).mkString(",") val skillState = playerdata.skillState.get.unsafeRunSync() @@ -110,11 +118,17 @@ object PlayerDataSaveTask { + ",selected_effect = " + { playerdata.skillEffectState.selection match { case effect: UnlockableActiveSkillEffect => s"'${effect.entryName}'" - case ActiveSkillEffect.NoEffect => "null" + case ActiveSkillEffect.NoEffect => "null" } } - + ",selected_active_skill = " + skillState.activeSkill.map(skill => s"'${skill.entryName}'").getOrElse("null") - + ",selected_assault_skill = " + skillState.assaultSkill.map(skill => s"'${skill.entryName}'").getOrElse("null") + + ",selected_active_skill = " + skillState + .activeSkill + .map(skill => s"'${skill.entryName}'") + .getOrElse("null") + + ",selected_assault_skill = " + skillState + .assaultSkill + .map(skill => s"'${skill.entryName}'") + .getOrElse("null") + ",rgnum = " + playerdata.regionCount + ",playtick = " + playerdata.playTick @@ -122,13 +136,23 @@ object PlayerDataSaveTask { + ",killlogflag = " + playerdata.settings.shouldDisplayDeathMessages + ",worldguardlogflag = " + playerdata.settings.shouldDisplayWorldGuardLogs - + ",multipleidbreakflag = " + playerdata.settings.multipleidbreakflag + + ",multipleidbreakflag = " + playerdata + .settings + .performMultipleIDBlockBreakWhenOutsideSeichiWorld + ",pvpflag = " + playerdata.settings.pvpflag + ",effectpoint = " + playerdata.effectPoint + ",totalexp = " + playerdata.totalexp - + ",everysound = " + playerdata.settings.getBroadcastMutingSettings.unsafeRunSync().shouldMuteSounds - + ",everymessage = " + playerdata.settings.getBroadcastMutingSettings.unsafeRunSync().shouldMuteMessages + + ",everysound = " + playerdata + .settings + .getBroadcastMutingSettings + .unsafeRunSync() + .shouldMuteSounds + + ",everymessage = " + playerdata + .settings + .getBroadcastMutingSettings + .unsafeRunSync() + .shouldMuteMessages + ",displayTypeLv = " + (playerdata.settings.nickname.style == NicknameStyle.Level) + ",displayTitle1No = " + playerdata.settings.nickname.id1 @@ -144,7 +168,7 @@ object PlayerDataSaveTask { + ",TotalJoin = " + playerdata.loginStatus.totalLoginDay + ",LimitedLoginCount = " + playerdata.LimitedLoginCount - //投票 + // 投票 + ",canVotingFairyUse = " + playerdata.usingVotingFairy + ",newVotingFairyTime = '" + playerdata.getVotingFairyStartTimeAsString + "'" + ",VotingFairyRecoveryValue = " + playerdata.VotingFairyRecoveryValue @@ -167,10 +191,10 @@ object PlayerDataSaveTask { def executeUpdate(): ActionStatus = { try { - //sqlコネクションチェック + // sqlコネクションチェック databaseGateway.ensureConnection() - //同ステートメントだとmysqlの処理がバッティングした時に止まってしまうので別ステートメントを作成する + // 同ステートメントだとmysqlの処理がバッティングした時に止まってしまうので別ステートメントを作成する val localStatement = databaseGateway.con.createStatement() updateActiveSkillEffectUnlockState(localStatement) updateSeichiSkillUnlockState(localStatement) @@ -186,25 +210,29 @@ object PlayerDataSaveTask { } } - val commitUpdate: F[ActionStatus] = Sync[F].delay(executeUpdate()) import cats.implicits._ Monad[F].tailRecM(3) { remaining => if (remaining == 0) { - Sync[F].delay { - println(s"$RED${playerdata.name}のプレイヤーデータ保存失敗") - }.as(Right(ActionStatus.Fail)) - } else commitUpdate.flatMap { result => - if (result == ActionStatus.Ok) { - Sync[F].delay { - println(s"$GREEN${player.getName}のプレイヤーデータ保存完了") - }.as(Right(ActionStatus.Ok)) - } else { - Monad[F].pure(Left(remaining - 1)) + Sync[F] + .delay { + println(s"$RED${playerdata.name}のプレイヤーデータ保存失敗") + } + .as(Right(ActionStatus.Fail)) + } else + commitUpdate.flatMap { result => + if (result == ActionStatus.Ok) { + Sync[F] + .delay { + println(s"$GREEN${player.getName}のプレイヤーデータ保存完了") + } + .as(Right(ActionStatus.Ok)) + } else { + Monad[F].pure(Left(remaining - 1)) + } } - } } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/task/VotingFairyTask.scala b/src/main/scala/com/github/unchama/seichiassist/task/VotingFairyTask.scala index c67861cf69..4059cd3b48 100644 --- a/src/main/scala/com/github/unchama/seichiassist/task/VotingFairyTask.scala +++ b/src/main/scala/com/github/unchama/seichiassist/task/VotingFairyTask.scala @@ -10,18 +10,22 @@ import org.bukkit.entity.Player import org.bukkit.{Bukkit, Sound} object VotingFairyTask { - //MinuteTaskRunnableから、妖精召喚中のプレイヤーを対象に毎分実行される + // MinuteTaskRunnableから、妖精召喚中のプレイヤーを対象に毎分実行される def run(p: Player)(implicit manaApi: ManaApi[IO, SyncIO, Player]): Unit = { val playermap = SeichiAssist.playermap val uuid = p.getUniqueId val playerdata = playermap.apply(uuid) - //マナ回復 + // マナ回復 VotingFairyListener.regeneMana(p) - //効果時間中か - if (!Util.isVotingFairyPeriod(playerdata.votingFairyStartTime, playerdata.votingFairyEndTime)) { + // 効果時間中か + if ( + !Util.isVotingFairyPeriod(playerdata.votingFairyStartTime, playerdata.votingFairyEndTime) + ) { speak(p, "あっ、もうこんな時間だ!", b = false) speak(p, s"じゃーねー!${p.getName}", b = true) - p.sendMessage(ChatColor.RESET + "" + ChatColor.YELLOW + "" + ChatColor.BOLD + "妖精はどこかへ行ってしまった") + p.sendMessage( + ChatColor.RESET + "" + ChatColor.YELLOW + "" + ChatColor.BOLD + "妖精はどこかへ行ってしまった" + ) playerdata.usingVotingFairy_$eq(false) } } @@ -31,21 +35,27 @@ object VotingFairyTask { p.sendMessage(s"${ChatColor.AQUA}${ChatColor.BOLD}<マナ妖精>${ChatColor.RESET}$msg") } - //妖精効果音 + // 妖精効果音 private def playSe(p: Player): Unit = { p.playSound(p.getLocation, Sound.BLOCK_NOTE_PLING, 2.0f, 1.0f) - Bukkit.getServer.getScheduler.runTaskLater( - SeichiAssist.instance, - (() => { - p.playSound(p.getLocation, Sound.BLOCK_NOTE_PLING, 2.0f, 1.5f) - Bukkit.getServer.getScheduler.runTaskLater( - SeichiAssist.instance, - (() => p.playSound(p.getLocation, Sound.BLOCK_NOTE_PLING, 2.0f, 2.0f)): Runnable, - 2 - ) - }): Runnable, - 2 - ) + Bukkit + .getServer + .getScheduler + .runTaskLater( + SeichiAssist.instance, + (() => { + p.playSound(p.getLocation, Sound.BLOCK_NOTE_PLING, 2.0f, 1.5f) + Bukkit + .getServer + .getScheduler + .runTaskLater( + SeichiAssist.instance, + (() => p.playSound(p.getLocation, Sound.BLOCK_NOTE_PLING, 2.0f, 2.0f)): Runnable, + 2 + ) + }): Runnable, + 2 + ) } def dispToggleVFTime(toggle: Int): String = { @@ -55,4 +65,4 @@ object VotingFairyTask { else if (toggle == 4) "2時間" else "エラー" } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/task/global/PlayerDataBackupRoutine.scala b/src/main/scala/com/github/unchama/seichiassist/task/global/PlayerDataBackupRoutine.scala index f4fa5cab42..61b1a7a762 100644 --- a/src/main/scala/com/github/unchama/seichiassist/task/global/PlayerDataBackupRoutine.scala +++ b/src/main/scala/com/github/unchama/seichiassist/task/global/PlayerDataBackupRoutine.scala @@ -35,7 +35,8 @@ object PlayerDataBackupRoutine { Bukkit.getOnlinePlayers.asScala.toList } _ <- players.traverse { player => - PlayerDataSaveTask.savePlayerData[IO](player, SeichiAssist.playermap(player.getUniqueId)) + PlayerDataSaveTask + .savePlayerData[IO](player, SeichiAssist.playermap(player.getUniqueId)) } _ <- IO { Util.sendMessageToEveryoneIgnoringPreference(s"${AQUA}プレイヤーデータセーブ完了") @@ -45,7 +46,7 @@ object PlayerDataBackupRoutine { } val updateRankingData = IO { - //ランキングリストを最新情報に更新する + // ランキングリストを最新情報に更新する if (!SeichiAssist.databaseGateway.playerDataManipulator.successRankingUpdate()) { SeichiAssist.instance.getLogger.info("ランキングデータの作成に失敗しました") } @@ -64,4 +65,4 @@ object PlayerDataBackupRoutine { RepeatingRoutine.permanentRoutine(getRepeatInterval, routineAction) } -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/seichiassist/task/global/PlayerDataRecalculationRoutine.scala b/src/main/scala/com/github/unchama/seichiassist/task/global/PlayerDataRecalculationRoutine.scala index 1883ba81a9..5d7ac38c1e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/task/global/PlayerDataRecalculationRoutine.scala +++ b/src/main/scala/com/github/unchama/seichiassist/task/global/PlayerDataRecalculationRoutine.scala @@ -14,10 +14,11 @@ import scala.concurrent.duration.FiniteDuration object PlayerDataRecalculationRoutine { - def apply() - (implicit onMainThread: OnMinecraftServerThread[IO], - context: RepeatingTaskContext, - manaApi: ManaApi[IO, SyncIO, Player]): IO[Nothing] = { + def apply()( + implicit onMainThread: OnMinecraftServerThread[IO], + context: RepeatingTaskContext, + manaApi: ManaApi[IO, SyncIO, Player] + ): IO[Nothing] = { val getRepeatInterval: IO[FiniteDuration] = IO { import scala.concurrent.duration._ @@ -27,14 +28,14 @@ object PlayerDataRecalculationRoutine { val routineOnMainThread = SyncIO { import scala.jdk.CollectionConverters._ - //オンラインプレイヤーの人数を取得 + // オンラインプレイヤーの人数を取得 val onlinePlayers = Bukkit.getServer.getOnlinePlayers.asScala - //プレイヤーマップに記録されているすべてのplayerdataについての処理 + // プレイヤーマップに記録されているすべてのplayerdataについての処理 for (player <- onlinePlayers) { val playerData = SeichiAssist.playermap(player.getUniqueId) - //放置判定 + // 放置判定 if (playerData.loc.contains(player.getLocation)) { // idletime加算 playerData.idleMinute = playerData.idleMinute + 1 @@ -48,7 +49,7 @@ object PlayerDataRecalculationRoutine { // 表示名とマナをレベルと同期する playerData.synchronizeDisplayNameToLevelState() - //総プレイ時間更新 + // 総プレイ時間更新 playerData.updatePlayTick() import SeichiAchievement._ @@ -59,27 +60,29 @@ object PlayerDataRecalculationRoutine { */ autoUnlockedAchievements .filterNot(achievement => playerData.TitleFlags.contains(achievement.id)) - .map { achievement => achievement.asUnlockable.shouldUnlockFor(player).map((achievement.id, _)) } + .map { achievement => + achievement.asUnlockable.shouldUnlockFor(player).map((achievement.id, _)) + } .toList .sequence .map(_.flatMap { case (achievementId, true) => Some(achievementId) - case _ => None - }) - .flatMap(unlockTargets => IO { - playerData.TitleFlags.addAll(unlockTargets) - unlockTargets - .map("実績No" + _ + "が解除されました!おめでとうございます!") - .foreach(player.sendMessage) + case _ => None }) + .flatMap(unlockTargets => + IO { + playerData.TitleFlags.addAll(unlockTargets) + unlockTargets.map("実績No" + _ + "が解除されました!おめでとうございます!").foreach(player.sendMessage) + } + ) .unsafeRunSync() - //投票妖精関連 + // 投票妖精関連 if (playerData.usingVotingFairy) { VotingFairyTask.run(player) } - //GiganticBerserk + // GiganticBerserk playerData.GBcd = 0 } diff --git a/src/main/scala/com/github/unchama/seichiassist/text/WarningsGenerator.scala b/src/main/scala/com/github/unchama/seichiassist/text/WarningsGenerator.scala index 3bdb323ea0..6c80bd2e1c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/text/WarningsGenerator.scala +++ b/src/main/scala/com/github/unchama/seichiassist/text/WarningsGenerator.scala @@ -7,9 +7,11 @@ import org.bukkit.entity.Player /** * Created by karayuu on 2019/04/30 * - * @param player 警告を表示する対象 + * @param player + * 警告を表示する対象 */ class WarningsGenerator(player: Player) { + /** * 整地ワールド以外では整地量・ガチャ券が増加しないという警告. */ @@ -17,8 +19,5 @@ class WarningsGenerator(player: Player) { if (ManagedWorld.fromBukkitWorld(player.getWorld).exists(_.isSeichi)) Nil else - List( - s"${RED}整地ワールド以外では", - s"${RED}整地量とガチャ券は増えません" - ) + List(s"${RED}整地ワールド以外では", s"${RED}整地量とガチャ券は増えません") } diff --git a/src/main/scala/com/github/unchama/seichiassist/util/BreakUtil.scala b/src/main/scala/com/github/unchama/seichiassist/util/BreakUtil.scala index 8b9492d708..6f6e875135 100644 --- a/src/main/scala/com/github/unchama/seichiassist/util/BreakUtil.scala +++ b/src/main/scala/com/github/unchama/seichiassist/util/BreakUtil.scala @@ -1,18 +1,25 @@ package com.github.unchama.seichiassist.util +import cats.Functor import cats.effect.{IO, SyncIO} +import cats.implicits._ import com.github.unchama.generic.effect.unsafe.EffectEnvironment import com.github.unchama.seichiassist.MaterialSets.{BlockBreakableBySkill, BreakTool} import com.github.unchama.seichiassist._ import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts import com.github.unchama.seichiassist.seichiskill.ActiveSkillRange._ -import com.github.unchama.seichiassist.seichiskill.SeichiSkill.{AssaultArmor, DualBreak, TrialBreak} +import com.github.unchama.seichiassist.seichiskill.SeichiSkill.{ + AssaultArmor, + DualBreak, + TrialBreak +} import com.github.unchama.seichiassist.seichiskill.SeichiSkillUsageMode.{Active, Disabled} import com.github.unchama.seichiassist.subsystems.breakcount.domain.CardinalDirection import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.SeichiExpAmount import com.github.unchama.targetedeffect.player.ActionBarMessageEffect import com.github.unchama.util.bukkit.ItemStackUtil import com.github.unchama.util.external.ExternalPlugins +import io.chrisdavenport.cats.effect.time.JavaTime import org.bukkit.ChatColor._ import org.bukkit._ import org.bukkit.block.Block @@ -21,34 +28,44 @@ import org.bukkit.entity.{Entity, EntityType, Player} import org.bukkit.inventory.ItemStack import org.bukkit.material.Dye +import java.time.ZoneId import java.util.Random import java.util.stream.IntStream - object BreakUtil { import ManagedWorld._ def unsafeGetLockedBlocks(): Set[Block] = - SeichiAssist.instance - .lockedBlockChunkScope.trackedHandlers.unsafeRunSync() - .flatten.map(x => x: Block) + SeichiAssist + .instance + .lockedBlockChunkScope + .trackedHandlers + .unsafeRunSync() + .flatten + .map(x => x: Block) /** * 他のプラグインの影響があってもブロックを破壊できるのかを判定する。 * * `lockedBlocks`は[[unsafeGetLockedBlocks()]]の結果が利用されるべきだが、 - * 複数ブロックのキャッシュのためにこれを事前にキャッシュして渡したほうが速い。 - * (引数を省略した場合呼び出しごとに再計算される) + * 複数ブロックのキャッシュのためにこれを事前にキャッシュして渡したほうが速い。 (引数を省略した場合呼び出しごとに再計算される) * - * @param player 破壊者 - * @param checkTarget 破壊対象のブロック - * @param lockedBlocks グローバルにロックされているブロックの集合 + * @param player + * 破壊者 + * @param checkTarget + * 破壊対象のブロック + * @param lockedBlocks + * グローバルにロックされているブロックの集合 */ - def canBreak(player: Player, checkTarget: Block, lockedBlocks: Set[Block] = unsafeGetLockedBlocks()): Boolean = { + def canBreak( + player: Player, + checkTarget: Block, + lockedBlocks: Set[Block] = unsafeGetLockedBlocks() + ): Boolean = { val playerData = SeichiAssist.playermap(player.getUniqueId) - //壊されるブロックがワールドガード範囲だった場合処理を終了 + // 壊されるブロックがワールドガード範囲だった場合処理を終了 if (!ExternalPlugins.getWorldGuard.canBuild(player, checkTarget.getLocation)) { if (playerData.settings.shouldDisplayWorldGuardLogs) { player.sendMessage(s"${RED}ワールドガードで保護されています。") @@ -61,7 +78,7 @@ object BreakUtil { if (wrapper == null) { Bukkit.getLogger.warning("CoreProtectにアクセスできませんでした。") } else { - //もし失敗したらプレイヤーに報告し処理を終了 + // もし失敗したらプレイヤーに報告し処理を終了 if (!wrapper.queueBlockRemoval(player, checkTarget)) { player.sendMessage(s"${RED}coreprotectに保存できませんでした。管理者に報告してください。") return false @@ -73,7 +90,11 @@ object BreakUtil { val halfBlockLayerYCoordinate = { val managedWorld = ManagedWorld.fromBukkitWorld(checkTarget.getWorld) // 整地専用サーバー(s5)のWORLD_SW_3(Earth整地)は、外部ワールドのため岩盤高度がY0 - if (SeichiAssist.seichiAssistConfig.getServerNum == 5 && managedWorld.contains(ManagedWorld.WORLD_SW_3)) 1 + if ( + SeichiAssist.seichiAssistConfig.getServerNum == 5 && managedWorld.contains( + ManagedWorld.WORLD_SW_3 + ) + ) 1 // エンド整地ワールドには岩盤がないが、Y0にハーフを設置するひとがいるため else if (managedWorld.contains(ManagedWorld.WORLD_SW_END)) 0 // それ以外なら通常通りY5 @@ -91,25 +112,23 @@ object BreakUtil { !lockedBlocks.contains(checkTarget) } - def canBreakWithSkill(player: Player, - checkTarget: Block, - lockedBlocks: Set[Block] = unsafeGetLockedBlocks()): Boolean = { + def canBreakWithSkill( + player: Player, + checkTarget: Block, + lockedBlocks: Set[Block] = unsafeGetLockedBlocks() + ): Boolean = { !isProtectedChest(player, checkTarget) && - canBreak(player, checkTarget, lockedBlocks) + canBreak(player, checkTarget, lockedBlocks) } def isProtectedChest(player: Player, checkTarget: Block): Boolean = { checkTarget.getType match { case Material.CHEST | Material.TRAPPED_CHEST => if (!SeichiAssist.playermap(player.getUniqueId).chestflag) { - ActionBarMessageEffect(s"${RED}スキルでのチェスト破壊は無効化されています") - .run(player) - .unsafeRunSync() + ActionBarMessageEffect(s"${RED}スキルでのチェスト破壊は無効化されています").run(player).unsafeRunSync() true } else if (!player.getWorld.isSeichi) { - ActionBarMessageEffect(s"${RED}スキルでのチェスト破壊は整地ワールドでのみ有効です") - .run(player) - .unsafeRunSync() + ActionBarMessageEffect(s"${RED}スキルでのチェスト破壊は整地ワールドでのみ有効です").run(player).unsafeRunSync() true } else { false @@ -124,13 +143,14 @@ object BreakUtil { world.shouldMuteCoreProtect } - //ブロックを破壊する処理、ドロップも含む、統計増加も含む - def breakBlock(player: Player, - targetBlock: BlockBreakableBySkill, - dropLocation: Location, - tool: BreakTool, - shouldPlayBreakSound: Boolean) - (implicit effectEnvironment: EffectEnvironment): Unit = + // ブロックを破壊する処理、ドロップも含む、統計増加も含む + def breakBlock( + player: Player, + targetBlock: BlockBreakableBySkill, + dropLocation: Location, + tool: BreakTool, + shouldPlayBreakSound: Boolean + )(implicit effectEnvironment: EffectEnvironment): Unit = effectEnvironment.unsafeRunEffectAsync( "単一ブロックを破壊する", massBreakBlock(player, Set(targetBlock), dropLocation, tool, shouldPlayBreakSound) @@ -149,33 +169,34 @@ object BreakUtil { /** * ブロックをツールで破壊した時のドロップを計算する * - * Bukkit/Spigotが提供するBlock.getDropsは信頼できる値を返さない。 - * 本来はNMSのメソッドを呼ぶのが確実らしいが、一時的な実装として使用している。 - * 参考: https://www.spigotmc.org/threads/getdrops-on-crops-not-functioning-as-expected.167751/#post-1779788 + * Bukkit/Spigotが提供するBlock.getDropsは信頼できる値を返さない。 本来はNMSのメソッドを呼ぶのが確実らしいが、一時的な実装として使用している。 参考: + * https://www.spigotmc.org/threads/getdrops-on-crops-not-functioning-as-expected.167751/#post-1779788 */ - private def dropItemOnTool(tool: BreakTool)(blockInformation: (Location, Material, Byte)): Option[BlockBreakResult] = { + private def dropItemOnTool( + tool: BreakTool + )(blockInformation: (Location, Material, Byte)): Option[BlockBreakResult] = { val fortuneLevel = tool.getEnchantmentLevel(Enchantment.LOOT_BONUS_BLOCKS) val (blockLocation, blockMaterial, blockData) = blockInformation blockMaterial match { - case Material.GRASS_PATH | Material.SOIL => return Some( - BlockBreakResult.ItemDrop(new ItemStack(Material.DIRT)) - ) - case Material.MOB_SPAWNER | Material.ENDER_PORTAL_FRAME | Material.ENDER_PORTAL => return None + case Material.GRASS_PATH | Material.SOIL => + return Some(BlockBreakResult.ItemDrop(new ItemStack(Material.DIRT))) + case Material.MOB_SPAWNER | Material.ENDER_PORTAL_FRAME | Material.ENDER_PORTAL => + return None case _ => } val rand = Math.random() val bonus = Math.max(1, rand * (fortuneLevel + 2) - 1).toInt - val blockDataLeast4Bits = (blockData & 0x0F).toByte + val blockDataLeast4Bits = (blockData & 0x0f).toByte val b_tree = (blockData & 0x03).toByte val silkTouch = tool.getEnchantmentLevel(Enchantment.SILK_TOUCH) if (silkTouch > 0) { - //シルクタッチの処理 + // シルクタッチの処理 Some { BlockBreakResult.ItemDrop { blockMaterial match { @@ -191,7 +212,7 @@ object BreakUtil { } } } else if (fortuneLevel > 0 && MaterialSets.fortuneMaterials.contains(blockMaterial)) { - //幸運の処理 + // 幸運の処理 Some { BlockBreakResult.ItemDrop { blockMaterial match { @@ -219,7 +240,7 @@ object BreakUtil { } } } else { - //シルク幸運なしの処理 + // シルク幸運なしの処理 blockMaterial match { case Material.COAL_ORE => Some(BlockBreakResult.ItemDrop(new ItemStack(Material.COAL))) @@ -239,10 +260,10 @@ object BreakUtil { Some { BlockBreakResult.ItemDrop { if (blockData.toInt == 0x00) { - //焼き石の処理 + // 焼き石の処理 new ItemStack(Material.COBBLESTONE) } else { - //他の石の処理 + // 他の石の処理 new ItemStack(blockMaterial, 1, blockDataLeast4Bits.toShort) } } @@ -268,34 +289,43 @@ object BreakUtil { case Material.LOG | Material.LOG_2 => Some(BlockBreakResult.ItemDrop(new ItemStack(blockMaterial, 1, b_tree.toShort))) case _ => - Some(BlockBreakResult.ItemDrop(new ItemStack(blockMaterial, 1, blockDataLeast4Bits.toShort))) + Some( + BlockBreakResult + .ItemDrop(new ItemStack(blockMaterial, 1, blockDataLeast4Bits.toShort)) + ) } } } /** - * world 内での整地量倍率を計算する。 * TODO: これはビジネスロジックである。breakcountシステムによって管理されるべき。 + * @param world + * 対象ワールド + * @return + * ワールドに対応する整地量の倍率を計算する作用 */ - def blockCountWeight(world: World): Double = { - val managedWorld = ManagedWorld.fromBukkitWorld(world) - val seichiWorldFactor = if (managedWorld.exists(_.isSeichi)) 1.0 else 0.0 - val sw01Penalty = if (managedWorld.contains(ManagedWorld.WORLD_SW)) 0.8 else 1.0 - - seichiWorldFactor * sw01Penalty - } + def blockCountWeight[F[_]: JavaTime: Functor](world: World): F[Double] = + JavaTime[F].getLocalDate(ZoneId.of("Asia/Tokyo" /* JST */ )).map { date => + val managedWorld = ManagedWorld.fromBukkitWorld(world) + val seichiWorldFactor = if (managedWorld.exists(_.isSeichi)) 1.0 else 0.0 + val isMonthlyPrizeDay = date.getDayOfMonth == 21 + val monthlyPrize = if (isMonthlyPrizeDay) 1.75 else 1.0 + val sw01Penalty = + if (managedWorld.contains(ManagedWorld.WORLD_SW) && !isMonthlyPrizeDay) 0.8 else 1.0 + + seichiWorldFactor * sw01Penalty * monthlyPrize + } /** - * マテリアルごとに倍率を掛けた整地量を計算する。 - * TODO: これはビジネスロジックである。breakcountシステムによって管理されるべき。 + * マテリアルごとに倍率を掛けた整地量を計算する。 TODO: これはビジネスロジックである。breakcountシステムによって管理されるべき。 */ def totalBreakCount(materials: Seq[Material]): Long = materials .filter(MaterialSets.materialsToCountBlockBreak.contains) .map { - //氷塊とマグマブロックの整地量を2倍 + // 氷塊とマグマブロックの整地量を2倍 case Material.PACKED_ICE | Material.MAGMA => 2L - case _ => 1L + case _ => 1L } .sum @@ -306,33 +336,38 @@ object BreakUtil { * * @return */ - def massBreakBlock(player: Player, - targetBlocks: Iterable[BlockBreakableBySkill], - dropLocation: Location, - miningTool: BreakTool, - shouldPlayBreakSound: Boolean, - toMaterial: Material = Material.AIR): IO[Unit] = { - + def massBreakBlock( + player: Player, + targetBlocks: Iterable[BlockBreakableBySkill], + dropLocation: Location, + miningTool: BreakTool, + shouldPlayBreakSound: Boolean, + toMaterial: Material = Material.AIR + ): IO[Unit] = { + + import PluginExecutionContexts.timer for { // 非同期実行ではワールドに触れないので必要な情報をすべて抜く - targetBlocksInformation <- PluginExecutionContexts.onMainThread.runAction(SyncIO { - val seq: Seq[(Location, Material, Byte)] = targetBlocks.toSeq - .filter { block => - block.getType match { - case Material.AIR => - Bukkit.getLogger - .warning(s"AIRの破壊が${block.getLocation.toString}にて試行されました。") - false - case _ => true + targetBlocksInformation <- PluginExecutionContexts + .onMainThread + .runAction(SyncIO { + val seq: Seq[(Location, Material, Byte)] = targetBlocks + .toSeq + .filter { block => + block.getType match { + case Material.AIR => + Bukkit.getLogger.warning(s"AIRの破壊が${block.getLocation.toString}にて試行されました。") + false + case _ => true + } } - } - .map(block => (block.getLocation.clone(), block.getType, block.getData)) + .map(block => (block.getLocation.clone(), block.getType, block.getData)) - // ブロックをすべて[[toMaterial]]に変える - targetBlocks.foreach(_.setType(toMaterial)) + // ブロックをすべて[[toMaterial]]に変える + targetBlocks.foreach(_.setType(toMaterial)) - seq - }) + seq + }) breakResults = { import cats.implicits._ @@ -340,10 +375,10 @@ object BreakUtil { val plainBreakResult = targetBlocksInformation.flatMap(dropItemOnTool(miningTool)) val drops = plainBreakResult.mapFilter { case BlockBreakResult.ItemDrop(itemStack) => Some(itemStack) - case BlockBreakResult.SpawnSilverFish(_) => None + case BlockBreakResult.SpawnSilverFish(_) => None } val silverFishLocations = plainBreakResult.mapFilter { - case BlockBreakResult.ItemDrop(_) => None + case BlockBreakResult.ItemDrop(_) => None case BlockBreakResult.SpawnSilverFish(location) => Some(location) } @@ -366,31 +401,40 @@ object BreakUtil { _ <- IO { // 壊した時の音を再生する if (shouldPlayBreakSound) { - targetBlocksInformation.foreach { case (location, material, _) => - dropLocation.getWorld.playEffect(location, Effect.STEP_SOUND, material) + targetBlocksInformation.foreach { + case (location, material, _) => + dropLocation.getWorld.playEffect(location, Effect.STEP_SOUND, material) } } } - //プレイヤーの統計を増やす + // プレイヤーの統計を増やす totalCount = totalBreakCount(targetBlocksInformation.map { case (_, m, _) => m }) - blockCountWeight <- IO(blockCountWeight(player.getWorld)) + blockCountWeight <- blockCountWeight[IO](player.getWorld) expIncrease = SeichiExpAmount.ofNonNegative(totalCount * blockCountWeight) - _ <- SeichiAssist.instance.breakCountSystem.api.incrementSeichiExp.of(player, expIncrease).toIO - - _ <- PluginExecutionContexts.onMainThread.runAction(SyncIO { - // アイテムドロップは非同期スレッドで行ってはならない - itemsToBeDropped.foreach(dropLocation.getWorld.dropItemNaturally(dropLocation, _)) - breakResults._2.foreach { location => - location.getWorld.spawnEntity(location, EntityType.SILVERFISH) - } - }) + _ <- SeichiAssist + .instance + .breakCountSystem + .api + .incrementSeichiExp + .of(player, expIncrease) + .toIO + + _ <- PluginExecutionContexts + .onMainThread + .runAction(SyncIO { + // アイテムドロップは非同期スレッドで行ってはならない + itemsToBeDropped.foreach(dropLocation.getWorld.dropItemNaturally(dropLocation, _)) + breakResults._2.foreach { location => + location.getWorld.spawnEntity(location, EntityType.SILVERFISH) + } + }) } yield () } def tryAddItemIntoMineStack(player: Player, itemstack: ItemStack): Boolean = { - //もしサバイバルでなければ処理を終了 + // もしサバイバルでなければ処理を終了 if (player.getGameMode != GameMode.SURVIVAL) return false if (SeichiAssist.DEBUG) { @@ -402,40 +446,44 @@ object BreakUtil { val playerData = SeichiAssist.playermap(player.getUniqueId) - //minestackflagがfalseの時は処理を終了 + // minestackflagがfalseの時は処理を終了 if (!playerData.settings.autoMineStack) return false /** * 必要であれば引数を対応するアイテム向けの[[Material]]へ変換する - * @param material 変換対象 - * @return 変換されたかもしれないMaterial + * @param material + * 変換対象 + * @return + * 変換されたかもしれないMaterial */ def intoItem(material: Material): Material = { material match { - case Material.ACACIA_DOOR => Material.ACACIA_DOOR_ITEM - case Material.BIRCH_DOOR => Material.BIRCH_DOOR_ITEM - case Material.BED_BLOCK => Material.BED + case Material.ACACIA_DOOR => Material.ACACIA_DOOR_ITEM + case Material.BIRCH_DOOR => Material.BIRCH_DOOR_ITEM + case Material.BED_BLOCK => Material.BED case Material.BREWING_STAND => Material.BREWING_STAND_ITEM - case Material.CAULDRON => Material.CAULDRON_ITEM + case Material.CAULDRON => Material.CAULDRON_ITEM case Material.DARK_OAK_DOOR => Material.DARK_OAK_DOOR_ITEM - case Material.FLOWER_POT => Material.FLOWER_POT_ITEM - case Material.JUNGLE_DOOR => Material.JUNGLE_DOOR_ITEM - case Material.SPRUCE_DOOR => Material.SPRUCE_DOOR_ITEM - case Material.SKULL => Material.SKULL_ITEM - case Material.WOODEN_DOOR => Material.WOOD_DOOR - case others => others + case Material.FLOWER_POT => Material.FLOWER_POT_ITEM + case Material.JUNGLE_DOOR => Material.JUNGLE_DOOR_ITEM + case Material.SPRUCE_DOOR => Material.SPRUCE_DOOR_ITEM + case Material.SKULL => Material.SKULL_ITEM + case Material.WOODEN_DOOR => Material.WOOD_DOOR + case others => others } } val amount = itemstack.getAmount val material = intoItem(itemstack.getType) - //線路・キノコなどの、拾った時と壊した時とでサブIDが違う場合の処理 - //拾った時のサブIDに合わせる - if (itemstack.getType == Material.RAILS + // 線路・キノコなどの、拾った時と壊した時とでサブIDが違う場合の処理 + // 拾った時のサブIDに合わせる + if ( + itemstack.getType == Material.RAILS || itemstack.getType == Material.HUGE_MUSHROOM_1 || itemstack.getType == Material.HUGE_MUSHROOM_2 || itemstack.getType == Material.PURPUR_STAIRS - || itemstack.getType == Material.BONE_BLOCK) { + || itemstack.getType == Material.BONE_BLOCK + ) { itemstack.setDurability(0.toShort) } @@ -443,10 +491,13 @@ object BreakUtil { MineStackObjectList.minestacklist.foreach { mineStackObj => def addToMineStackAfterLevelCheck(): Boolean = { val level = - SeichiAssist.instance - .breakCountSystem.api + SeichiAssist + .instance + .breakCountSystem + .api .seichiAmountDataRepository(player) - .read.unsafeRunSync() + .read + .unsafeRunSync() .levelCorrespondingToExp if (level.level < config.getMineStacklevel(mineStackObj.level)) { @@ -457,23 +508,37 @@ object BreakUtil { } } - //IDとサブIDが一致している - if (material == mineStackObj.material && itemstack.getDurability.toInt == mineStackObj.durability) { - //名前と説明文が無いアイテム - if (!mineStackObj.hasNameLore && !itemstack.getItemMeta.hasLore && !itemstack.getItemMeta.hasDisplayName) { + // IDとサブIDが一致している + if ( + material == mineStackObj.material && itemstack + .getDurability + .toInt == mineStackObj.durability + ) { + // 名前と説明文が無いアイテム + if ( + !mineStackObj.hasNameLore && !itemstack.getItemMeta.hasLore && !itemstack + .getItemMeta + .hasDisplayName + ) { return addToMineStackAfterLevelCheck() - } else if (mineStackObj.hasNameLore && itemstack.getItemMeta.hasDisplayName && itemstack.getItemMeta.hasLore) { - //ガチャ以外のアイテム(がちゃりんご) + } else if ( + mineStackObj.hasNameLore && itemstack.getItemMeta.hasDisplayName && itemstack + .getItemMeta + .hasLore + ) { + // ガチャ以外のアイテム(がちゃりんご) if (mineStackObj.gachaType == -1) { if (!itemstack.isSimilar(StaticGachaPrizeFactory.getGachaRingo)) return false return addToMineStackAfterLevelCheck() } else { - //ガチャ品 + // ガチャ品 val g = SeichiAssist.msgachadatalist(mineStackObj.gachaType) - //名前が記入されているはずのアイテムで名前がなければ - if (g.probability < 0.1 && !Util.itemStackContainsOwnerName(itemstack, player.getName)) return false + // 名前が記入されているはずのアイテムで名前がなければ + if ( + g.probability < 0.1 && !Util.itemStackContainsOwnerName(itemstack, player.getName) + ) return false if (g.itemStackEquals(itemstack)) { return addToMineStackAfterLevelCheck() @@ -487,34 +552,36 @@ object BreakUtil { } def calcManaDrop(player: Player): Double = { - val isSkillAvailable = SeichiAssist.instance.activeSkillAvailability(player).get.unsafeRunSync() + val isSkillAvailable = + SeichiAssist.instance.activeSkillAvailability(player).get.unsafeRunSync() - //0~1のランダムな値を取得 + // 0~1のランダムな値を取得 val rand = Math.random() - //10%の確率で経験値付与 + // 10%の確率で経験値付与 if (isSkillAvailable && rand < 0.1) SeichiAssist.playermap(player.getUniqueId).getPassiveExp else 0.0 } - //num回だけ耐久を減らす処理 + // num回だけ耐久を減らす処理 def calcDurability(enchantmentLevel: Int, num: Int): Short = { val rand = new Random() val probability = 1.0 / (enchantmentLevel + 1.0) - IntStream.range(0, num) - .filter { _ => probability > rand.nextDouble() } - .count().toShort + IntStream.range(0, num).filter { _ => probability > rand.nextDouble() }.count().toShort } /** - * @param player 破壊プレイヤー - * @param block 手動破壊対象またはアサルト/遠距離の指定座標 - * @param isAssault true: アサルトアーマーによる破壊 - * false: アクティブスキルまたは手動による破壊 - * @return 重力値(破壊範囲の上に積まれているブロック数) + * @param player + * 破壊プレイヤー + * @param block + * 手動破壊対象またはアサルト/遠距離の指定座標 + * @param isAssault + * true: アサルトアーマーによる破壊 false: アクティブスキルまたは手動による破壊 + * @return + * 重力値(破壊範囲の上に積まれているブロック数) */ def getGravity(player: Player, block: Block, isAssault: Boolean): Int = { // 1. 重力値を適用すべきか判定 @@ -523,11 +590,15 @@ object BreakUtil { return 0 // 2. 破壊要因判定 - /** 該当プレイヤーのPlayerData */ + /** + * 該当プレイヤーのPlayerData + */ val playerData = SeichiAssist.playermap(player.getUniqueId) val skillState = playerData.skillState.get.unsafeRunSync() - /** 重力値の計算を始めるY座標 */ + /** + * 重力値の計算を始めるY座標 + */ val startY: Int = if (!isAssault) { val usageMode = skillState.usageMode @@ -545,7 +616,9 @@ object BreakUtil { // 上向きによる発動 // block=破壊範囲の最下層ブロックにつき、startは破壊範囲の高さ effectChunkSize.y - } else if (Set(DualBreak, TrialBreak).contains(skill) && usageMode == Active) { + } else if ( + Set(DualBreak, TrialBreak).contains(skill) && usageMode == Active + ) { // 横向きによる発動のうち、デュアルorトリアルの上破壊 // 破壊ブロックの1マス上が破壊されるので、startは2段目から 1 @@ -578,17 +651,31 @@ object BreakUtil { } // 3. 重力値計算 - /** OPENHEIGHTマス以上のtransparentmateriallistブロックの連続により、地上判定とする。 */ + /** + * OPENHEIGHTマス以上のtransparentmateriallistブロックの連続により、地上判定とする。 + */ val OPENHEIGHT = 3 - /** OPENHEIGHTに達したかの計測カウンタ */ + + /** + * OPENHEIGHTに達したかの計測カウンタ + */ var openCount = 0 - /** 重力値 */ + + /** + * 重力値 + */ var gravity = 0 - /** 最大ループ数 */ + + /** + * 最大ループ数 + */ val YMAX = 255 for (checkPointer <- 1 until YMAX) { - /** 確認対象ブロック */ + + /** + * 確認対象ブロック + */ val target = block.getRelative(0, startY + checkPointer, 0) // 対象ブロックが地上判定ブロックの場合 if (MaterialSets.transparentMaterials.contains(target.getType)) { @@ -610,8 +697,10 @@ object BreakUtil { /** * エンティティが向いている方向を計算して取得する - * @param entity 対象とするエンティティ - * @return エンティティが向いている方向が座標軸方向に近似できた場合はnon-nullな[[CardinalDirection]]、そうでない場合は`null` + * @param entity + * 対象とするエンティティ + * @return + * エンティティが向いている方向が座標軸方向に近似できた場合はnon-nullな[[CardinalDirection]]、そうでない場合は`null` */ def getCardinalDirection(entity: Entity): CardinalDirection = { var rotation = ((entity.getLocation.getYaw + 180) % 360).toDouble @@ -649,7 +738,7 @@ object BreakUtil { val failure = !wrapper.queueBlockRemoval(player, removedBlock) - //もし失敗したらプレイヤーに報告し処理を終了 + // もし失敗したらプレイヤーに報告し処理を終了 if (failure) { player.sendMessage(RED.toString + "error:coreprotectに保存できませんでした。管理者に報告してください。") return false @@ -657,15 +746,36 @@ object BreakUtil { true } - def multiplyBreakValidlyEnabled(player: Player): SyncIO[Boolean] = for { - sad <- - SeichiAssist.instance - .breakCountSystem.api - .seichiAmountDataRepository(player).read + /** + * プレーヤーがスキルを使うときに複数種類ブロック同時破壊を行うかどうかを返す関数。 + * + * - プレーヤーの整地レベルが `SeichiAssist.seichiAssistConfig.getMultipleIDBlockBreakLevel` 以上である、かつ、 + * - 以下二条件のうちどちらかが満たされている + * - プレーヤーが「整地ワールド」に居る、または + * - `PlayerData.settings.performMultipleIDBlockBreakWhenOutsideSeichiWorld`(以下「フラグ」)が + * `true` になっている + * + * 「整地スキルを使えるワールド」と「整地ワールド」の概念が一致していない事から、 単純にフラグを返すだけではないので注意。 例えば、メインワールドでは、整地レベルが十分かつフラグが + * `true` のときのみ複数種類ブロック破壊をする。 + */ + def performsMultipleIDBlockBreakWhenUsingSkills(player: Player): SyncIO[Boolean] = for { + seichiAmountData <- + SeichiAssist.instance.breakCountSystem.api.seichiAmountDataRepository(player).read + currentWorld <- SyncIO(player.getWorld) + flag <- SyncIO( + SeichiAssist + .playermap(player.getUniqueId) + .settings + .performMultipleIDBlockBreakWhenOutsideSeichiWorld + ) } yield { import ManagedWorld._ - val playerData = SeichiAssist.playermap(player.getUniqueId) - sad.levelCorrespondingToExp.level >= SeichiAssist.seichiAssistConfig.getMultipleIDBlockBreaklevel && - playerData.settings.multipleidbreakflag + + val isLevelAboveThreshold = + seichiAmountData.levelCorrespondingToExp.level >= SeichiAssist + .seichiAssistConfig + .getMultipleIDBlockBreakLevel + + isLevelAboveThreshold && (currentWorld.isSeichi || flag) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/util/PlayerSendable.scala b/src/main/scala/com/github/unchama/seichiassist/util/PlayerSendable.scala index 6c1821d517..7b14dcc0a7 100644 --- a/src/main/scala/com/github/unchama/seichiassist/util/PlayerSendable.scala +++ b/src/main/scala/com/github/unchama/seichiassist/util/PlayerSendable.scala @@ -5,13 +5,11 @@ import com.github.unchama.minecraft.actions.OnMinecraftServerThread import net.md_5.bungee.api.chat.TextComponent import org.bukkit.entity.Player - trait PlayerSendable[-T, +F[_]] { def send(player: Player, content: T): F[Unit] } object PlayerSendable { - import scala.language.implicitConversions implicit def forString[F[_]: OnMinecraftServerThread]: PlayerSendable[String, F] = { (player, content) => @@ -20,17 +18,17 @@ object PlayerSendable { }) } - implicit def forStringArray[F[_]: OnMinecraftServerThread]: PlayerSendable[Array[String], F] = { - (player, content) => - OnMinecraftServerThread[F].runAction(SyncIO { - player.sendMessage(content) - }) + implicit def forStringArray[F[_]: OnMinecraftServerThread] + : PlayerSendable[Array[String], F] = { (player, content) => + OnMinecraftServerThread[F].runAction(SyncIO { + player.sendMessage(content) + }) } - implicit def forTextComponent[F[_]: OnMinecraftServerThread]: PlayerSendable[TextComponent, F] = { - (player, content) => - OnMinecraftServerThread[F].runAction(SyncIO { - player.spigot().sendMessage(content) - }) + implicit def forTextComponent[F[_]: OnMinecraftServerThread] + : PlayerSendable[TextComponent, F] = { (player, content) => + OnMinecraftServerThread[F].runAction(SyncIO { + player.spigot().sendMessage(content) + }) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/util/Util.scala b/src/main/scala/com/github/unchama/seichiassist/util/Util.scala index 3253ae7b7e..d7809c3a6d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/util/Util.scala +++ b/src/main/scala/com/github/unchama/seichiassist/util/Util.scala @@ -8,7 +8,11 @@ import com.github.unchama.minecraft.bukkit.actions.GetConnectedBukkitPlayers import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.onMainThread import com.github.unchama.seichiassist.minestack.MineStackObj -import com.github.unchama.seichiassist.{DefaultEffectEnvironment, MineStackObjectList, SeichiAssist} +import com.github.unchama.seichiassist.{ + DefaultEffectEnvironment, + MineStackObjectList, + SeichiAssist +} import com.github.unchama.util.bukkit.ItemStackUtil import enumeratum._ import org.bukkit.ChatColor._ @@ -29,7 +33,13 @@ object Util { import scala.jdk.CollectionConverters._ import scala.util.chaining._ - private val types = List(FireworkEffect.Type.BALL, FireworkEffect.Type.BALL_LARGE, FireworkEffect.Type.BURST, FireworkEffect.Type.CREEPER, FireworkEffect.Type.STAR) + private val types = List( + FireworkEffect.Type.BALL, + FireworkEffect.Type.BALL_LARGE, + FireworkEffect.Type.BURST, + FireworkEffect.Type.CREEPER, + FireworkEffect.Type.STAR + ) def sendPlayerDataNullMessage(player: Player): Unit = { player.sendMessage(RED.toString + "初回ログイン時の読み込み中か、読み込みに失敗しています") @@ -38,18 +48,15 @@ object Util { @deprecated("please use ManagedWorld#isSeichiSkillAllowed") def seichiSkillsAllowedIn(world: World): Boolean = { - val seichiWorldPrefix = if (SeichiAssist.DEBUG) SeichiAssist.DEBUGWORLDNAME else SeichiAssist.SEICHIWORLDNAME + val seichiWorldPrefix = + if (SeichiAssist.DEBUG) SeichiAssist.DEBUGWORLDNAME else SeichiAssist.SEICHIWORLDNAME val worldNameLowerCase = world.getName.toLowerCase() worldNameLowerCase match { case "world_sw_zero" => false // 整地ワールドzeroではスキル発動不可 - case "world" | - "world_2" | - "world_nether" | - "world_the_end" | - "world_TT" | - "world_nether_TT" | - "world_the_end_TT" => true + case "world" | "world_2" | "world_nether" | "world_the_end" | "world_TT" | + "world_nether_TT" | "world_the_end_TT" => + true case _ => worldNameLowerCase.startsWith(seichiWorldPrefix) } } @@ -57,25 +64,29 @@ object Util { /** * プレイヤーが整地ワールドにいるかどうかの判定処理(整地ワールド=true、それ以外=false) * - * @deprecated use ManagedWorld + * @deprecated + * use ManagedWorld */ @Deprecated() def isSeichiWorld(player: Player): Boolean = { - //デバッグモード時は全ワールドtrue(DEBUGWORLDNAME = worldの場合) + // デバッグモード時は全ワールドtrue(DEBUGWORLDNAME = worldの場合) var worldname = SeichiAssist.SEICHIWORLDNAME if (SeichiAssist.DEBUG) { worldname = SeichiAssist.DEBUGWORLDNAME } - //整地ワールドではtrue + // 整地ワールドではtrue player.getWorld.getName.toLowerCase().startsWith(worldname) } /** * プレイヤーに安全にアイテムを付与します。 * - * @param player 付与する対象プレイヤー - * @param itemStack 付与するアイテム - * @deprecated use [[grantItemStacksEffect]] + * @param player + * 付与する対象プレイヤー + * @param itemStack + * 付与するアイテム + * @deprecated + * use [[grantItemStacksEffect]] */ @deprecated def addItemToPlayerSafely(player: Player, itemStack: ItemStack): Unit = { // Javaから呼ばれているのでimplicitが使いづらい grantItemStacksEffectに置き換えたい @@ -88,47 +99,52 @@ object Util { } /** - * プレイヤーに複数のアイテムを一度に付与する。 - * インベントリに入り切らなかったアイテムはプレーヤーの立ち位置にドロップされる。 + * プレイヤーに複数のアイテムを一度に付与する。 インベントリに入り切らなかったアイテムはプレーヤーの立ち位置にドロップされる。 * - * @param itemStacks 付与するアイテム + * @param itemStacks + * 付与するアイテム */ - def grantItemStacksEffect[F[_] : OnMinecraftServerThread](itemStacks: ItemStack*): Kleisli[F, Player, Unit] = + def grantItemStacksEffect[F[_]: OnMinecraftServerThread]( + itemStacks: ItemStack* + ): Kleisli[F, Player, Unit] = data.Kleisli { player => val amalgamated = ItemStackUtil.amalgamate(itemStacks).filter(_.getType != Material.AIR) OnMinecraftServerThread[F].runAction(SyncIO { - player.getInventory + player + .getInventory .addItem(amalgamated: _*) - .values().asScala + .values() + .asScala .filter(_.getType != Material.AIR) .foreach(dropItem(player, _)) }) } - //プレイヤーのインベントリがフルかどうか確認 + // プレイヤーのインベントリがフルかどうか確認 def isPlayerInventoryFull(player: Player): Boolean = player.getInventory.firstEmpty() == -1 - //指定されたアイテムを指定されたプレイヤーにドロップする + // 指定されたアイテムを指定されたプレイヤーにドロップする def dropItem(player: Player, itemstack: ItemStack): Unit = { player.getWorld.dropItemNaturally(player.getLocation, itemstack) } - //指定されたアイテムを指定されたプレイヤーインベントリに追加する + // 指定されたアイテムを指定されたプレイヤーインベントリに追加する def addItem(player: Player, itemstack: ItemStack): Unit = { player.getInventory.addItem(itemstack) } - def sendMessageToEveryoneIgnoringPreference[T](content: T) - (implicit send: PlayerSendable[T, IO]): Unit = { + def sendMessageToEveryoneIgnoringPreference[T]( + content: T + )(implicit send: PlayerSendable[T, IO]): Unit = { implicit val g: GetConnectedBukkitPlayers[IO] = new GetConnectedBukkitPlayers[IO] sendMessageToEveryoneIgnoringPreferenceM[T, IO](content).unsafeRunAsyncAndForget() } - def sendMessageToEveryoneIgnoringPreferenceM[ - T, F[_] : Monad : GetConnectedPlayers[*[_], Player] - ](content: T)(implicit ev: PlayerSendable[T, F]): F[Unit] = { + def sendMessageToEveryoneIgnoringPreferenceM[T, F[_]: Monad: GetConnectedPlayers[*[ + _ + ], Player]](content: T)(implicit ev: PlayerSendable[T, F]): F[Unit] = { import cats.implicits._ for { @@ -137,16 +153,24 @@ object Util { } yield () } - def sendMessageToEveryone[T](content: T) - (implicit ev: PlayerSendable[T, IO]): Unit = { + def sendMessageToEveryone[T](content: T)(implicit ev: PlayerSendable[T, IO]): Unit = { import cats.implicits._ - Bukkit.getOnlinePlayers.asScala.map { player => - for { - playerSettings <- SeichiAssist.playermap(player.getUniqueId).settings.getBroadcastMutingSettings - _ <- IO { if (!playerSettings.shouldMuteMessages) ev.send(player, content) } - } yield () - }.toList.sequence.unsafeRunSync() + Bukkit + .getOnlinePlayers + .asScala + .map { player => + for { + playerSettings <- SeichiAssist + .playermap(player.getUniqueId) + .settings + .getBroadcastMutingSettings + _ <- IO { if (!playerSettings.shouldMuteMessages) ev.send(player, content) } + } yield () + } + .toList + .sequence + .unsafeRunSync() } def getEnchantName(vaname: String, enchlevel: Int): String = { @@ -183,25 +207,29 @@ object Util { ) val enchantmentLevelRepresentation = getEnchantLevelRome(enchlevel) - levelLessEnchantmentMapping.get(vaname).orElse( - leveledEnchantmentMapping.get(vaname) - .map(localizedName => s"$localizedName $enchantmentLevelRepresentation") - ).getOrElse(vaname) + levelLessEnchantmentMapping + .get(vaname) + .orElse( + leveledEnchantmentMapping + .get(vaname) + .map(localizedName => s"$localizedName $enchantmentLevelRepresentation") + ) + .getOrElse(vaname) } private def getEnchantLevelRome(enchantlevel: Int): String = { enchantlevel match { - case 1 => "Ⅰ" - case 2 => "Ⅱ" - case 3 => "Ⅲ" - case 4 => "Ⅳ" - case 5 => "Ⅴ" - case 6 => "Ⅵ" - case 7 => "Ⅶ" - case 8 => "Ⅷ" - case 9 => "Ⅸ" + case 1 => "Ⅰ" + case 2 => "Ⅱ" + case 3 => "Ⅲ" + case 4 => "Ⅳ" + case 5 => "Ⅴ" + case 6 => "Ⅵ" + case 7 => "Ⅶ" + case 8 => "Ⅷ" + case 9 => "Ⅸ" case 10 => "Ⅹ" - case _ => enchantlevel.toString + case _ => enchantlevel.toString } } @@ -209,30 +237,40 @@ object Util { def getDescFormat(list: List[String]): String = s" ${list.mkString("", "\n", "\n")}" def sendEverySound(kind: Sound, volume: Float, pitch: Float): Unit = { - Bukkit.getOnlinePlayers.forEach(player => - player.playSound(player.getLocation, kind, volume, pitch) - ) + Bukkit + .getOnlinePlayers + .forEach(player => player.playSound(player.getLocation, kind, volume, pitch)) } def sendEverySoundWithoutIgnore(kind: Sound, volume: Float, pitch: Float): Unit = { import cats.implicits._ - Bukkit.getOnlinePlayers.asScala.toList.map { player => - for { - settings <- SeichiAssist.playermap(player.getUniqueId).settings.getBroadcastMutingSettings - _ <- IO { - if (!settings.shouldMuteSounds) player.playSound(player.getLocation, kind, volume, pitch) - } - } yield () - }.sequence.unsafeRunSync() + Bukkit + .getOnlinePlayers + .asScala + .toList + .map { player => + for { + settings <- SeichiAssist + .playermap(player.getUniqueId) + .settings + .getBroadcastMutingSettings + _ <- IO { + if (!settings.shouldMuteSounds) + player.playSound(player.getLocation, kind, volume, pitch) + } + } yield () + } + .sequence + .unsafeRunSync() } def getName(name: String): String = { - //小文字にしてるだけだよ + // 小文字にしてるだけだよ name.toLowerCase() } - //指定された場所に花火を打ち上げる関数 + // 指定された場所に花火を打ち上げる関数 def launchFireWorks(loc: Location): Unit = { // 花火を作る val firework = loc.getWorld.spawn(loc, classOf[Firework]) @@ -266,7 +304,7 @@ object Util { firework.setFireworkMeta(meta) } - //カラーをランダムで決める + // カラーをランダムで決める def getRandomColors(length: Int): Array[Color] = { // 配列を作る val rand = new Random() @@ -277,7 +315,7 @@ object Util { (0 until length).map { _ => Color.fromBGR(rand.nextInt(1 << 24)) }.toArray } - //ガチャアイテムを含んでいるか調べる + // ガチャアイテムを含んでいるか調べる def containsGachaTicket(player: Player): Boolean = { player.getInventory.getStorageContents.exists(isGachaTicket) @@ -296,9 +334,12 @@ object Util { skullMeta.hasLore && skullMeta.getLore.asScala.exists(containsRightClickMessage) } - def removeItemfromPlayerInventory(inventory: PlayerInventory, - itemstack: ItemStack, count: Int): Boolean = { - //持っているアイテムを減らす処理 + def removeItemfromPlayerInventory( + inventory: PlayerInventory, + itemstack: ItemStack, + count: Int + ): Boolean = { + // 持っているアイテムを減らす処理 if (itemstack.getAmount == count) { // アイテムをcount個使うので、プレイヤーの手を素手にする inventory.setItemInMainHand(new ItemStack(Material.AIR)) @@ -320,23 +361,36 @@ object Util { Nil lore.exists(line => - line.contains("所有者:") && line.drop(line.indexOf("所有者:") + 4).toLowerCase == name.toLowerCase() + line.contains("所有者:") && line.drop(line.indexOf("所有者:") + 4).toLowerCase == name + .toLowerCase() ) } /** * GUIメニューアイコン作成用 * - * @author karayuu - * @param material メニューアイコンMaterial - * @param amount メニューアイコンのアイテム個数 - * @param displayName メニューアイコンのDisplayName - * @param lore メニューアイコンのLore - * @param isHideFlags 攻撃値・ダメージ値を隠すかどうか(true: 隠す / false: 隠さない) - * @return ItemStack型のメニューアイコン + * @author + * karayuu + * @param material + * メニューアイコンMaterial + * @param amount + * メニューアイコンのアイテム個数 + * @param displayName + * メニューアイコンのDisplayName + * @param lore + * メニューアイコンのLore + * @param isHideFlags + * 攻撃値・ダメージ値を隠すかどうか(true: 隠す / false: 隠さない) + * @return + * ItemStack型のメニューアイコン */ - def getMenuIcon(material: Material, amount: Int, - displayName: String, lore: List[String], isHideFlags: Boolean): ItemStack = { + def getMenuIcon( + material: Material, + amount: Int, + displayName: String, + lore: List[String], + isHideFlags: Boolean + ): ItemStack = { new ItemStack(material, amount).tap { itemStack => import itemStack._ setItemMeta { @@ -355,18 +409,33 @@ object Util { /** * GUIメニューアイコン作成用 * - * @author karayuu - * @param material メニューアイコンMaterial, not `null` - * @param amount メニューアイコンのアイテム個数 - * @param durabity メニューアイコンのダメージ値 - * @param displayName メニューアイコンのDisplayName, not `null` - * @param lore メニューアイコンのLore, not `null` - * @param isHideFlags 攻撃値・ダメージ値を隠すかどうか(true: 隠す / false: 隠さない) - * @throws IllegalArgumentException Material,DisplayName, Loreのいずれかが `null` の時 - * @return ItemStack型のメニューアイコン + * @author + * karayuu + * @param material + * メニューアイコンMaterial, not `null` + * @param amount + * メニューアイコンのアイテム個数 + * @param durabity + * メニューアイコンのダメージ値 + * @param displayName + * メニューアイコンのDisplayName, not `null` + * @param lore + * メニューアイコンのLore, not `null` + * @param isHideFlags + * 攻撃値・ダメージ値を隠すかどうか(true: 隠す / false: 隠さない) + * @throws IllegalArgumentException + * Material,DisplayName, Loreのいずれかが `null` の時 + * @return + * ItemStack型のメニューアイコン */ - def getMenuIcon(material: Material, amount: Int, durabity: Int, - displayName: String, lore: List[String], isHideFlags: Boolean): ItemStack = { + def getMenuIcon( + material: Material, + amount: Int, + durabity: Int, + displayName: String, + lore: List[String], + isHideFlags: Boolean + ): ItemStack = { new ItemStack(material, amount, durabity.toShort).tap { itemStack => import itemStack._ setItemMeta { @@ -386,7 +455,7 @@ object Util { if (rotation < 0) rotation += 360.0 - //0,360:south 90:west 180:north 270:east + // 0,360:south 90:west 180:north 270:east if (0.0 <= rotation && rotation < 45.0) Direction.NORTH else if (45.0 <= rotation && rotation < 135.0) Direction.EAST else if (135.0 <= rotation && rotation < 225.0) Direction.SOUTH @@ -472,7 +541,7 @@ object Util { def isMineHeadItem(itemstack: ItemStack): Boolean = { itemstack.getType == Material.CARROT_STICK && - loreIndexOf(itemstack.getItemMeta.getLore.asScala.toList, "頭を狩り取る形をしている...") >= 0 + loreIndexOf(itemstack.getItemMeta.getLore.asScala.toList, "頭を狩り取る形をしている...") >= 0 } def getSkullDataFromBlock(block: Block): Option[ItemStack] = { @@ -481,28 +550,31 @@ object Util { val skull = block.getState.asInstanceOf[Skull] val itemStack = new ItemStack(Material.SKULL_ITEM) - //SkullTypeがプレイヤー以外の場合,SkullTypeだけ設定して終わり + // SkullTypeがプレイヤー以外の場合,SkullTypeだけ設定して終わり if (skull.getSkullType != SkullType.PLAYER) { val durability = skull.getSkullType match { - case SkullType.CREEPER => SkullType.CREEPER.ordinal.toShort - case SkullType.DRAGON => SkullType.DRAGON.ordinal.toShort + case SkullType.CREEPER => SkullType.CREEPER.ordinal.toShort + case SkullType.DRAGON => SkullType.DRAGON.ordinal.toShort case SkullType.SKELETON => SkullType.SKELETON.ordinal.toShort - case SkullType.WITHER => SkullType.WITHER.ordinal.toShort - case SkullType.ZOMBIE => SkullType.ZOMBIE.ordinal.toShort - case _ => itemStack.getDurability + case SkullType.WITHER => SkullType.WITHER.ordinal.toShort + case SkullType.ZOMBIE => SkullType.ZOMBIE.ordinal.toShort + case _ => itemStack.getDurability } return Some(itemStack.tap(_.setDurability(durability))) } - //プレイヤーの頭の場合,ドロップアイテムからItemStackを取得.データ値をPLAYERにして返す + // プレイヤーの頭の場合,ドロップアイテムからItemStackを取得.データ値をPLAYERにして返す Some(block.getDrops.asScala.head.tap(_.setDurability(SkullType.PLAYER.ordinal.toShort))) } /** * 指定された`String`が指定された[[ItemStack]]のloreに含まれているかどうか * - * @param itemStack 確認する`ItemStack` - * @param sentence 探す文字列 - * @return 含まれていれば`true`、含まれていなければ`false`。ただし、`ItemStack`に`ItemMeta`と`Lore`のいずれかがなければfalse + * @param itemStack + * 確認する`ItemStack` + * @param sentence + * 探す文字列 + * @return + * 含まれていれば`true`、含まれていなければ`false`。ただし、`ItemStack`に`ItemMeta`と`Lore`のいずれかがなければfalse */ def isContainedInLore(itemStack: ItemStack, sentence: String): Boolean = if (!itemStack.hasItemMeta || !itemStack.getItemMeta.hasLore) false @@ -511,15 +583,15 @@ object Util { /** * loreを捜査して、要素の中に`find`が含まれているかを調べる。 * - * @param lore 探される対象 - * @param find 探す文字列 - * @return 見つかった場合はその添字、見つからなかった場合は-1 + * @param lore + * 探される対象 + * @param find + * 探す文字列 + * @return + * 見つかった場合はその添字、見つからなかった場合は-1 */ def loreIndexOf(lore: List[String], find: String): Int = { - IntStream.range(0, lore.size) - .filter { i => lore(i).contains(find) } - .findFirst() - .orElse(-1) + IntStream.range(0, lore.size).filter { i => lore(i).contains(find) }.findFirst().orElse(-1) } /** diff --git a/src/main/scala/com/github/unchama/targetedeffect/TargetedEffect.scala b/src/main/scala/com/github/unchama/targetedeffect/TargetedEffect.scala index 81695cee21..5b36768708 100644 --- a/src/main/scala/com/github/unchama/targetedeffect/TargetedEffect.scala +++ b/src/main/scala/com/github/unchama/targetedeffect/TargetedEffect.scala @@ -8,6 +8,7 @@ import cats.{Applicative, FlatMap} import scala.concurrent.duration.FiniteDuration object TargetedEffect { + /** * 何も作用を及ぼさないような[TargetedEffect]. */ @@ -16,10 +17,12 @@ object TargetedEffect { /** * 同期的な副作用`f`を`TargetedEffect`内に持ち回すようにする. */ - def delay[F[_] : Sync, T](f: T => Unit): Kleisli[F, T, Unit] = Kleisli(t => Sync[F].delay(f(t))) + def delay[F[_]: Sync, T](f: T => Unit): Kleisli[F, T, Unit] = + Kleisli(t => Sync[F].delay(f(t))) } object DeferredEffect { + /** * `F`計算の結果の作用を`F`内で実行するような計算を返す. * @@ -39,23 +42,22 @@ object DeferredEffect { } object SequentialEffect { - def apply[F[_] : Applicative, T](effects: Kleisli[F, T, Unit]*): Kleisli[F, T, Unit] = { + def apply[F[_]: Applicative, T](effects: Kleisli[F, T, Unit]*): Kleisli[F, T, Unit] = { SequentialEffect(effects.toList) } - def apply[F[_] : Applicative, T](effects: List[Kleisli[F, T, Unit]]): Kleisli[F, T, Unit] = { + def apply[F[_]: Applicative, T](effects: List[Kleisli[F, T, Unit]]): Kleisli[F, T, Unit] = { import cats.implicits._ // NOTE: [G[_] : Applicative, A]のときG[A]についていつもMonoid[G[A]]が提供されるわけではない implicit val ev: Monoid[F[Unit]] = Applicative.monoid[F, Unit] - Monoid[Kleisli[F, T, Unit]].combineAll( - effects - ) + Monoid[Kleisli[F, T, Unit]].combineAll(effects) } } object ComputedEffect { + /** * `f`により実行対象の[T]から[TargetedEffect]を純粋に計算して、それをすぐに実行するような作用を作成する. */ @@ -67,7 +69,8 @@ object UnfocusedEffect { } object DelayEffect { - def apply(duration: FiniteDuration)(implicit timer: Timer[IO]): TargetedEffect[Any] = Kleisli.liftF(IO.sleep(duration)) + def apply(duration: FiniteDuration)(implicit timer: Timer[IO]): TargetedEffect[Any] = + Kleisli.liftF(IO.sleep(duration)) } object RepeatedEffect { diff --git a/src/main/scala/com/github/unchama/targetedeffect/commandsender/MessageEffect.scala b/src/main/scala/com/github/unchama/targetedeffect/commandsender/MessageEffect.scala index 37d0d06847..424880affd 100644 --- a/src/main/scala/com/github/unchama/targetedeffect/commandsender/MessageEffect.scala +++ b/src/main/scala/com/github/unchama/targetedeffect/commandsender/MessageEffect.scala @@ -14,20 +14,18 @@ object MessageEffect { } /** - * [[MessageEffect]]の一般化。 - * 文脈が[[cats.effect.IO]]で固定されていない場合はこちらを使うと良い。 + * [[MessageEffect]]の一般化。 文脈が[[cats.effect.IO]]で固定されていない場合はこちらを使うと良い。 * - * NOTE: - * [[MessageEffectF]]の直後に[[CommandSender]]を適用する場合は、 + * NOTE: [[MessageEffectF]]の直後に[[CommandSender]]を適用する場合は、 * implicit引数である`Sync[F]`の場所に渡しているとScala2コンパイラに認識されるため、apply等と書くと良い。 * これがMessageEffectを残している理由である。Scala3に移行すればこの問題は解消される。 */ object MessageEffectF { - def apply[F[_] : Sync](string: String): Kleisli[F, CommandSender, Unit] = + def apply[F[_]: Sync](string: String): Kleisli[F, CommandSender, Unit] = TargetedEffect.delay(_.sendMessage(string)) - def apply[F[_] : Sync](stringList: List[String]): Kleisli[F, CommandSender, Unit] = + def apply[F[_]: Sync](stringList: List[String]): Kleisli[F, CommandSender, Unit] = TargetedEffect.delay(_.sendMessage(stringList.toArray)) } diff --git a/src/main/scala/com/github/unchama/targetedeffect/player/CommandEffect.scala b/src/main/scala/com/github/unchama/targetedeffect/player/CommandEffect.scala index 86601ea7f4..1da9b76645 100644 --- a/src/main/scala/com/github/unchama/targetedeffect/player/CommandEffect.scala +++ b/src/main/scala/com/github/unchama/targetedeffect/player/CommandEffect.scala @@ -7,8 +7,9 @@ import com.github.unchama.targetedeffect.TargetedEffect import org.bukkit.entity.Player object CommandEffect { - def apply(string: String) - (implicit ioOnMainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = + def apply( + string: String + )(implicit ioOnMainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = Kleisli { p => // 非同期スレッドからchatを呼ぶとコマンドがそのスレッドで実行される(Spigot 1.12.2)。 // コマンドの実行は基本的に同期スレッドで行ってほしいのでメインスレッドまで処理を飛ばす。 diff --git a/src/main/scala/com/github/unchama/targetedeffect/player/ForcedPotionEffect.scala b/src/main/scala/com/github/unchama/targetedeffect/player/ForcedPotionEffect.scala index 6040daa841..461988a389 100644 --- a/src/main/scala/com/github/unchama/targetedeffect/player/ForcedPotionEffect.scala +++ b/src/main/scala/com/github/unchama/targetedeffect/player/ForcedPotionEffect.scala @@ -10,11 +10,11 @@ object ForcedPotionEffect { trait Tag def apply(effect: PotionEffect): ForcedPotionEffect = { - val potionEffect = TargetedEffect.delay[IO, Player] { player => player.addPotionEffect(effect) } + val potionEffect = TargetedEffect.delay[IO, Player] { player => + player.addPotionEffect(effect) + } - generic.tag.tag - .apply[Tag] - .apply[TargetedEffect[Player]](potionEffect) + generic.tag.tag.apply[Tag].apply[TargetedEffect[Player]](potionEffect) } implicit class PotionEffectOps(val potionEffect: PotionEffect) { diff --git a/src/main/scala/com/github/unchama/targetedeffect/player/PlayerEffects.scala b/src/main/scala/com/github/unchama/targetedeffect/player/PlayerEffects.scala index 5138dc0c7a..564eaaab65 100644 --- a/src/main/scala/com/github/unchama/targetedeffect/player/PlayerEffects.scala +++ b/src/main/scala/com/github/unchama/targetedeffect/player/PlayerEffects.scala @@ -11,17 +11,21 @@ import org.bukkit.inventory.Inventory object PlayerEffects { val closeInventoryEffect: TargetedEffect[Player] = TargetedEffect.delay(_.closeInventory()) - def openInventoryEffect(inventory: => Inventory) - (implicit onMainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = + def openInventoryEffect( + inventory: => Inventory + )(implicit onMainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = Kleisli { player => // インベントリを開く操作はサーバースレッドでなければならない(Spigot 1.12.2) - onMainThread.runAction(SyncIO { - player.openInventory(inventory) - }).as(()) + onMainThread + .runAction(SyncIO { + player.openInventory(inventory) + }) + .as(()) } - def connectToServerEffect(serverIdentifier: String) - (implicit onMainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = + def connectToServerEffect( + serverIdentifier: String + )(implicit onMainThread: OnMinecraftServerThread[IO]): TargetedEffect[Player] = Kleisli { player => // BungeeCordのサーバ移動はサーバスレッドでなければならない(Spigot 1.12.2) onMainThread.runAction(SyncIO { @@ -32,7 +36,11 @@ object PlayerEffects { import byteArrayDataOutput._ writeUTF("Connect") writeUTF(serverIdentifier) - player.sendPluginMessage(SeichiAssist.instance, "BungeeCord", byteArrayDataOutput.toByteArray) + player.sendPluginMessage( + SeichiAssist.instance, + "BungeeCord", + byteArrayDataOutput.toByteArray + ) }) } diff --git a/src/main/scala/com/github/unchama/util/InventoryUtil.scala b/src/main/scala/com/github/unchama/util/InventoryUtil.scala index b5f557f09b..879b39a988 100644 --- a/src/main/scala/com/github/unchama/util/InventoryUtil.scala +++ b/src/main/scala/com/github/unchama/util/InventoryUtil.scala @@ -7,15 +7,17 @@ import org.bukkit.inventory.{Inventory, InventoryHolder} object InventoryUtil { - implicit class InventoryOps(val inventory: Inventory) extends AnyVal { + implicit class InventoryOps(private val inventory: Inventory) extends AnyVal { def row: Int = inventory.getSize / 9 } - def createInventory(holder: Option[InventoryHolder] = None, - size: InventorySize = 4.chestRows, - title: Option[String] = None): Inventory = + def createInventory( + holder: Option[InventoryHolder] = None, + size: InventorySize = 4.chestRows, + title: Option[String] = None + ): Inventory = size match { - case Left(size) => Bukkit.createInventory(holder.orNull, size.rows * 9, title.orNull) + case Left(size) => Bukkit.createInventory(holder.orNull, size.rows * 9, title.orNull) case Right(size) => Bukkit.createInventory(holder.orNull, size, title.orNull) } } diff --git a/src/main/scala/com/github/unchama/util/MillisecondTimer.scala b/src/main/scala/com/github/unchama/util/MillisecondTimer.scala index c1917cfcc5..7c79fb467b 100644 --- a/src/main/scala/com/github/unchama/util/MillisecondTimer.scala +++ b/src/main/scala/com/github/unchama/util/MillisecondTimer.scala @@ -3,7 +3,7 @@ package com.github.unchama.util import cats.effect.Sync import org.slf4j.Logger -class MillisecondTimer private() { +class MillisecondTimer private () { private var startTime: Long = 0 def resetTimer(): Unit = { @@ -11,7 +11,8 @@ class MillisecondTimer private() { } /** - * @deprecated use [[sendLapTimeMessageWithLogger]] + * @deprecated + * use [[sendLapTimeMessageWithLogger]] */ @Deprecated() def sendLapTimeMessage(message: String): Unit = { val recordedNanoSecondDuration = System.nanoTime() - startTime @@ -39,7 +40,7 @@ object MillisecondTimer { import cats.implicits._ - def timeF[F[_] : Sync, R](program: F[R])(message: String)(implicit logger: Logger): F[R] = + def timeF[F[_]: Sync, R](program: F[R])(message: String)(implicit logger: Logger): F[R] = for { timer <- Sync[F].delay { getInitializedTimerInstance diff --git a/src/main/scala/com/github/unchama/util/RandomEffect.scala b/src/main/scala/com/github/unchama/util/RandomEffect.scala index 601e3e2642..fc60e4cbda 100644 --- a/src/main/scala/com/github/unchama/util/RandomEffect.scala +++ b/src/main/scala/com/github/unchama/util/RandomEffect.scala @@ -36,7 +36,7 @@ trait RandomEffect[F[_]] { object RandomEffect { - def createFromRandom[F[_] : Sync](random: Random): RandomEffect[F] = new RandomEffect[F] { + def createFromRandom[F[_]: Sync](random: Random): RandomEffect[F] = new RandomEffect[F] { override def getNaturalLessThan(n: Int): F[Int] = Sync[F].delay { random.nextInt(n) } @@ -50,6 +50,6 @@ object RandomEffect { } } - def apply[F[_] : RandomEffect]: RandomEffect[F] = implicitly + def apply[F[_]: RandomEffect]: RandomEffect[F] = implicitly } diff --git a/src/main/scala/com/github/unchama/util/bukkit/EntityUtil.scala b/src/main/scala/com/github/unchama/util/bukkit/EntityUtil.scala index 8988921a3d..c8d3a6f4fb 100644 --- a/src/main/scala/com/github/unchama/util/bukkit/EntityUtil.scala +++ b/src/main/scala/com/github/unchama/util/bukkit/EntityUtil.scala @@ -8,8 +8,9 @@ import scala.reflect.ClassTag object EntityUtil { - def spawn[F[_], EntityType <: Entity : ClassTag](location: Location) - (implicit F: Sync[F]): F[EntityType] = { + def spawn[F[_], EntityType <: Entity: ClassTag]( + location: Location + )(implicit F: Sync[F]): F[EntityType] = { val classTag = implicitly[ClassTag[EntityType]] val entityClass = classTag.runtimeClass.asSubclass(classOf[Entity]) diff --git a/src/main/scala/com/github/unchama/util/bukkit/ItemStackUtil.scala b/src/main/scala/com/github/unchama/util/bukkit/ItemStackUtil.scala index 8068a27d83..3e38d8b37e 100644 --- a/src/main/scala/com/github/unchama/util/bukkit/ItemStackUtil.scala +++ b/src/main/scala/com/github/unchama/util/bukkit/ItemStackUtil.scala @@ -6,6 +6,7 @@ import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.ItemMeta object ItemStackUtil { + /** * `stacks` に含まれるアイテムスタックをできるだけマージしたような新たな `Seq` を返す */ @@ -48,7 +49,8 @@ object ItemStackUtil { def appendOwnerInformation(owner: Player)(itemStack: ItemStack): ItemStack = { import scala.jdk.CollectionConverters._ - modifyMeta { m => import m._ + modifyMeta { m => + import m._ setLore { val originalLore = if (itemStack.getItemMeta.hasLore) getLore.asScala else Nil val appended = originalLore ++ List(s"$RESET${DARK_GREEN}所有者:${owner.getName}") diff --git a/src/main/scala/com/github/unchama/util/bukkit/WorldUtil.scala b/src/main/scala/com/github/unchama/util/bukkit/WorldUtil.scala index f9402ae450..985501bf8e 100644 --- a/src/main/scala/com/github/unchama/util/bukkit/WorldUtil.scala +++ b/src/main/scala/com/github/unchama/util/bukkit/WorldUtil.scala @@ -7,8 +7,8 @@ object WorldUtil { def getAbsoluteWorldFolder(world: World): String = { val base = world.getWorldFolder.getAbsolutePath world.getEnvironment match { - case Environment.NORMAL => base - case Environment.NETHER => s"$base/DIM-1" + case Environment.NORMAL => base + case Environment.NETHER => s"$base/DIM-1" case Environment.THE_END => s"$base/DIM1" } } diff --git a/src/main/scala/com/github/unchama/util/collection/RandomizedCollection.scala b/src/main/scala/com/github/unchama/util/collection/RandomizedCollection.scala index e2036dc821..c64ea02200 100644 --- a/src/main/scala/com/github/unchama/util/collection/RandomizedCollection.scala +++ b/src/main/scala/com/github/unchama/util/collection/RandomizedCollection.scala @@ -7,7 +7,7 @@ import scala.util.Random class RandomizedCollection[Element](collection: NonEmptyList[Element]) { - def pickOne[F[_] : Sync]: F[Element] = Sync[F].delay { + def pickOne[F[_]: Sync]: F[Element] = Sync[F].delay { collection.toList(Random.nextInt(collection.size)) } diff --git a/src/main/scala/com/github/unchama/util/effect/BukkitResources.scala b/src/main/scala/com/github/unchama/util/effect/BukkitResources.scala index 07ff01f68b..ead3e0e5d7 100644 --- a/src/main/scala/com/github/unchama/util/effect/BukkitResources.scala +++ b/src/main/scala/com/github/unchama/util/effect/BukkitResources.scala @@ -7,16 +7,14 @@ import org.bukkit.entity.Entity import org.bukkit.{Location, Material} object BukkitResources { + /** * 参照された`Block`達が開放時に空気ブロックに書き換えられるような`Resource`としての`Block` */ - def vanishingBlockSetResource[ - F[_] : Sync : OnMinecraftServerThread, - B <: Block - ](referenceSet: Set[B]): Resource[F, Set[B]] = - Resource.make( - Sync[F].delay(referenceSet) - )(block => + def vanishingBlockSetResource[F[_]: Sync: OnMinecraftServerThread, B <: Block]( + referenceSet: Set[B] + ): Resource[F, Set[B]] = + Resource.make(Sync[F].delay(referenceSet))(block => OnMinecraftServerThread[F].runAction[SyncIO, Unit] { SyncIO { block.foreach(_.setType(Material.AIR)) @@ -27,17 +25,15 @@ object BukkitResources { /** * 確保された`Entity`が開放時に除去されるような`Resource`としての`Entity` */ - def vanishingEntityResource[ - F[_] : Sync : OnMinecraftServerThread, - E <: Entity - ](spawnLocation: Location, tag: Class[E]): Resource[F, E] = { - Resource.make( - OnMinecraftServerThread[F].runAction[SyncIO, E] { - SyncIO { - spawnLocation.getWorld.spawn(spawnLocation, tag) - } + def vanishingEntityResource[F[_]: Sync: OnMinecraftServerThread, E <: Entity]( + spawnLocation: Location, + tag: Class[E] + ): Resource[F, E] = { + Resource.make(OnMinecraftServerThread[F].runAction[SyncIO, E] { + SyncIO { + spawnLocation.getWorld.spawn(spawnLocation, tag) } - )(e => + })(e => OnMinecraftServerThread[F].runAction[SyncIO, Unit] { SyncIO { e.remove() diff --git a/src/main/scala/com/github/unchama/util/external/CoreProtectWrapper.scala b/src/main/scala/com/github/unchama/util/external/CoreProtectWrapper.scala index d34d9127b8..ecce685c9f 100644 --- a/src/main/scala/com/github/unchama/util/external/CoreProtectWrapper.scala +++ b/src/main/scala/com/github/unchama/util/external/CoreProtectWrapper.scala @@ -19,5 +19,5 @@ class CoreProtectWrapper(val backbone: CoreProtectAPI) { def queueBlockRemoval(who: Player, where: Location, data: BlockData): Boolean = { return backbone.logRemoval(who.getName, where, where.getBlock.getType, data) } - */ + */ } diff --git a/src/main/scala/com/github/unchama/util/external/ExternalServices.scala b/src/main/scala/com/github/unchama/util/external/ExternalServices.scala index a40275ae71..060dba6baf 100644 --- a/src/main/scala/com/github/unchama/util/external/ExternalServices.scala +++ b/src/main/scala/com/github/unchama/util/external/ExternalServices.scala @@ -9,31 +9,34 @@ import org.slf4j.Logger object ExternalServices { - def getChunkCoordinates[F[_] : Sync](chunkSearchCommand: String) - (world: World) - (implicit logger: Logger): F[Seq[(Int, Int)]] = { + def getChunkCoordinates[F[_]: Sync]( + chunkSearchCommand: String + )(world: World)(implicit logger: Logger): F[Seq[(Int, Int)]] = { import cats.implicits._ // 普通、この検索にはかなりの時間がかかるので要した時間をログに表示する - MillisecondTimer.timeF(Sync[F].delay { - val command = s"$chunkSearchCommand ${WorldUtil.getAbsoluteWorldFolder(world)}" - val result = - SearchResult.parseFrom(Runtime.getRuntime.exec(command).getInputStream) - .result - .flatMap { - // https://bukkit.org/threads/combining-world-and-world_nether.60319/ - // Bukkitの挙動でワールドとフォルダが一対一に対応するため、dimIdは無視して良い - case Chunk(Some(ChunkCoord(x, z, _)), _, _) => - Some((x, z)) - case _ => - None - } - result - })(s"${world.getName}内のチャンクを検索しました。").flatTap { seq => - Sync[F].delay { - logger.info(s"変換対象チャンク数${seq.size}") + MillisecondTimer + .timeF(Sync[F].delay { + val command = s"$chunkSearchCommand ${WorldUtil.getAbsoluteWorldFolder(world)}" + val result = + SearchResult + .parseFrom(Runtime.getRuntime.exec(command).getInputStream) + .result + .flatMap { + // https://bukkit.org/threads/combining-world-and-world_nether.60319/ + // Bukkitの挙動でワールドとフォルダが一対一に対応するため、dimIdは無視して良い + case Chunk(Some(ChunkCoord(x, z, _)), _, _) => + Some((x, z)) + case _ => + None + } + result + })(s"${world.getName}内のチャンクを検索しました。") + .flatTap { seq => + Sync[F].delay { + logger.info(s"変換対象チャンク数${seq.size}") + } } - } } } diff --git a/src/main/scala/com/github/unchama/util/instances/GregorianCalendarInstances.scala b/src/main/scala/com/github/unchama/util/instances/GregorianCalendarInstances.scala index a95e1c7f89..86db2921d4 100644 --- a/src/main/scala/com/github/unchama/util/instances/GregorianCalendarInstances.scala +++ b/src/main/scala/com/github/unchama/util/instances/GregorianCalendarInstances.scala @@ -3,10 +3,11 @@ package com.github.unchama.util.instances import java.util.GregorianCalendar trait GregorianCalendarInstances { - implicit val instanceForGregorianCalendarOrdering: Ordering[GregorianCalendar] = new GregorianCalendarOrdering + implicit val instanceForGregorianCalendarOrdering: Ordering[GregorianCalendar] = + new GregorianCalendarOrdering } class GregorianCalendarOrdering extends Ordering[GregorianCalendar] { override def compare(x: GregorianCalendar, y: GregorianCalendar): Int = x.getTimeInMillis.compare(y.getTimeInMillis) -} \ No newline at end of file +} diff --git a/src/main/scala/com/github/unchama/util/instances/instances.scala b/src/main/scala/com/github/unchama/util/instances/instances.scala index 193fe57c42..c3f9287590 100644 --- a/src/main/scala/com/github/unchama/util/instances/instances.scala +++ b/src/main/scala/com/github/unchama/util/instances/instances.scala @@ -1,4 +1,3 @@ package com.github.unchama.util -package object instances extends - GregorianCalendarInstances +package object instances extends GregorianCalendarInstances diff --git a/src/main/scala/com/github/unchama/util/logging/log4cats/PrefixedLogger.scala b/src/main/scala/com/github/unchama/util/logging/log4cats/PrefixedLogger.scala index 57e9edfd10..93ffd39196 100644 --- a/src/main/scala/com/github/unchama/util/logging/log4cats/PrefixedLogger.scala +++ b/src/main/scala/com/github/unchama/util/logging/log4cats/PrefixedLogger.scala @@ -4,6 +4,6 @@ import io.chrisdavenport.log4cats.Logger object PrefixedLogger { - def apply[F[_] : Logger](prefix: String): Logger[F] = TransformingLogger(prefix + _) + def apply[F[_]: Logger](prefix: String): Logger[F] = TransformingLogger(prefix + _) } diff --git a/src/main/scala/com/github/unchama/util/logging/log4cats/TransformingLogger.scala b/src/main/scala/com/github/unchama/util/logging/log4cats/TransformingLogger.scala index 1a8ab4d7a9..36c690a965 100644 --- a/src/main/scala/com/github/unchama/util/logging/log4cats/TransformingLogger.scala +++ b/src/main/scala/com/github/unchama/util/logging/log4cats/TransformingLogger.scala @@ -2,7 +2,7 @@ package com.github.unchama.util.logging.log4cats import io.chrisdavenport.log4cats.Logger -class TransformingLogger[F[_] : Logger](f: String => String) extends Logger[F] { +class TransformingLogger[F[_]: Logger](f: String => String) extends Logger[F] { override def error(t: Throwable)(message: => String): F[Unit] = Logger[F].error(t)(f(message)) override def warn(t: Throwable)(message: => String): F[Unit] = Logger[F].warn(t)(f(message)) @@ -26,6 +26,7 @@ class TransformingLogger[F[_] : Logger](f: String => String) extends Logger[F] { object TransformingLogger { - def apply[F[_] : Logger](f: String => String): TransformingLogger[F] = new TransformingLogger[F](f) + def apply[F[_]: Logger](f: String => String): TransformingLogger[F] = + new TransformingLogger[F](f) } diff --git a/src/main/scala/com/github/unchama/util/nms/v1_12_2/world/WorldChunkSaving.scala b/src/main/scala/com/github/unchama/util/nms/v1_12_2/world/WorldChunkSaving.scala index 4e4625a1ef..4b048b1366 100644 --- a/src/main/scala/com/github/unchama/util/nms/v1_12_2/world/WorldChunkSaving.scala +++ b/src/main/scala/com/github/unchama/util/nms/v1_12_2/world/WorldChunkSaving.scala @@ -2,7 +2,6 @@ package com.github.unchama.util.nms.v1_12_2.world import cats.effect.{Concurrent, Sync} - object WorldChunkSaving { import scala.jdk.CollectionConverters._ @@ -12,7 +11,8 @@ object WorldChunkSaving { private val craftBukkitPackage_1_12_R1 = "org.bukkit.craftbukkit.v1_12_R1" object FileIOThread { - private[Reflection] lazy val clazz: Class[_] = Class.forName(s"$nmsPackage_1_12_R1.FileIOThread") + private[Reflection] lazy val clazz: Class[_] = + Class.forName(s"$nmsPackage_1_12_R1.FileIOThread") // public static FileIOThread method() lazy val getInstance: () => AnyRef = { @@ -31,7 +31,8 @@ object WorldChunkSaving { } object Entity { - private[Reflection] lazy val clazz: Class[_] = Class.forName(s"$nmsPackage_1_12_R1.Entity") + private[Reflection] lazy val clazz: Class[_] = + Class.forName(s"$nmsPackage_1_12_R1.Entity") // public int field lazy val chunkX: AnyRef => Int = { @@ -96,16 +97,19 @@ object WorldChunkSaving { // public Chunk method(int, int) lazy val getChunkAtCoordinate: AnyRef => (Int, Int) => AnyRef = { val method = clazz.getDeclaredMethod("getChunkAt", Integer.TYPE, Integer.TYPE) - receiver => { - case (x, z) => method.invoke(receiver, x, z) - } + receiver => { case (x, z) => method.invoke(receiver, x, z) } } // public boolean method(int, int) // originally // protected boolean method(int, int, boolean) lazy val isChunkLoaded: AnyRef => (Int, Int) => Boolean = { - val method = clazz.getDeclaredMethod("isChunkLoaded", Integer.TYPE, Integer.TYPE, java.lang.Boolean.TYPE) + val method = clazz.getDeclaredMethod( + "isChunkLoaded", + Integer.TYPE, + Integer.TYPE, + java.lang.Boolean.TYPE + ) method.setAccessible(true) @@ -116,7 +120,8 @@ object WorldChunkSaving { } object CraftWorld { - private[Reflection] lazy val clazz: Class[_] = Class.forName(s"$craftBukkitPackage_1_12_R1.CraftWorld") + private[Reflection] lazy val clazz: Class[_] = + Class.forName(s"$craftBukkitPackage_1_12_R1.CraftWorld") // public final nms.WorldServer (<: nms.World) // originally @@ -135,38 +140,43 @@ object WorldChunkSaving { import Reflection._ /** - * In a running minecraft server, there is an internal queue which is used in controlling and limiting chunk saves. + * In a running minecraft server, there is an internal queue which is used in controlling and + * limiting chunk saves. * - * This action, when running, relaxes the save-queue throttle. - * The returned action completes when there are no more chunks to be saved. + * This action, when running, relaxes the save-queue throttle. The returned action completes + * when there are no more chunks to be saved. */ def relaxFileIOThreadThrottle[F[_]](implicit F: Concurrent[F]): F[Unit] = Sync[F].delay { FileIOThread.relaxThrottle(FileIOThread.instance)() } /** - * Every world has its internal queue to remove entities or tile-entities. - * They are normally only cleared when ticking the world, but this method forces to cleanup these queues. + * Every world has its internal queue to remove entities or tile-entities. They are normally + * only cleared when ticking the world, but this method forces to cleanup these queues. */ - def flushEntityRemovalQueue[F[_]](world: org.bukkit.World)(implicit F: Sync[F]): F[Unit] = F.delay { - val nmsWorldServer = CraftWorld.nmsWorld(world) - val removalQueueAlias = World.entityRemovalQueue(nmsWorldServer) + def flushEntityRemovalQueue[F[_]](world: org.bukkit.World)(implicit F: Sync[F]): F[Unit] = + F.delay { + val nmsWorldServer = CraftWorld.nmsWorld(world) + val removalQueueAlias = World.entityRemovalQueue(nmsWorldServer) - World.entityList(nmsWorldServer).removeAll(removalQueueAlias) + World.entityList(nmsWorldServer).removeAll(removalQueueAlias) - removalQueueAlias.asScala.foreach { entity => - val entityChunkX = Entity.chunkX(entity) - val entityChunkZ = Entity.chunkZ(entity) + removalQueueAlias.asScala.foreach { entity => + val entityChunkX = Entity.chunkX(entity) + val entityChunkZ = Entity.chunkZ(entity) - if (Entity.loadedToAChunk(entity) && World.isChunkLoaded(nmsWorldServer)(entityChunkX, entityChunkZ)) { - val chunk = World.getChunkAtCoordinate(nmsWorldServer)(entityChunkX, entityChunkZ) + if ( + Entity.loadedToAChunk(entity) && World + .isChunkLoaded(nmsWorldServer)(entityChunkX, entityChunkZ) + ) { + val chunk = World.getChunkAtCoordinate(nmsWorldServer)(entityChunkX, entityChunkZ) - Chunk.untrackEntity(chunk)(entity) + Chunk.untrackEntity(chunk)(entity) + } + + World.untrackEntity(nmsWorldServer)(entity) } - World.untrackEntity(nmsWorldServer)(entity) + removalQueueAlias.clear() } - - removalQueueAlias.clear() - } } diff --git a/src/main/scala/com/github/unchama/util/syntax/Nullability.scala b/src/main/scala/com/github/unchama/util/syntax/Nullability.scala index 716d824818..51b5aed643 100644 --- a/src/main/scala/com/github/unchama/util/syntax/Nullability.scala +++ b/src/main/scala/com/github/unchama/util/syntax/Nullability.scala @@ -3,13 +3,15 @@ package com.github.unchama.util.syntax import scala.language.implicitConversions trait NullabilitySyntax { - implicit def toNullabilityExtensionReceiverOps[T](x: T): Nullability.NullabilityExtensionReceiver[T] = + implicit def toNullabilityExtensionReceiverOps[T]( + x: T + ): Nullability.NullabilityExtensionReceiver[T] = new Nullability.NullabilityExtensionReceiver(x) } object Nullability { - implicit class NullabilityExtensionReceiver[T](val receiver: T) extends AnyVal { + implicit class NullabilityExtensionReceiver[T](private val receiver: T) extends AnyVal { def ifNull(f: => T): T = if (receiver == null) f else receiver def ifNotNull[R](f: T => R): R = if (receiver != null) f(receiver) else null.asInstanceOf[R] diff --git a/src/main/scala/com/github/unchama/util/syntax/ResultSetSyntax.scala b/src/main/scala/com/github/unchama/util/syntax/ResultSetSyntax.scala index 17df89e2ab..b7feb683a7 100644 --- a/src/main/scala/com/github/unchama/util/syntax/ResultSetSyntax.scala +++ b/src/main/scala/com/github/unchama/util/syntax/ResultSetSyntax.scala @@ -1,9 +1,8 @@ package com.github.unchama.util.syntax -import java.sql.ResultSet - import com.github.unchama.util.syntax +import java.sql.ResultSet import scala.language.implicitConversions trait ResultSetSyntax { diff --git a/src/main/scala/com/github/unchama/util/syntax/package.scala b/src/main/scala/com/github/unchama/util/syntax/package.scala index 11e6c19843..d625a53313 100644 --- a/src/main/scala/com/github/unchama/util/syntax/package.scala +++ b/src/main/scala/com/github/unchama/util/syntax/package.scala @@ -1,5 +1,3 @@ package com.github.unchama.util -package object syntax - extends NullabilitySyntax - with ResultSetSyntax +package object syntax extends NullabilitySyntax with ResultSetSyntax diff --git a/src/main/scala/com/github/unchama/util/time/LocalDateTimeUtil.scala b/src/main/scala/com/github/unchama/util/time/LocalDateTimeUtil.scala index 8f584f70ae..1a002e6381 100644 --- a/src/main/scala/com/github/unchama/util/time/LocalDateTimeUtil.scala +++ b/src/main/scala/com/github/unchama/util/time/LocalDateTimeUtil.scala @@ -4,12 +4,14 @@ import java.time.{LocalDateTime, ZoneOffset} import scala.concurrent.duration.FiniteDuration object LocalDateTimeUtil { + /** - * t2からどれくらい経過すればt1になるかを計算する。 - * t1がt2より前であった場合、負の[[FiniteDuration]]が返る。 + * t2からどれくらい経過すればt1になるかを計算する。 t1がt2より前であった場合、負の[[FiniteDuration]]が返る。 */ def difference(t1: LocalDateTime, t2: LocalDateTime): FiniteDuration = { import scala.concurrent.duration._ - (t1.toInstant(ZoneOffset.UTC).toEpochMilli - t2.toInstant(ZoneOffset.UTC).toEpochMilli).milliseconds + (t1.toInstant(ZoneOffset.UTC).toEpochMilli - t2 + .toInstant(ZoneOffset.UTC) + .toEpochMilli).milliseconds } } diff --git a/src/main/scala/com/github/unchama/util/time/LocalTimeUtil.scala b/src/main/scala/com/github/unchama/util/time/LocalTimeUtil.scala index 2c26545d60..cf1b9cea20 100644 --- a/src/main/scala/com/github/unchama/util/time/LocalTimeUtil.scala +++ b/src/main/scala/com/github/unchama/util/time/LocalTimeUtil.scala @@ -1,13 +1,12 @@ package com.github.unchama.util.time import java.time.LocalTime - import scala.concurrent.duration.FiniteDuration object LocalTimeUtil { + /** - * t2からどれくらい経過すればt1になるかを計算する。 - * t1がt2より前であった場合、負の[[FiniteDuration]]が返る。 + * t2からどれくらい経過すればt1になるかを計算する。 t1がt2より前であった場合、負の[[FiniteDuration]]が返る。 */ def difference(t1: LocalTime, t2: LocalTime): FiniteDuration = { import scala.concurrent.duration._ diff --git a/src/test/scala/com/github/unchama/generic/MapExtraSpec.scala b/src/test/scala/com/github/unchama/generic/MapExtraSpec.scala index d50097bb76..9057201b71 100644 --- a/src/test/scala/com/github/unchama/generic/MapExtraSpec.scala +++ b/src/test/scala/com/github/unchama/generic/MapExtraSpec.scala @@ -26,14 +26,14 @@ class MapExtraSpec extends AnyWordSpec { "return filled Map when given Map is empty" in { assert( - MapExtra.fillOnBaseSet( - Map(), - Set("A", "B", "C", "D"), - 42 - ) == Map("A" -> 42, "B" -> 42, "C" -> 42, "D" -> 42) + MapExtra.fillOnBaseSet(Map(), Set("A", "B", "C", "D"), 42) == Map( + "A" -> 42, + "B" -> 42, + "C" -> 42, + "D" -> 42 + ) ) } - } } diff --git a/src/test/scala/com/github/unchama/generic/OptionTExtraSpec.scala b/src/test/scala/com/github/unchama/generic/OptionTExtraSpec.scala index f9d9d7a91b..555bfcf135 100644 --- a/src/test/scala/com/github/unchama/generic/OptionTExtraSpec.scala +++ b/src/test/scala/com/github/unchama/generic/OptionTExtraSpec.scala @@ -16,7 +16,9 @@ class OptionTExtraSpec extends AnyWordSpec { } "produce succeeding OptionT[Id, *] on false" in { - assert(OptionTExtra.failIf[Id](failCondition = false) == OptionT.some(())(Applicative[Id])) + assert( + OptionTExtra.failIf[Id](failCondition = false) == OptionT.some(())(Applicative[Id]) + ) } "produce succeeding OptionT[IO, *] on false" in { diff --git a/src/test/scala/com/github/unchama/generic/algebra/typeclass/HasSuccessorSpec.scala b/src/test/scala/com/github/unchama/generic/algebra/typeclass/HasSuccessorSpec.scala index 5aba10ce51..d6591e4b03 100644 --- a/src/test/scala/com/github/unchama/generic/algebra/typeclass/HasSuccessorSpec.scala +++ b/src/test/scala/com/github/unchama/generic/algebra/typeclass/HasSuccessorSpec.scala @@ -7,10 +7,7 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -class HasSuccessorSpec - extends AnyWordSpec - with ScalaCheckPropertyChecks - with Matchers { +class HasSuccessorSpec extends AnyWordSpec with ScalaCheckPropertyChecks with Matchers { import cats.implicits._ @@ -42,7 +39,7 @@ class HasSuccessorSpec assert { range.sliding(2).forall { case Seq(a, b) => a + 1 == b - case _ => true + case _ => true } } } @@ -52,9 +49,7 @@ class HasSuccessorSpec "be sequence of elements between lower and upper inclusively" in { forAll { (lower: T, upper: T) => assert { - testTInstance.closedRange(lower, upper).forall { a => - lower <= a && a <= upper - } + testTInstance.closedRange(lower, upper).forall { a => lower <= a && a <= upper } } } } diff --git a/src/test/scala/com/github/unchama/generic/effect/ConcurrentExtraSpec.scala b/src/test/scala/com/github/unchama/generic/effect/ConcurrentExtraSpec.scala index ac231c0f95..54902a23ae 100644 --- a/src/test/scala/com/github/unchama/generic/effect/ConcurrentExtraSpec.scala +++ b/src/test/scala/com/github/unchama/generic/effect/ConcurrentExtraSpec.scala @@ -35,17 +35,19 @@ class ConcurrentExtraSpec extends AnyWordSpec with Matchers with MockFactory { val program = for { blockerList <- LinkedSequencer[IO].newBlockerList promise <- Deferred[IO, CancelToken[IO]] - _ <- ConcurrentExtra.withSelfCancellation[IO, Unit] { cancelToken => - for { - _ <- promise.complete(cancelToken) - //noinspection ZeroIndexToHead - _ <- { - blockerList(0).await() >> IO.never - }.guarantee { - runSubProcessFinalizer - } - } yield () - }.start + _ <- ConcurrentExtra + .withSelfCancellation[IO, Unit] { cancelToken => + for { + _ <- promise.complete(cancelToken) + // noinspection ZeroIndexToHead + _ <- { + blockerList(0).await() >> IO.never + }.guarantee { + runSubProcessFinalizer + } + } yield () + } + .start returnedCancelToken <- promise.get _ <- blockerList(1).await() // let started fiber reach IO.never _ <- returnedCancelToken // subProcessFinalizer should be called diff --git a/src/test/scala/com/github/unchama/generic/effect/ResourceScopeSpec.scala b/src/test/scala/com/github/unchama/generic/effect/ResourceScopeSpec.scala index 7a75704634..dae0b1b890 100644 --- a/src/test/scala/com/github/unchama/generic/effect/ResourceScopeSpec.scala +++ b/src/test/scala/com/github/unchama/generic/effect/ResourceScopeSpec.scala @@ -20,10 +20,11 @@ class ResourceScopeSpec extends AnyWordSpec with Matchers with MockFactory { val firstResourceScope: ResourceScope[IO, IO, NumberedObject] = ResourceScope.unsafeCreate val secondResourceScope: ResourceScope[IO, IO, NumberedObject] = ResourceScope.unsafeCreate - def useTracked[A](scope: ResourceScope[IO, IO, NumberedObject], - obj: NumberedObject, - impureFinalizer: NumberedObject => Unit = _ => ()) - (use: NumberedObject => IO[A]): IO[A] = { + def useTracked[A]( + scope: ResourceScope[IO, IO, NumberedObject], + obj: NumberedObject, + impureFinalizer: NumberedObject => Unit = _ => () + )(use: NumberedObject => IO[A]): IO[A] = { val resource = Resource.make(IO.pure(obj))(o => IO(impureFinalizer(o))) scope.useTracked(resource)(use) @@ -34,19 +35,26 @@ class ResourceScopeSpec extends AnyWordSpec with Matchers with MockFactory { useTracked(firstResourceScope, NumberedObject(0)) { _ => for { - _ <- IO { firstResourceScope.trackedHandlers.unsafeRunSync() mustBe Set(NumberedObject(0)) } + _ <- IO { + firstResourceScope.trackedHandlers.unsafeRunSync() mustBe Set(NumberedObject(0)) + } _ <- IO { secondResourceScope.trackedHandlers.unsafeRunSync() mustBe Set() } _ <- useTracked(firstResourceScope, NumberedObject(1)) { _ => for { _ <- IO { - firstResourceScope.trackedHandlers.unsafeRunSync() mustBe Set(NumberedObject(0), NumberedObject(1)) + firstResourceScope.trackedHandlers.unsafeRunSync() mustBe Set( + NumberedObject(0), + NumberedObject(1) + ) } _ <- IO { secondResourceScope.trackedHandlers.unsafeRunSync() mustBe Set() } } yield () } - _ <- IO { firstResourceScope.trackedHandlers.unsafeRunSync() mustBe Set(NumberedObject(0)) } + _ <- IO { + firstResourceScope.trackedHandlers.unsafeRunSync() mustBe Set(NumberedObject(0)) + } } yield () }.unsafeRunSync() @@ -58,7 +66,8 @@ class ResourceScopeSpec extends AnyWordSpec with Matchers with MockFactory { resourceEffect.expects(NumberedObject(0)).once() - useTracked(firstResourceScope, NumberedObject(0)) { o => IO { resourceEffect(o) } }.unsafeRunSync() + useTracked(firstResourceScope, NumberedObject(0)) { o => IO { resourceEffect(o) } } + .unsafeRunSync() } "call finalizer of the resource exactly once on release" in { @@ -91,7 +100,7 @@ class ResourceScopeSpec extends AnyWordSpec with Matchers with MockFactory { blockerList <- LinkedSequencer[IO].newBlockerList _ <- useTracked(firstResourceScope, NumberedObject(0), finalizer) { o => - //noinspection ZeroIndexToHead + // noinspection ZeroIndexToHead runImpureFunction(o) >> blockerList(0).await() >> IO.never @@ -128,7 +137,7 @@ class ResourceScopeSpec extends AnyWordSpec with Matchers with MockFactory { blockerList <- LinkedSequencer[IO].newBlockerList _ <- useTracked(firstResourceScope, NumberedObject(0), finalizer) { o => - //noinspection ZeroIndexToHead + // noinspection ZeroIndexToHead runImpureFunction(o) >> blockerList(0).await >> IO.never @@ -149,16 +158,21 @@ class ResourceScopeSpec extends AnyWordSpec with Matchers with MockFactory { implicit val shift: ContextShift[IO] = IO.contextShift(ExecutionContext.global) implicit val timer: Timer[IO] = IO.timer(ExecutionContext.global) - val firstResourceScope: SingleResourceScope[IO, SyncIO, NumberedObject] = ResourceScope.unsafeCreateSingletonScope - val secondResourceScope: SingleResourceScope[IO, SyncIO, NumberedObject] = ResourceScope.unsafeCreateSingletonScope - - def useTrackedForSome[A](scope: SingleResourceScope[IO, SyncIO, NumberedObject], - obj: NumberedObject, - impureFinalizer: NumberedObject => Unit = _ => ()) - (use: NumberedObject => IO[A]): IO[Option[A]] = { - val resource = Resource.make(IO.pure(obj))(o => IO { - impureFinalizer(o) - }) + val firstResourceScope: SingleResourceScope[IO, SyncIO, NumberedObject] = + ResourceScope.unsafeCreateSingletonScope + val secondResourceScope: SingleResourceScope[IO, SyncIO, NumberedObject] = + ResourceScope.unsafeCreateSingletonScope + + def useTrackedForSome[A]( + scope: SingleResourceScope[IO, SyncIO, NumberedObject], + obj: NumberedObject, + impureFinalizer: NumberedObject => Unit = _ => () + )(use: NumberedObject => IO[A]): IO[Option[A]] = { + val resource = Resource.make(IO.pure(obj))(o => + IO { + impureFinalizer(o) + } + ) scope.useTrackedForSome(resource)(use) } @@ -239,7 +253,7 @@ class ResourceScopeSpec extends AnyWordSpec with Matchers with MockFactory { blockerList <- LinkedSequencer[IO].newBlockerList _ <- useTrackedForSome(firstResourceScope, NumberedObject(0), finalizer) { o => - //noinspection ZeroIndexToHead + // noinspection ZeroIndexToHead runImpureFunction(o) >> blockerList(0).await >> IO.never @@ -276,7 +290,7 @@ class ResourceScopeSpec extends AnyWordSpec with Matchers with MockFactory { blockerList <- LinkedSequencer[IO].newBlockerList _ <- useTrackedForSome(firstResourceScope, NumberedObject(0), finalizer) { o => - //noinspection ZeroIndexToHead + // noinspection ZeroIndexToHead runImpureFunction(o) >> blockerList(0).await() >> IO.never diff --git a/src/test/scala/com/github/unchama/generic/effect/concurrent/AsymmetricSignallingRefSpec.scala b/src/test/scala/com/github/unchama/generic/effect/concurrent/AsymmetricSignallingRefSpec.scala index a6b7a2f9a1..b0d1642e15 100644 --- a/src/test/scala/com/github/unchama/generic/effect/concurrent/AsymmetricSignallingRefSpec.scala +++ b/src/test/scala/com/github/unchama/generic/effect/concurrent/AsymmetricSignallingRefSpec.scala @@ -13,7 +13,7 @@ import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import scala.concurrent.ExecutionContext class AsymmetricSignallingRefSpec - extends AnyWordSpec + extends AnyWordSpec with ScalaCheckPropertyChecks with Matchers with ConcurrentEffectTest @@ -21,8 +21,11 @@ class AsymmetricSignallingRefSpec import scala.concurrent.duration._ - implicit override val patienceConfig: PatienceConfig = PatienceConfig(timeout = 5.seconds, interval = 10.millis) - implicit val monixScheduler: TestScheduler = TestScheduler(ExecutionModel.AlwaysAsyncExecution) + implicit override val patienceConfig: PatienceConfig = + PatienceConfig(timeout = 5.seconds, interval = 10.millis) + implicit val monixScheduler: TestScheduler = TestScheduler( + ExecutionModel.AlwaysAsyncExecution + ) implicit val monixTimer: Timer[Task] = Task.timer(monixScheduler) implicit val contextShift: ContextShift[IO] = IO.contextShift(ExecutionContext.global) @@ -39,19 +42,17 @@ class AsymmetricSignallingRefSpec val task = for { ref <- AsymmetricSignallingRef.in[Task, Task, Task, Value](initialValue) updateResult <- - ref - .valuesAwait - .use { stream => - for { - resultFiber <- stream.take(updates.length).compile.toList.start - _ <- updates.traverse(ref.set) - result <- resultFiber.join - } yield result - } + ref.valuesAwait.use { stream => + for { + resultFiber <- stream.take(updates.length).compile.toList.start + _ <- updates.traverse(ref.set) + result <- resultFiber.join + } yield result + } } yield updateResult assertResult(updates)(awaitForProgram(task, 1.second)) } } } -} \ No newline at end of file +} diff --git a/src/test/scala/com/github/unchama/generic/effect/stream/ReorderingPipeSpec.scala b/src/test/scala/com/github/unchama/generic/effect/stream/ReorderingPipeSpec.scala index a88b34f10d..4b98364c81 100644 --- a/src/test/scala/com/github/unchama/generic/effect/stream/ReorderingPipeSpec.scala +++ b/src/test/scala/com/github/unchama/generic/effect/stream/ReorderingPipeSpec.scala @@ -14,7 +14,7 @@ import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import scala.util.Random class ReorderingPipeSpec - extends AnyWordSpec + extends AnyWordSpec with ScalaCheckPropertyChecks with Matchers with ConcurrentEffectTest @@ -22,8 +22,11 @@ class ReorderingPipeSpec import scala.concurrent.duration._ - implicit override val patienceConfig: PatienceConfig = PatienceConfig(timeout = 5.seconds, interval = 10.millis) - implicit val monixScheduler: TestScheduler = TestScheduler(ExecutionModel.AlwaysAsyncExecution) + implicit override val patienceConfig: PatienceConfig = + PatienceConfig(timeout = 5.seconds, interval = 10.millis) + implicit val monixScheduler: TestScheduler = TestScheduler( + ExecutionModel.AlwaysAsyncExecution + ) "ReorderingPipe" should { type TestInputType = Long @@ -54,9 +57,13 @@ class ReorderingPipeSpec } val program = - fs2.Stream + fs2 + .Stream .evals(createRandomizedInput) - .through(ReorderingPipe.withInitialToken[SyncIO, TestInputType](timeStamped.head.currentStamp)) + .through( + ReorderingPipe + .withInitialToken[SyncIO, TestInputType](timeStamped.head.currentStamp) + ) .compile .toList @@ -65,4 +72,4 @@ class ReorderingPipeSpec } } } -} \ No newline at end of file +} diff --git a/src/test/scala/com/github/unchama/generic/effect/stream/StreamExtraSpec.scala b/src/test/scala/com/github/unchama/generic/effect/stream/StreamExtraSpec.scala index 49b7e21476..e561ee3088 100644 --- a/src/test/scala/com/github/unchama/generic/effect/stream/StreamExtraSpec.scala +++ b/src/test/scala/com/github/unchama/generic/effect/stream/StreamExtraSpec.scala @@ -8,7 +8,7 @@ import org.scalatest.wordspec.AnyWordSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks class StreamExtraSpec - extends AnyWordSpec + extends AnyWordSpec with ScalaCheckPropertyChecks with Matchers with ConcurrentEffectTest @@ -16,7 +16,8 @@ class StreamExtraSpec "StreamExtra.takeEvery" should { "be equivalent to accessing every n elements" in { - implicit val positiveIntGenerator: Arbitrary[Int] = Arbitrary(Gen.chooseNum(1, Int.MaxValue)) + implicit val positiveIntGenerator: Arbitrary[Int] = + Arbitrary(Gen.chooseNum(1, Int.MaxValue)) val n = 3 diff --git a/src/test/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiterSpec.scala b/src/test/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiterSpec.scala index 3b47f2bee3..9debe21114 100644 --- a/src/test/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiterSpec.scala +++ b/src/test/scala/com/github/unchama/generic/ratelimiting/FixedWindowRateLimiterSpec.scala @@ -13,7 +13,7 @@ import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import scala.util.Random class FixedWindowRateLimiterSpec - extends GenericRateLimiterSpec + extends GenericRateLimiterSpec with ScalaCheckPropertyChecks with TaskDiscreteEventually { @@ -22,7 +22,9 @@ class FixedWindowRateLimiterSpec import scala.concurrent.duration._ - override def newRandomRateLimiter(seed: Int)(implicit monixTimer: Timer[Task]): Task[RateLimiter[Task, Natural]] = { + override def newRandomRateLimiter( + seed: Int + )(implicit monixTimer: Timer[Task]): Task[RateLimiter[Task, Natural]] = { val random = new Random(seed) val maxPermit = refineV[NonNegative].unsafeFrom(random.nextInt(1000)) val sleepTime = random.nextInt(60000).millis @@ -30,10 +32,14 @@ class FixedWindowRateLimiterSpec FixedWindowRateLimiter.in[Task, Natural](maxPermit, sleepTime) } - implicit override val patienceConfig: PatienceConfig = PatienceConfig(timeout = 5.seconds, interval = 10.millis) - implicit override val discreteEventuallyConfig: DiscreteEventuallyConfig = DiscreteEventuallyConfig(10000) + implicit override val patienceConfig: PatienceConfig = + PatienceConfig(timeout = 5.seconds, interval = 10.millis) + implicit override val discreteEventuallyConfig: DiscreteEventuallyConfig = + DiscreteEventuallyConfig(10000) - implicit val monixScheduler: TestScheduler = TestScheduler(ExecutionModel.SynchronousExecution) + implicit val monixScheduler: TestScheduler = TestScheduler( + ExecutionModel.SynchronousExecution + ) implicit val monixTimer: Timer[Task] = SchedulerEffect.timer(monixScheduler) "Fixed window limiter" should { @@ -76,12 +82,14 @@ class FixedWindowRateLimiterSpec val program = for { rateLimiter <- FixedWindowRateLimiter.in[Task, Natural](maxCount, 1.minute) - allowances <- (1 to windowCount).toList.traverse(_ => - (1 to maxCount) - .toList - .traverse(_ => rateLimiter.requestPermission(1)) - .flatTap(_ => monixTimer.sleep(1.minute + 1.second)) - ) + allowances <- (1 to windowCount) + .toList + .traverse(_ => + (1 to maxCount) + .toList + .traverse(_ => rateLimiter.requestPermission(1)) + .flatTap(_ => monixTimer.sleep(1.minute + 1.second)) + ) } yield { val expected = List.fill(windowCount * maxCount)(1: Natural) assert(allowances.flatten == expected) diff --git a/src/test/scala/com/github/unchama/generic/ratelimiting/GenericRateLimiterSpec.scala b/src/test/scala/com/github/unchama/generic/ratelimiting/GenericRateLimiterSpec.scala index 254247d401..2197b99f05 100644 --- a/src/test/scala/com/github/unchama/generic/ratelimiting/GenericRateLimiterSpec.scala +++ b/src/test/scala/com/github/unchama/generic/ratelimiting/GenericRateLimiterSpec.scala @@ -23,12 +23,14 @@ import scala.util.Random * Mix-inし、`newRandomRateLimiter` を各[[RateLimiter]]の実装でoverrideして利用されることを想定している。 */ trait GenericRateLimiterSpec - extends AnyWordSpecLike - with Matchers - with ConcurrentEffectTest - with MonixTestSchedulerTests { - - implicit private val monixScheduler: TestScheduler = TestScheduler(ExecutionModel.SynchronousExecution) + extends AnyWordSpecLike + with Matchers + with ConcurrentEffectTest + with MonixTestSchedulerTests { + + implicit private val monixScheduler: TestScheduler = TestScheduler( + ExecutionModel.SynchronousExecution + ) implicit private val monixTimer: Timer[Task] = SchedulerEffect.timer(monixScheduler) type Natural = Int Refined NonNegative @@ -48,20 +50,22 @@ trait GenericRateLimiterSpec /** * 新しい [[RateLimiter]] を - * - `seed` をパラメータ生成のシード - * - `monixTimer` をスケジューラ + * - `seed` をパラメータ生成のシード + * - `monixTimer` をスケジューラ * * として作成する。 */ - def newRandomRateLimiter(seed: Int)(implicit monixTimer: Timer[Task]): Task[RateLimiter[Task, Natural]] + def newRandomRateLimiter(seed: Int)( + implicit monixTimer: Timer[Task] + ): Task[RateLimiter[Task, Natural]] /** * [[RateLimiter.peekAvailablePermissions]] の呼び出し自体が、[[RateLimiter]]の動作に干渉しないことをテストする。 * * このテスト項目は、より具体的には以下のような手続きを取る: - * - レートリミッターを二つ作成し、片方で [[RateLimiter.peekAvailablePermissions]] を呼ぶ - * - ランダムな秒数 (60秒以下) 時計の針を進める - * - 二つのレートリミッターの [[RateLimiter]] を呼び、結果が等しいことを確認する + * - レートリミッターを二つ作成し、片方で [[RateLimiter.peekAvailablePermissions]] を呼ぶ + * - ランダムな秒数 (60秒以下) 時計の針を進める + * - 二つのレートリミッターの [[RateLimiter]] を呼び、結果が等しいことを確認する */ def keepPermitsEqual(): Unit = { import scala.concurrent.duration._ diff --git a/src/test/scala/com/github/unchama/seichiassist/ManagedWorldSpec.scala b/src/test/scala/com/github/unchama/seichiassist/ManagedWorldSpec.scala index b8068d178a..74e5550757 100644 --- a/src/test/scala/com/github/unchama/seichiassist/ManagedWorldSpec.scala +++ b/src/test/scala/com/github/unchama/seichiassist/ManagedWorldSpec.scala @@ -38,20 +38,22 @@ class ManagedWorldSpec extends AnyWordSpec with MockFactory { "World.isBlockLineUpSkillEnabled" should { "return the appropriate truth-value" in { - forAll(blockLineUpSkillEnableMap) { case (worldName, value) => - val world = mock[World] - (world.getName _).expects().returning(worldName) - assert(world.isBlockLineUpSkillEnabled == value) + forAll(blockLineUpSkillEnableMap) { + case (worldName, value) => + val world = mock[World] + (world.getName _).expects().returning(worldName) + assert(world.isBlockLineUpSkillEnabled == value) } } } "World.shouldTrackBuildBlock" should { "return the appropriate truth-value" in { - forAll(inTrackedWorldMap) { case (worldName, value) => - val world = mock[World] - (world.getName _).expects().returning(worldName) - assert(world.shouldTrackBuildBlock == value) + forAll(inTrackedWorldMap) { + case (worldName, value) => + val world = mock[World] + (world.getName _).expects().returning(worldName) + assert(world.shouldTrackBuildBlock == value) } } } diff --git a/src/test/scala/com/github/unchama/seichiassist/achievement/NickNameMappingSpec.scala b/src/test/scala/com/github/unchama/seichiassist/achievement/NickNameMappingSpec.scala index f892e34b5e..a9e2aef6b5 100644 --- a/src/test/scala/com/github/unchama/seichiassist/achievement/NickNameMappingSpec.scala +++ b/src/test/scala/com/github/unchama/seichiassist/achievement/NickNameMappingSpec.scala @@ -9,17 +9,22 @@ class NickNameMappingSpec extends AnyWordSpec { SeichiAchievement.values.foreach { achievement => info(s"for achievement $achievement") - val NicknameCombination(first, second, third) = NicknameMapping.getNicknameCombinationFor(achievement) + val NicknameCombination(first, second, third) = + NicknameMapping.getNicknameCombinationFor(achievement) assert(List(first, second, third).flatten.nonEmpty) } } "assign only valid references to nicknames" in { - def referenceExists(id: AchievementId, partSelector: NicknamesToBeUnlocked => Option[String]) = + def referenceExists( + id: AchievementId, + partSelector: NicknamesToBeUnlocked => Option[String] + ) = Nicknames.getNicknameFor(id).flatMap(partSelector).nonEmpty SeichiAchievement.values.foreach { achievement => - val NicknameCombination(first, second, third) = NicknameMapping.getNicknameCombinationFor(achievement) + val NicknameCombination(first, second, third) = + NicknameMapping.getNicknameCombinationFor(achievement) val selectFirst: NicknamesToBeUnlocked => Option[String] = _.head() val selectSecond: NicknamesToBeUnlocked => Option[String] = _.middle() @@ -28,11 +33,9 @@ class NickNameMappingSpec extends AnyWordSpec { info(s"for achievement $achievement") assert { - Seq( - (first, selectFirst), (second, selectSecond), (third, selectThird) - ).forall { + Seq((first, selectFirst), (second, selectSecond), (third, selectThird)).forall { case (Some(reference), selector) => referenceExists(reference, selector) - case (None, _) => true + case (None, _) => true } } } diff --git a/src/test/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevelAndStarLevelSpec.scala b/src/test/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevelAndStarLevelSpec.scala index 88543902b8..5ada4eb428 100644 --- a/src/test/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevelAndStarLevelSpec.scala +++ b/src/test/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevelAndStarLevelSpec.scala @@ -5,7 +5,7 @@ import org.scalatest.wordspec.AnyWordSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks class SeichiLevelAndStarLevelSpec - extends AnyWordSpec + extends AnyWordSpec with ScalaCheckPropertyChecks with Matchers { @@ -18,7 +18,9 @@ class SeichiLevelAndStarLevelSpec starLevelTable.levelAt(levelTable.expAt(levelTable.maxLevel)) != SeichiStarLevel.zero } assert { - levelTable.expAt(levelTable.maxLevel) == starLevelTable.expAt(SeichiStarLevel.ofNonNegative(1)) + levelTable.expAt(levelTable.maxLevel) == starLevelTable.expAt( + SeichiStarLevel.ofNonNegative(1) + ) } } } diff --git a/src/test/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevelSpec.scala b/src/test/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevelSpec.scala index 09f748965e..f29b26458e 100644 --- a/src/test/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevelSpec.scala +++ b/src/test/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiLevelSpec.scala @@ -4,10 +4,7 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -class SeichiLevelSpec - extends AnyWordSpec - with ScalaCheckPropertyChecks - with Matchers { +class SeichiLevelSpec extends AnyWordSpec with ScalaCheckPropertyChecks with Matchers { "SeichiLevel" should { "cap at 87115000" in { diff --git a/src/test/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiStarLevelTableSpec.scala b/src/test/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiStarLevelTableSpec.scala index 1986f4418e..634b110da7 100644 --- a/src/test/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiStarLevelTableSpec.scala +++ b/src/test/scala/com/github/unchama/seichiassist/subsystems/breakcount/domain/level/SeichiStarLevelTableSpec.scala @@ -4,10 +4,7 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -class SeichiStarLevelTableSpec - extends AnyWordSpec - with ScalaCheckPropertyChecks - with Matchers { +class SeichiStarLevelTableSpec extends AnyWordSpec with ScalaCheckPropertyChecks with Matchers { import LocalArbitrary._ import cats.implicits._ @@ -16,9 +13,7 @@ class SeichiStarLevelTableSpec "satisfy the contract" in { import SeichiStarLevelTable.{expAt, levelAt} - forAll { l: SeichiStarLevel => - levelAt(expAt(l)) == l - } + forAll { l: SeichiStarLevel => levelAt(expAt(l)) == l } forAll { e: SeichiExpAmount => expAt(levelAt(e)) <= e && e <= expAt(levelAt(e).increment) } diff --git a/src/test/scala/com/github/unchama/seichiassist/subsystems/mana/domain/ManaAmountCapSpec.scala b/src/test/scala/com/github/unchama/seichiassist/subsystems/mana/domain/ManaAmountCapSpec.scala index a8e2d338c7..5b52dfb961 100644 --- a/src/test/scala/com/github/unchama/seichiassist/subsystems/mana/domain/ManaAmountCapSpec.scala +++ b/src/test/scala/com/github/unchama/seichiassist/subsystems/mana/domain/ManaAmountCapSpec.scala @@ -1,14 +1,14 @@ package com.github.unchama.seichiassist.subsystems.mana.domain -import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.{SeichiLevel, SeichiLevelTable} +import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.{ + SeichiLevel, + SeichiLevelTable +} import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -class ManaAmountCapSpec - extends AnyWordSpec - with ScalaCheckPropertyChecks - with Matchers { +class ManaAmountCapSpec extends AnyWordSpec with ScalaCheckPropertyChecks with Matchers { import cats.implicits._ @@ -21,8 +21,9 @@ class ManaAmountCapSpec "ManaAmountCap" should { "agree on certain checkpoints" in { - checkpoints.foreach { case (level, amount) => - assertResult(amount)(ManaAmountCap.at(level)) + checkpoints.foreach { + case (level, amount) => + assertResult(amount)(ManaAmountCap.at(level)) } } "be increasing" in { diff --git a/src/test/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSessionFactorySpec.scala b/src/test/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSessionFactorySpec.scala index 333b8f53b5..7825b82f20 100644 --- a/src/test/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSessionFactorySpec.scala +++ b/src/test/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSessionFactorySpec.scala @@ -2,8 +2,16 @@ package com.github.unchama.seichiassist.subsystems.managedfly.application import cats.Monad import cats.effect.{SyncIO, Timer} -import com.github.unchama.seichiassist.subsystems.managedfly.domain.{Flying, HasMovedRecently, Idle, RemainingFlyDuration} -import com.github.unchama.testutil.concurrent.tests.{ConcurrentEffectTest, TaskDiscreteEventually} +import com.github.unchama.seichiassist.subsystems.managedfly.domain.{ + Flying, + HasMovedRecently, + Idle, + RemainingFlyDuration +} +import com.github.unchama.testutil.concurrent.tests.{ + ConcurrentEffectTest, + TaskDiscreteEventually +} import com.github.unchama.testutil.execution.MonixTestSchedulerTests import monix.catnap.SchedulerEffect import monix.eval.Task @@ -13,7 +21,7 @@ import org.scalatest.wordspec.AnyWordSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks class ActiveSessionFactorySpec - extends AnyWordSpec + extends AnyWordSpec with ScalaCheckPropertyChecks with Matchers with TaskDiscreteEventually @@ -24,8 +32,10 @@ class ActiveSessionFactorySpec import scala.concurrent.duration._ - implicit override val patienceConfig: PatienceConfig = PatienceConfig(timeout = 5.seconds, interval = 10.millis) - implicit override val discreteEventuallyConfig: DiscreteEventuallyConfig = DiscreteEventuallyConfig(10000) + implicit override val patienceConfig: PatienceConfig = + PatienceConfig(timeout = 5.seconds, interval = 10.millis) + implicit override val discreteEventuallyConfig: DiscreteEventuallyConfig = + DiscreteEventuallyConfig(10000) implicit val monixScheduler: TestScheduler = TestScheduler() implicit val monixTimer: Timer[Task] = SchedulerEffect.timer(monixScheduler) @@ -38,11 +48,10 @@ class ActiveSessionFactorySpec "be able to tell if it is active or not" in { // given implicit val configuration: SystemConfiguration = - SystemConfiguration( - expConsumptionAmount = 0 - ) + SystemConfiguration(expConsumptionAmount = 0) - implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = playerMockFlyStatusManipulation + implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = + playerMockFlyStatusManipulation val factory = new ActiveSessionFactory[Task, PlayerMockReference]() val program = for { @@ -78,11 +87,10 @@ class ActiveSessionFactorySpec "synchronize player's fly status once started" in { // given implicit val configuration: SystemConfiguration = - SystemConfiguration( - expConsumptionAmount = 0 - ) + SystemConfiguration(expConsumptionAmount = 0) - implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = playerMockFlyStatusManipulation + implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = + playerMockFlyStatusManipulation val factory = new ActiveSessionFactory[Task, PlayerMockReference]() val program = for { @@ -113,11 +121,10 @@ class ActiveSessionFactorySpec "synchronize player's fly status when cancelled or complete" in { // given implicit val configuration: SystemConfiguration = - SystemConfiguration( - expConsumptionAmount = 0 - ) + SystemConfiguration(expConsumptionAmount = 0) - implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = playerMockFlyStatusManipulation + implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = + playerMockFlyStatusManipulation val factory = new ActiveSessionFactory[Task, PlayerMockReference]() val program = for { @@ -146,11 +153,10 @@ class ActiveSessionFactorySpec "terminate immediately if the player does not have enough experience" in { // given implicit val configuration: SystemConfiguration = - SystemConfiguration( - expConsumptionAmount = 100 - ) + SystemConfiguration(expConsumptionAmount = 100) - implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = playerMockFlyStatusManipulation + implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = + playerMockFlyStatusManipulation val factory = new ActiveSessionFactory[Task, PlayerMockReference]() val program = for { @@ -180,11 +186,10 @@ class ActiveSessionFactorySpec // given implicit val configuration: SystemConfiguration = - SystemConfiguration( - expConsumptionAmount = 100 - ) + SystemConfiguration(expConsumptionAmount = 100) - implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = playerMockFlyStatusManipulation + implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = + playerMockFlyStatusManipulation val factory = new ActiveSessionFactory[Task, PlayerMockReference]() val program = for { @@ -222,11 +227,10 @@ class ActiveSessionFactorySpec // given implicit val configuration: SystemConfiguration = - SystemConfiguration( - expConsumptionAmount = 100 - ) + SystemConfiguration(expConsumptionAmount = 100) - implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = playerMockFlyStatusManipulation + implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = + playerMockFlyStatusManipulation val factory = new ActiveSessionFactory[Task, PlayerMockReference]() val program = for { @@ -274,11 +278,10 @@ class ActiveSessionFactorySpec // given implicit val configuration: SystemConfiguration = - SystemConfiguration( - expConsumptionAmount = 100 - ) + SystemConfiguration(expConsumptionAmount = 100) - implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = playerMockFlyStatusManipulation + implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = + playerMockFlyStatusManipulation val factory = new ActiveSessionFactory[Task, PlayerMockReference]() val program = for { @@ -314,7 +317,8 @@ class ActiveSessionFactorySpec }(_ < minutesToWait) _ <- playerRef.isIdleMutex.lockAndUpdate(_ => Task.pure(true)) _ <- Monad[Task].iterateWhileM(0) { sleptMinute => - val expectedExperience = FiniteNonNegativeExperience(originalExp - minutesToWait * 100) + val expectedExperience = + FiniteNonNegativeExperience(originalExp - minutesToWait * 100) for { _ <- discreteEventually { @@ -327,7 +331,8 @@ class ActiveSessionFactorySpec }(_ < minutesToWait) _ <- playerRef.isIdleMutex.lockAndUpdate(_ => Task.pure(false)) _ <- Monad[Task].iterateWhileM(0) { sleptMinute => - val expectedExperience = FiniteNonNegativeExperience(originalExp - (minutesToWait + sleptMinute) * 100) + val expectedExperience = + FiniteNonNegativeExperience(originalExp - (minutesToWait + sleptMinute) * 100) for { _ <- discreteEventually { @@ -346,15 +351,13 @@ class ActiveSessionFactorySpec awaitForProgram(runConcurrent(program)(100), (minutesToWait * 3).minutes + 30.seconds) } - "not tick whenever player is idle" in { // given implicit val configuration: SystemConfiguration = - SystemConfiguration( - expConsumptionAmount = 0 - ) + SystemConfiguration(expConsumptionAmount = 0) - implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = playerMockFlyStatusManipulation + implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = + playerMockFlyStatusManipulation val factory = new ActiveSessionFactory[Task, PlayerMockReference]() val program = for { @@ -364,7 +367,9 @@ class ActiveSessionFactorySpec InfiniteExperience, initiallyIdle = false ).coerceTo[Task] - session <- factory.start[SyncIO](RemainingFlyDuration.PositiveMinutes.fromPositive(100)).run(playerRef) + session <- factory + .start[SyncIO](RemainingFlyDuration.PositiveMinutes.fromPositive(100)) + .run(playerRef) // セッションが有効になるまで待つ _ <- discreteEventually { @@ -379,7 +384,9 @@ class ActiveSessionFactorySpec // then _ <- discreteEventually { Task { - session.latestFlyStatus.unsafeRunSync() shouldBe Flying(RemainingFlyDuration.PositiveMinutes.fromPositive(50)) + session.latestFlyStatus.unsafeRunSync() shouldBe Flying( + RemainingFlyDuration.PositiveMinutes.fromPositive(50) + ) } } @@ -390,7 +397,9 @@ class ActiveSessionFactorySpec // then _ <- discreteEventually { Task { - session.latestFlyStatus.unsafeRunSync() shouldBe Flying(RemainingFlyDuration.PositiveMinutes.fromPositive(50)) + session.latestFlyStatus.unsafeRunSync() shouldBe Flying( + RemainingFlyDuration.PositiveMinutes.fromPositive(50) + ) } } @@ -414,14 +423,13 @@ class ActiveSessionFactorySpec // given implicit val configuration: SystemConfiguration = - SystemConfiguration( - expConsumptionAmount = 1000 - ) + SystemConfiguration(expConsumptionAmount = 1000) // 消費は丁度10回でき、11回目の経験値チェックで飛行セッションが閉じるべきなので、 // 11分の経過を期待する。 - implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = playerMockFlyStatusManipulation + implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = + playerMockFlyStatusManipulation val factory = new ActiveSessionFactory[Task, PlayerMockReference]() val program = for { @@ -459,11 +467,10 @@ class ActiveSessionFactorySpec "send appropriate notification when player does not have enough experience" in { // given implicit val configuration: SystemConfiguration = - SystemConfiguration( - expConsumptionAmount = 100 - ) + SystemConfiguration(expConsumptionAmount = 100) - implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = playerMockFlyStatusManipulation + implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = + playerMockFlyStatusManipulation val factory = new ActiveSessionFactory[Task, PlayerMockReference]() val program = for { @@ -481,7 +488,11 @@ class ActiveSessionFactorySpec // then _ <- discreteEventually { Task { - playerRef.messageLog.readLatest.unsafeRunSync().last shouldBe InterruptionMessageMock(PlayerExpNotEnough) + playerRef + .messageLog + .readLatest + .unsafeRunSync() + .last shouldBe InterruptionMessageMock(PlayerExpNotEnough) } } } yield () @@ -492,11 +503,10 @@ class ActiveSessionFactorySpec "send appropriate notification of remaining fly time every minute" in { // given implicit val configuration: SystemConfiguration = - SystemConfiguration( - expConsumptionAmount = 0 - ) + SystemConfiguration(expConsumptionAmount = 0) - implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = playerMockFlyStatusManipulation + implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = + playerMockFlyStatusManipulation val factory = new ActiveSessionFactory[Task, PlayerMockReference]() val program = for { @@ -508,7 +518,9 @@ class ActiveSessionFactorySpec ).coerceTo[Task] // when - _ <- factory.start[SyncIO](RemainingFlyDuration.PositiveMinutes.fromPositive(10)).run(playerRef) + _ <- factory + .start[SyncIO](RemainingFlyDuration.PositiveMinutes.fromPositive(10)) + .run(playerRef) _ <- monixTimer.sleep(4.minutes + 30.seconds) _ <- playerRef.isIdleMutex.lockAndUpdate(_ => Task.pure(true)) _ <- monixTimer.sleep(2.minutes) @@ -519,18 +531,48 @@ class ActiveSessionFactorySpec _ <- discreteEventually { Task { playerRef.messageLog.readLatest.unsafeRunSync() shouldBe Vector( - StatusMessageMock(HasMovedRecently, RemainingFlyDuration.PositiveMinutes.fromPositive(10)), - StatusMessageMock(HasMovedRecently, RemainingFlyDuration.PositiveMinutes.fromPositive(9)), - StatusMessageMock(HasMovedRecently, RemainingFlyDuration.PositiveMinutes.fromPositive(8)), - StatusMessageMock(HasMovedRecently, RemainingFlyDuration.PositiveMinutes.fromPositive(7)), - StatusMessageMock(HasMovedRecently, RemainingFlyDuration.PositiveMinutes.fromPositive(6)), + StatusMessageMock( + HasMovedRecently, + RemainingFlyDuration.PositiveMinutes.fromPositive(10) + ), + StatusMessageMock( + HasMovedRecently, + RemainingFlyDuration.PositiveMinutes.fromPositive(9) + ), + StatusMessageMock( + HasMovedRecently, + RemainingFlyDuration.PositiveMinutes.fromPositive(8) + ), + StatusMessageMock( + HasMovedRecently, + RemainingFlyDuration.PositiveMinutes.fromPositive(7) + ), + StatusMessageMock( + HasMovedRecently, + RemainingFlyDuration.PositiveMinutes.fromPositive(6) + ), StatusMessageMock(Idle, RemainingFlyDuration.PositiveMinutes.fromPositive(6)), StatusMessageMock(Idle, RemainingFlyDuration.PositiveMinutes.fromPositive(6)), - StatusMessageMock(HasMovedRecently, RemainingFlyDuration.PositiveMinutes.fromPositive(5)), - StatusMessageMock(HasMovedRecently, RemainingFlyDuration.PositiveMinutes.fromPositive(4)), - StatusMessageMock(HasMovedRecently, RemainingFlyDuration.PositiveMinutes.fromPositive(3)), - StatusMessageMock(HasMovedRecently, RemainingFlyDuration.PositiveMinutes.fromPositive(2)), - StatusMessageMock(HasMovedRecently, RemainingFlyDuration.PositiveMinutes.fromPositive(1)), + StatusMessageMock( + HasMovedRecently, + RemainingFlyDuration.PositiveMinutes.fromPositive(5) + ), + StatusMessageMock( + HasMovedRecently, + RemainingFlyDuration.PositiveMinutes.fromPositive(4) + ), + StatusMessageMock( + HasMovedRecently, + RemainingFlyDuration.PositiveMinutes.fromPositive(3) + ), + StatusMessageMock( + HasMovedRecently, + RemainingFlyDuration.PositiveMinutes.fromPositive(2) + ), + StatusMessageMock( + HasMovedRecently, + RemainingFlyDuration.PositiveMinutes.fromPositive(1) + ), InterruptionMessageMock(FlyDurationExpired) ) } @@ -545,14 +587,14 @@ class ActiveSessionFactorySpec "terminate exactly when the minute specified has passed if the player has enough experience" in { // given implicit val configuration: SystemConfiguration = - SystemConfiguration( - expConsumptionAmount = 100 - ) + SystemConfiguration(expConsumptionAmount = 100) val sessionLengthInMinutes = 10 - val originalSessionLength = RemainingFlyDuration.PositiveMinutes.fromPositive(sessionLengthInMinutes) + val originalSessionLength = + RemainingFlyDuration.PositiveMinutes.fromPositive(sessionLengthInMinutes) - implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = playerMockFlyStatusManipulation + implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = + playerMockFlyStatusManipulation val factory = new ActiveSessionFactory[Task, PlayerMockReference]() val program = for { @@ -582,14 +624,14 @@ class ActiveSessionFactorySpec "send appropriate notification when a session expires" in { // given implicit val configuration: SystemConfiguration = - SystemConfiguration( - expConsumptionAmount = 100 - ) + SystemConfiguration(expConsumptionAmount = 100) val sessionLengthInMinutes = 10 - val originalSessionLength = RemainingFlyDuration.PositiveMinutes.fromPositive(sessionLengthInMinutes) + val originalSessionLength = + RemainingFlyDuration.PositiveMinutes.fromPositive(sessionLengthInMinutes) - implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = playerMockFlyStatusManipulation + implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = + playerMockFlyStatusManipulation val factory = new ActiveSessionFactory[Task, PlayerMockReference]() val program = for { @@ -608,7 +650,11 @@ class ActiveSessionFactorySpec // then _ <- discreteEventually { Task { - playerRef.messageLog.readLatest.unsafeRunSync().last shouldBe InterruptionMessageMock(FlyDurationExpired) + playerRef + .messageLog + .readLatest + .unsafeRunSync() + .last shouldBe InterruptionMessageMock(FlyDurationExpired) } } } yield () diff --git a/src/test/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSessionReferenceSpec.scala b/src/test/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSessionReferenceSpec.scala index 46e309977f..0636caaff6 100644 --- a/src/test/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSessionReferenceSpec.scala +++ b/src/test/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSessionReferenceSpec.scala @@ -1,8 +1,15 @@ package com.github.unchama.seichiassist.subsystems.managedfly.application import cats.effect.{SyncIO, Timer} -import com.github.unchama.seichiassist.subsystems.managedfly.domain.{Flying, NotFlying, RemainingFlyDuration} -import com.github.unchama.testutil.concurrent.tests.{ConcurrentEffectTest, TaskDiscreteEventually} +import com.github.unchama.seichiassist.subsystems.managedfly.domain.{ + Flying, + NotFlying, + RemainingFlyDuration +} +import com.github.unchama.testutil.concurrent.tests.{ + ConcurrentEffectTest, + TaskDiscreteEventually +} import com.github.unchama.testutil.execution.MonixTestSchedulerTests import monix.catnap.SchedulerEffect import monix.eval.Task @@ -12,7 +19,7 @@ import org.scalatest.wordspec.AnyWordSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks class ActiveSessionReferenceSpec - extends AnyWordSpec + extends AnyWordSpec with ScalaCheckPropertyChecks with Matchers with TaskDiscreteEventually @@ -23,8 +30,10 @@ class ActiveSessionReferenceSpec import scala.concurrent.duration._ - implicit override val patienceConfig: PatienceConfig = PatienceConfig(timeout = 5.seconds, interval = 10.millis) - implicit override val discreteEventuallyConfig: DiscreteEventuallyConfig = DiscreteEventuallyConfig(1000000) + implicit override val patienceConfig: PatienceConfig = + PatienceConfig(timeout = 5.seconds, interval = 10.millis) + implicit override val discreteEventuallyConfig: DiscreteEventuallyConfig = + DiscreteEventuallyConfig(1000000) implicit val monixScheduler: TestScheduler = TestScheduler() implicit val monixTimer: Timer[Task] = SchedulerEffect.timer(monixScheduler) @@ -48,15 +57,15 @@ class ActiveSessionReferenceSpec "correctly expose the fly status of a started session" in { // given implicit val configuration: SystemConfiguration = - SystemConfiguration( - expConsumptionAmount = 0 - ) + SystemConfiguration(expConsumptionAmount = 0) - implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = playerMockFlyStatusManipulation + implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = + playerMockFlyStatusManipulation val factory = new ActiveSessionFactory[Task, PlayerMockReference]() val sessionLengthInMinutes = 10 - val sessionDuration = RemainingFlyDuration.PositiveMinutes.fromPositive(sessionLengthInMinutes) + val sessionDuration = + RemainingFlyDuration.PositiveMinutes.fromPositive(sessionLengthInMinutes) val program = for { // given @@ -96,11 +105,10 @@ class ActiveSessionReferenceSpec "be able to stop a running session" in { // given implicit val configuration: SystemConfiguration = - SystemConfiguration( - expConsumptionAmount = 0 - ) + SystemConfiguration(expConsumptionAmount = 0) - implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = playerMockFlyStatusManipulation + implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = + playerMockFlyStatusManipulation val factory = new ActiveSessionFactory[Task, PlayerMockReference]() val program = for { @@ -118,7 +126,9 @@ class ActiveSessionReferenceSpec _ <- sessionRef.replaceSession(createSession.run(playerRef)) _ <- discreteEventually { Task { - sessionRef.getLatestFlyStatus.unsafeRunSync() shouldBe Flying(RemainingFlyDuration.Infinity) + sessionRef.getLatestFlyStatus.unsafeRunSync() shouldBe Flying( + RemainingFlyDuration.Infinity + ) } } _ <- sessionRef.stopAnyRunningSession @@ -137,15 +147,15 @@ class ActiveSessionReferenceSpec "be able to replace a session" in { // given implicit val configuration: SystemConfiguration = - SystemConfiguration( - expConsumptionAmount = 0 - ) + SystemConfiguration(expConsumptionAmount = 0) - implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = playerMockFlyStatusManipulation + implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = + playerMockFlyStatusManipulation val factory = new ActiveSessionFactory[Task, PlayerMockReference]() val targetSessionLength = 10 - val targetSessionDuration = RemainingFlyDuration.PositiveMinutes.fromPositive(targetSessionLength) + val targetSessionDuration = + RemainingFlyDuration.PositiveMinutes.fromPositive(targetSessionLength) val program = for { // given @@ -157,13 +167,19 @@ class ActiveSessionReferenceSpec sessionRef <- ActiveSessionReference.createNew[Task, SyncIO].coerceTo[Task] // when - _ <- sessionRef.replaceSession(factory.start[SyncIO](RemainingFlyDuration.Infinity).run(playerRef)) + _ <- sessionRef.replaceSession( + factory.start[SyncIO](RemainingFlyDuration.Infinity).run(playerRef) + ) _ <- discreteEventually { Task { - sessionRef.getLatestFlyStatus.unsafeRunSync() shouldBe Flying(RemainingFlyDuration.Infinity) + sessionRef.getLatestFlyStatus.unsafeRunSync() shouldBe Flying( + RemainingFlyDuration.Infinity + ) } } - _ <- sessionRef.replaceSession(factory.start[SyncIO](targetSessionDuration).run(playerRef)) + _ <- sessionRef.replaceSession( + factory.start[SyncIO](targetSessionDuration).run(playerRef) + ) // then _ <- discreteEventually { @@ -179,18 +195,19 @@ class ActiveSessionReferenceSpec "not allow more than one session to be present" in { // given implicit val configuration: SystemConfiguration = - SystemConfiguration( - expConsumptionAmount = 0 - ) + SystemConfiguration(expConsumptionAmount = 0) - implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = playerMockFlyStatusManipulation + implicit val manipulationMock: PlayerFlyStatusManipulation[PlayerAsyncKleisli] = + playerMockFlyStatusManipulation val factory = new ActiveSessionFactory[Task, PlayerMockReference]() val firstSessionLength = 10 - val firstSessionDuration = RemainingFlyDuration.PositiveMinutes.fromPositive(firstSessionLength) + val firstSessionDuration = + RemainingFlyDuration.PositiveMinutes.fromPositive(firstSessionLength) val secondSessionLength = 20 - val secondSessionDuration = RemainingFlyDuration.PositiveMinutes.fromPositive(secondSessionLength) + val secondSessionDuration = + RemainingFlyDuration.PositiveMinutes.fromPositive(secondSessionLength) assert(firstSessionLength < secondSessionLength) @@ -204,13 +221,17 @@ class ActiveSessionReferenceSpec sessionRef <- ActiveSessionReference.createNew[Task, SyncIO].coerceTo[Task] // when - _ <- sessionRef.replaceSession(factory.start[SyncIO](firstSessionDuration).run(playerRef)) + _ <- sessionRef.replaceSession( + factory.start[SyncIO](firstSessionDuration).run(playerRef) + ) _ <- discreteEventually { Task { sessionRef.getLatestFlyStatus.unsafeRunSync() shouldBe Flying(firstSessionDuration) } } - _ <- sessionRef.replaceSession(factory.start[SyncIO](secondSessionDuration).run(playerRef)) + _ <- sessionRef.replaceSession( + factory.start[SyncIO](secondSessionDuration).run(playerRef) + ) _ <- monixTimer.sleep(firstSessionLength.minutes) // then diff --git a/src/test/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/Mock.scala b/src/test/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/Mock.scala index b87c320535..cb6ebdbdb9 100644 --- a/src/test/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/Mock.scala +++ b/src/test/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/Mock.scala @@ -7,10 +7,9 @@ import com.github.unchama.generic.ContextCoercion import com.github.unchama.generic.effect.concurrent.Mutex import com.github.unchama.seichiassist.subsystems.managedfly.domain._ -private[managedfly] class Mock[ - AsyncContext[_] : Concurrent, - SyncContext[_] : Sync : ContextCoercion[*[_], AsyncContext] -] { +private[managedfly] class Mock[AsyncContext[_]: Concurrent, SyncContext[ + _ +]: Sync: ContextCoercion[*[_], AsyncContext]] { sealed trait ExperienceMock { def consume(amount: BigInt): Option[ExperienceMock] @@ -20,7 +19,8 @@ private[managedfly] class Mock[ require(internalAmount >= 0) override def consume(amount: BigInt): Option[ExperienceMock] = - if (amount <= internalAmount) Some(FiniteNonNegativeExperience(internalAmount - amount)) else None + if (amount <= internalAmount) Some(FiniteNonNegativeExperience(internalAmount - amount)) + else None } case object InfiniteExperience extends ExperienceMock { @@ -31,31 +31,38 @@ private[managedfly] class Mock[ case class InterruptionMessageMock(interruption: InternalInterruption) extends MessageMock - case class StatusMessageMock(idleStatus: IdleStatus, remainingFlyDuration: RemainingFlyDuration) extends MessageMock + case class StatusMessageMock( + idleStatus: IdleStatus, + remainingFlyDuration: RemainingFlyDuration + ) extends MessageMock import ContextCoercion._ import cats.implicits._ - case class PlayerMockReference(isFlyingMutex: Mutex[AsyncContext, SyncContext, Boolean], - experienceMutex: Mutex[AsyncContext, SyncContext, ExperienceMock], - isIdleMutex: Mutex[AsyncContext, SyncContext, Boolean], - messageLog: Mutex[AsyncContext, SyncContext, Vector[MessageMock]]) { + case class PlayerMockReference( + isFlyingMutex: Mutex[AsyncContext, SyncContext, Boolean], + experienceMutex: Mutex[AsyncContext, SyncContext, ExperienceMock], + isIdleMutex: Mutex[AsyncContext, SyncContext, Boolean], + messageLog: Mutex[AsyncContext, SyncContext, Vector[MessageMock]] + ) { def sendMessage(message: MessageMock): AsyncContext[Unit] = { messageLog - .lockAndUpdate { current => - Monad[AsyncContext].pure(current.appended(message)) - } + .lockAndUpdate { current => Monad[AsyncContext].pure(current.appended(message)) } .as(()) } } object PlayerMockReference { - def apply(initiallyFlying: Boolean, - initialExperience: ExperienceMock, - initiallyIdle: Boolean): SyncContext[PlayerMockReference] = + def apply( + initiallyFlying: Boolean, + initialExperience: ExperienceMock, + initiallyIdle: Boolean + ): SyncContext[PlayerMockReference] = for { isFlyingMutex <- Mutex.of[AsyncContext, SyncContext, Boolean](initiallyFlying) - experienceMutex <- Mutex.of[AsyncContext, SyncContext, ExperienceMock](initialExperience) + experienceMutex <- Mutex.of[AsyncContext, SyncContext, ExperienceMock]( + initialExperience + ) idleMutex <- Mutex.of[AsyncContext, SyncContext, Boolean](initiallyIdle) messageLog <- Mutex.of[AsyncContext, SyncContext, Vector[MessageMock]](Vector()) } yield { @@ -65,36 +72,41 @@ private[managedfly] class Mock[ type PlayerAsyncKleisli[R] = Kleisli[AsyncContext, PlayerMockReference, R] - def playerMockFlyStatusManipulation(implicit configuration: SystemConfiguration): PlayerFlyStatusManipulation[PlayerAsyncKleisli] = { + def playerMockFlyStatusManipulation( + implicit configuration: SystemConfiguration + ): PlayerFlyStatusManipulation[PlayerAsyncKleisli] = { new PlayerFlyStatusManipulation[PlayerAsyncKleisli] { - override val ensurePlayerExp: PlayerAsyncKleisli[Unit] = Kleisli { player: PlayerMockReference => + override val ensurePlayerExp + : PlayerAsyncKleisli[Unit] = Kleisli { player: PlayerMockReference => player .experienceMutex .lockAndUpdate { experience => experience.consume(configuration.expConsumptionAmount) match { case Some(_) => Monad[AsyncContext].pure(experience) - case None => Sync[AsyncContext].raiseError(PlayerExpNotEnough) + case None => Sync[AsyncContext].raiseError(PlayerExpNotEnough) } } .as(()) } - override val consumePlayerExp: PlayerAsyncKleisli[Unit] = Kleisli { player: PlayerMockReference => + override val consumePlayerExp + : PlayerAsyncKleisli[Unit] = Kleisli { player: PlayerMockReference => player .experienceMutex .lockAndUpdate { experience => experience.consume(configuration.expConsumptionAmount) match { case Some(consumed) => Monad[AsyncContext].pure(consumed) - case None => Sync[AsyncContext].raiseError(PlayerExpNotEnough) + case None => Sync[AsyncContext].raiseError(PlayerExpNotEnough) } } .as(()) } - override val isPlayerIdle: PlayerAsyncKleisli[IdleStatus] = Kleisli { player: PlayerMockReference => - player.isIdleMutex.readLatest.coerceTo[AsyncContext].map { - if (_) Idle else HasMovedRecently - } + override val isPlayerIdle: PlayerAsyncKleisli[IdleStatus] = Kleisli { + player: PlayerMockReference => + player.isIdleMutex.readLatest.coerceTo[AsyncContext].map { + if (_) Idle else HasMovedRecently + } } override val synchronizeFlyStatus: PlayerFlyStatus => PlayerAsyncKleisli[Unit] = { @@ -108,16 +120,16 @@ private[managedfly] class Mock[ } } - override val sendNotificationsOnInterruption: InternalInterruption => PlayerAsyncKleisli[Unit] = { interruption => + override val sendNotificationsOnInterruption + : InternalInterruption => PlayerAsyncKleisli[Unit] = { interruption => Kleisli { player: PlayerMockReference => player.sendMessage(InterruptionMessageMock(interruption)) } } - override val notifyRemainingDuration: (IdleStatus, RemainingFlyDuration) => PlayerAsyncKleisli[Unit] = { (i, d) => - Kleisli { player: PlayerMockReference => - player.sendMessage(StatusMessageMock(i, d)) - } + override val notifyRemainingDuration + : (IdleStatus, RemainingFlyDuration) => PlayerAsyncKleisli[Unit] = { (i, d) => + Kleisli { player: PlayerMockReference => player.sendMessage(StatusMessageMock(i, d)) } } } } diff --git a/src/test/scala/com/github/unchama/seichiassist/subsystems/managedfly/domain/RemainingFlyDurationSpec.scala b/src/test/scala/com/github/unchama/seichiassist/subsystems/managedfly/domain/RemainingFlyDurationSpec.scala index f6a06b3930..9b64800415 100644 --- a/src/test/scala/com/github/unchama/seichiassist/subsystems/managedfly/domain/RemainingFlyDurationSpec.scala +++ b/src/test/scala/com/github/unchama/seichiassist/subsystems/managedfly/domain/RemainingFlyDurationSpec.scala @@ -9,7 +9,10 @@ class RemainingFlyDurationSpec extends AnyWordSpec with ScalaCheckPropertyChecks "decrement remaining minute if greater than 1" in { forAll { minute: Int => whenever(minute > 1) { - RemainingFlyDuration.PositiveMinutes.fromPositive(minute).tickOneMinute shouldBe Some { + RemainingFlyDuration + .PositiveMinutes + .fromPositive(minute) + .tickOneMinute shouldBe Some { RemainingFlyDuration.PositiveMinutes.fromPositive(minute - 1) } } diff --git a/src/test/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/codec/BukkitMebiusAppearanceMaterialCodecSpec.scala b/src/test/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/codec/BukkitMebiusAppearanceMaterialCodecSpec.scala index 87a6c54199..b8e7e633cd 100644 --- a/src/test/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/codec/BukkitMebiusAppearanceMaterialCodecSpec.scala +++ b/src/test/scala/com/github/unchama/seichiassist/subsystems/mebius/bukkit/codec/BukkitMebiusAppearanceMaterialCodecSpec.scala @@ -10,11 +10,9 @@ class BukkitMebiusAppearanceMaterialCodecSpec extends AnyWordSpec { "Appearance Codec" should { "return some non-air material for all levels" in { - (1 until MebiusLevel.max.value) - .map(MebiusLevel.apply) - .foreach { mebiusLevel => - assert(appearanceMaterialAt(mebiusLevel) != Material.AIR) - } + (1 until MebiusLevel.max.value).map(MebiusLevel.apply).foreach { mebiusLevel => + assert(appearanceMaterialAt(mebiusLevel) != Material.AIR) + } } "assign leather helmet to level 1 mebius" in { diff --git a/src/test/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusEnchantmentSpec.scala b/src/test/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusEnchantmentSpec.scala index 9d2ca5b08c..aeecddde2b 100644 --- a/src/test/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusEnchantmentSpec.scala +++ b/src/test/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusEnchantmentSpec.scala @@ -1,6 +1,5 @@ package com.github.unchama.seichiassist.subsystems.mebius.domain.property -import com.github.unchama.seichiassist.subsystems.mebius.domain.property.{MebiusEnchantment, MebiusLevel} import org.scalatest.wordspec.AnyWordSpec class MebiusEnchantmentSpec extends AnyWordSpec { diff --git a/src/test/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusPropertySpec.scala b/src/test/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusPropertySpec.scala index 9ece41b1d5..d721becc29 100644 --- a/src/test/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusPropertySpec.scala +++ b/src/test/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/property/MebiusPropertySpec.scala @@ -1,11 +1,11 @@ package com.github.unchama.seichiassist.subsystems.mebius.domain.property -import java.util.UUID import cats.Monad import cats.effect.SyncIO -import com.github.unchama.seichiassist.subsystems.mebius.domain.property.{MebiusEnchantment, MebiusProperty, NormalMebius} import org.scalatest.wordspec.AnyWordSpec +import java.util.UUID + class MebiusPropertySpec extends AnyWordSpec { val testPlayerName: String = "testPlayer" val testPlayerUuid: String = UUID.randomUUID().toString @@ -19,7 +19,8 @@ class MebiusPropertySpec extends AnyWordSpec { "be able to be upgraded all the way to the maximum level" in { val upgradedToMaximum = { - val initialProperty = MebiusProperty.initialProperty(NormalMebius, testPlayerName, testPlayerUuid) + val initialProperty = + MebiusProperty.initialProperty(NormalMebius, testPlayerName, testPlayerUuid) Monad[SyncIO] .iterateWhileM(initialProperty)(_.upgradeByOneLevel[SyncIO])(!_.level.isMaximum) @@ -39,7 +40,8 @@ class MebiusPropertySpec extends AnyWordSpec { "Property with the largest level" should { "always contain Unbreakable enchantment" in { val upgradedToMaximum = { - val initialProperty = MebiusProperty.initialProperty(NormalMebius, testPlayerName, testPlayerUuid) + val initialProperty = + MebiusProperty.initialProperty(NormalMebius, testPlayerName, testPlayerUuid) Monad[SyncIO] .iterateWhileM(initialProperty)(_.upgradeByOneLevel[SyncIO])(!_.level.isMaximum) @@ -51,14 +53,19 @@ class MebiusPropertySpec extends AnyWordSpec { "allow toggling forced materials" in { val upgradedToMaximum = { - val initialProperty = MebiusProperty.initialProperty(NormalMebius, testPlayerName, testPlayerUuid) + val initialProperty = + MebiusProperty.initialProperty(NormalMebius, testPlayerName, testPlayerUuid) Monad[SyncIO] .iterateWhileM(initialProperty)(_.upgradeByOneLevel[SyncIO])(!_.level.isMaximum) .unsafeRunSync() } - assert(upgradedToMaximum.toggleForcedMaterial.forcedMaterial != upgradedToMaximum.forcedMaterial) + assert( + upgradedToMaximum + .toggleForcedMaterial + .forcedMaterial != upgradedToMaximum.forcedMaterial + ) } } @@ -67,7 +74,8 @@ class MebiusPropertySpec extends AnyWordSpec { val testIterationCount = 1000 (1 to testIterationCount).foreach { _ => - val initialProperty = MebiusProperty.initialProperty(NormalMebius, testPlayerName, testPlayerUuid) + val initialProperty = + MebiusProperty.initialProperty(NormalMebius, testPlayerName, testPlayerUuid) import cats.implicits._ diff --git a/src/test/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/speech/MebiusSpeechBlockageStateSpec.scala b/src/test/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/speech/MebiusSpeechBlockageStateSpec.scala index f006163f4e..a5542ef0a3 100644 --- a/src/test/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/speech/MebiusSpeechBlockageStateSpec.scala +++ b/src/test/scala/com/github/unchama/seichiassist/subsystems/mebius/domain/speech/MebiusSpeechBlockageStateSpec.scala @@ -1,7 +1,6 @@ package com.github.unchama.seichiassist.subsystems.mebius.domain.speech import cats.effect.IO -import com.github.unchama.seichiassist.subsystems.mebius.domain.speech.MebiusSpeechBlockageState import org.scalatest.wordspec.AnyWordSpec import scala.util.Random diff --git a/src/test/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/domain/DateTimeDurationSpec.scala b/src/test/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/domain/DateTimeDurationSpec.scala new file mode 100644 index 0000000000..f972d5b432 --- /dev/null +++ b/src/test/scala/com/github/unchama/seichiassist/subsystems/seasonalevents/domain/DateTimeDurationSpec.scala @@ -0,0 +1,91 @@ +package com.github.unchama.seichiassist.subsystems.seasonalevents.domain + +import org.scalatest.wordspec.AnyWordSpec + +import java.time.{LocalDate, LocalDateTime, LocalTime} + +class DateTimeDurationSpec extends AnyWordSpec { + private val rebootTime = LocalTime.of(4, 10) + private val t = LocalDateTime.of(LocalDate.of(2022, 1, 1), rebootTime) + private val tPlusOneYear = t.plusYears(1) + private val dateFrom = t.toLocalDate + private val dateTo = tPlusOneYear.toLocalDate + + "DateTimeDuration" should { + "be generated successfully" in { + val duration = DateTimeDuration(t, tPlusOneYear) + assert(duration.from.isEqual(t)) + assert(duration.to.isEqual(tPlusOneYear)) + } + + "be generated successfully with the same LocalDateTime" in { + val localDateTime = t + val duration = DateTimeDuration(localDateTime, localDateTime) + assert(duration.from.isEqual(localDateTime)) + assert(duration.to.isEqual(localDateTime)) + } + + "not be generated successfully with illegal LocalDateTime" in { + assertThrows[IllegalArgumentException](DateTimeDuration(t, tPlusOneYear.minusYears(2))) + } + + "be generated successfully from LocalDate" in { + require(t.toLocalTime.equals(rebootTime)) + require(tPlusOneYear.toLocalTime.equals(rebootTime)) + val duration = DateTimeDuration.fromLocalDate(dateFrom, dateTo) + assert(duration.from.isEqual(t)) + assert(duration.to.isEqual(tPlusOneYear)) + } + + "be generated successfully from the same LocalDate" in { + val localDateTime = t + val localDate = dateFrom + require(localDateTime.toLocalTime.equals(rebootTime)) + val duration = DateTimeDuration.fromLocalDate(localDate, localDate) + assert(duration.from.isEqual(localDateTime)) + assert(duration.to.isEqual(localDateTime)) + } + + "not be generated successfully with illegal LocalDate" in { + assertThrows[IllegalArgumentException]( + DateTimeDuration.fromLocalDate(dateFrom, dateTo.minusYears(2)) + ) + } + } + + "DateTimeDuration#contains" when { + val duration = DateTimeDuration(t, tPlusOneYear) + + "the same as from and to" should { + "be true" in { + assert(duration.contains(t)) + assert(duration.contains(tPlusOneYear)) + } + } + "shortly after from" should { + "be true" in assert(duration.contains(t.plusMinutes(1))) + } + + "before from" should { + "be false" in assert(!duration.contains(t.minusYears(1))) + } + "after to" should { + "be false" in assert(!duration.contains(tPlusOneYear.plusYears(1))) + } + } + + "DateTimeDuration#isEntirelyAfter" when { + val duration = DateTimeDuration(t, tPlusOneYear) + + "the same as from" should { + "be true" in assert(duration.isEntirelyAfter(duration.from)) + } + "before from" should { + "be true" in assert(duration.isEntirelyAfter(duration.from.minusYears(1))) + } + + "after from" should { + "be false" in assert(!duration.isEntirelyAfter(duration.from.plusYears(1))) + } + } +} diff --git a/src/test/scala/com/github/unchama/testutil/concurrent/Blocker.scala b/src/test/scala/com/github/unchama/testutil/concurrent/Blocker.scala index 1f1c714ba8..cf294e63a9 100644 --- a/src/test/scala/com/github/unchama/testutil/concurrent/Blocker.scala +++ b/src/test/scala/com/github/unchama/testutil/concurrent/Blocker.scala @@ -1,6 +1,7 @@ package com.github.unchama.testutil.concurrent trait Blocker[F[_]] { + /** * ブロッカーが完了されるまで、ブロッキングを待ち続けるアクションを返す。 */ diff --git a/src/test/scala/com/github/unchama/testutil/concurrent/sequencer/LinkedSequencer.scala b/src/test/scala/com/github/unchama/testutil/concurrent/sequencer/LinkedSequencer.scala index 189748cafc..a59f16f1e3 100644 --- a/src/test/scala/com/github/unchama/testutil/concurrent/sequencer/LinkedSequencer.scala +++ b/src/test/scala/com/github/unchama/testutil/concurrent/sequencer/LinkedSequencer.scala @@ -1,18 +1,16 @@ package com.github.unchama.testutil.concurrent.sequencer -import cats.effect.{Async, Sync} import cats.effect.concurrent.Deferred -import com.github.unchama.testutil.concurrent.Blocker +import cats.effect.{Async, Sync} import cats.implicits._ +import com.github.unchama.testutil.concurrent.Blocker -class LinkedSequencer[F[_] : Async] extends Sequencer[F] { +class LinkedSequencer[F[_]: Async] extends Sequencer[F] { override val newBlockerList: F[LazyList[Blocker[F]]] = Async[F].delay { import LinkedSequencer._ - LazyList.iterate( - LinkedBlocker(None, CompletableBlocker.unsafeBlocked) - )(previous => + LazyList.iterate(LinkedBlocker(None, CompletableBlocker.unsafeBlocked))(previous => LinkedBlocker(Some(previous), CompletableBlocker.unsafeBlocked) ) } @@ -26,7 +24,7 @@ object LinkedSequencer { /** * DeferredのBlockerとしてのラッパー */ - class CompletableBlocker[F[_]] private(promise: Deferred[F, Unit]) extends Blocker[F] { + class CompletableBlocker[F[_]] private (promise: Deferred[F, Unit]) extends Blocker[F] { def complete: F[Unit] = promise.complete(()) @@ -35,16 +33,19 @@ object LinkedSequencer { } object CompletableBlocker { - def unsafeBlocked[F[_] : Async]: CompletableBlocker[F] = new CompletableBlocker[F](Deferred.unsafeUncancelable) + def unsafeBlocked[F[_]: Async]: CompletableBlocker[F] = + new CompletableBlocker[F](Deferred.unsafeUncancelable) } - case class LinkedBlocker[F[_] : Async](previous: Option[LinkedBlocker[F]], - internalBlocker: CompletableBlocker[F]) extends Blocker[F] { + case class LinkedBlocker[F[_]: Async]( + previous: Option[LinkedBlocker[F]], + internalBlocker: CompletableBlocker[F] + ) extends Blocker[F] { /** * awaitが返す計算は、実行された時次の事後条件を満たす: - * - [[previous]] が空でない場合、中にある [[previous]] の [[internalBlocker]] が完了している - * - このオブジェクトの [[internalBlocker]] が完了している + * - [[previous]] が空でない場合、中にある [[previous]] の [[internalBlocker]] が完了している + * - このオブジェクトの [[internalBlocker]] が完了している */ override def await(): F[Unit] = { val waitPrevious = previous.map(_.internalBlocker.await()) @@ -53,5 +54,5 @@ object LinkedSequencer { } } - def apply[F[_] : Async]: LinkedSequencer[F] = new LinkedSequencer[F] + def apply[F[_]: Async]: LinkedSequencer[F] = new LinkedSequencer[F] } diff --git a/src/test/scala/com/github/unchama/testutil/concurrent/sequencer/Sequencer.scala b/src/test/scala/com/github/unchama/testutil/concurrent/sequencer/Sequencer.scala index 60acca0b05..28cfd9c9cc 100644 --- a/src/test/scala/com/github/unchama/testutil/concurrent/sequencer/Sequencer.scala +++ b/src/test/scala/com/github/unchama/testutil/concurrent/sequencer/Sequencer.scala @@ -5,8 +5,7 @@ import com.github.unchama.testutil.concurrent.Blocker trait Sequencer[F[_]] { /** - * 一つ前のブロッカーのawaitが完了するまでブロックを行うブロッカーのリストを返す計算。 - * 返されるリストの最初のブロッカーは直ちに完了する。 + * 一つ前のブロッカーのawaitが完了するまでブロックを行うブロッカーのリストを返す計算。 返されるリストの最初のブロッカーは直ちに完了する。 */ val newBlockerList: F[LazyList[Blocker[F]]] diff --git a/src/test/scala/com/github/unchama/testutil/concurrent/sequencer/SequencerSpec.scala b/src/test/scala/com/github/unchama/testutil/concurrent/sequencer/SequencerSpec.scala index c96dc35a4e..0fe6cd1aae 100644 --- a/src/test/scala/com/github/unchama/testutil/concurrent/sequencer/SequencerSpec.scala +++ b/src/test/scala/com/github/unchama/testutil/concurrent/sequencer/SequencerSpec.scala @@ -1,10 +1,9 @@ package com.github.unchama.testutil.concurrent.sequencer -import java.util.concurrent.ConcurrentLinkedQueue - import cats.effect.{ContextShift, IO} import org.scalatest.wordspec.AnyWordSpec +import java.util.concurrent.ConcurrentLinkedQueue import scala.concurrent.ExecutionContext import scala.util.Random @@ -12,9 +11,7 @@ class SequencerSpec extends AnyWordSpec { implicit val ec: ExecutionContext = ExecutionContext.global implicit val shift: ContextShift[IO] = IO.contextShift(ec) - val sequencerImplementations: List[Sequencer[IO]] = List( - LinkedSequencer[IO] - ) + val sequencerImplementations: List[Sequencer[IO]] = List(LinkedSequencer[IO]) val randomizedProgramListSize = 20000 @@ -23,24 +20,21 @@ class SequencerSpec extends AnyWordSpec { sequencerImplementations.foreach { sequencer => info(sequencer.toString) - import scala.jdk.CollectionConverters._ import cats.implicits._ + import scala.jdk.CollectionConverters._ + val queue = new ConcurrentLinkedQueue[Int]() val program = for { blockerList <- sequencer.newBlockerList indexedPrograms = { - blockerList - .take(randomizedProgramListSize * 2) - .toList - .grouped(2) - .zipWithIndex - .map { case (adjacentBlockers, index) => + blockerList.take(randomizedProgramListSize * 2).toList.grouped(2).zipWithIndex.map { + case (adjacentBlockers, index) => val List(pre, post) = adjacentBlockers pre.await() >> IO(queue.add(index)) >> post.await() - } + } } scrambledPrograms = Random.shuffle(indexedPrograms).toList startedFibers <- scrambledPrograms.traverse(_.start) diff --git a/src/test/scala/com/github/unchama/testutil/concurrent/tests/ConcurrentEffectTest.scala b/src/test/scala/com/github/unchama/testutil/concurrent/tests/ConcurrentEffectTest.scala index 1883de830e..c22555a2d7 100644 --- a/src/test/scala/com/github/unchama/testutil/concurrent/tests/ConcurrentEffectTest.scala +++ b/src/test/scala/com/github/unchama/testutil/concurrent/tests/ConcurrentEffectTest.scala @@ -7,11 +7,11 @@ trait ConcurrentEffectTest { import cats.effect.implicits._ import cats.implicits._ - def runConcurrent[F[_] : Concurrent, R](program: F[R])(concurrency: Int)(implicit shift: ContextShift[F]): F[List[R]] = { + def runConcurrent[F[_]: Concurrent, R]( + program: F[R] + )(concurrency: Int)(implicit shift: ContextShift[F]): F[List[R]] = { for { - startedFibers <- List.fill(concurrency)(program) - .map(_.start) - .sequence + startedFibers <- List.fill(concurrency)(program).map(_.start).sequence results <- startedFibers.map(_.join).sequence } yield results diff --git a/src/test/scala/com/github/unchama/testutil/concurrent/tests/TaskDiscreteEventually.scala b/src/test/scala/com/github/unchama/testutil/concurrent/tests/TaskDiscreteEventually.scala index e8942dd28c..92eb4ebaa0 100644 --- a/src/test/scala/com/github/unchama/testutil/concurrent/tests/TaskDiscreteEventually.scala +++ b/src/test/scala/com/github/unchama/testutil/concurrent/tests/TaskDiscreteEventually.scala @@ -9,9 +9,13 @@ trait TaskDiscreteEventually { case class DiscreteEventuallyConfig(trialCount: Int) - implicit val discreteEventuallyConfig: DiscreteEventuallyConfig = DiscreteEventuallyConfig(1000) + implicit val discreteEventuallyConfig: DiscreteEventuallyConfig = DiscreteEventuallyConfig( + 1000 + ) - def discreteEventually[T](task: Task[T])(implicit config: DiscreteEventuallyConfig, pos: Position): Task[T] = { + def discreteEventually[T]( + task: Task[T] + )(implicit config: DiscreteEventuallyConfig, pos: Position): Task[T] = { for { taskResult <- Monad[Task].tailRecM[Int, T](1) { attemptCount => for { diff --git a/src/test/scala/com/github/unchama/testutil/execution/MonixTestSchedulerTests.scala b/src/test/scala/com/github/unchama/testutil/execution/MonixTestSchedulerTests.scala index e9c1823490..e5467e5f67 100644 --- a/src/test/scala/com/github/unchama/testutil/execution/MonixTestSchedulerTests.scala +++ b/src/test/scala/com/github/unchama/testutil/execution/MonixTestSchedulerTests.scala @@ -8,8 +8,9 @@ import scala.concurrent.duration.{Duration, FiniteDuration} trait MonixTestSchedulerTests extends ScalaFutures { - def awaitForProgram[U](program: Task[U], tickDuration: FiniteDuration = Duration.Zero) - (implicit testScheduler: TestScheduler): U = { + def awaitForProgram[U](program: Task[U], tickDuration: FiniteDuration = Duration.Zero)( + implicit testScheduler: TestScheduler + ): U = { val future = program.runToFuture testScheduler.tick(tickDuration) diff --git a/src/test/scala/com/github/unchama/util/TrySpec.scala b/src/test/scala/com/github/unchama/util/TrySpec.scala index 44bbebfabf..89d43e07ac 100644 --- a/src/test/scala/com/github/unchama/util/TrySpec.scala +++ b/src/test/scala/com/github/unchama/util/TrySpec.scala @@ -1,10 +1,10 @@ package com.github.unchama.util -import java.util.Optional - import com.github.unchama.util.failable.{FailableAction, Try} import org.scalatest.wordspec.AnyWordSpec +import java.util.Optional + class TrySpec extends AnyWordSpec { "successful try" should { val result = Try.sequence(new FailableAction(1, () => ActionStatus.Ok))