diff --git a/.github/workflows/1_18.yml b/.github/workflows/1_18.yml new file mode 100644 index 0000000000..769c87b710 --- /dev/null +++ b/.github/workflows/1_18.yml @@ -0,0 +1,99 @@ +name: 1.18 build check + +on: + push: + branches: + - 1_18 + paths: + - src/** + - build.sbt + - .scalafix.conf + - .scalafmt.conf + - project/* + - .github/workflows/**.yml + - .github/actions/**/**.yml + +jobs: + build_check: + env: + BUILD_ENVIRONMENT_IS_CI_OR_LOCAL: "CI" + runs-on: ubuntu-24.04 + container: ghcr.io/giganticminecraft/seichiassist-builder-v2:1df7cf5 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: 'recursive' + + - name: Prepare build dependencies cache + uses: actions/cache@v4 + env: + cache-name: cache-build-dependencies + cache-version: v-5 + with: + # sbt等は$HOMEではなくユーザーディレクトリを見ているようで、 + # GH Actionsでの ~ は /github/home/ に展開されるにもかかわらず + # 実際のキャッシュは /root/ 以下に配備される。 + # + # /root/.ivy/cache, /root/.sbt - sbt関連のキャッシュ + # /root/.m2 - ビルドログを観察した感じprotoc等はここを利用する + # /root/.cache - cousierがscalasbt等をキャッシュするのに使っている + path: | + /root/.ivy2/cache + /root/.sbt + /root/.m2 + /root/.cache + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.cache-version }}-${{ github.ref }}-${{ hashFiles('**/build.sbt') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.cache-version }}-${{ github.ref }}- + ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.cache-version }}- + + - name: Prepare build cache + if: github.ref != 'refs/heads/master' + uses: actions/cache@v4 + env: + cache-name: cache-build + cache-version: v-5 + with: + path: | + target + project/target + project/project/target + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.cache-version }}-${{ github.ref }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.cache-version }}-${{ github.ref }}- + ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.cache-version }}- + + # CIでのcheckoutはファイルのタイムスタンプをチェックアウト時刻に設定するため、 + # そのままビルドするとlocalDependenciesにあるjarに変更が行われたと見なされ + # 不要なインクリメンタルコンパイルが走る + # タイムスタンプをコミット時刻に設定することでこれが回避できる + - name: Restore localDependencies' timestamps + # 参考: https://qiita.com/tomlla/items/219cea9dd071c8a9e147 + run: | + git config --global --add safe.directory /__w/SeichiAssist/SeichiAssist + for jar in localDependencies/*.jar; do + timestamp=`git log -1 --pretty=format:'%cd' --date=format:'%Y%m%d%H%M.%S' $jar` + touch -t "$timestamp" $jar + done + + # scalapbは.protoの再コンパイルの必要性を判定する際にタイムスタンプを見ているから、コミット時刻に合わせる + - name: Restore protocol timestamps + ## 参考: https://qiita.com/tomlla/items/219cea9dd071c8a9e147 + run: | + for proto in protocol/*.proto; do + timestamp=`git log -1 --pretty=format:'%cd' --date=format:'%Y%m%d%H%M.%S' $proto` + touch -t "$timestamp" $proto + done + + - name: Check format with Scalafmt + run: ./sbt scalafmtCheckAll + + - name: Check lint with Scalafix on push + run: ./sbt "scalafix --check" + + - name: Test and build artifact + run: mkdir -p target/build && ./sbt assembly + + - name: Clean build artifact for caching target folder + run: rm -r target/build diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml index dbd30c1b0a..b31315ac90 100644 --- a/.github/workflows/build_and_deploy.yml +++ b/.github/workflows/build_and_deploy.yml @@ -5,7 +5,6 @@ on: branches: - develop - master - pull_request: branches: - develop @@ -14,8 +13,8 @@ jobs: build_test_and_upload: env: BUILD_ENVIRONMENT_IS_CI_OR_LOCAL: "CI" - runs-on: ubuntu-22.04 - container: ghcr.io/giganticminecraft/seichiassist-builder:1a64049 + runs-on: ubuntu-24.04 + container: ghcr.io/giganticminecraft/seichiassist-builder-v2:1df7cf5 steps: - name: Checkout repository uses: actions/checkout@v4 @@ -23,7 +22,7 @@ jobs: submodules: 'recursive' - name: Prepare build dependencies cache - uses: actions/cache@v3 + uses: actions/cache@v4 env: cache-name: cache-build-dependencies cache-version: v-5 @@ -47,7 +46,7 @@ jobs: - name: Prepare build cache if: github.ref != 'refs/heads/master' - uses: actions/cache@v3 + uses: actions/cache@v4 env: cache-name: cache-build cache-version: v-5 @@ -68,6 +67,8 @@ jobs: - name: Restore localDependencies' timestamps # 参考: https://qiita.com/tomlla/items/219cea9dd071c8a9e147 run: | + git config --global --add safe.directory /__w/SeichiAssist/SeichiAssist + for jar in localDependencies/*.jar; do timestamp=`git log -1 --pretty=format:'%cd' --date=format:'%Y%m%d%H%M.%S' $jar` touch -t "$timestamp" $jar @@ -102,7 +103,7 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} - name: Test and build artifact - run: sbt assembly + run: mkdir -p target/build && sbt assembly - name: Upload artifact uses: actions/upload-artifact@v4 @@ -125,7 +126,7 @@ jobs: output-sha: name: 最終コミットのSHA値を取得する - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 outputs: sha: ${{ steps.output-sha.outputs.sha }} steps: @@ -140,7 +141,7 @@ jobs: create_prerelease: name: GitHub ReleasesにPreReleaseを作成する - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: github.ref == 'refs/heads/develop' needs: - build_test_and_upload @@ -171,7 +172,7 @@ jobs: prerelease: true push_artifact_to_debug_server_definition: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 if: github.ref == 'refs/heads/develop' needs: - build_test_and_upload @@ -237,7 +238,7 @@ jobs: avatar_url: ${{ secrets.DISCORD_AVATAR_URL }} deploy_artifact_to_production: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 if: github.ref == 'refs/heads/master' needs: - build_test_and_upload diff --git a/.github/workflows/check-sql-version-duplicated-files.yml b/.github/workflows/check-sql-version-duplicated-files.yml index 73588d78fe..2a3ef1e724 100644 --- a/.github/workflows/check-sql-version-duplicated-files.yml +++ b/.github/workflows/check-sql-version-duplicated-files.yml @@ -15,7 +15,7 @@ on: - .github/workflows/check-sql-version-duplicated-files.yml jobs: check: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/.github/workflows/create_1_18_release.yml b/.github/workflows/create_1_18_release.yml new file mode 100644 index 0000000000..575bffcb3f --- /dev/null +++ b/.github/workflows/create_1_18_release.yml @@ -0,0 +1,111 @@ +name: Create 1.18 release + +on: + pull_request: + paths: + - src/** + - build.sbt + - .scalafix.conf + - .scalafmt.conf + - project/* + - .github/workflows/**.yml + - .github/actions/**/**.yml + +jobs: + create_release: + runs-on: ubuntu-24.04 + container: ghcr.io/giganticminecraft/seichiassist-builder-v2:1df7cf5 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: 'recursive' + + - name: Prepare build dependencies cache + uses: actions/cache@v4 + env: + cache-name: cache-build-dependencies + cache-version: v-5 + with: + # sbt等は$HOMEではなくユーザーディレクトリを見ているようで、 + # GH Actionsでの ~ は /github/home/ に展開されるにもかかわらず + # 実際のキャッシュは /root/ 以下に配備される。 + # + # /root/.ivy/cache, /root/.sbt - sbt関連のキャッシュ + # /root/.m2 - ビルドログを観察した感じprotoc等はここを利用する + # /root/.cache - cousierがscalasbt等をキャッシュするのに使っている + path: | + /root/.ivy2/cache + /root/.sbt + /root/.m2 + /root/.cache + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.cache-version }}-${{ github.ref }}-${{ hashFiles('**/build.sbt') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.cache-version }}-${{ github.ref }}- + ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.cache-version }}- + + - name: Prepare build cache + if: github.ref != 'refs/heads/master' + uses: actions/cache@v4 + env: + cache-name: cache-build + cache-version: v-5 + with: + path: | + target + project/target + project/project/target + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.cache-version }}-${{ github.ref }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.cache-version }}-${{ github.ref }}- + ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.cache-version }}- + + # CIでのcheckoutはファイルのタイムスタンプをチェックアウト時刻に設定するため、 + # そのままビルドするとlocalDependenciesにあるjarに変更が行われたと見なされ + # 不要なインクリメンタルコンパイルが走る + # タイムスタンプをコミット時刻に設定することでこれが回避できる + - name: Restore localDependencies' timestamps + # 参考: https://qiita.com/tomlla/items/219cea9dd071c8a9e147 + run: | + git config --global --add safe.directory /__w/SeichiAssist/SeichiAssist + for jar in localDependencies/*.jar; do + timestamp=`git log -1 --pretty=format:'%cd' --date=format:'%Y%m%d%H%M.%S' $jar` + touch -t "$timestamp" $jar + done + + # scalapbは.protoの再コンパイルの必要性を判定する際にタイムスタンプを見ているから、コミット時刻に合わせる + - name: Restore protocol timestamps + ## 参考: https://qiita.com/tomlla/items/219cea9dd071c8a9e147 + run: | + for proto in protocol/*.proto; do + timestamp=`git log -1 --pretty=format:'%cd' --date=format:'%Y%m%d%H%M.%S' $proto` + touch -t "$timestamp" $proto + done + + # sbt-assembly 2以降からディレクトリを作ってくれなくなった + - name: Build artifact + run: mkdir -p target/build && ./sbt assembly + + - name: Create and push a tag + id: tag-name + # GiganticMinecraftにあるSeichiAssistリポジトリのブランチからのPRのみ実行 + if: ${{ github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name }} + run: | + TAG_NAME=pr-${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.sha }} + git tag $TAG_NAME + git push origin $TAG_NAME + echo "value=$TAG_NAME" >> $GITHUB_OUTPUT + + - name: Create release + uses: softprops/action-gh-release@v1 + # GiganticMinecraftにあるSeichiAssistリポジトリのブランチからのPRのみ実行 + if: ${{ github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + files: target/build/SeichiAssist.jar + tag_name: ${{ steps.tag-name.outputs.value }} + draft: false + + - name: Clean build artifact for caching target folder + run: rm -r target/build diff --git a/.github/workflows/create_new_release.yml b/.github/workflows/create_new_release.yml index 9d8fa2b7cd..1119dc135d 100644 --- a/.github/workflows/create_new_release.yml +++ b/.github/workflows/create_new_release.yml @@ -5,7 +5,7 @@ on: jobs: get_branch_name: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 outputs: value: ${{ steps.job.outputs.value }} steps: @@ -14,7 +14,7 @@ jobs: run: echo "value=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_OUTPUT bump_version: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: get_branch_name outputs: old_version: ${{ steps.bump.outputs.old_version }} @@ -72,21 +72,21 @@ jobs: HEAD:${{ needs.get_branch_name.outputs.value }} create_release: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: bump_version steps: - uses: actions/checkout@master with: # これがないとchunk_searchで引っかかってリリースのjarアップロードに失敗する - submodules: 'recursive' + submodules: "recursive" - uses: actions/setup-java@v4 with: - distribution: 'temurin' - java-version: '8' + distribution: "temurin" + java-version: "17" - name: build artifacts - run: sbt assembly + run: mkdir -p target/build && sbt assembly - name: Create release id: create_release @@ -100,7 +100,7 @@ jobs: prerelease: false create_pull-request_to_master: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: [get_branch_name, bump_version] steps: - uses: actions/checkout@master diff --git a/.github/workflows/publish_develop_builds.yml b/.github/workflows/publish_develop_builds.yml new file mode 100644 index 0000000000..e72d05e0b6 --- /dev/null +++ b/.github/workflows/publish_develop_builds.yml @@ -0,0 +1,18 @@ +name: publish develop build + +on: + push: + branches: + - develop + +jobs: + publish_build: + runs-on: ubuntu-24.04 + steps: + - name: Publish develop build + env: + SEICHIASSIST_DOWNLOADER_TOKEN: ${{ secrets.SEICHIASSIST_DOWNLOADER_TOKEN }} + run: | + curl -s -X 'POST' 'http://localhost/publish/develop' \ + -H 'accept: */*' \ + -H 'Authorization: Bearer $SEICHIASSIST_DOWNLOADER_TOKEN' diff --git a/.github/workflows/publish_stable_builds.yml b/.github/workflows/publish_stable_builds.yml new file mode 100644 index 0000000000..0b00893ede --- /dev/null +++ b/.github/workflows/publish_stable_builds.yml @@ -0,0 +1,18 @@ +name: publish stable build + +on: + push: + branches: + - master + +jobs: + publish_build: + runs-on: ubuntu-24.04 + steps: + - name: Publish stable build + env: + SEICHIASSIST_DOWNLOADER_TOKEN: ${{ secrets.SEICHIASSIST_DOWNLOADER_TOKEN }} + run: | + curl -s -X 'POST' 'http://localhost/publish/stable' \ + -H 'accept: */*' \ + -H 'Authorization: Bearer $SEICHIASSIST_DOWNLOADER_TOKEN' diff --git a/.gitignore b/.gitignore index bade594553..36e104a5a2 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,8 @@ # Local files deployLocal.sh -localDependencies/spigot-1.12.2.jar +localDependencies/spigot-1.18.2.jar +plugins/* # Docker docker/spigot/serverfiles/eula.txt @@ -25,3 +26,6 @@ docker/spigot/serverfiles/eula.txt .metals project/.bloop/ project/metals.sbt + +# scalac に -Yprofile-trace profile.trace を渡しているので、Chrome Trace file が生成される +/profile.trace diff --git a/.java-version b/.java-version new file mode 100644 index 0000000000..c7604a4e6f --- /dev/null +++ b/.java-version @@ -0,0 +1 @@ +17.0.10 diff --git a/.scalafmt.conf b/.scalafmt.conf index 1083745791..5701df8ade 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version = 3.4.3 +version = 3.8.3 preset=IntelliJ runner.dialect = scala213 diff --git a/CODING_GUIDELINE.md b/CODING_GUIDELINE.md index efbdef77c5..2de468f9c9 100644 --- a/CODING_GUIDELINE.md +++ b/CODING_GUIDELINE.md @@ -3,7 +3,7 @@ ## \[Scala\] nullを使わない Scalaのファイルにおいては、`null`を使用する代わりに`Option`を使用してください。 -Java (特に、Bukkit/Spigot API) から入ってきた値が`null`になる可能性がある場合は、その呼び出しを早いうちに[`Option(...)`](https://www.scala-lang.org/api/2.13.4/scala/Option$.html#apply[A](x:A):Option[A])で囲い、`Option`にすることが推奨されます。この呼び出しで、引数が`null`だった場合は`None`に、そうでなかった場合は`Some(...)`になります。 +Java (特に、Bukkit/Spigot API) から入ってきた値が`null`になる可能性がある場合は、その呼び出しを早いうちに[`Option(...)`](https://www.scala-lang.org/api/2.13.14/scala/Option$.html#apply[A](x:A):Option[A])で囲い、`Option`にすることが推奨されます。この呼び出しで、引数が`null`だった場合は`None`に、そうでなかった場合は`Some(...)`になります。 ## \[Scala\] 例外を使わない 例外の代わりに`Either`を使用してください。`Either`を使用すると低コストで合成を行うことができるためです。 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 209f3292d6..c9b19c70f4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,18 +2,18 @@ ## 開発を始めるために必要なもの - [IntelliJ IDEA](https://www.jetbrains.com/idea/) などの統合開発環境 -- [JDK 8](https://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot) +- [AdoptOpenJDK 17](https://adoptium.net/temurin/releases/?version=17) - [sbt 1.9](https://www.scala-sbt.org/1.x/docs/Setup.html) - [Scala 2.13](https://www.scala-lang.org/download/) -- Spigot 1.12.2 +- Spigot 1.18.2 - Docker - GitHubのアカウント - Git ### 準備 #### Java Development Kit -最初に、Java Development Kit (JDK) 8をインストールする必要があります。 -[AdoptOpenJDK 1.8](https://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot) のインストールを推奨します。 +最初に、Java Development Kit (JDK) 17をインストールする必要があります。 +[AdoptOpenJDK 17](https://adoptium.net/temurin/releases/?version=17) のインストールを推奨します。 #### 統合開発環境 次に、[IntelliJ IDEA](https://www.jetbrains.com/idea/)などの統合開発環境を導入します。 @@ -63,7 +63,7 @@ GitHubにアカウントを[登録](https://github.com/join)します。 ```bash $ rm -rf target/build # 再ビルドしたいなら既存のターゲットは削除 -$ docker run --rm -it -v `pwd`:/app ghcr.io/giganticminecraft/seichiassist-builder:1a64049 sh -c "cd /app && sbt assembly" +$ docker run --rm -it -v `pwd`:/app ghcr.io/giganticminecraft/seichiassist-builder-v2:1df7cf5 sh -c "cd /app && sbt assembly" $ sudo chown -R `whoami` target/build # docker上でsbtを実行するとrootになってしまうため権限を変える $ cp -n docker/spigot/eula.txt docker/spigot/serverfiles/eula.txt || true $ docker compose up --build -d @@ -152,6 +152,10 @@ IntelliJ IDEAの設定でフォーマットに `scalafmt` を使う #### 手元でデバッグ SeichiAssistは手元でデバッグできる環境を整えています。環境を立ち上げるためには、Dockerが必要です。 +/pluginsディレクトリに対してjarファイル配置すると、そのjarファイルとSeichiAssistを同時に起動した場合の動作を確認することができます。 +整地鯖で利用しているプラグインはGiganticMinecraftのメンバーのみ[MinIOからダウンロード](https://minio-console.onp-k8s.admin.seichi.click/browser/seichi-plugins/ZGViLTEtMTYtNS8=)することができます。 +接続情報などの詳しい情報は、Discordで聞いてください。 + ##### Dockerを立ち上げる Linux環境では、`./prepare-docker.sh`、Windowsでは`prepare-docker.bat`を実行することで @@ -167,7 +171,7 @@ Linux環境では、`./prepare-docker.sh`、Windowsでは`prepare-docker.bat`を サーバーやDB等を停止する場合、 `docker compose down` を実行してください。 -なお、SeichiAssistがJDK 8以外でコンパイルされた場合は、実行時にエラーとなります。必ずJDKのバージョンを揃えるようにしてください。 +なお、SeichiAssistがJava 17未満でコンパイルされた場合は、実行時にエラーとなります。必ずJDKのバージョンを揃えるようにしてください。 ##### デバッグ用環境への接続 diff --git a/README.md b/README.md index 1ef9bf3a88..632f0bcc5f 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,14 @@ [![GitHub Actions](https://github.com/GiganticMinecraft/SeichiAssist/actions/workflows/build_and_deploy.yml/badge.svg)](https://github.com/GiganticMinecraft/SeichiAssist/actions/workflows/build_and_deploy.yml) ## 前提プラグイン -- [CoreProtect-2.14.4](https://www.spigotmc.org/resources/coreprotect.8631/download?version=231781) -- [item-nbt-api-plugin-1.8.2-SNAPSHOT](https://www.spigotmc.org/resources/item-entity-tile-nbt-api.7939/download?version=241690) -- [Multiverse-Core-2.5.0](https://dev.bukkit.org/projects/multiverse-core/files/2428161/download) -- [Multiverse-Portals-2.5.0](https://dev.bukkit.org/projects/multiverse-portals/files/2428333/download) +- [CoreProtect-2.15.0](https://www.spigotmc.org/resources/coreprotect.8631/download?version=231781) +- [item-nbt-api-plugin-2.11.2](https://www.spigotmc.org/resources/item-entity-tile-nbt-api.7939/download?version=241690) +- [Multiverse-Core-4.3.1](https://dev.bukkit.org/projects/multiverse-core/files/2428161/download) +- [Multiverse-Portals-4.2.1](https://dev.bukkit.org/projects/multiverse-portals/files/2428333/download) - [ParticleAPI_v2.1.1](https://dl.inventivetalent.org/download/?file=plugin/ParticleAPI_v2.1.1) -- [WorldBorder1.8.7](https://dev.bukkit.org/projects/worldborder/files/2415838/download) -- [worldedit-bukkit-6.1.9](https://dev.bukkit.org/projects/worldedit/files/2597538/download) -- [worldguard-bukkit-6.2.2](https://dev.bukkit.org/projects/worldguard/files/2610618/download) +- [WorldBorder1.9.10 (beta)](https://www.spigotmc.org/resources/worldborder.60905/download?version=275003) +- [worldedit-bukkit-7.0.0](https://dev.bukkit.org/projects/worldedit/files/2597538/download) +- [worldguard-bukkit-7.0.0](https://dev.bukkit.org/projects/worldguard/files/2610618/download) ## 前提プラグイン(整地鯖内製) - RegenWorld [リポジトリ](https://github.com/GiganticMinecraft/RegenWorld) | [jar](https://redmine.seichi.click/attachments/download/890/RegenWorld-1.0.jar) diff --git a/build.sbt b/build.sbt index fa7972da82..3c4c525e30 100644 --- a/build.sbt +++ b/build.sbt @@ -5,7 +5,7 @@ import java.io._ // region 全プロジェクト共通のメタデータ -ThisBuild / scalaVersion := "2.13.12" +ThisBuild / scalaVersion := "2.13.14" // ThisBuild / version はGitHub Actionsによって取得/自動更新される。 // 次の行は ThisBuild / version := "(\d*)" の形式でなければならない。 ThisBuild / version := "89" @@ -23,7 +23,7 @@ ThisBuild / scalafixScalaBinaryVersion := // region 雑多な設定 // kind-projector 構文を使いたいため -addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.13.2" cross CrossVersion.full) +addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.13.3" cross CrossVersion.full) // CIビルドで詳細なログを確認するため ThisBuild / logLevel := { @@ -57,23 +57,29 @@ resolvers ++= Seq( ) val providedDependencies = Seq( - "org.jetbrains" % "annotations" % "24.1.0", - "org.spigotmc" % "spigot-api" % "1.12.2-R0.1-SNAPSHOT", + "org.jetbrains" % "annotations" % "26.0.1", + "org.apache.commons" % "commons-lang3" % "3.17.0", + "commons-codec" % "commons-codec" % "1.17.1", + "org.spigotmc" % "spigot-api" % "1.18.2-R0.1-SNAPSHOT", // https://maven.enginehub.org/repo/com/sk89q/worldedit/worldedit-bukkit/ - "com.sk89q.worldguard" % "worldguard-legacy" % "6.2", - "net.coreprotect" % "coreprotect" % "2.14.2", - "com.mojang" % "authlib" % "1.6.25", + "com.sk89q.worldguard" % "worldguard-bukkit" % "7.0.7", + "net.coreprotect" % "coreprotect" % "21.3", + "com.mojang" % "authlib" % "6.0.55", // no runtime "org.typelevel" %% "simulacrum" % "1.0.1" ).map(_ % "provided") val scalafixCoreDep = - "ch.epfl.scala" %% "scalafix-core" % _root_.scalafix.sbt.BuildInfo.scalafixVersion % ScalafixConfig + "ch.epfl.scala" %% "scalafix-core" % _root_ + .scalafix + .sbt + .BuildInfo + .scalafixVersion % ScalafixConfig val testDependencies = Seq( "org.scalamock" %% "scalamock" % "5.2.0", - "org.scalatest" %% "scalatest" % "3.2.17", + "org.scalatest" %% "scalatest" % "3.2.19", "org.scalatestplus" %% "scalacheck-1-14" % "3.2.2.0", // テスト用のTestSchedulerを使うため "io.monix" %% "monix" % "3.4.1" @@ -84,16 +90,17 @@ val dependenciesToEmbed = Seq( // DB "org.mariadb.jdbc" % "mariadb-java-client" % "3.4.1", - "org.flywaydb" % "flyway-core" % "5.2.4", - "org.scalikejdbc" %% "scalikejdbc" % "3.5.0", + "org.flywaydb" % "flyway-core" % "10.22.0", + "org.flywaydb" % "flyway-mysql" % "10.22.0", + "org.scalikejdbc" %% "scalikejdbc" % "4.3.2", // redis "com.github.etaty" %% "rediscala" % "1.9.0", // effect system - "org.typelevel" %% "cats-core" % "2.10.0", + "org.typelevel" %% "cats-core" % "2.12.0", "org.typelevel" %% "cats-effect" % "2.5.5", - "co.fs2" %% "fs2-core" % "2.5.11", + "co.fs2" %% "fs2-core" % "2.5.12", // algebra "io.chrisdavenport" %% "log4cats-core" % "1.1.1", @@ -106,22 +113,22 @@ val dependenciesToEmbed = Seq( "com.typesafe.scala-logging" % "scala-logging-slf4j_2.10" % "2.1.2", // type-safety utils - "eu.timepit" %% "refined" % "0.11.0", + "eu.timepit" %% "refined" % "0.11.2", "com.beachape" %% "enumeratum" % "1.7.4", // protobuf "com.thesamet.scalapb" %% "scalapb-runtime" % scalapb.compiler.Version.scalapbVersion, // JSON - "io.circe" %% "circe-core" % "0.14.6", - "io.circe" %% "circe-generic" % "0.14.6", - "io.circe" %% "circe-parser" % "0.14.6", + "io.circe" %% "circe-core" % "0.14.10", + "io.circe" %% "circe-generic" % "0.14.10", + "io.circe" %% "circe-parser" % "0.14.10", // ajd4jp "com.github.KisaragiEffective" % "ajd4jp-mirror" % "8.0.2.2021", // Sentry - "io.sentry" % "sentry" % "7.12.1" + "io.sentry" % "sentry" % "7.18.1" ) // endregion @@ -141,6 +148,8 @@ assembly / assemblyExcludedJars := { // protocol配下とルートのLICENSEが衝突してCIが落ちる // cf. https://github.com/sbt/sbt-assembly/issues/141 assembly / assemblyMergeStrategy := { + // cf. https://qiita.com/yokra9/items/1e72646623f962ce02ee と ChatGPTに聞いた + case PathList("META-INF", "versions", "9", "module-info.class") => MergeStrategy.discard case PathList(ps @ _*) if ps.last endsWith "LICENSE" => MergeStrategy.rename case PathList("org", "apache", "commons", "logging", xs @ _*) => MergeStrategy.last @@ -196,6 +205,8 @@ lazy val root = (project in file(".")).settings( excludeDependencies := Seq(ExclusionRule(organization = "org.bukkit", name = "bukkit")), unmanagedBase := baseDirectory.value / "localDependencies", scalacOptions ++= Seq( + "-Yprofile-trace", + "profile.trace", "-encoding", "utf8", "-unchecked", @@ -208,7 +219,11 @@ lazy val root = (project in file(".")).settings( ), javacOptions ++= Seq("-encoding", "utf8"), assembly / assemblyShadeRules ++= Seq( - ShadeRule.rename("org.mariadb.jdbc.**" -> "com.github.unchama.seichiassist.relocateddependencies.org.mariadb.jdbc.@1").inAll + ShadeRule + .rename( + "org.mariadb.jdbc.**" -> "com.github.unchama.seichiassist.relocateddependencies.org.mariadb.jdbc.@1" + ) + .inAll ), // sbt-assembly 1.0.0からはTestを明示的にタスクツリーに入れる必要がある // cf. https://github.com/sbt/sbt-assembly/pull/432/commits/361224a6202856bc2e572df811d0e6a1f1efda98 diff --git a/compose.yml b/compose.yml index 386425942b..b9306adc4a 100644 --- a/compose.yml +++ b/compose.yml @@ -21,9 +21,9 @@ services: - "25566:25565" - "7091:7091" environment: - - VERSION=1.12.2 + - VERSION=1.18.2 - EULA=TRUE - - TYPE=SPIGOT + - TYPE=PAPER - ONLINE_MODE=FALSE - ENABLE_JMX=true - JMX_PORT=7091 @@ -51,9 +51,9 @@ services: - "25567:25565" - "7092:7091" environment: - - VERSION=1.12.2 + - VERSION=1.18.2 - EULA=TRUE - - TYPE=SPIGOT + - TYPE=PAPER - ONLINE_MODE=FALSE - ENABLE_JMX=true - JMX_PORT=7091 @@ -70,7 +70,7 @@ services: - redis stdin_open: true bungeecord: - image: itzg/bungeecord:java8 + image: itzg/bungeecord:java17 ports: - "25565:25577" volumes: diff --git a/docker/mariadb/Dockerfile b/docker/mariadb/Dockerfile index 63f4614a66..0aae7061f2 100644 --- a/docker/mariadb/Dockerfile +++ b/docker/mariadb/Dockerfile @@ -1,5 +1,5 @@ -# syntax=docker/dockerfile:1.9 -FROM mariadb:10.11.6 +# syntax=docker/dockerfile:1.10 +FROM mariadb:10.11.10 WORKDIR /docker-entrypoint-initdb.d diff --git a/docker/spigot/Dockerfile b/docker/spigot/Dockerfile index 15b0d13acd..43e81c47c3 100644 --- a/docker/spigot/Dockerfile +++ b/docker/spigot/Dockerfile @@ -1,28 +1,26 @@ -# syntax=docker/dockerfile:1.9 -FROM ghcr.io/giganticminecraft/chunk-search-rs:sha-598517c as chunk-search-provider -FROM ghcr.io/giganticminecraft/seichiassist-runner:ba2aa54 +# syntax=docker/dockerfile:1.10 +FROM ghcr.io/giganticminecraft/chunk-search-rs:sha-f1943b1 as chunk-search-provider +FROM ghcr.io/giganticminecraft/seichiassist-runner-v2:890105f +FROM mikefarah/yq:4.44.3 as yq -FROM itzg/minecraft-server:java8-multiarch +FROM itzg/minecraft-server:java17-jdk -# JDKとnkfをインストール -RUN apt-get update && apt-get install -y \ - nkf \ - openjdk-8-jdk \ -&& apt-get clean \ -&& rm -rf /var/lib/apt/lists/* +# nkfをインストール +RUN apt-get update && apt-get install -y nkf \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* #yqをインストール -RUN curl -LJO https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 && \ -mv yq_linux_amd64 /usr/local/bin/yq && \ -chmod a+x /usr/local/bin/yq +COPY --from=yq /usr/bin/yq /usr/bin/yq # chunk-search-rsをインストール COPY --link --from=chunk-search-provider /build/chunk-search-rs /usr/bin/chunk-search-rs # プラグインとスクリプトをローカルからコピーする -COPY --link ./localDependencies/ /data/plugins/ COPY --link ./target/build/ /SeichiAssist/ COPY --link ./docker/spigot/ /data/ +ADD ./localDependencies/ /data/plugins/ +ADD ./plugins /plugins RUN chmod a+x /data/update-seichiassist.sh RUN nkf -Lu --overwrite /data/update-seichiassist.sh diff --git a/docker/spigot/spigot.yml b/docker/spigot/spigot.yml new file mode 100644 index 0000000000..ff92ba13da --- /dev/null +++ b/docker/spigot/spigot.yml @@ -0,0 +1,171 @@ +# This is the main configuration file for Spigot. +# As you can see, there's tons to configure. Some options may impact gameplay, so use +# with caution, and make sure you know what each option does before configuring. +# For a reference for any variable inside this file, check out the Spigot wiki at +# http://www.spigotmc.org/wiki/spigot-configuration/ +# +# If you need help with the configuration or have any questions related to Spigot, +# join us at the Discord or drop by our forums and leave a post. +# +# Discord: https://www.spigotmc.org/go/discord +# Forums: http://www.spigotmc.org/ + +messages: + whitelist: You are not whitelisted on this server! + unknown-command: Unknown command. Type "/help" for help. + server-full: The server is full! + outdated-client: Outdated client! Please use {0} + outdated-server: Outdated server! I'm still on {0} + restart: Server is restarting +advancements: + disable-saving: false + disabled: + - minecraft:story/disabled +settings: + sample-count: 12 + bungeecord: true + player-shuffle: 0 + user-cache-size: 1000 + save-user-cache-on-stop-only: false + moved-wrongly-threshold: 0.0625 + moved-too-quickly-multiplier: 10.0 + timeout-time: 60 + restart-on-crash: true + restart-script: ./start.sh + netty-threads: 4 + attribute: + maxHealth: + max: 2048.0 + movementSpeed: + max: 2048.0 + attackDamage: + max: 2048.0 + log-villager-deaths: true + log-named-deaths: true + debug: false +players: + disable-saving: false +commands: + spam-exclusions: + - /skill + silent-commandblock-console: false + replace-commands: + - setblock + - summon + - testforblock + - tellraw + log: true + tab-complete: 0 + send-namespaced: true +config-version: 12 +stats: + disable-saving: false + forced-stats: {} +world-settings: + default: + below-zero-generation-in-existing-chunks: true + verbose: false + merge-radius: + exp: 3.0 + item: 2.5 + growth: + cactus-modifier: 100 + cane-modifier: 100 + melon-modifier: 100 + mushroom-modifier: 100 + pumpkin-modifier: 100 + sapling-modifier: 100 + beetroot-modifier: 100 + carrot-modifier: 100 + potato-modifier: 100 + wheat-modifier: 100 + netherwart-modifier: 100 + vine-modifier: 100 + cocoa-modifier: 100 + bamboo-modifier: 100 + sweetberry-modifier: 100 + kelp-modifier: 100 + twistingvines-modifier: 100 + weepingvines-modifier: 100 + cavevines-modifier: 100 + glowberry-modifier: 100 + entity-activation-range: + animals: 32 + monsters: 32 + raiders: 48 + misc: 16 + water: 16 + villagers: 32 + flying-monsters: 32 + wake-up-inactive: + animals-max-per-tick: 4 + animals-every: 1200 + animals-for: 100 + monsters-max-per-tick: 8 + monsters-every: 400 + monsters-for: 100 + villagers-max-per-tick: 4 + villagers-every: 600 + villagers-for: 100 + flying-monsters-max-per-tick: 8 + flying-monsters-every: 200 + flying-monsters-for: 100 + villagers-work-immunity-after: 100 + villagers-work-immunity-for: 20 + villagers-active-for-panic: true + tick-inactive-villagers: true + ignore-spectators: false + entity-tracking-range: + players: 48 + animals: 48 + monsters: 48 + misc: 32 + other: 64 + ticks-per: + hopper-transfer: 8 + hopper-check: 1 + hopper-amount: 1 + dragon-death-sound-radius: 0 + seed-village: 10387312 + seed-desert: 14357617 + seed-igloo: 14357618 + seed-jungle: 14357619 + seed-swamp: 14357620 + seed-monument: 10387313 + seed-shipwreck: 165745295 + seed-ocean: 14357621 + seed-outpost: 165745296 + seed-endcity: 10387313 + seed-slime: 987234911 + seed-nether: 30084232 + seed-mansion: 10387319 + seed-fossil: 14357921 + seed-portal: 34222645 + seed-stronghold: default + hunger: + jump-walk-exhaustion: 0.05 + jump-sprint-exhaustion: 0.2 + combat-exhaustion: 0.1 + regen-exhaustion: 6.0 + swim-multiplier: 0.01 + sprint-multiplier: 0.1 + other-multiplier: 0.0 + max-tnt-per-tick: 100 + max-tick-time: + tile: 50 + entity: 50 + arrow-despawn-rate: 1200 + trident-despawn-rate: 1200 + hanging-tick-frequency: 100 + view-distance: default + simulation-distance: default + thunder-chance: 100000 + item-despawn-rate: 6000 + enable-zombie-pigmen-portal-spawns: true + nerf-spawner-mobs: false + wither-spawn-sound-radius: 0 + zombie-aggressive-towards-villager: true + mob-spawn-range: 8 + end-portal-sound-radius: 0 + worldeditregentempworld: + verbose: false diff --git a/localDependencies/Multiverse-Core-2.5.0.jar b/localDependencies/Multiverse-Core-2.5.0.jar deleted file mode 100644 index bb694d66ae..0000000000 Binary files a/localDependencies/Multiverse-Core-2.5.0.jar and /dev/null differ diff --git a/localDependencies/Multiverse-Core-4.3.1.jar b/localDependencies/Multiverse-Core-4.3.1.jar new file mode 100644 index 0000000000..09b089552f Binary files /dev/null and b/localDependencies/Multiverse-Core-4.3.1.jar differ diff --git a/localDependencies/Multiverse-Portals-2.5.0.jar b/localDependencies/Multiverse-Portals-2.5.0.jar deleted file mode 100644 index 1527c60b6a..0000000000 Binary files a/localDependencies/Multiverse-Portals-2.5.0.jar and /dev/null differ diff --git a/localDependencies/Multiverse-Portals-4.2.1.jar b/localDependencies/Multiverse-Portals-4.2.1.jar new file mode 100644 index 0000000000..f4ed83cc7c Binary files /dev/null and b/localDependencies/Multiverse-Portals-4.2.1.jar differ diff --git a/localDependencies/WorldBorder.jar b/localDependencies/WorldBorder.jar index a6388760d2..151682c937 100644 Binary files a/localDependencies/WorldBorder.jar and b/localDependencies/WorldBorder.jar differ diff --git a/localDependencies/item-nbt-api-plugin-1.8.2-SNAPSHOT.jar b/localDependencies/item-nbt-api-plugin-1.8.2-SNAPSHOT.jar deleted file mode 100644 index 6c399eab4a..0000000000 Binary files a/localDependencies/item-nbt-api-plugin-1.8.2-SNAPSHOT.jar and /dev/null differ diff --git a/localDependencies/item-nbt-api-plugin-2.11.2.jar b/localDependencies/item-nbt-api-plugin-2.11.2.jar new file mode 100644 index 0000000000..f1f6904215 Binary files /dev/null and b/localDependencies/item-nbt-api-plugin-2.11.2.jar differ diff --git a/localDependencies/worldedit-bukkit-6.1.9.jar b/localDependencies/worldedit-bukkit-6.1.9.jar deleted file mode 100644 index fd5d0036f5..0000000000 Binary files a/localDependencies/worldedit-bukkit-6.1.9.jar and /dev/null differ diff --git a/localDependencies/worldedit-bukkit-7.2.14.jar b/localDependencies/worldedit-bukkit-7.2.14.jar new file mode 100644 index 0000000000..76af69ee03 Binary files /dev/null and b/localDependencies/worldedit-bukkit-7.2.14.jar differ diff --git a/localDependencies/worldguard-bukkit-6.2.2.jar b/localDependencies/worldguard-bukkit-6.2.2.jar deleted file mode 100644 index 2f615d493c..0000000000 Binary files a/localDependencies/worldguard-bukkit-6.2.2.jar and /dev/null differ diff --git a/localDependencies/worldguard-bukkit-7.0.7-dist.jar b/localDependencies/worldguard-bukkit-7.0.7-dist.jar new file mode 100644 index 0000000000..9c4ea0bb77 Binary files /dev/null and b/localDependencies/worldguard-bukkit-7.0.7-dist.jar differ diff --git a/prepare-docker.bat b/prepare-docker.bat index 8477178b7c..942f3069da 100644 --- a/prepare-docker.bat +++ b/prepare-docker.bat @@ -1,5 +1,5 @@ call rd /s /q target\build - +mkdir target\build call sbt assembly || goto :onerror if "%1" == "update-gachadata" ( diff --git a/prepare-docker.sh b/prepare-docker.sh index e64d5d8193..08214265d1 100755 --- a/prepare-docker.sh +++ b/prepare-docker.sh @@ -6,7 +6,7 @@ build_image() { rm -r target/build || true ## ソースコードからSeichiAssist.jarをビルド - ./sbt assembly + mkdir -p target/build && ./sbt assembly ## dockerイメージのビルド docker compose build -m 2g diff --git a/project/build.properties b/project/build.properties index ee4c672cd0..e88a0d817d 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.10.1 +sbt.version=1.10.6 diff --git a/project/plugins.sbt b/project/plugins.sbt index afd81e0668..44e634dc07 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,8 +1,8 @@ // プラグインJarを(依存関係にあるJarをすべて同梱して)出力するため -addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.2.0") +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.2.0") // Lintを掛けるため -addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.11.1") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.13.0") // コードフォーマットするため addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") diff --git a/project/scalapb.sbt b/project/scalapb.sbt index 6bd8afeb02..318bd48b7a 100644 --- a/project/scalapb.sbt +++ b/project/scalapb.sbt @@ -1,3 +1,3 @@ -addSbtPlugin("com.thesamet" % "sbt-protoc" % "1.0.6") +addSbtPlugin("com.thesamet" % "sbt-protoc" % "1.0.7") -libraryDependencies += "com.thesamet.scalapb" %% "compilerplugin" % "0.11.14" +libraryDependencies += "com.thesamet.scalapb" %% "compilerplugin" % "0.11.17" diff --git a/sbt b/sbt index f63a9ee5a7..e79e6a0271 100755 --- a/sbt +++ b/sbt @@ -34,11 +34,11 @@ set -o pipefail -declare -r sbt_release_version="1.9.7" -declare -r sbt_unreleased_version="1.9.7" +declare -r sbt_release_version="1.10.6" +declare -r sbt_unreleased_version="1.10.6" -declare -r latest_213="2.13.12" -declare -r latest_212="2.12.18" +declare -r latest_213="2.13.15" +declare -r latest_212="2.12.20" declare -r latest_211="2.11.12" declare -r latest_210="2.10.7" declare -r latest_29="2.9.3" diff --git a/src/main/resources/db/migration/V1.18.2__Add_gachaevent_foreign_key.sql b/src/main/resources/db/migration/V1.18.2__Add_gachaevent_foreign_key.sql new file mode 100644 index 0000000000..80be304b87 --- /dev/null +++ b/src/main/resources/db/migration/V1.18.2__Add_gachaevent_foreign_key.sql @@ -0,0 +1,7 @@ +USE seichiassist; + +-- V1.18.1 で外部キー制約を追加しようとしていたが、gacha_events テーブルに存在しない event_id を持つレコードが gachadata テーブルに存在していたため、外部キー制約の追加に失敗していた。 +-- しかし、マイグレーション自体は success になっていたので、このバージョンで外部キー制約を追加することで、不整合データを削除する。 +DELETE FROM gachadata WHERE event_id NOT IN (SELECT id FROM gacha_events) AND event_id IS NOT NULL; + +ALTER TABLE gachadata ADD FOREIGN KEY (event_id) REFERENCES gacha_events(id) ON DELETE CASCADE; diff --git a/src/main/resources/db/migration/V1.19.0__Create_break_suppression_preference_table.sql b/src/main/resources/db/migration/V1.19.0__Create_break_suppression_preference_table.sql new file mode 100644 index 0000000000..6154fc19c8 --- /dev/null +++ b/src/main/resources/db/migration/V1.19.0__Create_break_suppression_preference_table.sql @@ -0,0 +1,8 @@ +USE seichiassist; + +CREATE TABLE player_break_suppression_preference( + uuid CHAR(36) NOT NULL, + do_break_suppression_due_to_mana BOOL NOT NULL DEFAULT FALSE, + PRIMARY KEY (uuid), + CONSTRAINT fk_player_break_suppression_preference_uuid FOREIGN KEY (uuid) REFERENCES playerdata(uuid) +); diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 3def2e3633..99dfab09e6 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -2,7 +2,9 @@ name: @name@ main: com.github.unchama.seichiassist.SeichiAssist version: @version@ -softdepend: [WorldGuard, CoreProtect] +api-version: 1.18 + +softdepend: [WorldGuard, CoreProtect, NBTAPI, Multiverse-Core, RegenWorld] loadbefore: # ワールドマイグレーション時にこれらプラグインのタスクがworldへの参照を持ちデータのGCが行われなくなる diff --git a/src/main/scala/com/github/unchama/buildassist/BuildAssist.scala b/src/main/scala/com/github/unchama/buildassist/BuildAssist.scala index 32072d60da..3201cd2419 100644 --- a/src/main/scala/com/github/unchama/buildassist/BuildAssist.scala +++ b/src/main/scala/com/github/unchama/buildassist/BuildAssist.scala @@ -11,6 +11,7 @@ import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.B import com.github.unchama.seichiassist.subsystems.mana.ManaApi import com.github.unchama.seichiassist.subsystems.managedfly.ManagedFlyApi import com.github.unchama.seichiassist.subsystems.minestack.MineStackAPI +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.{DefaultEffectEnvironment, subsystems} import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack @@ -26,7 +27,8 @@ class BuildAssist(plugin: Plugin)( buildCountAPI: subsystems.buildcount.BuildCountAPI[IO, SyncIO, Player], manaApi: ManaApi[IO, SyncIO, Player], mineStackAPI: MineStackAPI[IO, Player, ItemStack], - ioConcurrentEffect: ConcurrentEffect[IO] + ioConcurrentEffect: ConcurrentEffect[IO], + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) { // TODO この辺のフィールドを整理する @@ -97,8 +99,11 @@ object BuildAssist { , Material.COBBLESTONE // 丸石 , - Material.WOOD // 木 - , + Material.ACACIA_WOOD, + Material.BIRCH_WOOD, + Material.DARK_OAK_WOOD, + Material.JUNGLE_WOOD, + Material.OAK_WOOD, Material.SAND // 砂 , Material.GRAVEL // 砂利 @@ -109,18 +114,34 @@ object BuildAssist { , Material.COAL_ORE // 石炭鉱石 , - Material.LOG // 原木 - , + Material.ACACIA_LOG, + Material.BIRCH_LOG, + Material.DARK_OAK_LOG, + Material.JUNGLE_LOG, + Material.OAK_LOG, Material.GLASS // ガラス , Material.LAPIS_ORE // ラピス鉱石 , Material.LAPIS_BLOCK // ラピスB , - Material.SANDSTONE // 砂岩 - , - Material.WOOL // 羊毛 - , + Material.SANDSTONE, // 砂岩 + Material.BLACK_WOOL, + Material.BLUE_WOOL, + Material.BROWN_WOOL, + Material.CYAN_WOOL, + Material.GRAY_WOOL, + Material.GREEN_WOOL, + Material.LIGHT_BLUE_WOOL, + Material.LIGHT_GRAY_WOOL, + Material.LIME_WOOL, + Material.MAGENTA_WOOL, + Material.ORANGE_WOOL, + Material.PINK_WOOL, + Material.PURPLE_WOOL, + Material.RED_WOOL, + Material.WHITE_WOOL, + Material.YELLOW_WOOL, Material.GOLD_BLOCK // 金B , Material.IRON_BLOCK // 鉄B @@ -149,38 +170,76 @@ object BuildAssist { , Material.SOUL_SAND // ソウルサンド , - Material.GLOWSTONE // グロウストーン - , - Material.STAINED_GLASS // 色付きガラス - , - Material.SMOOTH_BRICK // 石レンガ - , - Material.MYCEL // 菌糸 + Material.GLOWSTONE, + Material.BLACK_STAINED_GLASS, + Material.BLUE_STAINED_GLASS, + Material.BROWN_STAINED_GLASS, + Material.CYAN_STAINED_GLASS, + Material.GRAY_STAINED_GLASS, + Material.GREEN_STAINED_GLASS, + Material.LIGHT_BLUE_STAINED_GLASS, + Material.LIGHT_GRAY_STAINED_GLASS, + Material.LIME_STAINED_GLASS, + Material.MAGENTA_STAINED_GLASS, + Material.ORANGE_STAINED_GLASS, + Material.PINK_STAINED_GLASS, + Material.PURPLE_STAINED_GLASS, + Material.RED_STAINED_GLASS, + Material.WHITE_STAINED_GLASS, + Material.YELLOW_STAINED_GLASS, + Material.STONE_BRICKS // 石レンガ + , + Material.MYCELIUM // 菌糸 , Material.NETHER_BRICK // ネザーレンガ , - Material.ENDER_STONE // エンドストーン + Material.END_STONE // エンドストーン , Material.EMERALD_ORE // エメ鉱石 , Material.EMERALD_BLOCK // エメB , - Material.COBBLE_WALL // 丸石の壁 + Material.COBBLESTONE_WALL // 丸石の壁 , - Material.QUARTZ_ORE // 水晶鉱石 + Material.NETHER_QUARTZ_ORE // 水晶鉱石 , Material.QUARTZ_BLOCK // 水晶B , - Material.STAINED_CLAY // 色付き固焼き粘土 - , - Material.LOG_2 // 原木2 - , + Material.BLACK_TERRACOTTA, + Material.BLUE_TERRACOTTA, + Material.BROWN_TERRACOTTA, + Material.CYAN_TERRACOTTA, + Material.GRAY_TERRACOTTA, + Material.GREEN_TERRACOTTA, + Material.LIGHT_BLUE_TERRACOTTA, + Material.LIGHT_GRAY_TERRACOTTA, + Material.LIME_TERRACOTTA, + Material.MAGENTA_TERRACOTTA, + Material.ORANGE_TERRACOTTA, + Material.PINK_TERRACOTTA, + Material.PURPLE_TERRACOTTA, + Material.RED_TERRACOTTA, + Material.WHITE_TERRACOTTA, + Material.YELLOW_TERRACOTTA, Material.PRISMARINE // プリズマリン , - Material.SEA_LANTERN // シーランタン - , - Material.HARD_CLAY // 固焼き粘土 - , + Material.SEA_LANTERN, // シーランタン + Material.BLACK_GLAZED_TERRACOTTA, + Material.BLUE_GLAZED_TERRACOTTA, + Material.BROWN_GLAZED_TERRACOTTA, + Material.CYAN_GLAZED_TERRACOTTA, + Material.GRAY_GLAZED_TERRACOTTA, + Material.GREEN_GLAZED_TERRACOTTA, + Material.LIGHT_BLUE_GLAZED_TERRACOTTA, + Material.LIGHT_GRAY_GLAZED_TERRACOTTA, + Material.LIME_GLAZED_TERRACOTTA, + Material.MAGENTA_GLAZED_TERRACOTTA, + Material.ORANGE_GLAZED_TERRACOTTA, + Material.PINK_GLAZED_TERRACOTTA, + Material.PURPLE_GLAZED_TERRACOTTA, + Material.RED_GLAZED_TERRACOTTA, + Material.WHITE_GLAZED_TERRACOTTA, + Material.YELLOW_GLAZED_TERRACOTTA, Material.COAL_BLOCK // 石炭B , Material.PACKED_ICE // 氷塊 @@ -191,22 +250,49 @@ object BuildAssist { , Material.PURPUR_PILLAR // 柱状プルパーブ , - Material.END_BRICKS // エンドレンガB + Material.END_STONE_BRICKS // エンドレンガB , - Material.RED_NETHER_BRICK // 赤ネザーレンガB + Material.RED_NETHER_BRICKS // 赤ネザーレンガB , Material.BONE_BLOCK // 骨B , - Material.NETHER_WART_BLOCK // ネザーウォートB - , - Material.CONCRETE // コンクリート - , - Material.CONCRETE_POWDER // コンクリートパウダー - , + Material.NETHER_WART_BLOCK, // ネザーウォートB + Material.BLACK_CONCRETE, + Material.BLUE_CONCRETE, + Material.BROWN_CONCRETE, + Material.CYAN_CONCRETE, + Material.GRAY_CONCRETE, + Material.GREEN_CONCRETE, + Material.LIGHT_BLUE_CONCRETE, + Material.LIGHT_GRAY_CONCRETE, + Material.LIME_CONCRETE, + Material.MAGENTA_CONCRETE, + Material.ORANGE_CONCRETE, + Material.PINK_CONCRETE, + Material.PURPLE_CONCRETE, + Material.RED_CONCRETE, + Material.WHITE_CONCRETE, + Material.YELLOW_CONCRETE, + Material.BLACK_CONCRETE_POWDER, + Material.BLUE_CONCRETE_POWDER, + Material.BROWN_CONCRETE_POWDER, + Material.CYAN_CONCRETE_POWDER, + Material.GRAY_CONCRETE_POWDER, + Material.GREEN_CONCRETE_POWDER, + Material.LIGHT_BLUE_CONCRETE_POWDER, + Material.LIGHT_GRAY_CONCRETE_POWDER, + Material.LIME_CONCRETE_POWDER, + Material.MAGENTA_CONCRETE_POWDER, + Material.ORANGE_CONCRETE_POWDER, + Material.PINK_CONCRETE_POWDER, + Material.PURPLE_CONCRETE_POWDER, + Material.RED_CONCRETE_POWDER, + Material.WHITE_CONCRETE_POWDER, + Material.YELLOW_CONCRETE_POWDER, Material.ACACIA_STAIRS, Material.ACACIA_FENCE, Material.ACACIA_FENCE_GATE, - Material.BIRCH_WOOD_STAIRS, + Material.BIRCH_STAIRS, Material.BIRCH_FENCE, Material.BIRCH_FENCE_GATE, Material.BONE_BLOCK, @@ -215,28 +301,33 @@ object BuildAssist { 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.END_STONE_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.STRIPPED_JUNGLE_WOOD, Material.LADDER, - Material.LEAVES, - Material.LEAVES_2, - Material.LOG, - Material.LOG_2, + Material.OAK_LEAVES, + Material.BIRCH_LEAVES, + Material.JUNGLE_LEAVES, + Material.ACACIA_LEAVES, + Material.DARK_OAK_LEAVES, + Material.SPRUCE_LEAVES, + Material.ACACIA_LOG, + Material.BIRCH_LOG, + Material.DARK_OAK_LOG, + Material.JUNGLE_LOG, + Material.OAK_LOG, Material.NETHER_BRICK, Material.NETHER_BRICK_STAIRS, Material.NETHER_WART_BLOCK, - Material.RED_NETHER_BRICK, + Material.RED_NETHER_BRICKS, Material.OBSIDIAN, Material.PACKED_ICE, Material.PRISMARINE, @@ -252,31 +343,72 @@ object BuildAssist { Material.SANDSTONE_STAIRS, Material.SEA_LANTERN, Material.SLIME_BLOCK, - Material.SMOOTH_BRICK, - Material.SMOOTH_STAIRS, + Material.SMOOTH_STONE, + Material.CHISELED_STONE_BRICKS, + Material.CRACKED_STONE_BRICKS, + Material.INFESTED_STONE_BRICKS, + Material.MOSSY_STONE_BRICKS, + Material.STONE_BRICK_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.STRIPPED_ACACIA_WOOD, + Material.STRIPPED_BIRCH_WOOD, + Material.STRIPPED_DARK_OAK_WOOD, + Material.STRIPPED_JUNGLE_WOOD, + Material.STRIPPED_OAK_WOOD, + Material.OAK_FENCE, + Material.ACACIA_FENCE, + Material.BIRCH_FENCE, + Material.DARK_OAK_FENCE, + Material.JUNGLE_FENCE, + Material.OAK_FENCE_GATE, + Material.ACACIA_FENCE_GATE, + Material.BIRCH_FENCE_GATE, + Material.DARK_OAK_FENCE_GATE, + Material.JUNGLE_FENCE_GATE, + Material.OAK_FENCE_GATE, + Material.BLACK_STAINED_GLASS_PANE, + Material.BLUE_STAINED_GLASS_PANE, + Material.BROWN_STAINED_GLASS_PANE, + Material.CYAN_STAINED_GLASS_PANE, + Material.GRAY_STAINED_GLASS_PANE, + Material.GREEN_STAINED_GLASS_PANE, + Material.LIGHT_BLUE_STAINED_GLASS_PANE, + Material.LIGHT_GRAY_STAINED_GLASS_PANE, + Material.LIME_STAINED_GLASS_PANE, + Material.MAGENTA_STAINED_GLASS_PANE, + Material.ORANGE_STAINED_GLASS_PANE, + Material.PINK_STAINED_GLASS_PANE, + Material.PURPLE_STAINED_GLASS_PANE, + Material.RED_STAINED_GLASS_PANE, + Material.WHITE_STAINED_GLASS_PANE, + Material.YELLOW_STAINED_GLASS_PANE, + Material.ACACIA_SLAB, + Material.BIRCH_SLAB, + Material.DARK_OAK_SLAB, + Material.JUNGLE_SLAB, + Material.OAK_SLAB, Material.STONE, - Material.STONE_SLAB2, - Material.THIN_GLASS, + Material.STONE_SLAB, Material.TORCH, - Material.WOOD, - Material.WOOD_STAIRS, - Material.WOOD_STEP, - Material.WOOL, - Material.CARPET, - Material.WORKBENCH, - // #1008 - Material.LEAVES, - Material.LEAVES_2 + Material.BLACK_CARPET, + Material.BLUE_CARPET, + Material.BROWN_CARPET, + Material.CYAN_CARPET, + Material.GRAY_CARPET, + Material.GREEN_CARPET, + Material.LIGHT_BLUE_CARPET, + Material.LIGHT_GRAY_CARPET, + Material.LIME_CARPET, + Material.MAGENTA_CARPET, + Material.ORANGE_CARPET, + Material.PINK_CARPET, + Material.PURPLE_CARPET, + Material.RED_CARPET, + Material.WHITE_CARPET, + Material.YELLOW_CARPET, + Material.CRAFTING_TABLE ) // 直列設置ブロックの対象リスト @@ -291,8 +423,11 @@ object BuildAssist { , Material.COBBLESTONE // 丸石 , - Material.WOOD // 木 - , + Material.ACACIA_WOOD, + Material.BIRCH_WOOD, + Material.DARK_OAK_WOOD, + Material.JUNGLE_WOOD, + Material.OAK_WOOD, Material.SAND // 砂 , Material.GRAVEL // 砂利 @@ -303,8 +438,11 @@ object BuildAssist { , Material.COAL_ORE // 石炭鉱石 , - Material.LOG // 原木 - , + Material.ACACIA_LOG, + Material.BIRCH_LOG, + Material.DARK_OAK_LOG, + Material.JUNGLE_LOG, + Material.OAK_LOG, Material.GLASS // ガラス , Material.LAPIS_ORE // ラピス鉱石 @@ -313,8 +451,22 @@ object BuildAssist { , Material.SANDSTONE // 砂岩 , - Material.WOOL // 羊毛 - , + Material.BLACK_WOOL, + Material.BLUE_WOOL, + Material.BROWN_WOOL, + Material.CYAN_WOOL, + Material.GRAY_WOOL, + Material.GREEN_WOOL, + Material.LIGHT_BLUE_WOOL, + Material.LIGHT_GRAY_WOOL, + Material.LIME_WOOL, + Material.MAGENTA_WOOL, + Material.ORANGE_WOOL, + Material.PINK_WOOL, + Material.PURPLE_WOOL, + Material.RED_WOOL, + Material.WHITE_WOOL, + Material.YELLOW_WOOL, Material.GOLD_BLOCK // 金B , Material.IRON_BLOCK // 鉄B @@ -345,36 +497,76 @@ object BuildAssist { , Material.GLOWSTONE // グロウストーン , - Material.STAINED_GLASS // 色付きガラス - , - Material.SMOOTH_BRICK // 石レンガ - , - Material.MYCEL // 菌糸 + Material.BLACK_STAINED_GLASS, + Material.BLUE_STAINED_GLASS, + Material.BROWN_STAINED_GLASS, + Material.CYAN_STAINED_GLASS, + Material.GRAY_STAINED_GLASS, + Material.GREEN_STAINED_GLASS, + Material.LIGHT_BLUE_STAINED_GLASS, + Material.LIGHT_GRAY_STAINED_GLASS, + Material.LIME_STAINED_GLASS, + Material.MAGENTA_STAINED_GLASS, + Material.ORANGE_STAINED_GLASS, + Material.PINK_STAINED_GLASS, + Material.PURPLE_STAINED_GLASS, + Material.RED_STAINED_GLASS, + Material.WHITE_STAINED_GLASS, + Material.YELLOW_STAINED_GLASS, + Material.STONE_BRICKS // 石レンガ + , + Material.MYCELIUM // 菌糸 , Material.NETHER_BRICK // ネザーレンガ , - Material.ENDER_STONE // エンドストーン + Material.END_STONE // エンドストーン , Material.EMERALD_ORE // エメ鉱石 , Material.EMERALD_BLOCK // エメB , - Material.COBBLE_WALL // 丸石の壁 + Material.COBBLESTONE_WALL // 丸石の壁 , - Material.QUARTZ_ORE // 水晶鉱石 + Material.NETHER_QUARTZ_ORE // 水晶鉱石 , Material.QUARTZ_BLOCK // 水晶B , - Material.STAINED_CLAY // 色付き固焼き粘土 - , - Material.LOG_2 // 原木2 - , + Material.BLACK_TERRACOTTA, + Material.BLUE_TERRACOTTA, + Material.BROWN_TERRACOTTA, + Material.CYAN_TERRACOTTA, + Material.GRAY_TERRACOTTA, + Material.GREEN_TERRACOTTA, + Material.LIGHT_BLUE_TERRACOTTA, + Material.LIGHT_GRAY_TERRACOTTA, + Material.LIME_TERRACOTTA, + Material.MAGENTA_TERRACOTTA, + Material.ORANGE_TERRACOTTA, + Material.PINK_TERRACOTTA, + Material.PURPLE_TERRACOTTA, + Material.RED_TERRACOTTA, + Material.WHITE_TERRACOTTA, + Material.YELLOW_TERRACOTTA, Material.PRISMARINE // プリズマリン , Material.SEA_LANTERN // シーランタン , - Material.HARD_CLAY // 固焼き粘土 - , + Material.BLACK_GLAZED_TERRACOTTA, + Material.BLUE_GLAZED_TERRACOTTA, + Material.BROWN_GLAZED_TERRACOTTA, + Material.CYAN_GLAZED_TERRACOTTA, + Material.GRAY_GLAZED_TERRACOTTA, + Material.GREEN_GLAZED_TERRACOTTA, + Material.LIGHT_BLUE_GLAZED_TERRACOTTA, + Material.LIGHT_GRAY_GLAZED_TERRACOTTA, + Material.LIME_GLAZED_TERRACOTTA, + Material.MAGENTA_GLAZED_TERRACOTTA, + Material.ORANGE_GLAZED_TERRACOTTA, + Material.PINK_GLAZED_TERRACOTTA, + Material.PURPLE_GLAZED_TERRACOTTA, + Material.RED_GLAZED_TERRACOTTA, + Material.WHITE_GLAZED_TERRACOTTA, + Material.YELLOW_GLAZED_TERRACOTTA, Material.COAL_BLOCK // 石炭B , Material.PACKED_ICE // 氷塊 @@ -385,22 +577,53 @@ object BuildAssist { , Material.PURPUR_PILLAR // 柱状プルパーブ , - Material.END_BRICKS // エンドレンガB + Material.END_STONE_BRICKS // エンドレンガB , - Material.RED_NETHER_BRICK // 赤ネザーレンガB + Material.RED_NETHER_BRICKS // 赤ネザーレンガB , Material.BONE_BLOCK // 骨B , - Material.FENCE // オークフェンス - , - Material.IRON_FENCE // 鉄フェンス - , - Material.THIN_GLASS // 板ガラス - , - Material.NETHER_FENCE // ネザーフェンス - , - Material.STAINED_GLASS_PANE // 色付き板ガラス - , + Material.OAK_FENCE, + Material.ACACIA_FENCE, + Material.BIRCH_FENCE, + Material.DARK_OAK_FENCE, + Material.JUNGLE_FENCE, + Material.IRON_BARS // 鉄フェンス + , + Material.BLACK_STAINED_GLASS, + Material.BLUE_STAINED_GLASS, + Material.BROWN_STAINED_GLASS, + Material.CYAN_STAINED_GLASS, + Material.GRAY_STAINED_GLASS, + Material.GREEN_STAINED_GLASS, + Material.LIGHT_BLUE_STAINED_GLASS, + Material.LIGHT_GRAY_STAINED_GLASS, + Material.LIME_STAINED_GLASS, + Material.MAGENTA_STAINED_GLASS, + Material.ORANGE_STAINED_GLASS, + Material.PINK_STAINED_GLASS, + Material.PURPLE_STAINED_GLASS, + Material.RED_STAINED_GLASS, + Material.WHITE_STAINED_GLASS, + Material.YELLOW_STAINED_GLASS, + Material.NETHER_BRICK_FENCE // ネザーフェンス + , + Material.BLACK_STAINED_GLASS_PANE, + Material.BLUE_STAINED_GLASS_PANE, + Material.BROWN_STAINED_GLASS_PANE, + Material.CYAN_STAINED_GLASS_PANE, + Material.GRAY_STAINED_GLASS_PANE, + Material.GREEN_STAINED_GLASS_PANE, + Material.LIGHT_BLUE_STAINED_GLASS_PANE, + Material.LIGHT_GRAY_STAINED_GLASS_PANE, + Material.LIME_STAINED_GLASS_PANE, + Material.MAGENTA_STAINED_GLASS_PANE, + Material.ORANGE_STAINED_GLASS_PANE, + Material.PINK_STAINED_GLASS_PANE, + Material.PURPLE_STAINED_GLASS_PANE, + Material.RED_STAINED_GLASS_PANE, + Material.WHITE_STAINED_GLASS_PANE, + Material.YELLOW_STAINED_GLASS_PANE, Material.SLIME_BLOCK // スライムB , Material.SPRUCE_FENCE // 松フェンス @@ -413,40 +636,79 @@ object BuildAssist { , Material.ACACIA_FENCE // アカシアフェンス , - Material.NETHER_WART_BLOCK // ネザーウォートB - , - Material.CONCRETE // コンクリート - , - Material.CONCRETE_POWDER // コンクリートパウダー - , - Material.LEAVES, - Material.LEAVES_2 + Material.NETHER_WART_BLOCK, // ネザーウォートB + Material.BLACK_CONCRETE, + Material.BLUE_CONCRETE, + Material.BROWN_CONCRETE, + Material.CYAN_CONCRETE, + Material.GRAY_CONCRETE, + Material.GREEN_CONCRETE, + Material.LIGHT_BLUE_CONCRETE, + Material.LIGHT_GRAY_CONCRETE, + Material.LIME_CONCRETE, + Material.MAGENTA_CONCRETE, + Material.ORANGE_CONCRETE, + Material.PINK_CONCRETE, + Material.PURPLE_CONCRETE, + Material.RED_CONCRETE, + Material.WHITE_CONCRETE, + Material.YELLOW_CONCRETE, + Material.BLACK_CONCRETE_POWDER, + Material.BLUE_CONCRETE_POWDER, + Material.BROWN_CONCRETE_POWDER, + Material.CYAN_CONCRETE_POWDER, + Material.GRAY_CONCRETE_POWDER, + Material.GREEN_CONCRETE_POWDER, + Material.LIGHT_BLUE_CONCRETE_POWDER, + Material.LIGHT_GRAY_CONCRETE_POWDER, + Material.LIME_CONCRETE_POWDER, + Material.MAGENTA_CONCRETE_POWDER, + Material.ORANGE_CONCRETE_POWDER, + Material.PINK_CONCRETE_POWDER, + Material.PURPLE_CONCRETE_POWDER, + Material.RED_CONCRETE_POWDER, + Material.WHITE_CONCRETE_POWDER, + Material.YELLOW_CONCRETE_POWDER, + Material.OAK_LEAVES, + Material.BIRCH_LEAVES, + Material.JUNGLE_LEAVES, + Material.ACACIA_LEAVES, + Material.DARK_OAK_LEAVES, + Material.SPRUCE_LEAVES ) // ハーフブロックとして扱うMaterial val material_slab2: java.util.Set[Material] = util .EnumSet .of( - Material.STONE_SLAB2 // 赤砂岩 - , - Material.PURPUR_SLAB // プルパー - , - Material.WOOD_STEP // 木 - , - Material.STEP // 石 + Material.RED_SANDSTONE_SLAB, + Material.PURPUR_SLAB, + Material.ACACIA_SLAB, + Material.BIRCH_SLAB, + Material.DARK_OAK_SLAB, + Material.JUNGLE_SLAB, + Material.OAK_SLAB, + Material.STONE_SLAB, + Material.STONE_BRICK_SLAB ) val material_destruction: java.util.Set[Material] = util .EnumSet .of( - Material.LONG_GRASS // 草 - , + Material.GRASS, + Material.TALL_GRASS, Material.DEAD_BUSH // 枯れ木 , - Material.YELLOW_FLOWER // タンポポ - , - Material.RED_ROSE // 花9種 - , + Material.DANDELION, + Material.POPPY, + Material.BLUE_ORCHID, + Material.ALLIUM, + Material.AZURE_BLUET, + Material.RED_TULIP, + Material.ORANGE_TULIP, + Material.WHITE_TULIP, + Material.PINK_TULIP, + Material.OXEYE_DAISY, Material.BROWN_MUSHROOM // きのこ , Material.RED_MUSHROOM // 赤きのこ @@ -455,16 +717,15 @@ object BuildAssist { , Material.SNOW // 雪 , - Material.DOUBLE_PLANT // 高い花、草 - , + Material.SUNFLOWER, + Material.LILAC, + Material.LARGE_FERN, + Material.ROSE_BUSH, + Material.PEONY, Material.WATER // 水 , - Material.STATIONARY_WATER // 水 - , Material.LAVA // 溶岩 , - Material.STATIONARY_LAVA // 溶岩 - , Material.VINE // ツタ ) diff --git a/src/main/scala/com/github/unchama/buildassist/MenuInventoryData.scala b/src/main/scala/com/github/unchama/buildassist/MenuInventoryData.scala index 95666d3373..fded2b11c5 100644 --- a/src/main/scala/com/github/unchama/buildassist/MenuInventoryData.scala +++ b/src/main/scala/com/github/unchama/buildassist/MenuInventoryData.scala @@ -1,12 +1,14 @@ package com.github.unchama.buildassist -import cats.effect.SyncIO +import cats.effect.{IO, SyncIO} import com.github.unchama.buildassist.util.AsyncInventorySetter +import com.github.unchama.itemstackbuilder.{IconItemStackBuilder, SkullItemStackBuilder} +import com.github.unchama.seichiassist.SkullOwners import com.github.unchama.seichiassist.subsystems.itemmigration.infrastructure.minecraft.JdbcBackedUuidRepository -import com.github.unchama.seichiassist.util.ItemMetaFactory +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import org.bukkit.ChatColor._ import org.bukkit.entity.Player -import org.bukkit.inventory.meta.{ItemMeta, SkullMeta} +import org.bukkit.inventory.meta.ItemMeta import org.bukkit.inventory.{Inventory, ItemStack} import org.bukkit.{Bukkit, Material} @@ -17,7 +19,9 @@ object MenuInventoryData { JdbcBackedUuidRepository.initializeStaticInstance[SyncIO].unsafeRunSync().apply[SyncIO] // ブロックを並べる設定メニュー - def getBlockLineUpData(p: Player): Inventory = { + def getBlockLineUpData( + p: Player + )(implicit playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player]): Inventory = { // プレイヤーを取得 val player = p.getPlayer // UUID取得 @@ -26,41 +30,34 @@ object MenuInventoryData { val playerdata = BuildAssist.instance.temporaryData(uuid) val inventory = Bukkit.getServer.createInventory(null, 4 * 9, s"$DARK_PURPLE$BOLD「直列設置」設定") - var itemstack = new ItemStack(Material.SKULL_ITEM, 1) - var itemmeta: ItemMeta = Bukkit.getItemFactory.getItemMeta(Material.WOOD) - val skullmeta: SkullMeta = ItemMetaFactory.SKULL.getValue + var itemstack = new ItemStack(Material.PLAYER_HEAD, 11) + var itemmeta: ItemMeta = itemstack.getItemMeta var lore = List(s"$RESET$DARK_RED${UNDERLINE}クリックで移動") // ホームを開く - itemstack.setDurability(3.toShort) - skullmeta.setDisplayName(s"$YELLOW$UNDERLINE${BOLD}ホームへ") - skullmeta.setLore(lore.asJava) - - /** - * 参加したことのないプレーヤーはgetOfflinePlayerでデータが取れないのでこうするしか無い - */ - skullmeta.setOwner("MHF_ArrowLeft") - itemstack.setItemMeta(skullmeta) + itemstack = new SkullItemStackBuilder(SkullOwners.MHF_ArrowLeft) + .title(s"$YELLOW$UNDERLINE${BOLD}ホームへ") + .lore(lore) + .build() 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)}" - ) - lore = List( - s"$RESET${GRAY}オフハンドに木の棒、メインハンドに設置したいブロックを持って", - s"$RESET${GRAY}左クリックすると向いてる方向に並べて設置します。", - s"$RESET${GRAY}建築Lv${BuildAssist.config.getblocklineuplevel}以上で利用可能", - s"$RESET${GRAY}クリックで切り替え" - ) - itemmeta.setLore(lore.asJava) - itemstack.setItemMeta(itemmeta) + itemstack = new IconItemStackBuilder(Material.OAK_PLANKS) + .title( + s"$YELLOW$UNDERLINE${BOLD}直列設置 :${BuildAssist.line_up_str(playerdata.line_up_flg)}" + ) + .lore( + s"$RESET${GRAY}オフハンドに木の棒、メインハンドに設置したいブロックを持って", + s"$RESET${GRAY}左クリックすると向いてる方向に並べて設置します。", + s"$RESET${GRAY}建築Lv${BuildAssist.config.getblocklineuplevel}以上で利用可能", + s"$RESET${GRAY}クリックで切り替え" + ) + .build() + inventory.setItem(0, itemstack) // 直列設置ハーフブロック設定 - itemstack = new ItemStack(Material.STEP, 1) - itemmeta = Bukkit.getItemFactory.getItemMeta(Material.STEP) + itemstack = new ItemStack(Material.STONE_SLAB, 1) + itemmeta = itemstack.getItemMeta itemmeta.setDisplayName( s"$YELLOW$UNDERLINE${BOLD}ハーフブロック設定 :${BuildAssist.line_up_step_str(playerdata.line_up_step_flg)}" ) diff --git a/src/main/scala/com/github/unchama/buildassist/PlayerInventoryListener.scala b/src/main/scala/com/github/unchama/buildassist/PlayerInventoryListener.scala index 539535777f..c79c7875b5 100644 --- a/src/main/scala/com/github/unchama/buildassist/PlayerInventoryListener.scala +++ b/src/main/scala/com/github/unchama/buildassist/PlayerInventoryListener.scala @@ -5,6 +5,7 @@ import com.github.unchama.generic.effect.unsafe.EffectEnvironment import com.github.unchama.menuinventory.router.CanOpen import com.github.unchama.seichiassist.effects.player.CommonSoundEffects import com.github.unchama.seichiassist.menus.BuildMainMenu +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import net.md_5.bungee.api.ChatColor._ import org.bukkit.entity.{EntityType, Player} import org.bukkit.event.inventory.{InventoryClickEvent, InventoryType} @@ -13,7 +14,8 @@ import org.bukkit.{Material, Sound} class PlayerInventoryListener( implicit effectEnvironment: EffectEnvironment, - ioCanOpenBuildMainMenu: IO CanOpen BuildMainMenu.type + ioCanOpenBuildMainMenu: IO CanOpen BuildMainMenu.type, + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) extends Listener { import com.github.unchama.targetedeffect._ @@ -58,7 +60,7 @@ class PlayerInventoryListener( // プレイヤーデータが無い場合は処理終了 // インベントリ名が以下の時処理 - if (topinventory.getTitle == s"${DARK_PURPLE.toString}$BOLD「直列設置」設定") { + if (view.getTitle == s"${DARK_PURPLE.toString}$BOLD「直列設置」設定") { event.setCancelled(true) // プレイヤーインベントリのクリックの場合終了 @@ -68,7 +70,7 @@ class PlayerInventoryListener( /* * クリックしたボタンに応じた各処理内容の記述ここから */ - if (itemstackcurrent.getType == Material.SKULL_ITEM) { + if (itemstackcurrent.getType == Material.PLAYER_HEAD) { // ホームメニューへ帰還 effectEnvironment.unsafeRunAsyncTargetedEffect(player)( @@ -78,7 +80,7 @@ class PlayerInventoryListener( ), "BuildMainMenuを開く" ) - } else if (itemstackcurrent.getType == Material.WOOD) { + } else if (itemstackcurrent.getType == Material.OAK_PLANKS) { // 直列設置設定 if (playerLevel < BuildAssist.config.getblocklineuplevel) { player.sendMessage(RED.toString + "建築Lvが足りません") @@ -91,7 +93,7 @@ class PlayerInventoryListener( player.playSound(player.getLocation, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) player.openInventory(MenuInventoryData.getBlockLineUpData(player)) } - } else if (itemstackcurrent.getType == Material.STEP) { + } else if (itemstackcurrent.getType == Material.STONE_SLAB) { // 直列設置ハーフブロック設定 if (playerdata.line_up_step_flg >= 2) { playerdata.line_up_step_flg = 0 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 5aa13a032a..f1ec54ae36 100644 --- a/src/main/scala/com/github/unchama/buildassist/listener/BlockLineUpTriggerListener.scala +++ b/src/main/scala/com/github/unchama/buildassist/listener/BlockLineUpTriggerListener.scala @@ -7,7 +7,8 @@ import com.github.unchama.seichiassist.subsystems.buildcount.application.actions import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount import com.github.unchama.seichiassist.subsystems.mana.ManaApi import com.github.unchama.seichiassist.subsystems.minestack.MineStackAPI -import com.github.unchama.util.external.ExternalPlugins +import com.github.unchama.util.external.WorldGuardWrapper +import org.bukkit.block.data.`type`.Slab import org.bukkit.entity.Player import org.bukkit.event.block.Action import org.bukkit.event.player.PlayerInteractEvent @@ -15,6 +16,7 @@ import org.bukkit.event.{EventHandler, Listener} import org.bukkit.inventory.ItemStack import org.bukkit.{Material, Sound} +import scala.util.chaining.scalaUtilChainingOps import scala.util.control.Breaks class BlockLineUpTriggerListener[ @@ -61,7 +63,6 @@ class BlockLineUpTriggerListener[ val pl = player.getLocation val mainHandItemType = mainHandItem.getType - val mainHandItemData = mainHandItem.getData.getData // 仰角は下向きがプラスで上向きがマイナス val pitch = pl.getPitch @@ -103,8 +104,10 @@ class BlockLineUpTriggerListener[ if (buildAssistData.line_up_minestack_flg == 1) { mineStackAPI .mineStackObjectList - .findBySignedItemStack(mainHandItem, player) + .findBySignedItemStacks(Vector(mainHandItem), player) .unsafeRunSync() + .head + ._2 } else None val maxBlockUsage = { @@ -132,30 +135,20 @@ class BlockLineUpTriggerListener[ Seq(Some(available), manaCap, Some(64L)).flatten.min }.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 - } + def slabToDoubleSlab(material: Material): Material = + if (material.createBlockData().isInstanceOf[Slab]) { + material.asInstanceOf[Slab].tap(slab => slab.setType(Slab.Type.DOUBLE)).getMaterial + } else material val playerHoldsSlabBlock = BuildAssist.material_slab2.contains(mainHandItemType) - val doesHoldLeaves = - (mainHandItemType eq Material.LEAVES) || (mainHandItemType eq Material.LEAVES_2) + (mainHandItemType eq Material.OAK_LEAVES) || (mainHandItemType eq Material.DARK_OAK_LEAVES) || ( + mainHandItemType eq Material.BIRCH_LEAVES + ) || (mainHandItemType eq Material.ACACIA_LEAVES) || ( + mainHandItemType eq Material.JUNGLE_LEAVES + ) || (mainHandItemType eq Material.SPRUCE_LEAVES) val slabLineUpStepMode = buildAssistData.line_up_step_flg val shouldPlaceDoubleSlabs = playerHoldsSlabBlock && slabLineUpStepMode == 2 - val upsideBit = 8 - val noDecayBit = 4 - val placingBlockData: Byte = - if (playerHoldsSlabBlock && slabLineUpStepMode == 0) - (mainHandItemData | upsideBit).toByte - else if (doesHoldLeaves) - (mainHandItemData | noDecayBit).toByte - else - mainHandItemData - val (placingBlockType, itemConsumptionPerPlacement, placementIteration) = if (shouldPlaceDoubleSlabs) (slabToDoubleSlab(mainHandItemType), 2, maxBlockUsage / 2) @@ -174,7 +167,7 @@ class BlockLineUpTriggerListener[ val block = playerWorld.getBlockAt(px, py, pz) // 他人の保護がかかっている場合は設置終わり - if (!ExternalPlugins.getWorldGuard.canBuild(player, block.getLocation)) b.break + if (!WorldGuardWrapper.canBuild(player, block.getLocation)) b.break() if (block.getType != Material.AIR) { // 空気以外にぶつかり、ブロック破壊をしないならば終わる @@ -183,7 +176,7 @@ class BlockLineUpTriggerListener[ .material_destruction .contains(block.getType) || buildAssistData.line_up_des_flg == 0 ) { - b.break + b.break() } block.getDrops.asScala.foreach { @@ -192,7 +185,6 @@ class BlockLineUpTriggerListener[ } block.setType(placingBlockType) - block.setData(placingBlockData) placedBlockCount += itemConsumptionPerPlacement } 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 6b530f7166..2a41a3b462 100644 --- a/src/main/scala/com/github/unchama/buildassist/listener/TilingSkillTriggerListener.scala +++ b/src/main/scala/com/github/unchama/buildassist/listener/TilingSkillTriggerListener.scala @@ -2,11 +2,13 @@ package com.github.unchama.buildassist.listener import cats.effect.ConcurrentEffect.ops.toAllConcurrentEffectOps import cats.effect.{ConcurrentEffect, SyncEffect, SyncIO} -import com.github.unchama.buildassist.{BuildAssist, Util} +import com.github.unchama.buildassist.BuildAssist import com.github.unchama.seichiassist.subsystems.buildcount.application.actions.IncrementBuildExpWhenBuiltWithSkill import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount import com.github.unchama.seichiassist.subsystems.minestack.MineStackAPI +import com.github.unchama.util.external.WorldGuardWrapper import org.bukkit.ChatColor.RED +import org.bukkit.block.data.`type`.Leaves import org.bukkit.entity.Player import org.bukkit.event.block.Action import org.bukkit.event.player.PlayerInteractEvent @@ -48,10 +50,7 @@ class TilingSkillTriggerListener[G[_]: ConcurrentEffect, F[ ) return val clickedBlock = event.getClickedBlock - val offHandItemSelector = offHandItem.getData.getData - if ( - !(offHandItem.getType == clickedBlock.getType && offHandItemSelector == clickedBlock.getData) - ) { + if (!(offHandItem.getType == clickedBlock.getType)) { player.sendMessage(s"$RED「オフハンドと同じブロック」をクリックしてください。(基準になります)") return } @@ -71,37 +70,40 @@ class TilingSkillTriggerListener[G[_]: ConcurrentEffect, F[ val minestackObjectToUse = mineStackAPI .mineStackObjectList - .findBySignedItemStack(offHandItem, player) + .findBySignedItemStacks(Vector(offHandItem), player) .toIO .unsafeRunSync() + .head + ._2 .filter(_ => buildAssistPlayerData.zs_minestack_flag) val replaceableMaterials = Set( Material.AIR, Material.SNOW, - Material.LONG_GRASS, + Material.TALL_GRASS, Material.DEAD_BUSH, - Material.YELLOW_FLOWER, - Material.RED_ROSE, + Material.DANDELION, + Material.POPPY, + Material.BLUE_ORCHID, + Material.ALLIUM, + Material.AZURE_BLUET, + Material.RED_TULIP, + Material.ORANGE_TULIP, + Material.WHITE_TULIP, + Material.PINK_TULIP, + Material.OXEYE_DAISY, + Material.SUNFLOWER, + Material.LILAC, + Material.LARGE_FERN, + Material.ROSE_BUSH, + Material.PEONY, Material.RED_MUSHROOM, Material.BROWN_MUSHROOM ) - val fillTargetMaterials = Set( - Material.AIR, - Material.LAVA, - Material.STATIONARY_LAVA, - Material.WATER, - Material.STATIONARY_WATER - ) + val fillTargetMaterials = Set(Material.AIR, Material.LAVA, Material.WATER) val b1 = new Breaks - val rawDataModifier: Byte => Byte = offHandItem.getType match { - case Material.LEAVES | Material.LEAVES_2 => - val noDecayBit: Byte = 8 - x => (x | noDecayBit).asInstanceOf[Byte] - case _ => identity - } b1.breakable { val targetXValues = centerX - areaInt to centerX + areaInt @@ -121,7 +123,7 @@ class TilingSkillTriggerListener[G[_]: ConcurrentEffect, F[ val blockToBeReplaced = fillLocation.getBlock if (fillTargetMaterials.contains(blockToBeReplaced.getType)) { - if (Util.getWorldGuard.canBuild(player, fillLocation)) { + if (WorldGuardWrapper.canBuild(player, fillLocation)) { blockToBeReplaced.setType(Material.DIRT) } else { // 他人の保護がかかっている場合は通知を行う @@ -137,7 +139,15 @@ class TilingSkillTriggerListener[G[_]: ConcurrentEffect, F[ } targetSurfaceBlock.setType(offHandItem.getType) - targetSurfaceBlock.setData(rawDataModifier(offHandItemSelector)) + + val block = targetSurfaceLocation.getBlock + + block.getBlockData match { + case leavesData: Leaves => + leavesData.setPersistent(true) + block.setBlockData(leavesData) + case _ => + } placementCount += 1 } @@ -179,7 +189,7 @@ class TilingSkillTriggerListener[G[_]: ConcurrentEffect, F[ if (replaceableMaterials.contains(targetSurfaceBlock.getType)) { // 他人の保護がかかっている場合は処理を終了 - if (!Util.getWorldGuard.canBuild(player, targetSurfaceLocation)) { + if (!WorldGuardWrapper.canBuild(player, targetSurfaceLocation)) { player.sendMessage(s"${RED}付近に誰かの保護がかかっているようです") b1.break() } 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 858e921d01..13b4685adc 100644 --- a/src/main/scala/com/github/unchama/buildassist/menu/BlockPlacementSkillMenu.scala +++ b/src/main/scala/com/github/unchama/buildassist/menu/BlockPlacementSkillMenu.scala @@ -7,8 +7,10 @@ import com.github.unchama.menuinventory.router.CanOpen import com.github.unchama.menuinventory.slot.button.action.LeftClickButtonEffect import com.github.unchama.menuinventory.slot.button.{Button, RecomputedButton} import com.github.unchama.menuinventory.{Menu, MenuFrame, MenuSlotLayout} +import com.github.unchama.seichiassist.SkullOwners import com.github.unchama.seichiassist.effects.player.CommonSoundEffects import com.github.unchama.seichiassist.menus.BuildMainMenu +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.player.FocusedSoundEffect import com.github.unchama.targetedeffect.{ @@ -30,7 +32,10 @@ object BlockPlacementSkillMenu extends Menu { } import menuinventory.syntax._ - class Environment(implicit val canOpenMainMenu: CanOpen[IO, BuildMainMenu.type]) + class Environment( + implicit val canOpenMainMenu: CanOpen[IO, BuildMainMenu.type], + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] + ) override val frame: MenuFrame = MenuFrame(4.chestRows, s"$DARK_PURPLE$BOLD「範囲設置スキル」設定画面") @@ -54,6 +59,7 @@ object BlockPlacementSkillMenu extends Menu { import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.layoutPreparationContext import player._ + import environment._ private def computeCurrentSkillAreaInt(range: Int): Int = (range - 1) / 2 private val maxRange = 15 @@ -115,7 +121,7 @@ object BlockPlacementSkillMenu extends Menu { val playerData = BuildAssist.instance.temporaryData(getUniqueId) val currentRange = playerData.computeCurrentSkillRange() - val iconItemStack = new SkullItemStackBuilder("MHF_ArrowUp") + val iconItemStack = new SkullItemStackBuilder(SkullOwners.MHF_ArrowUp) .title(s"$RED$UNDERLINE${BOLD}範囲設定を最大値に変更") .lore( s"$RESET${AQUA}現在の範囲設定: $currentRange×$currentRange", @@ -142,7 +148,7 @@ object BlockPlacementSkillMenu extends Menu { val currentRange = playerData.computeCurrentSkillRange() val changedRange = 11 - val iconItemStack = new SkullItemStackBuilder("MHF_ArrowUp") + val iconItemStack = new SkullItemStackBuilder(SkullOwners.MHF_ArrowUp) .title(s"$RED$UNDERLINE${BOLD}範囲設定を$changedRange×${changedRange}に変更") .lore( s"$RESET${AQUA}現在の範囲設定: $currentRange×$currentRange", @@ -169,7 +175,7 @@ object BlockPlacementSkillMenu extends Menu { val currentRange = playerData.computeCurrentSkillRange() val changedRange = currentRange + 2 - val iconItemStack = new SkullItemStackBuilder("MHF_ArrowUp") + val iconItemStack = new SkullItemStackBuilder(SkullOwners.MHF_ArrowUp) .title(s"$YELLOW$UNDERLINE${BOLD}範囲設定を一段階大きくする") .lore { List(s"$RESET${AQUA}現在の範囲設定: $currentRange×$currentRange").concat( @@ -208,7 +214,7 @@ object BlockPlacementSkillMenu extends Menu { val currentRange = playerData.computeCurrentSkillRange() val changedRange = 5 - val iconItemStack = new SkullItemStackBuilder("MHF_TNT") + val iconItemStack = new SkullItemStackBuilder(SkullOwners.MHF_TNT) .title(s"$RED$UNDERLINE${BOLD}範囲設定を初期値に変更") .lore( s"$RESET${AQUA}現在の範囲設定: $currentRange×$currentRange", @@ -235,7 +241,7 @@ object BlockPlacementSkillMenu extends Menu { val currentRange = playerData.computeCurrentSkillRange() val changedRange = currentRange - 2 - val iconItemStack = new SkullItemStackBuilder("MHF_ArrowDown") + val iconItemStack = new SkullItemStackBuilder(SkullOwners.MHF_ArrowDown) .title(s"$YELLOW$UNDERLINE${BOLD}範囲設定を一段階小さくする") .lore( List(s"$RESET${AQUA}現在の範囲設定: $currentRange×$currentRange") @@ -272,7 +278,7 @@ object BlockPlacementSkillMenu extends Menu { val playerData = BuildAssist.instance.temporaryData(getUniqueId) val currentRange = playerData.computeCurrentSkillRange() - val iconItemStack = new SkullItemStackBuilder("MHF_ArrowDown") + val iconItemStack = new SkullItemStackBuilder(SkullOwners.MHF_ArrowDown) .title(s"$RED$UNDERLINE${BOLD}範囲設定を最小値に変更") .lore( s"$RESET${AQUA}現在の範囲設定: $currentRange×$currentRange", 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 5c974a67f1..9580080427 100644 --- a/src/main/scala/com/github/unchama/buildassist/menu/BuildAssistMenuRouter.scala +++ b/src/main/scala/com/github/unchama/buildassist/menu/BuildAssistMenuRouter.scala @@ -7,6 +7,7 @@ import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.menus.BuildMainMenu import com.github.unchama.seichiassist.subsystems.managedfly.ManagedFlyApi import com.github.unchama.seichiassist.subsystems.minestack.MineStackAPI +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack @@ -19,7 +20,8 @@ object BuildAssistMenuRouter { implicit flyApi: ManagedFlyApi[SyncIO, Player], mineStackAPI: MineStackAPI[IO, Player, ItemStack], layoutPreparationContext: LayoutPreparationContext, - onMainThread: OnMinecraftServerThread[IO] + onMainThread: OnMinecraftServerThread[IO], + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ): BuildAssistMenuRouter[IO] = new BuildAssistMenuRouter[IO] { implicit lazy val blockPlacementSkillMenuEnvironment: BlockPlacementSkillMenu.Environment = new BlockPlacementSkillMenu.Environment 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 e3fcd892ea..c62f2235e3 100644 --- a/src/main/scala/com/github/unchama/buildassist/menu/MineStackMassCraftMenu.scala +++ b/src/main/scala/com/github/unchama/buildassist/menu/MineStackMassCraftMenu.scala @@ -13,6 +13,7 @@ import com.github.unchama.seichiassist.SkullOwners import com.github.unchama.seichiassist.menus.{BuildMainMenu, ColorScheme, CommonButtons} import com.github.unchama.seichiassist.subsystems.minestack.MineStackAPI import com.github.unchama.seichiassist.subsystems.minestack.domain.minestackobject.MineStackObject +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.targetedeffect.SequentialEffect import com.github.unchama.targetedeffect.TargetedEffect.emptyEffect import com.github.unchama.targetedeffect.commandsender.{MessageEffect, MessageEffectF} @@ -40,7 +41,8 @@ object MineStackMassCraftMenu { class Environment( implicit val canOpenBuildMainMenu: CanOpen[IO, BuildMainMenu.type], val canOpenItself: CanOpen[IO, MineStackMassCraftMenu], - val mineStackAPI: MineStackAPI[IO, Player, ItemStack] + val mineStackAPI: MineStackAPI[IO, Player, ItemStack], + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) case class MassCraftRecipe( 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 12b9c10096..f8fd70fdcb 100644 --- a/src/main/scala/com/github/unchama/contextualexecutor/builder/Parsers.scala +++ b/src/main/scala/com/github/unchama/contextualexecutor/builder/Parsers.scala @@ -93,6 +93,6 @@ object Parsers { } catch { case _: DateTimeParseException => Left(failureMessage) - case e => throw e + case e: Throwable => throw e } } 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 27063da5f5..182e352452 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 @@ -164,7 +164,7 @@ object BukkitRepositoryControls { private def backupProcess[F[_]: Sync, Key, R]( finalization: RepositoryFinalization[F, Key, R] )(dataMap: TrieMap[Key, R]): F[Unit] = { - Sync[F].suspend { + Sync[F].defer { dataMap.toList.traverse(finalization.persistPair.tupled).as(()) } } 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 58bced3bb3..806fdb33ed 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 @@ -210,10 +210,8 @@ object Fs3Topic { def close: F[Unit] = { Bracket[F, Throwable].uncancelable { signalClosure.complete(()).flatMap { _ => - state - .get - .flatMap { case (subs, _) => foreach(subs)(_.close.void) } - .as(Fs3Topic.rightUnit) + state.get.flatMap { case (subs, _) => foreach(subs)(_.close.void) } +// .as(Fs3Topic.rightUnit) } } } diff --git a/src/main/scala/com/github/unchama/generic/Diff.scala b/src/main/scala/com/github/unchama/generic/Diff.scala index 3d517bb6d5..2cd94183b5 100644 --- a/src/main/scala/com/github/unchama/generic/Diff.scala +++ b/src/main/scala/com/github/unchama/generic/Diff.scala @@ -7,7 +7,7 @@ import cats.Eq * * [[A]] に関連付いた [[Eq]] インスタンスによって [[left]] と [[right]] が等価でないと判定されることが保証される。 */ -case class Diff[A: Eq] private (left: A, right: A) +case class Diff[A] private (left: A, right: A) object Diff { diff --git a/src/main/scala/com/github/unchama/generic/ListExtra.scala b/src/main/scala/com/github/unchama/generic/ListExtra.scala index 57eedb323e..06622109e1 100644 --- a/src/main/scala/com/github/unchama/generic/ListExtra.scala +++ b/src/main/scala/com/github/unchama/generic/ListExtra.scala @@ -2,24 +2,6 @@ package com.github.unchama.generic object ListExtra { - /** - * Listの内容を[[B]]または[[C]]に分類し、 - * tupleとして返します。 - */ - def partitionWith[A, B, C]( - list: List[A] - )(partitioningFunction: A => Either[B, C]): (List[B], List[C]) = { - list match { - case ::(head, next) => - val partitionedNext = partitionWith(next)(partitioningFunction) - partitioningFunction(head) match { - case Left(value) => (value :: partitionedNext._1, partitionedNext._2) - case Right(value) => (partitionedNext._1, value :: partitionedNext._2) - } - case Nil => (Nil, Nil) - } - } - /** * Listの中身で条件に一致するものがあったときに`element`を先頭に追加し直します * listの中に与えられたpredicateに合致する要素があった場合、その要素をelementで写して変換します。そのような要素がなかった場合、そのままlistを返却します。 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 a48ed7755b..a287935e9a 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 @@ -90,7 +90,7 @@ object AsymmetricSignallingRef { override val valuesAwait: Resource[F, Stream[F, A]] = changeTopic.subscribeAwait(topicQueueSize).flatMap { subscription => Resource - .liftF { + .eval { state.get.map { currentValue => subscription .through(ReorderingPipe.withInitialToken[F, A](currentValue.nextStamp)) 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 17373b4c1b..d6debefe1a 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 @@ -119,7 +119,7 @@ object AsymmetricTryableDeferred { implicit F: Concurrent[F] ) extends AsymmetricTryableDeferred[F, A] { def get: F[A] = - F.suspend { + F.defer { ref.get match { case State.Set(a) => F.pure(a) @@ -168,7 +168,7 @@ object AsymmetricTryableDeferred { } def complete(a: A): F[Unit] = - F.suspend(unsafeComplete(a)) + F.defer(unsafeComplete(a)) @tailrec private def unsafeComplete(a: A): F[Unit] = 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 54664f1147..e2f66e33c2 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 @@ -74,6 +74,7 @@ object WorldLevelData { val creator = WorldCreator.name(world.getName).copy(world) if (!Bukkit.unloadWorld(world, true)) { + // NOTE: Bukkitにデフォルトとして設定されているワールド(初期値world)はアンロードに失敗する logger.warn(s"${world.getName}はアンロードされませんでした。") } Bukkit.createWorld(creator) @@ -103,13 +104,13 @@ object WorldLevelData { } // メモリ解放を促す - if (!world.unloadChunk(chunk)) { + if (!world.unloadChunkRequest(chunk.getX, chunk.getZ)) { logger.warn(s"チャンク(${chunk.getX}, ${chunk.getZ})はアンロードされませんでした。") } } private def queueChunkSaverFlush[F[_]](implicit F: Concurrent[F], logger: Logger) = { - import com.github.unchama.util.nms.v1_12_2.world.WorldChunkSaving + import com.github.unchama.util.nms.v1_18_2.world.WorldChunkSaving F.delay { logger.info("チャンクの保存キューの処理を要求します…") @@ -122,12 +123,6 @@ object WorldLevelData { .as(()) } - 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] = { @@ -143,7 +138,7 @@ object WorldLevelData { private final val progressLogInterval = 1000 private final val reloadWorldInterval = 10000 - def convertChunkWise[F[_]]( + private def convertChunkWise[F[_]]( originalWorld: World, targetChunks: Seq[(Int, Int)], conversion: ItemStack => ItemStack @@ -168,10 +163,18 @@ object WorldLevelData { * * OutOfMemoryErrorが観測された際には、プロファイラで残留しているワールドのインスタンスを確認し、 * GC Rootからの参照パスを特定することを推奨する。 + * + * + * 2024/02/20 追記: flushEntityRemovalQueueが短期的なメモリ確保に寄与するとあるが、 + * 1.12.2から1.18.2にアップデートする際に、この処理内で使われている「EntityRemovalQueue」 + * というものが1.18.2で存在しているかが不明であること(調べても存在が明らかではなかった)と、 + * ドキュメントとコードを読む限りパフォーマンス以外の影響がないと思われることから無効化した。 + * しかしながら、本当に他の影響が出ないかがまだ不鮮明なためコメントアウトにとどめているが、 + * 本当に影響がないと確認されれば削除して良い。 */ chunkConversionEffects .atEvery(chunkSaverQueueFlushInterval)(_ => - flushEntityRemovalQueue(worldRef) >> queueChunkSaverFlush + /*flushEntityRemovalQueue(worldRef) >>*/ queueChunkSaverFlush ) .atEvery(progressLogInterval)(index => logProgress(index, chunkConversionEffects.size)(worldRef) diff --git a/src/main/scala/com/github/unchama/itemstackbuilder/AbstractItemStackBuilder.scala b/src/main/scala/com/github/unchama/itemstackbuilder/AbstractItemStackBuilder.scala index 2048320be8..dd2db3b87a 100644 --- a/src/main/scala/com/github/unchama/itemstackbuilder/AbstractItemStackBuilder.scala +++ b/src/main/scala/com/github/unchama/itemstackbuilder/AbstractItemStackBuilder.scala @@ -13,12 +13,10 @@ import org.bukkit.inventory.{ItemFlag, ItemStack} * @author * karayuu */ -abstract class AbstractItemStackBuilder[-M <: ItemMeta] protected ( - material: Material, - durability: Short -) extends ItemStackBuilder { +abstract class AbstractItemStackBuilder[-M <: ItemMeta] protected (material: Material) + extends ItemStackBuilder { - private val component: IconComponent = new IconComponent(material, durability) + private val component: IconComponent = new IconComponent(material) final override def build(): ItemStack = { val itemStack = component.itemStack() diff --git a/src/main/scala/com/github/unchama/itemstackbuilder/IconItemStackBuilder.scala b/src/main/scala/com/github/unchama/itemstackbuilder/IconItemStackBuilder.scala index 1e1a3fa24b..ff33a32f77 100644 --- a/src/main/scala/com/github/unchama/itemstackbuilder/IconItemStackBuilder.scala +++ b/src/main/scala/com/github/unchama/itemstackbuilder/IconItemStackBuilder.scala @@ -12,8 +12,8 @@ import org.bukkit.inventory.meta.ItemMeta * @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) + extends AbstractItemStackBuilder[ItemMeta](material) { private var shouldShowAttribute: Boolean = false /** diff --git a/src/main/scala/com/github/unchama/itemstackbuilder/SkullItemStackBuilder.scala b/src/main/scala/com/github/unchama/itemstackbuilder/SkullItemStackBuilder.scala index 32a5326e42..3e8c417fb8 100644 --- a/src/main/scala/com/github/unchama/itemstackbuilder/SkullItemStackBuilder.scala +++ b/src/main/scala/com/github/unchama/itemstackbuilder/SkullItemStackBuilder.scala @@ -1,50 +1,44 @@ package com.github.unchama.itemstackbuilder +import cats.effect.IO +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.mojang.authlib.GameProfile import com.mojang.authlib.properties.Property +import org.bukkit.entity.Player import org.bukkit.inventory.meta.SkullMeta -import org.bukkit.{Bukkit, Material, SkullType} +import org.bukkit.{Bukkit, Material} +import java.net.URI 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)( + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] +) extends AbstractItemStackBuilder[SkullMeta](Material.PLAYER_HEAD) { /** - * プレーヤーがサーバーに参加したことのない場合に 頭のスキンを読み込むことができないため、そのようなケースが想定されるされる箇所では - * プレーヤー名を[[String]]として取るコンストラクタを使用せよ。 - * - * それ以外の場合はこのコンストラクタを使うようにせよ。 Bukkitは`Persistent storage of users should be by UUID`と記している。 - * - * @see - * SkullMeta.setOwner * @param ownerUUID - * [Material.SKULL_ITEM] に表示するプレーヤーのUUID + * [Material.PLAYER_HEAD] に表示するプレーヤーのUUID */ - def this(ownerUUID: UUID) = this(SkullOwnerUuid(ownerUUID)) + def this(ownerUUID: UUID)(implicit playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player]) = + this(SkullOwnerUuid(ownerUUID))(playerHeadSkinAPI) - /** - * @param ownerName - * [Material.SKULL_ITEM] に表示するプレーヤーの名前 - */ - def this(ownerName: String) = this(SkullOwnerName(ownerName)) - - override def transformItemMetaOnBuild(meta: SkullMeta): Unit = { + override protected def transformItemMetaOnBuild(meta: SkullMeta): Unit = { owner match { case SkullOwnerUuid(uuid) => meta.setOwningPlayer(Bukkit.getOfflinePlayer(uuid)) - case SkullOwnerName(name) => - /** - * 参加したことのないプレーヤーはgetOfflinePlayerでデータが取れないのでこうするしか無い - */ - // noinspection ScalaDeprecation - meta.setOwner(name) + + playerHeadSkinAPI.playerHeadSkinUrlByUUID(uuid).unsafeRunSync() match { + case Some(url) => + val playerProfile = Bukkit.createPlayerProfile(uuid) + playerProfile.getTextures.setSkin(URI.create(url.url).toURL) + meta.setOwnerProfile(playerProfile) + case None => + } + + case SkullOwnerUuidWithNameWithTextureUrl(uuid, name, url) => + val playerProfile = Bukkit.createPlayerProfile(uuid, name) + playerProfile.getTextures.setSkin(URI.create(url).toURL) + meta.setOwnerProfile(playerProfile) /** * @see diff --git a/src/main/scala/com/github/unchama/itemstackbuilder/SkullOwnerReference.scala b/src/main/scala/com/github/unchama/itemstackbuilder/SkullOwnerReference.scala index 9f86fd223a..0774e700e3 100644 --- a/src/main/scala/com/github/unchama/itemstackbuilder/SkullOwnerReference.scala +++ b/src/main/scala/com/github/unchama/itemstackbuilder/SkullOwnerReference.scala @@ -6,9 +6,14 @@ sealed trait SkullOwnerReference case class SkullOwnerUuid(uuid: UUID) extends SkullOwnerReference -case class SkullOwnerName(name: String) extends SkullOwnerReference - /** * UUID指定だけでは、カスタムテクスチャをヘッドに設定することができない場合に備えて、`TextureValue`を指定するもの */ case class SkullOwnerTextureValue(textureValue: String) extends SkullOwnerReference + +/** + * `textureUrl`は以下の記事の方法で取得されたスキンのURL + * @see https://qiita.com/yuta0801/items/edb4804dfb867ea82c5a + */ +case class SkullOwnerUuidWithNameWithTextureUrl(uuid: UUID, name: String, textureUrl: String) + extends SkullOwnerReference diff --git a/src/main/scala/com/github/unchama/itemstackbuilder/TippedArrowIconItemStackBuilder.scala b/src/main/scala/com/github/unchama/itemstackbuilder/TippedArrowIconItemStackBuilder.scala index 2cfde169b9..02113dd995 100644 --- a/src/main/scala/com/github/unchama/itemstackbuilder/TippedArrowIconItemStackBuilder.scala +++ b/src/main/scala/com/github/unchama/itemstackbuilder/TippedArrowIconItemStackBuilder.scala @@ -6,7 +6,7 @@ import org.bukkit.inventory.meta.PotionMeta import org.bukkit.potion.{PotionData, PotionType} class TippedArrowIconItemStackBuilder(val potionData: PotionData) - extends AbstractItemStackBuilder[PotionMeta](Material.TIPPED_ARROW, 0) { + extends AbstractItemStackBuilder[PotionMeta](Material.TIPPED_ARROW) { 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 f36c28c4b1..9164c87019 100644 --- a/src/main/scala/com/github/unchama/itemstackbuilder/component/IconComponent.scala +++ b/src/main/scala/com/github/unchama/itemstackbuilder/component/IconComponent.scala @@ -13,7 +13,7 @@ import scala.jdk.javaapi.CollectionConverters.asJava * * Created by karayuu on 2019/04/09 */ -class IconComponent(val material: Material, private val durability: Short = 0.toShort) { +class IconComponent(val material: Material) { var title: String = Bukkit.getItemFactory.getItemMeta(material).ifNotNull(_.getDisplayName) var lore: List[String] = Nil @@ -24,7 +24,7 @@ class IconComponent(val material: Material, private val durability: Short = 0.to var itemFlagSet: Set[ItemFlag] = Set() - def itemStack(): ItemStack = new ItemStack(material, amount, durability) + def itemStack(): ItemStack = new ItemStack(material, amount) def itemMeta(): ItemMeta = { val meta = Bukkit.getItemFactory.getItemMeta(material) diff --git a/src/main/scala/com/github/unchama/minecraft/algebra/HasName.scala b/src/main/scala/com/github/unchama/minecraft/algebra/HasName.scala new file mode 100644 index 0000000000..6c6a340a5c --- /dev/null +++ b/src/main/scala/com/github/unchama/minecraft/algebra/HasName.scala @@ -0,0 +1,13 @@ +package com.github.unchama.minecraft.algebra + +trait HasName[T] { + + def of(x: T): String + +} + +object HasName { + + def apply[T](implicit ev: HasName[T]): HasName[T] = ev + +} 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 a94d055081..42b34164da 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 @@ -1,10 +1,10 @@ package com.github.unchama.minecraft.bukkit.actions -import cats.effect.{Sync, SyncIO} +import cats.effect.SyncIO import com.github.unchama.minecraft.actions.{BroadcastMinecraftMessage, OnMinecraftServerThread} import org.bukkit.Bukkit -class BroadcastBukkitMessage[F[_]: Sync: OnMinecraftServerThread] +class BroadcastBukkitMessage[F[_]: OnMinecraftServerThread] extends BroadcastMinecraftMessage[F] { import scala.jdk.CollectionConverters._ @@ -19,7 +19,7 @@ class BroadcastBukkitMessage[F[_]: Sync: OnMinecraftServerThread] object BroadcastBukkitMessage { - def apply[F[_]: Sync: OnMinecraftServerThread]: BroadcastMinecraftMessage[F] = + def apply[F[_]: 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 0684e38bbf..df103ae51c 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 @@ -1,11 +1,11 @@ package com.github.unchama.minecraft.bukkit.actions -import cats.effect.{Sync, SyncIO} +import cats.effect.SyncIO import com.github.unchama.minecraft.actions.{GetConnectedPlayers, OnMinecraftServerThread} import org.bukkit.Bukkit import org.bukkit.entity.Player -class GetConnectedBukkitPlayers[F[_]: Sync: OnMinecraftServerThread] +class GetConnectedBukkitPlayers[F[_]: OnMinecraftServerThread] extends GetConnectedPlayers[F, Player] { import scala.jdk.CollectionConverters._ diff --git a/src/main/scala/com/github/unchama/minecraft/bukkit/algebra/BukkitPlayerHasName.scala b/src/main/scala/com/github/unchama/minecraft/bukkit/algebra/BukkitPlayerHasName.scala new file mode 100644 index 0000000000..c6e3f153fa --- /dev/null +++ b/src/main/scala/com/github/unchama/minecraft/bukkit/algebra/BukkitPlayerHasName.scala @@ -0,0 +1,16 @@ +package com.github.unchama.minecraft.bukkit.algebra + +import com.github.unchama.minecraft.algebra.HasName +import org.bukkit.entity.Player + +class BukkitPlayerHasName extends HasName[Player] { + + override def of(x: Player): String = x.getName + +} + +object BukkitPlayerHasName { + + implicit val instance: HasName[Player] = new BukkitPlayerHasName + +} diff --git a/src/main/scala/com/github/unchama/minecraft/bukkit/objects/BukkitMaterial.scala b/src/main/scala/com/github/unchama/minecraft/bukkit/objects/BukkitMaterial.scala index 67e7db1990..48b02de127 100644 --- a/src/main/scala/com/github/unchama/minecraft/bukkit/objects/BukkitMaterial.scala +++ b/src/main/scala/com/github/unchama/minecraft/bukkit/objects/BukkitMaterial.scala @@ -5,6 +5,7 @@ import org.bukkit.Material import org.bukkit.inventory.ItemStack class BukkitMaterial extends MinecraftMaterial[Material, ItemStack] { - override def toItemStack(material: Material, durability: Short): ItemStack = - new ItemStack(material, 1, durability) + override def toItemStack(material: Material): ItemStack = + new ItemStack(material, 1) + } diff --git a/src/main/scala/com/github/unchama/minecraft/objects/MinecraftMaterial.scala b/src/main/scala/com/github/unchama/minecraft/objects/MinecraftMaterial.scala index 8446c9c693..e2b567916b 100644 --- a/src/main/scala/com/github/unchama/minecraft/objects/MinecraftMaterial.scala +++ b/src/main/scala/com/github/unchama/minecraft/objects/MinecraftMaterial.scala @@ -5,6 +5,6 @@ package com.github.unchama.minecraft.objects */ trait MinecraftMaterial[Material, ItemStack] { - def toItemStack(material: Material, durability: Short): ItemStack + def toItemStack(material: Material): ItemStack } diff --git a/src/main/scala/com/github/unchama/seichiassist/ManagedWorld.scala b/src/main/scala/com/github/unchama/seichiassist/ManagedWorld.scala index a7cc146843..2621cafc9a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/ManagedWorld.scala +++ b/src/main/scala/com/github/unchama/seichiassist/ManagedWorld.scala @@ -83,6 +83,8 @@ case object ManagedWorld extends Enum[ManagedWorld] { def isSeichi: Boolean = asManagedWorld().exists(_.isSeichi) + def isNotSeichi: Boolean = !isSeichi + def shouldTrackBuildBlock: Boolean = asManagedWorld().exists(_.shouldTrackBuildBlock) def isSeichiSkillAllowed: Boolean = asManagedWorld().exists(_.isSeichiSkillAllowed) diff --git a/src/main/scala/com/github/unchama/seichiassist/MaterialSets.scala b/src/main/scala/com/github/unchama/seichiassist/MaterialSets.scala index 5f8a9a6498..426abbe212 100644 --- a/src/main/scala/com/github/unchama/seichiassist/MaterialSets.scala +++ b/src/main/scala/com/github/unchama/seichiassist/MaterialSets.scala @@ -7,151 +7,150 @@ import org.bukkit.block.Block 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.QUARTZ_ORE, - Material.MELON_BLOCK, - Material.SEA_LANTERN - ) + + private val notApplicableSeichiSkillMaterials = + Set( + Material.WATER, + Material.LAVA, + Material.AIR, + Material.BEDROCK, + Material.TORCH, + Material.SOUL_TORCH, + Material.REDSTONE_TORCH, + Material.REPEATER, + Material.COMPARATOR, + Material.END_ROD, + Material.FIRE, + Material.WHITE_CARPET, + Material.ORANGE_CARPET, + Material.MAGENTA_CARPET, + Material.LIGHT_BLUE_CARPET, + Material.YELLOW_CARPET, + Material.LIME_CARPET, + Material.PINK_CARPET, + Material.GRAY_CARPET, + Material.LIGHT_GRAY_CARPET, + Material.CYAN_CARPET, + Material.PURPLE_CARPET, + Material.BLUE_CARPET, + Material.BROWN_CARPET, + Material.GREEN_CARPET, + Material.RED_CARPET, + Material.BLACK_CARPET, + Material.WHITE_BANNER, + Material.ORANGE_BANNER, + Material.MAGENTA_BANNER, + Material.LIGHT_BLUE_BANNER, + Material.YELLOW_BANNER, + Material.LIME_BANNER, + Material.PINK_BANNER, + Material.GRAY_BANNER, + Material.LIGHT_GRAY_BANNER, + Material.CYAN_BANNER, + Material.PURPLE_BANNER, + Material.BLUE_BANNER, + Material.BROWN_BANNER, + Material.GREEN_BANNER, + Material.RED_BANNER, + Material.BLACK_BANNER, + Material.RAIL, + Material.POWERED_RAIL, + Material.ACTIVATOR_RAIL, + Material.DETECTOR_RAIL, + Material.REDSTONE, + Material.REDSTONE_WIRE, + Material.TRIPWIRE_HOOK, + Material.OAK_WALL_SIGN, + Material.SPRUCE_WALL_SIGN, + Material.BIRCH_WALL_SIGN, + Material.ACACIA_WALL_SIGN, + Material.JUNGLE_WALL_SIGN, + Material.DARK_OAK_WALL_SIGN, + Material.WARPED_WALL_SIGN, + Material.OAK_SIGN, + Material.SPRUCE_SIGN, + Material.BIRCH_SIGN, + Material.ACACIA_SIGN, + Material.JUNGLE_SIGN, + Material.DARK_OAK_SIGN, + Material.WARPED_SIGN, + Material.LEVER, + Material.STONE_BUTTON, + Material.POLISHED_BLACKSTONE_BUTTON, + Material.OAK_BUTTON, + Material.SPRUCE_BUTTON, + Material.BIRCH_BUTTON, + Material.JUNGLE_BUTTON, + Material.ACACIA_BUTTON, + Material.DARK_OAK_BUTTON, + Material.CRIMSON_BUTTON, + Material.WARPED_BUTTON, + Material.STONE_PRESSURE_PLATE, + Material.POLISHED_BLACKSTONE_PRESSURE_PLATE, + Material.LIGHT_WEIGHTED_PRESSURE_PLATE, + Material.HEAVY_WEIGHTED_PRESSURE_PLATE, + Material.OAK_PRESSURE_PLATE, + Material.SPRUCE_PRESSURE_PLATE, + Material.BIRCH_PRESSURE_PLATE, + Material.JUNGLE_PRESSURE_PLATE, + Material.ACACIA_PRESSURE_PLATE, + Material.DARK_OAK_PRESSURE_PLATE, + Material.CRIMSON_PRESSURE_PLATE, + Material.WARPED_PRESSURE_PLATE, + Material.DROPPER, + Material.PISTON, + Material.OAK_SAPLING, + Material.SPRUCE_SAPLING, + Material.BIRCH_SAPLING, + Material.JUNGLE_SAPLING, + Material.ACACIA_SAPLING, + Material.DARK_OAK_SAPLING, + Material.DEAD_BUSH, + Material.SEA_PICKLE, + Material.CORNFLOWER, + Material.LILY_OF_THE_VALLEY, + Material.WITHER_ROSE, + Material.SPORE_BLOSSOM, + Material.IRON_BLOCK, + Material.COPPER_BLOCK, + Material.DIAMOND_BLOCK, + Material.NETHERITE_BLOCK, + Material.FLOWER_POT, + Material.ANVIL, + Material.BEACON, + Material.ENCHANTING_TABLE, + Material.LADDER, + Material.SNOW, + Material.WITHER_SKELETON_SKULL, + Material.DRAGON_HEAD, + Material.PLAYER_HEAD, + Material.SHULKER_BOX, + Material.JUKEBOX, + Material.HOPPER, + Material.DAYLIGHT_DETECTOR, + Material.OBSERVER, + Material.CAKE, + Material.NOTE_BLOCK, + Material.REDSTONE_LAMP, + Material.EMERALD_BLOCK, + Material.COAL_BLOCK, + Material.LAPIS_BLOCK + ) // この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.RED_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が別れている - 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 + // TODO(1.18): 1.18のコードに書き換えるときに、materialsの列挙をホワイトリスト方式からブラックリスト方式に書き換えたので、 + // 元のコードとMaterial.values()のdiffを取って、なぜホワイトリスト方式が採用されたかを考えてみる + val materials: Set[Material] = Material.values().toSet.diff(notApplicableSeichiSkillMaterials) // これらのマテリアルを持つブロックは破壊を整地量に計上しない val exclude: Set[Material] = Set( - Material.GRASS_PATH, - Material.SOIL, - Material.MOB_SPAWNER, + Material.DIRT_PATH, + Material.FARMLAND, + Material.SPAWNER, Material.CAULDRON, Material.ENDER_CHEST, - Material.ENDER_PORTAL_FRAME, - Material.ENDER_PORTAL + Material.END_PORTAL_FRAME, + Material.END_PORTAL ) val materialsToCountBlockBreak: Set[Material] = materials -- exclude @@ -163,21 +162,25 @@ object MaterialSets { * * 例えば石をシャベルで掘った時にも、ツールのエンチャントを保ったままダイヤツルハシで掘ったものとして計算し、 結果得られるスタック数が最大のものが結果として採用される。 */ - val breakTestToolMaterials: Seq[Material] = - Seq(Material.DIAMOND_PICKAXE, Material.DIAMOND_AXE, Material.DIAMOND_SPADE) + private val breakTestToolMaterials: Set[Material] = + Set(Material.DIAMOND_PICKAXE, Material.DIAMOND_AXE, Material.DIAMOND_SHOVEL) val breakToolMaterials: Set[Material] = Set( - Material.WOOD_PICKAXE, - Material.WOOD_SPADE, + Material.WOODEN_AXE, + Material.WOODEN_PICKAXE, + Material.WOODEN_SHOVEL, Material.STONE_PICKAXE, Material.STONE_AXE, - Material.STONE_SPADE, + Material.STONE_SHOVEL, Material.IRON_PICKAXE, Material.IRON_AXE, - Material.IRON_SPADE, - Material.GOLD_PICKAXE, - Material.GOLD_AXE, - Material.GOLD_SPADE + Material.IRON_SHOVEL, + Material.GOLDEN_PICKAXE, + Material.GOLDEN_AXE, + Material.GOLDEN_SHOVEL, + Material.NETHERITE_PICKAXE, + Material.NETHERITE_AXE, + Material.NETHERITE_SHOVEL ) ++ breakTestToolMaterials val cancelledMaterials: Set[Material] = Set( @@ -189,10 +192,15 @@ object MaterialSets { Material.BEACON, Material.BIRCH_DOOR, Material.BIRCH_FENCE_GATE, - Material.BIRCH_WOOD_STAIRS, - Material.BOAT, + Material.BIRCH_STAIRS, + Material.BIRCH_BOAT, + Material.OAK_BOAT, + Material.ACACIA_BOAT, + Material.JUNGLE_BOAT, + Material.DARK_OAK_BOAT, + Material.SPRUCE_BOAT, Material.FURNACE, - Material.WORKBENCH, + Material.CRAFTING_TABLE, Material.HOPPER, Material.MINECART ) @@ -200,7 +208,19 @@ object MaterialSets { val transparentMaterials: Set[Material] = Set(Material.BEDROCK, Material.AIR) val gravityMaterials: Set[Material] = - Set(Material.LOG, Material.LOG_2, Material.LEAVES, Material.LEAVES_2) + Set( + Material.ACACIA_LOG, + Material.BIRCH_LOG, + Material.DARK_OAK_LOG, + Material.JUNGLE_LOG, + Material.OAK_LOG, + Material.OAK_LEAVES, + Material.BIRCH_LEAVES, + Material.JUNGLE_LEAVES, + Material.ACACIA_LEAVES, + Material.DARK_OAK_LEAVES, + Material.SPRUCE_LEAVES + ) val fluidMaterials: Set[Material] = Set(Material.WATER, Material.LAVA) diff --git a/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala b/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala index b77a5063e8..f0bd4e3822 100644 --- a/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala +++ b/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala @@ -59,9 +59,11 @@ import com.github.unchama.seichiassist.menus.{BuildMainMenu, TopLevelRouter} import com.github.unchama.seichiassist.meta.subsystem.Subsystem import com.github.unchama.seichiassist.subsystems._ import com.github.unchama.seichiassist.subsystems.anywhereender.AnywhereEnderChestAPI +import com.github.unchama.seichiassist.subsystems.autosave.application.SystemConfiguration import com.github.unchama.seichiassist.subsystems.breakcount.{BreakCountAPI, BreakCountReadAPI} import com.github.unchama.seichiassist.subsystems.breakcountbar.BreakCountBarAPI import com.github.unchama.seichiassist.subsystems.breakskilltargetconfig.BreakSkillTargetConfigAPI +import com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.BreakSuppressionPreferenceAPI import com.github.unchama.seichiassist.subsystems.buildcount.BuildCountAPI import com.github.unchama.seichiassist.subsystems.discordnotification.DiscordNotificationAPI import com.github.unchama.seichiassist.subsystems.donate.DonatePremiumPointAPI @@ -86,6 +88,7 @@ import com.github.unchama.seichiassist.subsystems.mana.{ManaApi, ManaReadApi} import com.github.unchama.seichiassist.subsystems.managedfly.ManagedFlyApi import com.github.unchama.seichiassist.subsystems.minestack.MineStackAPI import com.github.unchama.seichiassist.subsystems.minestack.bukkit.MineStackCommand +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.present.infrastructure.GlobalPlayerAccessor import com.github.unchama.seichiassist.subsystems.seasonalevents.api.SeasonalEventsAPI import com.github.unchama.seichiassist.subsystems.sharedinventory.SharedInventoryAPI @@ -221,7 +224,6 @@ class SeichiAssist extends JavaPlugin() { private lazy val itemMigrationSystem: subsystems.itemmigration.System[IO] = { import PluginExecutionContexts.asyncShift - implicit val effectEnvironment: EffectEnvironment = DefaultEffectEnvironment subsystems.itemmigration.System.wired[IO, SyncIO].unsafeRunSync() } @@ -259,6 +261,9 @@ class SeichiAssist extends JavaPlugin() { implicit val globalNotification: DiscordNotificationAPI[IO] = discordNotificationSystem.globalNotification + implicit val getConnectedPlayers: GetConnectedPlayers[IO, Player] = + new GetConnectedBukkitPlayers[IO] + subsystems.buildcount.System.wired[IO, SyncIO].unsafeRunSync() } @@ -291,6 +296,7 @@ class SeichiAssist extends JavaPlugin() { implicit val concurrentEffect: ConcurrentEffect[IO] = IO.ioConcurrentEffect(asyncShift) implicit val manaApi: ManaApi[IO, SyncIO, Player] = manaSystem.manaApi implicit val gtToSiinaAPI: GtToSiinaAPI[ItemStack] = gtToSiinaSystem.api + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] = playerHeadSkinSystem.api subsystems.seasonalevents.System.wired[IO, SyncIO, IO](this) } @@ -344,8 +350,7 @@ 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 playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] = playerHeadSkinSystem.api subsystems.gachapoint.System.wired[IO, SyncIO](breakCountSystem.api).unsafeRunSync() } @@ -385,7 +390,6 @@ class SeichiAssist extends JavaPlugin() { private lazy val presentSystem: Subsystem[IO] = { import PluginExecutionContexts.{asyncShift, onMainThread} - implicit val effectEnvironment: EffectEnvironment = DefaultEffectEnvironment implicit val concurrentEffect: ConcurrentEffect[IO] = IO.ioConcurrentEffect(asyncShift) implicit val uuidToLastSeenName: UuidToLastSeenName[IO] = new GlobalPlayerAccessor[IO] subsystems.present.System.wired @@ -405,7 +409,7 @@ class SeichiAssist extends JavaPlugin() { mineStackSystem.api private lazy val sharedInventorySystem: subsystems.sharedinventory.System[IO] = { - import PluginExecutionContexts.timer + import PluginExecutionContexts.{timer, onMainThread} subsystems.sharedinventory.System.wired[IO, IO].unsafeRunSync() } @@ -417,6 +421,8 @@ class SeichiAssist extends JavaPlugin() { private lazy val gachaSystem: subsystems.gacha.System[IO, Player] = { implicit val gachaTicketAPI: GachaTicketAPI[IO] = gachaTicketSystem.api + implicit val getConnectedPlayers: GetConnectedPlayers[IO, Player] = + new GetConnectedBukkitPlayers[IO] subsystems.gacha.System.wired[IO] } @@ -435,6 +441,8 @@ class SeichiAssist extends JavaPlugin() { private lazy val gachaTradeSystem: Subsystem[IO] = { implicit val gachaPointApi: GachaPointApi[IO, SyncIO, Player] = gachaPointSystem.api + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] = playerHeadSkinSystem.api + subsystems.tradesystems.subsystems.gachatrade.System.wired[IO, SyncIO] } @@ -460,6 +468,7 @@ class SeichiAssist extends JavaPlugin() { // TODO: これはprivateであるべきだが、Achievementシステムが再実装されるまでやむを得ずpublicにする lazy val voteSystem: subsystems.vote.System[IO, Player] = { implicit val breakCountAPI: BreakCountAPI[IO, SyncIO, Player] = breakCountSystem.api + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] = playerHeadSkinSystem.api subsystems.vote.System.wired[IO, SyncIO] } @@ -487,6 +496,10 @@ class SeichiAssist extends JavaPlugin() { lazy val breakSkillTargetConfigSystem: subsystems.breakskilltargetconfig.System[IO, Player] = subsystems.breakskilltargetconfig.System.wired[IO, SyncIO].unsafeRunSync() + lazy val breakSuppressionPreferenceSystem + : subsystems.breaksuppressionpreference.System[IO, Player] = + subsystems.breaksuppressionpreference.System.wired[IO, SyncIO].unsafeRunSync() + /* TODO: mineStackSystemは本来privateであるべきだが、mineStackにアイテムを格納するAPIが現状の BreakUtilの実装から呼び出されている都合上やむを得ずpublicになっている。*/ lazy val mineStackSystem: subsystems.minestack.System[IO, Player, ItemStack] = @@ -495,6 +508,27 @@ class SeichiAssist extends JavaPlugin() { private lazy val gridRegionSystem: subsystems.gridregion.System[IO, Player, Location] = subsystems.gridregion.System.wired[IO, SyncIO].unsafeRunSync() + private lazy val joinAndQuitMessenger: Subsystem[IO] = + subsystems.joinandquitmessenger.System.wired[IO] + + private lazy val elevatorSystem: Subsystem[IO] = { + implicit val effectEnvironment: EffectEnvironment = DefaultEffectEnvironment + + subsystems.elevator.System.wired[IO] + } + + private lazy val blockLiquidStreamSystem: Subsystem[IO] = + subsystems.blockliquidstream.System.wired[IO] + + private lazy val cancelDamageByFallingBlocksSystem: Subsystem[IO] = + subsystems.canceldamagebyfallingblocks.System.wired[IO] + + private lazy val playerHeadSkinSystem: subsystems.playerheadskin.System[IO, Player] = { + import PluginExecutionContexts.asyncShift + + subsystems.playerheadskin.System.wired[IO] + } + private lazy val wiredSubsystems: List[Subsystem[IO]] = List( mebiusSystem, expBottleStackSystem, @@ -532,13 +566,20 @@ class SeichiAssist extends JavaPlugin() { consumeGachaTicketSystem, openirontrapdoor.System.wired, gridRegionSystem, - breakSkillTargetConfigSystem + breakSkillTargetConfigSystem, + breakSuppressionPreferenceSystem, + joinAndQuitMessenger, + elevatorSystem, + blockLiquidStreamSystem, + cancelDamageByFallingBlocksSystem, + playerHeadSkinSystem ) private lazy val buildAssist: BuildAssist = { implicit val flyApi: ManagedFlyApi[SyncIO, Player] = managedFlySystem.api implicit val buildCountAPI: BuildCountAPI[IO, SyncIO, Player] = buildCountSystem.api implicit val manaApi: ManaApi[IO, SyncIO, Player] = manaSystem.manaApi + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] = playerHeadSkinSystem.api new BuildAssist(this) } @@ -602,8 +643,8 @@ class SeichiAssist extends JavaPlugin() { // BungeeCordとのI/O Bukkit .getMessenger - .registerIncomingPluginChannel(this, "SeichiAssistBungee", new BungeeReceiver(this)) - Bukkit.getMessenger.registerOutgoingPluginChannel(this, "SeichiAssistBungee") + .registerIncomingPluginChannel(this, "BungeeCord", new BungeeReceiver(this)) + Bukkit.getMessenger.registerOutgoingPluginChannel(this, "BungeeCord") // コンフィグ系の設定は全てConfig.javaに移動 SeichiAssist.seichiAssistConfig = Config.loadFrom(this) @@ -613,7 +654,7 @@ class SeichiAssist extends JavaPlugin() { if (!serverId.startsWith("local-")) { Sentry.init { options => options.setDsn( - "https://7f241763b17c49db982ea29ad64b0264@sentry.onp.admin.seichi.click/2" + "https://66a9eb71bd1663f76df971d0b632b855@sentry.onp.admin.seichi.click/2" ) // パフォーマンスモニタリングに使うトレースサンプルの送信割合 // tracesSampleRateを1.0にすると全てのイベントが送られるため、送りすぎないように調整する必要がある @@ -670,6 +711,7 @@ class SeichiAssist extends JavaPlugin() { ) } + // TODO: 後でもどす itemMigrationSystem.entryPoints.runDatabaseMigration[SyncIO].unsafeRunSync() itemMigrationSystem.entryPoints.runWorldMigration.unsafeRunSync() @@ -710,6 +752,9 @@ class SeichiAssist extends JavaPlugin() { implicit val gridRegionAPI: GridRegionAPI[IO, Player, Location] = gridRegionSystem.api implicit val breakSkillTargetConfigAPI: BreakSkillTargetConfigAPI[IO, Player] = breakSkillTargetConfigSystem.api + implicit val breakSuppressionPreferenceAPI: BreakSuppressionPreferenceAPI[IO, Player] = + breakSuppressionPreferenceSystem.api + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] = playerHeadSkinSystem.api val menuRouter = TopLevelRouter.apply import SeichiAssist.Scopes.globalChatInterceptionScope @@ -760,7 +805,7 @@ class SeichiAssist extends JavaPlugin() { new ChatInterceptor(List(globalChatInterceptionScope)), new MenuHandler(), SpawnRegionProjectileInterceptor, - Y5DoubleSlabCanceller + YMinus59DoubleSlabCanceller ).concat(bungeeSemaphoreResponderSystem.listenersToBeRegistered) .concat { Seq( @@ -835,8 +880,12 @@ class SeichiAssist extends JavaPlugin() { implicit val ioConcurrent: ConcurrentEffect[IO] = IO.ioConcurrentEffect(asyncShift) implicit val sendMessages: SendMinecraftMessage[IO, Player] = new SendBukkitMessage[IO] - val dragonNightTimeProcess: IO[Nothing] = + val dragonNightTimeProcess: IO[Nothing] = { + implicit val getConnectedPlayers: GetConnectedPlayers[IO, Player] = + new GetConnectedBukkitPlayers[IO] + subsystems.dragonnighttime.System.backgroundProcess[IO, SyncIO, Player] + } val halfHourRankingRoutineOption: Option[IO[Nothing]] = // 公共鯖(7)と建築鯖(8)なら整地量のランキングを表示する必要はない @@ -851,9 +900,12 @@ class SeichiAssist extends JavaPlugin() { subsystems.seichilevelupmessage.System.backgroundProcess[IO, SyncIO, Player] val autoSaveProcess: IO[Nothing] = { - val configuration = seichiAssistConfig.getAutoSaveSystemConfiguration + implicit val configuration: SystemConfiguration = + seichiAssistConfig.getAutoSaveSystemConfiguration + implicit val getConnectedPlayers: GetConnectedPlayers[IO, Player] = + new GetConnectedBukkitPlayers[IO] - subsystems.autosave.System.backgroundProcess[IO, IO](configuration) + subsystems.autosave.System.backgroundProcess[IO] } val programs: List[IO[Nothing]] = diff --git a/src/main/scala/com/github/unchama/seichiassist/SkullOwners.scala b/src/main/scala/com/github/unchama/seichiassist/SkullOwners.scala index d4ca6a2313..41ca62c9c5 100644 --- a/src/main/scala/com/github/unchama/seichiassist/SkullOwners.scala +++ b/src/main/scala/com/github/unchama/seichiassist/SkullOwners.scala @@ -1,25 +1,70 @@ package com.github.unchama.seichiassist -import com.github.unchama.itemstackbuilder.{SkullOwnerName, SkullOwnerReference} +import com.github.unchama.itemstackbuilder.SkullOwnerUuidWithNameWithTextureUrl + +import java.util.UUID /** * プレーヤーヘッドにownerとして設定されるプレーヤー達に関する定数を保持するオブジェクト */ object SkullOwners { - val whitecat_haru: SkullOwnerReference = "whitecat_haru".asSkullOwnerReference() - val unchama: SkullOwnerReference = "unchama".asSkullOwnerReference() + val whitecat_haru: SkullOwnerUuidWithNameWithTextureUrl = + SkullOwnerUuidWithNameWithTextureUrl( + UUID.fromString("394f76df-883d-4855-9e6a-d1a800c1ab1c"), + "whitecat_haru", + "http://textures.minecraft.net/texture/2e3a634a9276303c0fa480460391ea16fc50363913ba6cad078163398435b3dd" + ) + val unchama: SkullOwnerUuidWithNameWithTextureUrl = + SkullOwnerUuidWithNameWithTextureUrl( + UUID.fromString("b66cc3f6-a045-42ad-b4b8-320f20caf140"), + "unchama", + "http://textures.minecraft.net/texture/7abe76c114f44d0b114db30a49a5539f53fefad03c683306f5af9f3e76bfd36d" + ) + + val MHF_ArrowUp: SkullOwnerUuidWithNameWithTextureUrl = SkullOwnerUuidWithNameWithTextureUrl( + UUID.fromString("fef039ef-e6cd-4987-9c84-26a3e6134277"), + "MHF_ArrowUp", + "http://textures.minecraft.net/texture/a156b31cbf8f774547dc3f9713a770ecc5c727d967cb0093f26546b920457387" + ) + + val MHF_ArrowDown: SkullOwnerUuidWithNameWithTextureUrl = + SkullOwnerUuidWithNameWithTextureUrl( + UUID.fromString("68f59b9b-5b0b-4b05-a9f2-e1d1405aa348"), + "MHF_ArrowDown", + "http://textures.minecraft.net/texture/fe3d755cecbb13a39e8e9354823a9a02a01dce0aca68ffd42e3ea9a9d29e2df2" + ) - val MHF_ArrowUp: SkullOwnerReference = "MHF_ArrowUp".asSkullOwnerReference() - val MHF_ArrowDown: SkullOwnerReference = "MHF_ArrowDown".asSkullOwnerReference() - val MHF_ArrowLeft: SkullOwnerReference = "MHF_ArrowLeft".asSkullOwnerReference() - val MHF_ArrowRight: SkullOwnerReference = "MHF_ArrowRight".asSkullOwnerReference() + val MHF_ArrowLeft: SkullOwnerUuidWithNameWithTextureUrl = + SkullOwnerUuidWithNameWithTextureUrl( + UUID.fromString("a68f0b64-8d14-4000-a95f-4b9ba14f8df9"), + "MHF_ArrowLeft", + "http://textures.minecraft.net/texture/f7aacad193e2226971ed95302dba433438be4644fbab5ebf818054061667fbe2" + ) - val MHF_Exclamation: SkullOwnerReference = "MHF_Exclamation".asSkullOwnerReference() + val MHF_ArrowRight: SkullOwnerUuidWithNameWithTextureUrl = + SkullOwnerUuidWithNameWithTextureUrl( + UUID.fromString("50c8510b-5ea0-4d60-be9a-7d542d6cd156"), + "MHF_ArrowRight", + "http://textures.minecraft.net/texture/d34ef0638537222b20f480694dadc0f85fbe0759d581aa7fcdf2e43139377158" + ) - val MHF_Villager: SkullOwnerReference = "MHF_Villager".asSkullOwnerReference() + val MHF_Exclamation: SkullOwnerUuidWithNameWithTextureUrl = + SkullOwnerUuidWithNameWithTextureUrl( + UUID.fromString("d3c47f6f-ae3a-45c1-ad7c-e2c762b03ae6"), + "MHF_Exclamation", + "http://textures.minecraft.net/texture/40b05e699d28b3a278a92d169dca9d57c0791d07994d82de3f9ed4a48afe0e1d" + ) - implicit class StringOps(val string: String) { - def asSkullOwnerReference(): SkullOwnerReference = SkullOwnerName(string) - } + val MHF_Villager: SkullOwnerUuidWithNameWithTextureUrl = SkullOwnerUuidWithNameWithTextureUrl( + UUID.fromString("bd482739-767c-45dc-a1f8-c33c40530952"), + "MHF_Villager", + "http://textures.minecraft.net/texture/b4bd832813ac38e68648938d7a32f6ba29801aaf317404367f214b78b4d4754c" + ) + val MHF_TNT: SkullOwnerUuidWithNameWithTextureUrl = + SkullOwnerUuidWithNameWithTextureUrl( + UUID.fromString("d43af93c-c330-4a3d-bab8-ee74234a011a"), + "MHF_TNT", + "http://textures.minecraft.net/texture/f92408fe8d0a3ef5531065e9f566c31aa6eb37484031a46e4466615daf64f705" + ) } 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 bdac55431f..0731288c0e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/achievement/AchievementConditions.scala +++ b/src/main/scala/com/github/unchama/seichiassist/achievement/AchievementConditions.scala @@ -214,7 +214,7 @@ object AchievementConditions { import scala.util.chaining._ stack != null && - stack.getType == Material.SKULL_ITEM && + stack.getType == Material.PLAYER_HEAD && stack .getItemMeta .asInstanceOf[SkullMeta] 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 31cf66ec16..8641097b10 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 @@ -28,4 +28,27 @@ object AchievementGroup { case object VoteCounts extends AchievementGroup("JMS投票数", Specials) case object Secrets extends AchievementGroup("極秘任務", Specials) + + private val achievementIdRangeToGroupNameList = List( + (1001 to 1012, BrokenBlockRanking), + (2001 to 2014, PlacedBlockAmount), + (3001 to 3019, BrokenBlockAmount), + (4001 to 4023, PlayTime), + (5101 to 5125, TotalLogins), + (5001 to 5008, ConsecutiveLogins), + (6001 to 6008, VoteCounts), + (7001 to 7027, OfficialEvent), + (7901 to 7906, OfficialEvent), + (8001 to 8003, Secrets), + (9001 to 9047, Anniversaries) + ) + + /** + * @return 実績IDから実績IDが属する実績グループ名を取得する + */ + def getGroupNameByEntryId(entryId: Int): Option[String] = { + achievementIdRangeToGroupNameList.collectFirst { + case (range, group) if range contains entryId => group.name + } + } } 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 5d09be1140..1985c70e42 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/HatCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/HatCommand.scala @@ -4,6 +4,8 @@ import cats.effect.IO import com.github.unchama.seichiassist.commands.contextual.builder.BuilderTemplates import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.{SequentialEffect, TargetedEffect} +import com.github.unchama.targetedeffect.TargetedEffect.emptyEffect +import org.bukkit.Material import org.bukkit.command.TabExecutor import org.bukkit.entity.Player @@ -16,14 +18,18 @@ object HatCommand { val currentHeadItem = player.getInventory.getHelmet IO { - SequentialEffect( - TargetedEffect.delay[IO, Player] { p => - // swapすることでアイテムの過不足を防ぐ - p.getInventory.setHelmet(mainHandItem) - p.getInventory.setItemInOffHand(currentHeadItem) - }, - MessageEffect("メインハンドに持っていたアイテムを頭にかぶりました。") - ) + if (mainHandItem.getType != Material.AIR) { + SequentialEffect( + TargetedEffect.delay[IO, Player] { p => + // swapすることでアイテムの過不足を防ぐ + p.getInventory.setHelmet(mainHandItem) + p.getInventory.setItemInMainHand(currentHeadItem) + }, + MessageEffect("メインハンドに持っていたアイテムを頭にかぶりました。") + ) + } else { + emptyEffect + } } } .asNonBlockingTabExecutor() 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 08135a03bd..c732886a91 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/RegionOwnerTransferCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/RegionOwnerTransferCommand.scala @@ -1,69 +1,72 @@ 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.seichiassist.commands.contextual.builder.BuilderTemplates.playerCommandBuilder -import com.github.unchama.targetedeffect.TargetedEffect -import com.github.unchama.targetedeffect.commandsender.MessageEffect +import com.github.unchama.targetedeffect.TargetedEffectF +import com.github.unchama.targetedeffect.commandsender.MessageEffectF import com.github.unchama.util.external.WorldGuardWrapper import com.sk89q.worldguard.bukkit.WorldGuardPlugin import com.sk89q.worldguard.protection.regions.ProtectedRegion import org.bukkit.Bukkit import org.bukkit.command.TabExecutor import org.bukkit.entity.Player -import shapeless.{HNil, ::} +import shapeless.{::, HNil} object RegionOwnerTransferCommand { import com.github.unchama.contextualexecutor.builder.ParserResponse._ val executor: TabExecutor = playerCommandBuilder .thenParse(Parsers.identity) - .thenParse(recipientName => { + .thenParse { recipientName => Bukkit.getPlayer(recipientName) match { case recipient: Player => succeedWith(recipient) case _ => failWith(s"${recipientName}というプレイヤーはサーバーに参加したことがありません。") } - }) - .buildWithExecutionF { context => - val (regionName :: newOwner :: HNil) = context.args.parsed + } + .buildWithExecutionCSEffect { context => + val regionName :: newOwner :: HNil = context.args.parsed val sender = context.sender - IO { - WorldGuardPlugin.inst().getRegionManager(sender.getWorld).getRegion(regionName) - }.flatMap { region => - if (region == null) { - MessageEffect(s"${regionName}という名前の保護は存在しません。").run(sender) - } else { - attemptRegionTransfer(sender, newOwner, region) - } + val region = + WorldGuardWrapper.findByRegionName(regionName) + + region match { + case Some(region) => + attemptRegionTransfer(sender, newOwner, region.getRegion(regionName)) + case None => MessageEffectF[IO](s"${regionName}という名前の保護は存在しません。") } } .asNonBlockingTabExecutor() + import cats.implicits._ + private def attemptRegionTransfer( donner: Player, recipient: Player, region: ProtectedRegion - ): IO[TargetedEffect[Player]] = IO { - val owners = region.getOwners - val regionWorld = donner.getWorld - - val recipientLimit = WorldGuardWrapper.getMaxRegionCount(recipient, regionWorld) - val recipientHas = WorldGuardWrapper.getNumberOfRegions(recipient, regionWorld) - + ): TargetedEffectF[IO, Player] = (for { + owners <- Kleisli.liftF(IO(region.getOwners)) + regionWorld <- Kleisli.liftF(IO(donner.getWorld)) + recipientLimit <- Kleisli.liftF(IO(WorldGuardWrapper.getMaxRegion(recipient, regionWorld))) + recipientHas <- Kleisli.liftF( + IO(WorldGuardWrapper.getNumberOfRegions(recipient, regionWorld)) + ) + } yield { if (recipientLimit <= recipientHas) { - MessageEffect(s"相手が保護を上限 ($recipientLimit)まで所持しているため権限を譲渡できません。") + MessageEffectF[IO](s"相手が保護を上限 ($recipientLimit)まで所持しているため権限を譲渡できません。") } else if (owners.contains(WorldGuardPlugin.inst().wrapPlayer(recipient))) { - MessageEffect("相手がすでにオーナーであるため権限を譲渡できません。") + MessageEffectF[IO]("相手がすでにオーナーであるため権限を譲渡できません。") } else if (!owners.contains(donner.getUniqueId)) { - MessageEffect("オーナーではないため権限を譲渡できません。") + MessageEffectF[IO]("オーナーではないため権限を譲渡できません。") } else if (owners.size() != 1) { - MessageEffect("オーナーが複数人いるため権限を譲渡できません。") + MessageEffectF[IO]("オーナーが複数人いるため権限を譲渡できません。") } else { owners.clear() owners.addPlayer(recipient.getUniqueId) - MessageEffect(s"${recipient.getName}に${region.getId}のオーナー権限を譲渡しました。") + MessageEffectF[IO](s"${recipient.getName}に${region.getId}のオーナー権限を譲渡しました。") } - } + }).flatten } 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 cbc782e1a1..5e781c3df8 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/RmpCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/RmpCommand.scala @@ -12,7 +12,7 @@ import com.github.unchama.seichiassist.{ManagedWorld, SeichiAssist} import com.github.unchama.targetedeffect import com.github.unchama.targetedeffect.TargetedEffect import com.github.unchama.targetedeffect.commandsender.MessageEffect -import com.github.unchama.util.external.ExternalPlugins +import com.github.unchama.util.external.WorldGuardWrapper import com.sk89q.worldguard.protection.regions.ProtectedRegion import org.bukkit.ChatColor._ import org.bukkit.command.{CommandSender, ConsoleCommandSender, TabExecutor} @@ -78,13 +78,7 @@ object RmpCommand { case None | Some(false) => MessageEffect(s"第1整地以外の保護をかけて整地する整地ワールドでのみ使用出来ます") case Some(true) => getOldRegionsIn(world, days).map { removalTargets => - removalTargets.foreach { target => - ExternalPlugins - .getWorldGuard - .getRegionContainer - .get(world) - .removeRegion(target.getId) - } + removalTargets.foreach(WorldGuardWrapper.removeByProtectedRegionRegion(world, _)) // メッセージ生成 if (removalTargets.isEmpty) { @@ -112,15 +106,15 @@ object RmpCommand { return Left(MessageEffect(s"${RED}データベースアクセスに失敗しました。")) } - val regions = ExternalPlugins.getWorldGuard.getRegionContainer.get(world).getRegions.asScala + val regions = WorldGuardWrapper.getRegions(world) - val oldRegions = regions - .values - .filter { region => - region.getId != "__global__" && region.getId != "spawn" && - region.getOwners.getUniqueIds.asScala.forall(leavers.contains(_)) - } - .toList + val oldRegions = regions.filter { region => + region.getId != "spawn" && region + .getOwners + .getUniqueIds + .asScala + .forall(leavers.contains(_)) + } Right(oldRegions) } 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 8fec08a81e..54e3c9ae73 100644 --- a/src/main/scala/com/github/unchama/seichiassist/commands/StickCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/commands/StickCommand.scala @@ -14,7 +14,7 @@ object StickCommand { val executor: TabExecutor = playerCommandBuilder .buildWithExecutionF { context => // 初見プレイヤー用とは別に簡潔な説明 - val stickLore = List("棒を持って右クリックもしくは左クリックでメニューを開きます。", "各メニューの詳細は公式サイトで確認できます。") + val stickLore = List("棒を持って右クリックもしくは", "左クリックでメニューを開きます。", "各メニューの詳細は公式サイトで確認できます。") val stickItemStack = new ItemStack(Material.STICK, 1).tap { itemStack => import itemStack._ val meta = getItemMeta diff --git a/src/main/scala/com/github/unchama/seichiassist/data/MenuInventoryData.java b/src/main/scala/com/github/unchama/seichiassist/data/MenuInventoryData.java index f5a75a1d6c..fc0159d7d6 100644 --- a/src/main/scala/com/github/unchama/seichiassist/data/MenuInventoryData.java +++ b/src/main/scala/com/github/unchama/seichiassist/data/MenuInventoryData.java @@ -450,8 +450,14 @@ public static Inventory getGiganticBerserkBeforeEvolutionMenu(final Player p) { final Inventory inventory = getEmptyInventory(6, ChatColor.DARK_PURPLE + "" + ChatColor.BOLD + "スキルを進化させますか?"); { // 色 - final byte[] table = {12, 15, 4, 0, 3}; - final ItemStack itemstack = new ItemStack(Material.STAINED_GLASS_PANE, 1, table[playerdata.giganticBerserk().stage()]); + final Material[] table = { + Material.ORANGE_STAINED_GLASS_PANE, + Material.BLACK_STAINED_GLASS_PANE, + Material.YELLOW_STAINED_GLASS_PANE, + Material.WHITE_STAINED_GLASS_PANE, + Material.LIGHT_BLUE_STAINED_GLASS_PANE + }; + final ItemStack itemstack = new ItemStack(table[playerdata.giganticBerserk().stage()], 1); final ItemMeta itemmeta = itemstack.getItemMeta(); itemmeta.setDisplayName(" "); itemstack.setItemMeta(itemmeta); @@ -495,10 +501,16 @@ public static Inventory getGiganticBerserkAfterEvolutionMenu(final Player p) { if (isError(p, playerdata, "GiganticBerserk進化後画面")) return null; final Inventory inventory = getEmptyInventory(6, ChatColor.LIGHT_PURPLE + "" + ChatColor.BOLD + "スキルを進化させました"); { - final byte[] table = {12, 15, 4, 0, 3, 12}; - final byte b = table[playerdata.giganticBerserk().stage()]; + final Material[] table = { + Material.BROWN_STAINED_GLASS_PANE, + Material.BLACK_STAINED_GLASS_PANE, + Material.YELLOW_STAINED_GLASS_PANE, + Material.WHITE_STAINED_GLASS_PANE, + Material.LIGHT_BLUE_STAINED_GLASS_PANE, + Material.BROWN_STAINED_GLASS_PANE + }; - final ItemStack itemstack = new ItemStack(Material.STAINED_GLASS_PANE, 1, b); + final ItemStack itemstack = new ItemStack(table[playerdata.giganticBerserk().stage()], 1); final ItemMeta itemmeta = itemstack.getItemMeta(); if (playerdata.giganticBerserk().stage() >= 4) { @@ -570,7 +582,7 @@ private static ItemStack buildPlayerSkull(final String name, final List } private static ItemStack buildPlayerSkull(final String name, final List lore, final String owner, final Consumer modify) { - final ItemStack ret = new ItemStack(Material.SKULL_ITEM, 1, PLAYER_SKULL); + final ItemStack ret = new ItemStack(Material.PLAYER_HEAD, 1, PLAYER_SKULL); final SkullMeta sm = ItemMetaFactory.SKULL.getValue(); if (name != null) { sm.setDisplayName(name); 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 823d126560..d165487061 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 @@ -25,12 +25,12 @@ case class GiganticBerserk( def materialOnUI(): Material = { stage match { - case 0 => Material.WOOD_SWORD + case 0 => Material.WOODEN_SWORD case 1 => Material.STONE_SWORD - case 2 => Material.GOLD_SWORD + case 2 => Material.GOLDEN_SWORD case 3 => Material.IRON_SWORD case 4 => Material.DIAMOND_SWORD - case 5 => Material.WOOD_SWORD + case 5 => Material.WOODEN_SWORD case _ => throw new RuntimeException("This branch should not be reached") } } @@ -44,4 +44,15 @@ case class GiganticBerserk( val current = stage * 10 + level LevelThresholds.giganticBerserkLevelList(current) } + + /** + * @return 今までに倒した敵の総数 + */ + def totalNumberOfKilledEnemies: Int = { + val currentStage = stage * 10 + val previousLevel = level - 1 + val previousStageLevel = currentStage + previousLevel + LevelThresholds.giganticBerserkLevelList.take(previousStageLevel).sum + exp + } + } 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 35ba034cf3..5122d81c7b 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 @@ -220,7 +220,7 @@ class PlayerData(@Deprecated() val uuid: UUID, val name: String) { // 総プレイ時間を更新する def updatePlayTick(): Unit = { // WARN: 1分毎にupdatePlayTickが呼び出されるというコンテクストに依存している. - val nowTotalPlayTick = player.getStatistic(Statistic.PLAY_ONE_TICK).toLong + val nowTotalPlayTick = player.getStatistic(Statistic.PLAY_ONE_MINUTE).toLong val diff = nowTotalPlayTick - totalPlayTick.getOrElse(nowTotalPlayTick) totalPlayTick = Some(nowTotalPlayTick) 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 a4f9e1b93f..9d4f08533b 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 @@ -48,7 +48,7 @@ class PlayerSettings { val toggleHalfBreakFlag: TargetedEffect[Player] = DeferredEffect(IO { allowBreakingHalfBlocks = !allowBreakingHalfBlocks - val newStatus = if (allowBreakingHalfBlocks) s"${GREEN}破壊可能" else "${RED}破壊不可能" + val newStatus = if (allowBreakingHalfBlocks) s"${GREEN}破壊可能" else s"${RED}破壊不可能" val responseMessage = s"現在ハーフブロックは$newStatus${RESET}です." MessageEffect(responseMessage) 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 ca722bada6..c5844b9b60 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 @@ -22,6 +22,7 @@ class FiniteExpLevelTable[L: PositiveInt, ExpAmount: Order: LowerBounded]( internalTable.sliding(2).forall { case Seq(x1, x2) => x1 <= x2 + case _ => false } }, "internalTable must be sorted" diff --git a/src/main/scala/com/github/unchama/seichiassist/infrastructure/minecraft/JdbcLastSeenNameToUuid.scala b/src/main/scala/com/github/unchama/seichiassist/infrastructure/minecraft/JdbcLastSeenNameToUuid.scala index 446ef4d58b..f5e29253d5 100644 --- a/src/main/scala/com/github/unchama/seichiassist/infrastructure/minecraft/JdbcLastSeenNameToUuid.scala +++ b/src/main/scala/com/github/unchama/seichiassist/infrastructure/minecraft/JdbcLastSeenNameToUuid.scala @@ -31,7 +31,6 @@ class JdbcLastSeenNameToUuid[F[_]: Sync] val foundUuid = sql"SELECT uuid FROM playerdata WHERE name = $playerName" .map(rs => UUID.fromString(rs.string("uuid"))) .toList() - .apply() if (foundUuid.isEmpty) Left(LastSeenNameToUuidError.NotFound) else if (foundUuid.length >= 2) Left(LastSeenNameToUuidError.MultipleFound) 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 28acc5ae32..90c9d5882e 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 @@ -18,7 +18,7 @@ object ScalikeJDBCConfiguration { singleLineMode = true, printUnprocessedStackTrace = false, stackTraceDepth = 15, - logLevel = Symbol("debug"), + logLevel = "debug", warningEnabled = false ) 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 3cd8534404..7784de4316 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/BuildMainMenuOpener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/BuildMainMenuOpener.scala @@ -21,18 +21,15 @@ class BuildMainMenuOpener( @EventHandler def onPlayerLeftClickWithStick(event: PlayerInteractEvent): Unit = { val player = event.getPlayer + val action = event.getAction - event.getAction match { - case Action.LEFT_CLICK_AIR | Action.LEFT_CLICK_BLOCK => - case _ => return - } + if (action != Action.LEFT_CLICK_AIR && action != Action.LEFT_CLICK_BLOCK) return - { - val hasStickOnMainHand = player.getInventory.getItemInMainHand.getType == Material.STICK - val actionWasOnMainHand = event.getHand == EquipmentSlot.HAND + val hasNotStickOnMainHand = + player.getInventory.getItemInMainHand.getType != Material.STICK + val actionWasNotOnMainHand = event.getHand != EquipmentSlot.HAND - if (!hasStickOnMainHand || !actionWasOnMainHand) return - } + if (hasNotStickOnMainHand || actionWasNotOnMainHand) return effectEnvironment.unsafeRunAsyncTargetedEffect(player)( SequentialEffect( 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 a3fd697648..55063127d2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/EntityListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/EntityListener.scala @@ -18,6 +18,7 @@ import org.bukkit.enchantments.Enchantment import org.bukkit.entity.{Player, Projectile} import org.bukkit.event.entity._ import org.bukkit.event.{EventHandler, Listener} +import org.bukkit.inventory.meta.Damageable class EntityListener( implicit effectEnvironment: EffectEnvironment, @@ -77,16 +78,21 @@ class EntityListener( ) // 耐久値がマイナスかつ耐久無限ツールでない時処理を終了 - if (tool.getDurability > tool.getType.getMaxDurability && !tool.getItemMeta.isUnbreakable) + if ( + tool.getItemMeta.asInstanceOf[Damageable].getDamage > tool + .getType + .getMaxDurability && !tool.getItemMeta.isUnbreakable + ) return - runArrowSkillOfHitBlock(player, block, tool) + runArrowSkillOfHitBlock(player, block, tool, projectile) } private def runArrowSkillOfHitBlock( player: Player, hitBlock: BlockBreakableBySkill, - tool: BreakTool + tool: BreakTool, + projectile: Projectile ): Unit = { val playerData = playermap(player.getUniqueId) @@ -132,7 +138,7 @@ class EntityListener( val nextDurability = { val durabilityEnchantment = tool.getEnchantmentLevel(Enchantment.DURABILITY) - tool.getDurability + + tool.getItemMeta.asInstanceOf[Damageable].getDamage + BreakUtil.calcDurability( durabilityEnchantment, breakBlocks.size + 10 * (lavaBlocks.size + waterBlocks.size) @@ -157,7 +163,11 @@ class EntityListener( return // 耐久値を減らす - if (!tool.getItemMeta.isUnbreakable) tool.setDurability(nextDurability) + if (!tool.getItemMeta.isUnbreakable) { + val meta = tool.getItemMeta + meta.asInstanceOf[Damageable].setDamage(nextDurability) + tool.setItemMeta(meta) + } // 以降破壊する処理 // 溶岩と水を破壊する @@ -166,6 +176,8 @@ class EntityListener( // 元ブロックの真ん中の位置 val centerOfBlock = hitBlock.getLocation.add(0.5, 0.5, 0.5) + projectile.remove() + effectEnvironment.unsafeRunEffectAsync( "破壊エフェクトを再生する", playerData 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 d46674543f..c182d0490b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerBlockBreakListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerBlockBreakListener.scala @@ -15,21 +15,25 @@ import com.github.unchama.seichiassist.subsystems.mana.ManaApi import com.github.unchama.seichiassist.subsystems.mana.domain.ManaAmount import com.github.unchama.seichiassist.subsystems.minestack.MineStackAPI import com.github.unchama.seichiassist.util.BreakUtil -import com.github.unchama.seichiassist.util.BreakUtil.BlockBreakResult import com.github.unchama.seichiassist.{MaterialSets, SeichiAssist} import com.github.unchama.targetedeffect.player.FocusedSoundEffect +import com.github.unchama.targetedeffect.player.ActionBarMessageEffect +import com.github.unchama.util.bukkit.ItemStackUtil import com.github.unchama.util.effect.BukkitResources -import com.github.unchama.util.external.ExternalPlugins +import com.github.unchama.util.external.WorldGuardWrapper import org.bukkit.ChatColor.RED import org.bukkit._ -import org.bukkit.block.Block +import org.bukkit.block.{Block, Container} +import org.bukkit.block.data.`type`.Slab import org.bukkit.enchantments.Enchantment import org.bukkit.entity.Player import org.bukkit.event.block.BlockBreakEvent import org.bukkit.event.{EventHandler, EventPriority, Listener} import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.meta.Damageable import scala.collection.mutable.ArrayBuffer +import scala.jdk.CollectionConverters.CollectionHasAsScala import scala.util.control.Breaks class PlayerBlockBreakListener( @@ -48,6 +52,8 @@ class PlayerBlockBreakListener( def onPlayerActiveSkillEvent(event: BlockBreakEvent): Unit = { val player = event.getPlayer + if (!player.getWorld.isSeichiSkillAllowed) return + val block = MaterialSets .refineBlock(event.getBlock, MaterialSets.materials) .getOrElse( @@ -68,14 +74,6 @@ class PlayerBlockBreakListener( return } - if (!player.getWorld.isSeichiSkillAllowed) return - - // 破壊不可能ブロックの時処理を終了 - if (!BreakUtil.canBreakWithSkill(player, block)) { - event.setCancelled(true) - return - } - // 実際に使用するツール val tool: BreakTool = MaterialSets .refineItemStack(player.getInventory.getItemInMainHand, MaterialSets.breakToolMaterials) @@ -84,7 +82,11 @@ class PlayerBlockBreakListener( ) // 耐久値がマイナスかつ耐久無限ツールでない時処理を終了 - if (tool.getDurability > tool.getType.getMaxDurability && !tool.getItemMeta.isUnbreakable) + if ( + tool.getItemMeta.asInstanceOf[Damageable].getDamage > tool + .getType + .getMaxDurability && !tool.getItemMeta.isUnbreakable + ) return // もしサバイバルでなければ、またはフライ中なら終了 @@ -93,8 +95,6 @@ class PlayerBlockBreakListener( val playerData = SeichiAssist.playermap(player.getUniqueId) val skillState = playerData.skillState.get.unsafeRunSync() - if (!player.getWorld.isSeichiSkillAllowed) return - // クールダウンタイム中は処理を終了 if (!activeSkillAvailability(player).get.unsafeRunSync()) { // SEを再生 @@ -102,37 +102,90 @@ class PlayerBlockBreakListener( return } - // 追加マナ獲得 - manaApi - .manaAmount(player) - .restoreAbsolute(ManaAmount(BreakUtil.calcManaDrop(player))) - .unsafeRunSync() - + // 選択したスキル val selectedSkill = skillState .activeSkill .getOrElse( return ) - if (!selectedSkill.range.isInstanceOf[MultiArea] || skillState.usageMode == Disabled) return + // 消費するマナが不足しているか判定 + { + // プレイヤーのY座標 + val playerLocY = player.getLocation.getBlockY - 1 + // スキル破壊範囲 + val skillArea = BreakArea(selectedSkill, skillState.usageMode) + // 破壊エリアリスト + val breakAreaList = skillArea.makeBreakArea(player).unsafeRunSync() + // 複数種類ブロック同時破壊設定 + val isMultiTypeBreakingSkillEnabled = + BreakUtil.performsMultipleIDBlockBreakWhenUsingSkills(player).unsafeRunSync() + // 破壊範囲のブロック計算 + val totalBreakRangeVolume = { + val breakLength = skillArea.breakLength + breakLength.x * breakLength.y * breakLength.z * skillArea.breakNum + } + breakAreaList.foreach { breakArea => + import com.github.unchama.seichiassist.data.syntax._ + val BlockSearching.Result(breakBlocks, waterBlocks, lavaBlocks) = + BlockSearching + .searchForBlocksBreakableWithSkill(player, breakArea.gridPoints(), block) + .unsafeRunSync() + .filterSolids(targetBlock => + isMultiTypeBreakingSkillEnabled || BlockSearching + .multiTypeBreakingFilterPredicate(block)(targetBlock) + ) + .filterAll(targetBlock => + player.isSneaking || targetBlock + .getLocation + .getBlockY > playerLocY || targetBlock == block + ) + + // 破壊範囲で消費されるマナ計算 + val manaToConsumeOnBreakArea = ManaAmount { + (gravity + 1) * selectedSkill.manaCost * (breakBlocks.size + 1).toDouble / totalBreakRangeVolume + } + // 消費マナが不足している場合は処理を終了 + manaApi.manaAmount(player).canAcquire(manaToConsumeOnBreakArea).unsafeRunSync() match { + case false if isBreakBlockManaFullyConsumed(player).unsafeRunSync() => + event.setCancelled(true) + return + case _ => + } + } + } + + // 追加マナ獲得 + manaApi + .manaAmount(player) + .restoreAbsolute(ManaAmount(BreakUtil.calcManaDrop(player))) + .unsafeRunSync() + + // 破壊不可能ブロックの時処理を終了 + if (!BreakUtil.canBreakWithSkill(player, block)) { + event.setCancelled(true) + return + } + event.setCancelled(true) + // ブロック破壊時に行う処理 { - // プレイヤーの足のy座標を取得 + // プレイヤーのY座標 val playerLocY = player.getLocation.getBlockY - 1 - + // スキル破壊範囲 val skillArea = BreakArea(selectedSkill, skillState.usageMode) + // 破壊エリアリスト val breakAreaList = skillArea.makeBreakArea(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]] // 壊される溶岩の全てのリストデータ @@ -140,7 +193,7 @@ class PlayerBlockBreakListener( // 壊される水ブロックの全てのリストデータ val multiWaterList = new ArrayBuffer[Set[Block]] // 全ての耐久消費量 - var toolDamageToSet = tool.getDurability.toInt + var toolDamageToSet = tool.getItemMeta.asInstanceOf[Damageable].getDamage // 消費が予約されたマナ val reservedMana = new ArrayBuffer[ManaAmount] @@ -164,7 +217,6 @@ class PlayerBlockBreakListener( .getLocation .getBlockY > playerLocY || targetBlock == block ) - // このチャンクで消費されるマナ val manaToConsumeOnThisChunk = ManaAmount { (gravity + 1) * selectedSkill.manaCost * (breakBlocks.size + 1).toDouble / totalBreakRangeVolume @@ -272,12 +324,16 @@ class PlayerBlockBreakListener( // ツールの耐久値を減らす val adjustManaAndDurability = IO { - if (!tool.getItemMeta.isUnbreakable) tool.setDurability(toolDamageToSet.toShort) + if (!tool.getItemMeta.isUnbreakable) { + val meta = tool.getItemMeta + meta.asInstanceOf[Damageable].setDamage(toolDamageToSet) + tool.setItemMeta(meta) + } } effectEnvironment.unsafeRunEffectAsync( "複数破壊エフェクトを実行する", - effectPrograms.toList.sequence[IO, Fiber[IO, Unit]] + effectPrograms.sequence[IO, Fiber[IO, Unit]] ) effectEnvironment.unsafeRunEffectAsync( "複数破壊エフェクトの後処理を実行する", @@ -297,51 +353,48 @@ class PlayerBlockBreakListener( .map(multiplier => BreakUtil.totalBreakCount(Seq(block.getType)) * multiplier) .unsafeRunSync() } + + val program = for { + block <- IO(event.getBlock) + isContainer <- IO(block.getState.isInstanceOf[Container]) + // NOTE: Spigot 1.18.2のAPIではチェストの中身のドロップを計算することは不可能である。 + // そのため、破壊したブロックがインベントリをもつ場合はドロップをキャンセルせず、 + // MineStackの中身に入れることはせずにそのままドロップする + // + // また、ドロップしたアイテムをイベントで検知して取得するのも難しい。 + // BlockDropItemEventは、playerがブロックを破壊したことをトリガーとするが、 + // player#breakBlock関数を使用してブロックを破壊するとBlockBreakEventが再度発火し、 + // その場合のみイベントの処理を実行しなかったとしてもサーバーに負荷がかかるので現実的ではない。 + // これらのことから、やむを得ずこのような実装になっている。 + _ <- (for { + _ <- IO(event.setDropItems(false)) + blockDrops <- IO( + event.getBlock.getDrops(player.getInventory.getItemInMainHand).asScala.toVector + ) + drops = ItemStackUtil.amalgamate(blockDrops).toVector + currentAutoMineStackState <- mineStackAPI.autoMineStack(player) + intoFailedItemStacksAndSuccessItemStacks <- whenAOrElse(currentAutoMineStackState)( + mineStackAPI.mineStackRepository.tryIntoMineStack(player, drops), + (drops, Vector.empty) + ) + _ <- IO { + intoFailedItemStacksAndSuccessItemStacks._1.foreach { itemStack => + player.getWorld.dropItemNaturally(player.getLocation, itemStack) + } + } + } yield ()).unlessA(isContainer) + } yield () + effectEnvironment.unsafeRunEffectAsync( "通常破壊されたブロックを整地量に計上する", SeichiAssist.instance.breakCountSystem.api.incrementSeichiExp.of(player, amount).toIO ) - val tool: BreakTool = MaterialSets - .refineItemStack(player.getInventory.getItemInMainHand, MaterialSets.breakToolMaterials) - .getOrElse( - return - ) - - /** - * 手彫りで破壊したアイテムを直接MineStackに入れる - * 一つのBlockBreakEventから複数の種類のアイテムが出てくることはない。 - * チェスト等のインベントリスロットのあるブロック`b`を破壊したときは、 - * 破壊された`b`のみが`BlockBreakEvent`のドロップ対象となるため、 - * 中身のドロップがキャンセルされることはない。 - */ - val drops = BreakUtil - .dropItemOnTool(tool)((block.getLocation(), block.getType, block.getData)) - .getOrElse( - return - ) - - drops match { - case BlockBreakResult.ItemDrop(itemStack) => - val program = for { - currentAutoMineStackState <- mineStackAPI.autoMineStack(player) - isSucceedTryIntoMineStack <- whenAOrElse(currentAutoMineStackState)( - mineStackAPI - .mineStackRepository - .tryIntoMineStack(player, itemStack, itemStack.getAmount), - false - ) - } yield { - if (isSucceedTryIntoMineStack) event.setDropItems(false) - else () - } - program.unsafeRunSync() - case _ => () - } + effectEnvironment.unsafeRunEffectAsync("破壊されたアイテムをMineStackに入れるかドロップする", program) } /** - * y5ハーフブロック破壊抑制 + * y-59ハーフブロック破壊抑制 * * @param event * BlockBreakEvent @@ -349,22 +402,40 @@ class PlayerBlockBreakListener( @EventHandler(priority = EventPriority.LOWEST) @SuppressWarnings(Array("deprecation")) def onPlayerBlockHalf(event: BlockBreakEvent): Unit = { - val p = event.getPlayer - val b = event.getBlock - val world = p.getWorld + val player = event.getPlayer + val block = event.getBlock + val world = player.getWorld // そもそも自分の保護じゃなきゃ処理かけない - if (!ExternalPlugins.getWorldGuard.canBuild(p, b.getLocation)) return - if ((b.getType eq Material.DOUBLE_STEP) && b.getData == 0) { - b.setType(Material.STEP) - b.setData(0.toByte) - val location = b.getLocation - world.dropItemNaturally(location, new ItemStack(Material.STEP)) + if (!WorldGuardWrapper.canBuild(player, block.getLocation)) return + block.getBlockData match { + case slab: Slab if slab.getType == Slab.Type.DOUBLE => + val location = block.getLocation + world.dropItemNaturally(location, new ItemStack(block.getType)) + case _: Slab => + case _ => return } - if (b.getType ne Material.STEP) return - if (b.getY > 5) return - if (b.getData != 0) return + if (block.getY > -59) return + if (block.getBlockData.asInstanceOf[Slab].getType != Slab.Type.BOTTOM) return if (!world.isSeichi) return event.setCancelled(true) - p.sendMessage(s"${RED}Y5以下に敷かれたハーフブロックは破壊不可能です。") + player.sendMessage(s"${RED}Y-59以下に敷かれたハーフブロックは破壊不可能です。") + } + + /** + * ブロック破壊時、「マナ切れブロック破壊停止設定」を取得する。 + * マナ切れブロック破壊設定が `true` になっている場合、プレイヤーに破壊抑制メッセージを送信する。 + * @param player マナ切れブロック破壊停止設定を取得するプレイヤー + */ + private def isBreakBlockManaFullyConsumed(player: Player): IO[Boolean] = { + for { + breakSuppressionPreference <- SeichiAssist + .instance + .breakSuppressionPreferenceSystem + .api + .isBreakSuppressionEnabled(player) + _ <- ActionBarMessageEffect(s"${RED}マナ切れでブロック破壊を止めるスキルは有効化されています") + .run(player) + .whenA(breakSuppressionPreference) + } yield breakSuppressionPreference } } 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 6ddf552e27..b8521d7972 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerClickListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerClickListener.scala @@ -116,7 +116,7 @@ class PlayerClickListener( val equipmentSlot = event.getHand val currentItem = player.getInventory.getItemInMainHand.getType - if (currentItem == Material.STICK || currentItem == Material.SKULL_ITEM) return + if (currentItem == Material.STICK || currentItem == Material.PLAYER_HEAD) return val playerData = playerMap(player.getUniqueId) val playerLevel = SeichiAssist @@ -242,7 +242,7 @@ class PlayerClickListener( val targetBlock = e.getClickedBlock // 頭じゃない場合無視 - if (targetBlock.getType != Material.SKULL) { + if (targetBlock.getType != Material.PLAYER_HEAD) { return } diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerDeathEventListener.java b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerDeathEventListener.java index 3ea7629d96..781156094e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerDeathEventListener.java +++ b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerDeathEventListener.java @@ -35,5 +35,7 @@ public void onDeath(PlayerDeathEvent event) { p.sendMessage(msg); } } + + event.setDeathMessage(null); } } 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 9371b85643..fbb2a2132d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerInventoryListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerInventoryListener.scala @@ -49,7 +49,7 @@ class PlayerInventoryListener( // インベントリサイズが54でない時終了 if (inventory.row != 6) return - if (inventory.getTitle != s"$LIGHT_PURPLE${BOLD}交換したい鉱石を入れてください") return + if (event.getView.getTitle != s"$LIGHT_PURPLE${BOLD}交換したい鉱石を入れてください") return /* * step1 for文でinventory内の対象商品の個数を計算 @@ -57,36 +57,40 @@ class PlayerInventoryListener( */ // 石炭とラピスラズリを適切に処理するため、typeとdurabilityを持つクラスを用意 - case class ExchangeableMaterial(materialType: Material, durability: Short) + case class ExchangeableMaterial(materialType: Material) val requiredAmountPerTicket = Map( - ExchangeableMaterial(Material.COAL_ORE, 0) -> 128, - ExchangeableMaterial(Material.IRON_ORE, 0) -> 64, - ExchangeableMaterial(Material.GOLD_ORE, 0) -> 8, - ExchangeableMaterial(Material.LAPIS_ORE, 0) -> 8, - ExchangeableMaterial(Material.DIAMOND_ORE, 0) -> 4, - ExchangeableMaterial(Material.REDSTONE_ORE, 0) -> 32, - ExchangeableMaterial(Material.EMERALD_ORE, 0) -> 4, - ExchangeableMaterial(Material.QUARTZ_ORE, 0) -> 16, - ExchangeableMaterial(Material.COAL, 0) -> 432, - ExchangeableMaterial(Material.REDSTONE, 0) -> 288, - ExchangeableMaterial(Material.INK_SACK, 4) -> 64, - ExchangeableMaterial(Material.DIAMOND, 0) -> 8 + ExchangeableMaterial(Material.COAL_ORE) -> 128, + ExchangeableMaterial(Material.COPPER_ORE) -> 128, + ExchangeableMaterial(Material.IRON_ORE) -> 64, + ExchangeableMaterial(Material.GOLD_ORE) -> 8, + ExchangeableMaterial(Material.LAPIS_ORE) -> 8, + ExchangeableMaterial(Material.DIAMOND_ORE) -> 4, + ExchangeableMaterial(Material.REDSTONE_ORE) -> 32, + ExchangeableMaterial(Material.EMERALD_ORE) -> 4, + ExchangeableMaterial(Material.NETHER_QUARTZ_ORE) -> 16, + ExchangeableMaterial(Material.NETHER_GOLD_ORE) -> 32, + ExchangeableMaterial(Material.DEEPSLATE_COAL_ORE) -> 128, + ExchangeableMaterial(Material.DEEPSLATE_COPPER_ORE) -> 128, + ExchangeableMaterial(Material.DEEPSLATE_IRON_ORE) -> 64, + ExchangeableMaterial(Material.DEEPSLATE_GOLD_ORE) -> 8, + ExchangeableMaterial(Material.DEEPSLATE_LAPIS_ORE) -> 8, + ExchangeableMaterial(Material.DEEPSLATE_DIAMOND_ORE) -> 4, + ExchangeableMaterial(Material.DEEPSLATE_REDSTONE_ORE) -> 32, + ExchangeableMaterial(Material.DEEPSLATE_EMERALD_ORE) -> 4 ) val inventoryContents = inventory.getContents.filter(_ != null) val (itemsToExchange, rejectedItems) = inventoryContents.partition { stack => - requiredAmountPerTicket.contains( - ExchangeableMaterial(stack.getType, stack.getDurability) - ) + requiredAmountPerTicket.contains(ExchangeableMaterial(stack.getType)) } - val exchangingAmount = itemsToExchange - .groupBy(stacks => ExchangeableMaterial(stacks.getType, stacks.getDurability)) - .toList - .map { case (key, stacks) => key -> stacks.map(_.getAmount).sum } + val exchangingAmount = + itemsToExchange.groupBy(stacks => ExchangeableMaterial(stacks.getType)).toList.map { + case (key, stacks) => key -> stacks.map(_.getAmount).sum + } val ticketAmount = exchangingAmount.map { case (exchangeableMaterial, amount) => @@ -139,13 +143,7 @@ class PlayerInventoryListener( case (exchangedMaterial, exchangedAmount) => val returningAmount = exchangedAmount % requiredAmountPerTicket(exchangedMaterial) if (returningAmount != 0) - Some( - new ItemStack( - exchangedMaterial.materialType, - returningAmount, - exchangedMaterial.durability - ) - ) + Some(new ItemStack(exchangedMaterial.materialType, returningAmount)) else None } @@ -186,15 +184,15 @@ class PlayerInventoryListener( val uuid = player.getUniqueId val playerdata = playerMap(uuid) - if (topinventory.getTitle == DARK_PURPLE.toString + "" + BOLD + "スキルを進化させますか?") { + if (view.getTitle == DARK_PURPLE.toString + "" + BOLD + "スキルを進化させますか?") { event.setCancelled(true) if (itemstackcurrent.getType == Material.NETHER_STAR) { playerdata.giganticBerserk = GiganticBerserk(0, 0, playerdata.giganticBerserk.stage + 1) player.playSound(player.getLocation, Sound.BLOCK_END_GATEWAY_SPAWN, 1f, 0.5f) - player.playSound(player.getLocation, Sound.ENTITY_ENDERDRAGON_AMBIENT, 1f, 0.8f) + player.playSound(player.getLocation, Sound.ENTITY_ENDER_DRAGON_AMBIENT, 1f, 0.8f) player.openInventory(MenuInventoryData.getGiganticBerserkAfterEvolutionMenu(player)) } - } else if (topinventory.getTitle == LIGHT_PURPLE.toString + "" + BOLD + "スキルを進化させました") { + } else if (view.getTitle == LIGHT_PURPLE.toString + "" + BOLD + "スキルを進化させました") { event.setCancelled(true) } 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 8a219d981b..b7a63143ee 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerJoinListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerJoinListener.scala @@ -13,7 +13,7 @@ import com.github.unchama.seichiassist.subsystems.mebius.domain.property.{ } import com.github.unchama.seichiassist.util.{SendMessageEffect, SendSoundEffect} import com.github.unchama.targetedeffect.player.FocusedSoundEffect -import net.coreprotect.model.Config +import net.coreprotect.config.ConfigHandler import org.bukkit.ChatColor._ import org.bukkit.enchantments.Enchantment import org.bukkit.entity.Player @@ -97,12 +97,17 @@ class PlayerJoinListener extends Listener { // 初見さんへの処理 if (!player.hasPlayedBefore) { // 初見さんであることを全体告知 - SendMessageEffect.sendMessageToEveryoneIgnoringPreference( - s"$LIGHT_PURPLE$BOLD${player.getName}さんはこのサーバーに初めてログインしました!" - ) - SendMessageEffect.sendMessageToEveryoneIgnoringPreference( - s"${WHITE}webサイトはもう読みましたか?→$YELLOW${UNDERLINE}https://www.seichi.network/gigantic" - ) + SendMessageEffect + .sendMessageToEveryoneIgnoringPreferenceIO( + s"$LIGHT_PURPLE$BOLD${player.getName}さんはこのサーバーに初めてログインしました!" + ) + .unsafeRunAsyncAndForget() + SendMessageEffect + .sendMessageToEveryoneIgnoringPreferenceIO( + s"${WHITE}webサイトはもう読みましたか?→$YELLOW${UNDERLINE}https://www.seichi.network/gigantic" + ) + .unsafeRunAsyncAndForget() + SendSoundEffect.sendEverySound(Sound.ENTITY_PLAYER_LEVELUP, 1f, 1f) // 同時に【はじめての方へ】ページに誘導したほうがただWebサイトに誘導するよりまだ可能性がありそう @@ -125,10 +130,14 @@ class PlayerJoinListener extends Listener { // 初見プレイヤー向けの Lore (説明文) を設定 // /stick で入手できる木の棒は、簡略化された説明文にしておく。 val stickLore = List( - "この棒を持って右クリックもしくは左クリックするとメニューが開きます。", - "メニューからはいろんな機能が使えます。試してみよう。", - "この棒をなくしても /stick コマンドを実行すると再入手できます。", - "ヒント: もしサーバー内で迷子になったら /spawn コマンドを実行することでいつでも戻れます。" + "この棒を持って右クリックもしくは", + "左クリックするとメニューが開きます。", + "試してみよう。", + "", + "この棒をなくしても /stick コマンドを", + "実行すると再入手できます。", + "ヒント: もしサーバー内で迷子になったら /spawn", + "コマンドを実行することでいつでも戻れます。" ) val stick = new ItemStack(Material.STICK, 1).tap { itemStack => import itemStack._ @@ -144,13 +153,13 @@ class PlayerJoinListener extends Listener { // 耐久Ⅲ .tap(_.addEnchantment(Enchantment.DURABILITY, 3)) inv.addItem(pickaxe) - inv.addItem(new ItemStack(Material.DIAMOND_SPADE)) + inv.addItem(new ItemStack(Material.DIAMOND_SHOVEL)) 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.OAK_LOG, 64), + new ItemStack(Material.OAK_LOG, 64), + new ItemStack(Material.BIRCH_LOG, 64), + new ItemStack(Material.DARK_OAK_LOG, 64) ) inv.addItem(new ItemStack(Material.BAKED_POTATO, 64)) @@ -196,8 +205,7 @@ class PlayerJoinListener extends Listener { BukkitMebiusItemStackCodec.materialize( // **getDisplayNameは二つ名も含むのでMCIDにはgetNameが適切** MebiusProperty - .initialProperty(NormalMebius, player.getName, player.getUniqueId.toString), - damageValue = 0.toShort + .initialProperty(NormalMebius, player.getName, player.getUniqueId.toString) ) ) @@ -215,27 +223,12 @@ class PlayerJoinListener extends Listener { // 整地専用サーバーの場合は上級者向けのサーバーである旨を通知 if (SeichiAssist.seichiAssistConfig.getServerNum == 5) player.sendTitle( - s"${WHITE}ここは$BLUE${UNDERLINE}上級者向けのサーバー${WHITE}", + s"${WHITE}ここは$BLUE${UNDERLINE}上級者向けのサーバー$WHITE", s"${WHITE}始めたては他がおすすめ", 10, 70, 20 ) - - // エデンサーバーへ入場する際に警告を行う - // TODO: エデンサーバーの不具合が解消されたら削除すること - if (SeichiAssist.seichiAssistConfig.getServerNum == 2) { - player.sendMessage( - Array( - s"${RED}${BOLD}${UNDERLINE}【ご注意ください】${RESET}", - s"${YELLOW}${BOLD}エデンサーバーは現在、管理者の意図しないタイミングでシャットダウン(いわゆる「鯖落ち」)が起こることがあります。", - s"${YELLOW}${BOLD}もし鯖落ちによりアイテムの消失等が発生しても、補償はできかねます。", - s"${YELLOW}${BOLD}当サーバーは以上の内容をご理解の上ご利用ください。", - s"${YELLOW}${BOLD}不安な場合はアルカディアサーバーやヴァルハラサーバーのご利用をおすすめいたします。", - s"${YELLOW}${BOLD}ご迷惑をおかけいたしまして申し訳ございません。。" - ) - ) - } } // プレイヤーがワールドを移動したとき @@ -249,10 +242,10 @@ class PlayerJoinListener extends Listener { // coreprotectを切る // inspectマップにtrueで登録されている場合 - if (Config.inspecting.getOrDefault(p.getName, false)) { + if (ConfigHandler.inspecting.getOrDefault(p.getName, false)) { // falseに変更する p.sendMessage("§3CoreProtect §f- Inspector now disabled.") - Config.inspecting.put(p.getName, false) + ConfigHandler.inspecting.put(p.getName, false) } // アサルトスキルを切る 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 5f7cb471e8..294ad05168 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/SpawnRegionProjectileInterceptor.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/SpawnRegionProjectileInterceptor.scala @@ -1,6 +1,6 @@ package com.github.unchama.seichiassist.listener -import com.github.unchama.util.external.WorldGuardWrapper.getRegions +import com.github.unchama.util.external.WorldGuardWrapper.getRegion import org.bukkit.Material._ import org.bukkit.event.block.Action import org.bukkit.event.player.PlayerInteractEvent @@ -19,10 +19,10 @@ object SpawnRegionProjectileInterceptor extends Listener { BOW, EGG, ENDER_PEARL, - EXP_BOTTLE, - EYE_OF_ENDER, + EXPERIENCE_BOTTLE, + ENDER_EYE, LINGERING_POTION, - SNOW_BALL, + SNOWBALL, SPLASH_POTION ) @@ -34,7 +34,7 @@ object SpawnRegionProjectileInterceptor extends Listener { val isRightClickEvent = action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK val isInSpawnRegion = - getRegions(player.getLocation).map(_.getId).exists(spawnRegionNames.contains) + getRegion(player.getLocation).map(_.getId).exists(spawnRegionNames.contains) // Projectileを持った状態で右クリックし、playerがいる保護がspawn保護の中であった場合はイベントをキャンセルする if (hasProjectile && isRightClickEvent && isInSpawnRegion) { diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/WorldRegenListener.java b/src/main/scala/com/github/unchama/seichiassist/listener/WorldRegenListener.java index 93d27d1800..06e201cbd9 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/WorldRegenListener.java +++ b/src/main/scala/com/github/unchama/seichiassist/listener/WorldRegenListener.java @@ -2,15 +2,15 @@ import com.github.unchama.seichiassist.Config; import com.github.unchama.seichiassist.SeichiAssist; -import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitWorld; +import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldguard.WorldGuard; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import com.wimbli.WorldBorder.CoordXZ; @@ -20,10 +20,14 @@ import io.monchi.regenworld.event.RegenWorldEvent; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.World; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; +import java.util.Collection; +import java.util.Objects; + /** * @author Mon_chi */ @@ -33,8 +37,6 @@ public class WorldRegenListener implements Listener { private final int roadLength; private final int spaceHeight; private final int worldSize; - private final BaseBlock roadBlock; - private final BaseBlock spaceBlock; private final WorldEdit worldEdit; private final WorldGuardPlugin worldGuard; @@ -46,8 +48,6 @@ public WorldRegenListener() { this.roadLength = config.getRoadLength(); this.spaceHeight = config.getSpaceHeight(); this.worldSize = config.getWorldSize(); - this.roadBlock = new BaseBlock(config.getRoadBlockID(), config.getRoadBlockDamage()); - this.spaceBlock = new BaseBlock(0); this.worldEdit = WorldEdit.getInstance(); this.worldGuard = WorldGuardPlugin.inst(); @@ -73,20 +73,26 @@ public void onWorldRegen(RegenWorldEvent event) { com.wimbli.WorldBorder.Config.fillTask.setTaskID(task); } - RegionManager regionManager = worldGuard.getRegionManager(world); - regionManager.getRegions().keySet().stream() - .filter(region -> !region.equalsIgnoreCase("__global__")) - .forEach(regionManager::removeRegion); + Collection regions = Objects.requireNonNull(WorldGuard.getInstance().getPlatform() + .getRegionContainer() + .get(BukkitAdapter.adapt(world))) + .getRegions() + .values(); + + regions.forEach(region -> { + Objects.requireNonNull(WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world))).removeRegion(region.getId()); + }); + EditSession session = worldEdit.getEditSessionFactory().getEditSession(bukkitWorld, 99999999); try { // spawnの地形造成 - setupRoadWithWorldGuard(session, world, "spawn", new BlockVector(0, roadY, 0), new BlockVector(15, roadY, 15)); + setupRoadWithWorldGuard(session, world, "spawn", BlockVector3.at(0, roadY, 0), BlockVector3.at(15, roadY, 15)); // 東西南北へ続くroadの地形造成 - setupRoad(session, world, new BlockVector(16, roadY, 0), new BlockVector(15 + 16 * roadLength, roadY, 15)); - setupRoad(session, world, new BlockVector(-1, roadY, 0), new BlockVector(-(16 * roadLength), roadY, 15)); - setupRoad(session, world, new BlockVector(0, roadY, 16), new BlockVector(15, roadY, 15 + 16 * roadLength)); - setupRoad(session, world, new BlockVector(0, roadY, -1), new BlockVector(15, roadY, -(16 * roadLength))); + setupRoad(session, world, BlockVector3.at(16, roadY, 0), BlockVector3.at(15 + 16 * roadLength, roadY, 15)); + setupRoad(session, world, BlockVector3.at(-1, roadY, 0), BlockVector3.at(-(16 * roadLength), roadY, 15)); + setupRoad(session, world, BlockVector3.at(0, roadY, 16), BlockVector3.at(15, roadY, 15 + 16 * roadLength)); + setupRoad(session, world, BlockVector3.at(0, roadY, -1), BlockVector3.at(15, roadY, -(16 * roadLength))); } catch (MaxChangedBlocksException e) { e.printStackTrace(); } @@ -95,33 +101,24 @@ public void onWorldRegen(RegenWorldEvent event) { /** * 地形造成を行う * - * @param session - * @param world - * @param pos1 - * @param pos2 * @throws MaxChangedBlocksException */ - private void setupRoad(EditSession session, World world, BlockVector pos1, BlockVector pos2) throws MaxChangedBlocksException { + private void setupRoad(EditSession session, World world, BlockVector3 pos1, BlockVector3 pos2) throws MaxChangedBlocksException { BukkitWorld bukkitWorld = new BukkitWorld(world); - session.setBlocks(new CuboidRegion(bukkitWorld, pos1, pos2), roadBlock); - session.setBlocks(new CuboidRegion(bukkitWorld, pos1.add(0, 1, 0), pos2.add(0, 1 + spaceHeight, 0)), spaceBlock); + session.setBlocks(new CuboidRegion(bukkitWorld, pos1, pos2), BukkitAdapter.adapt(Material.BEDROCK.createBlockData())); + session.setBlocks(new CuboidRegion(bukkitWorld, pos1.add(0, 1, 0), pos2.add(0, 1 + spaceHeight, 0)), BukkitAdapter.adapt(Material.AIR.createBlockData())); } /** * 地形造成と、地形造成を行った場所にWorldGuardRegionも設定する * - * @param session - * @param world - * @param protName - * @param pos1 - * @param pos2 * @throws MaxChangedBlocksException */ - private void setupRoadWithWorldGuard(EditSession session, World world, String protName, BlockVector pos1, BlockVector pos2) throws MaxChangedBlocksException { + private void setupRoadWithWorldGuard(EditSession session, World world, String protName, BlockVector3 pos1, BlockVector3 pos2) throws MaxChangedBlocksException { BukkitWorld bukkitWorld = new BukkitWorld(world); - session.setBlocks(new CuboidRegion(bukkitWorld, pos1, pos2), roadBlock); - session.setBlocks(new CuboidRegion(bukkitWorld, pos1.add(0, 1, 0), pos2.add(0, 1 + spaceHeight, 0)), spaceBlock); - ProtectedRegion region = new ProtectedCuboidRegion(protName, new BlockVector(pos1.getX(), 0, pos1.getZ()), new BlockVector(pos2.getX(), 255, pos2.getZ())); - WorldGuardPlugin.inst().getRegionManager(world).addRegion(region); + session.setBlocks(new CuboidRegion(bukkitWorld, pos1, pos2), BukkitAdapter.adapt(Material.BEDROCK.createBlockData())); + session.setBlocks(new CuboidRegion(bukkitWorld, pos1.add(0, 1, 0), pos2.add(0, 1 + spaceHeight, 0)), BukkitAdapter.adapt(Material.AIR.createBlockData())); + ProtectedRegion region = new ProtectedCuboidRegion(protName, BlockVector3.at(pos1.getX(), 0, pos1.getZ()), BlockVector3.at(pos2.getX(), 255, pos2.getZ())); + WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world)).addRegion(region); } } diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/Y5DoubleSlabCanceller.scala b/src/main/scala/com/github/unchama/seichiassist/listener/YMinus59DoubleSlabCanceller.scala similarity index 60% rename from src/main/scala/com/github/unchama/seichiassist/listener/Y5DoubleSlabCanceller.scala rename to src/main/scala/com/github/unchama/seichiassist/listener/YMinus59DoubleSlabCanceller.scala index fd84ecbdfa..f2d85be0e5 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/Y5DoubleSlabCanceller.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/YMinus59DoubleSlabCanceller.scala @@ -1,11 +1,11 @@ package com.github.unchama.seichiassist.listener import com.github.unchama.seichiassist.ManagedWorld._ -import org.bukkit.Material +import org.bukkit.block.data.`type`.Slab import org.bukkit.event.block.BlockPlaceEvent import org.bukkit.event.{EventHandler, Listener} -object Y5DoubleSlabCanceller extends Listener { +object YMinus59DoubleSlabCanceller extends Listener { /** * 以下の条件をすべて満たすときにブロックの設置をキャンセルし、その旨を示すメッセージを送出する @@ -13,22 +13,22 @@ object Y5DoubleSlabCanceller extends Listener { * - プレイヤーが手に持っているブロックが焼き石のハーフブロックである * - 対象座標の改変後ブロックが焼き石の二段重ねハーフブロックである * - 対象座標が整地ワールドを指している - * - 対象ブロックのY座標が5である + * - 対象ブロックのY座標が-59である * @see * https://github.com/GiganticMinecraft/SeichiAssist/issues/775 * @param event * 対象イベント */ @EventHandler - def onPlaceDoubleSlabAtY5(event: BlockPlaceEvent): Unit = { + def onPlaceDoubleSlabAtYMinus59(event: BlockPlaceEvent): Unit = { if (!event.canBuild) return - if (event.getItemInHand.getType ne Material.STEP) return - if (event.getItemInHand.getDurability != 0) return - if (event.getBlockPlaced.getType ne Material.DOUBLE_STEP) return - if (event.getBlockPlaced.getData != 0) return if (!event.getBlockPlaced.getWorld.isSeichi) return - if (event.getBlockPlaced.getY != 5) return - event.setCancelled(true) - event.getPlayer.sendMessage("Y5のハーフブロックを二段重ねにすることはできません。") + if (event.getBlockPlaced.getY != -59) return + event.getBlockPlaced.getBlockData match { + case slab: Slab if slab.getType == Slab.Type.DOUBLE => + event.setCancelled(true) + event.getPlayer.sendMessage("Y-59のハーフブロックを二段重ねにすることはできません。") + case _ => + } } } 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 4122f4b310..bcb60755ee 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 @@ -57,7 +57,11 @@ object OnClickTitleMenu { if (topInventory.row != 4) { return } - val current = event.getCurrentItem + val current = event + .getCurrentItem + .ifNull( + return + ) val player = he.asInstanceOf[Player] val pd = SeichiAssist.playermap(player.getUniqueId) @@ -68,191 +72,169 @@ object OnClickTitleMenu { } val mat = current.getType - val isSkull = mat == Material.SKULL_ITEM - topInventory.getTitle match { + val isSkull = mat == Material.PLAYER_HEAD + view.getTitle match { case MenuType.HEAD.invName => event.setCancelled(true) - mat match { - case Material.WATER_BUCKET => - clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) - - val id = current.getItemMeta.getDisplayName.toInt - val length = Nicknames - .getCombinedNicknameFor(id, pd.settings.nickname.id2, pd.settings.nickname.id3) - .getOrElse("") - .length - if (length > MAX_LENGTH) { - player.sendMessage(LENGTH_LIMIT_EXCEEDED) - } else { - pd.updateNickname(id1 = id) - player.sendMessage( - "前パーツ「" + Nicknames - .getHeadPartFor(pd.settings.nickname.id1) - .getOrElse("") + "」をセットしました。" - ) - } - - case Material.GRASS => - // unselect - clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) - pd.updateNickname(id1 = 0) - player.sendMessage("前パーツの選択を解除しました。") - - case Material.BARRIER => - clickedSound(player, Sound.BLOCK_FENCE_GATE_OPEN, 0.1f) - ioCanOpenNicknameMenu.open(NickNameMenu).apply(player).unsafeRunAsyncAndForget() - - case _ if isSkull && isApplicableAsNextPageButton(current) => - // 次ページ - 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 + if (mat == Material.WATER_BUCKET) { + clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) + + val id = current.getItemMeta.getDisplayName.toInt + val length = Nicknames + .getCombinedNicknameFor(id, pd.settings.nickname.id2, pd.settings.nickname.id3) + .getOrElse("") + .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.openInventory(MenuInventoryData.computeHeadPartCustomMenu(player)) - - case _ => + } + } else if (mat == Material.GRASS) { + // unselect + clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) + pd.updateNickname(id1 = 0) + player.sendMessage("前パーツの選択を解除しました。") + } else if (mat == Material.BARRIER) { + clickedSound(player, Sound.BLOCK_FENCE_GATE_OPEN, 0.1f) + ioCanOpenNicknameMenu.open(NickNameMenu).apply(player).unsafeRunAsyncAndForget() + } else if (isSkull && isApplicableAsNextPageButton(current)) { + // 次ページ + 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 + ) + player.openInventory(MenuInventoryData.computeHeadPartCustomMenu(player)) } case MenuType.MIDDLE.invName => event.setCancelled(true) - mat match { - case Material.MILK_BUCKET => - clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) - - val id = current.getItemMeta.getDisplayName.toInt - val length = Nicknames - .getCombinedNicknameFor(pd.settings.nickname.id1, id, pd.settings.nickname.id3) - .getOrElse("") - .length - if (length > MAX_LENGTH) { - player.sendMessage(LENGTH_LIMIT_EXCEEDED) - } else { - pd.updateNickname(id2 = id) - player.sendMessage( - "中パーツ「" + Nicknames - .getMiddlePartFor(pd.settings.nickname.id2) - .getOrElse("") + "」をセットしました。" - ) - } - - case Material.GRASS => - clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) - pd.updateNickname(id2 = 0) - player.sendMessage("中パーツの選択を解除しました。") - case Material.BARRIER => - clickedSound(player, Sound.BLOCK_FENCE_GATE_OPEN, 0.1f) - ioCanOpenNicknameMenu.open(NickNameMenu).apply(player).unsafeRunAsyncAndForget() - - case _ if isSkull && isApplicableAsNextPageButton(current) => - 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 + if (mat == Material.MILK_BUCKET) { + clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) + + val id = current.getItemMeta.getDisplayName.toInt + val length = Nicknames + .getCombinedNicknameFor(pd.settings.nickname.id1, id, pd.settings.nickname.id3) + .getOrElse("") + .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.openInventory(MenuInventoryData.computeMiddlePartCustomMenu(player)) - - case _ => + } + } else if (mat == Material.GRASS) { + clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) + pd.updateNickname(id2 = 0) + player.sendMessage("中パーツの選択を解除しました。") + } else if (mat == Material.BARRIER) { + clickedSound(player, Sound.BLOCK_FENCE_GATE_OPEN, 0.1f) + ioCanOpenNicknameMenu.open(NickNameMenu).apply(player).unsafeRunAsyncAndForget() + } else if (isSkull && isApplicableAsNextPageButton(current)) { + 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 + ) + player.openInventory(MenuInventoryData.computeMiddlePartCustomMenu(player)) } case MenuType.TAIL.invName => event.setCancelled(true) - mat match { - case Material.LAVA_BUCKET => - clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) - - val id = current.getItemMeta.getDisplayName.toInt - val length = Nicknames - .getCombinedNicknameFor(pd.settings.nickname.id1, pd.settings.nickname.id2, id) - .getOrElse("") - .length - if (length > MAX_LENGTH) { - player.sendMessage(LENGTH_LIMIT_EXCEEDED) - } else { - pd.updateNickname(id3 = id) - player.sendMessage( - "後パーツ「" + Nicknames - .getTailPartFor(pd.settings.nickname.id3) - .getOrElse("") + "」をセットしました。" - ) - } - case Material.GRASS => - clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) - pd.updateNickname(id3 = 0) - player.sendMessage("後パーツの選択を解除しました。") - - case Material.BARRIER => - clickedSound(player, Sound.BLOCK_FENCE_GATE_OPEN, 0.1f) - ioCanOpenNicknameMenu.open(NickNameMenu).apply(player).unsafeRunAsyncAndForget() - - case _ if isSkull && isApplicableAsNextPageButton(current) => - 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 + if (mat == Material.LAVA_BUCKET) { + clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) + + val id = current.getItemMeta.getDisplayName.toInt + val length = Nicknames + .getCombinedNicknameFor(pd.settings.nickname.id1, pd.settings.nickname.id2, id) + .getOrElse("") + .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.openInventory(MenuInventoryData.computeTailPartCustomMenu(player)) - - case _ => + } + } else if (mat == Material.GRASS) { + clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) + pd.updateNickname(id3 = 0) + player.sendMessage("後パーツの選択を解除しました。") + } else if (mat == Material.BARRIER) { + clickedSound(player, Sound.BLOCK_FENCE_GATE_OPEN, 0.1f) + ioCanOpenNicknameMenu.open(NickNameMenu).apply(player).unsafeRunAsyncAndForget() + } else if (isSkull && isApplicableAsNextPageButton(current)) { + 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 + ) + player.openInventory(MenuInventoryData.computeTailPartCustomMenu(player)) } case MenuType.SHOP.invName => event.setCancelled(true) - mat match { + if (mat == Material.EMERALD_ORE) { // 実績ポイント最新化 - case Material.EMERALD_ORE => - clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) - pd.recalculateAchievePoint() - pd.samepageflag = true - player.openInventory(MenuInventoryData.computePartsShopMenu(player)) - + clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) + pd.recalculateAchievePoint() + pd.samepageflag = true + player.openInventory(MenuInventoryData.computePartsShopMenu(player)) + } else if (mat == Material.BEDROCK) { // 購入処理 - case Material.BEDROCK => - clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) - - 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) } - - if (pd.achievePoint.left >= required) { - pd.TitleFlags.addOne(num) - pd.consumeAchievePoint(required) - player.sendMessage("パーツ「" + getPart(num).getOrElse("") + "」を購入しました。") - pd.samepageflag = true - player.openInventory(MenuInventoryData.computePartsShopMenu(player)) - } else { - player.sendMessage("実績ポイントが不足しています。") - } - - case Material.BARRIER => - clickedSound(player, Sound.BLOCK_FENCE_GATE_OPEN, 0.1f) - ioCanOpenNicknameMenu.open(NickNameMenu).apply(player).unsafeRunAsyncAndForget() - - case _ if isSkull && isApplicableAsNextPageButton(current) => - 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 - ) + clickedSound(player, Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1.0f) + + 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) } + + if (pd.achievePoint.left >= required) { + pd.TitleFlags.addOne(num) + pd.consumeAchievePoint(required) + player.sendMessage("パーツ「" + getPart(num).getOrElse("") + "」を購入しました。") + pd.samepageflag = true player.openInventory(MenuInventoryData.computePartsShopMenu(player)) - - case _ => + } else { + player.sendMessage("実績ポイントが不足しています。") + } + } else if (mat == Material.BARRIER) { + clickedSound(player, Sound.BLOCK_FENCE_GATE_OPEN, 0.1f) + ioCanOpenNicknameMenu.open(NickNameMenu).apply(player).unsafeRunAsyncAndForget() + } else if (isSkull && isApplicableAsNextPageButton(current)) { + 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 + ) + player.openInventory(MenuInventoryData.computePartsShopMenu(player)) } // それ以外のインベントリの名前だった場合何もしない! 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 f655e40618..f6d55c8841 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/BuildMainMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/BuildMainMenu.scala @@ -21,6 +21,7 @@ import com.github.unchama.seichiassist.subsystems.managedfly.domain.{ NotFlying, RemainingFlyDuration } +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.player.PlayerEffects.{ closeInventoryEffect, @@ -39,7 +40,8 @@ import org.bukkit.inventory.ItemFlag import org.bukkit.{Material, Sound} private case class ButtonComputations(player: Player)( - implicit ioOnMainThread: OnMinecraftServerThread[IO] + implicit ioOnMainThread: OnMinecraftServerThread[IO], + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) { import BuildMainMenu._ @@ -207,7 +209,7 @@ private case class ButtonComputations(player: Player)( IO { val openerData = BuildAssist.instance.temporaryData(getUniqueId) - val iconItemStack = new IconItemStackBuilder(Material.WOOD) + val iconItemStack = new IconItemStackBuilder(Material.OAK_PLANKS) .title(s"$YELLOW${EMPHASIZE}直列設置: ${BuildAssist.line_up_str(openerData.line_up_flg)}") .lore( s"$RESET${GRAY}オフハンドに木の棒、メインハンドに設置したいブロックを持って", @@ -279,7 +281,7 @@ private case class ButtonComputations(player: Player)( def computeButtonToOpenMenuToCraftItemsWhereMineStack( implicit canOpenMassCraftMenu: CanOpen[IO, MineStackMassCraftMenu] ): IO[Button] = IO { - val iconItemStackBuilder = new IconItemStackBuilder(Material.WORKBENCH) + val iconItemStackBuilder = new IconItemStackBuilder(Material.CRAFTING_TABLE) .title(s"$YELLOW${EMPHASIZE}MineStackブロック一括クラフト画面へ") .lore(s"$RESET$DARK_RED${UNDERLINE}クリックで移動") .build() @@ -398,7 +400,8 @@ object BuildMainMenu extends Menu { implicit val flyApi: ManagedFlyApi[SyncIO, Player], val ioOnMainThread: OnMinecraftServerThread[IO], val canOpenBlockPlacementSkillMenu: CanOpen[IO, BlockPlacementSkillMenu.type], - val canOpenMassCraftMenu: CanOpen[IO, MineStackMassCraftMenu] + val canOpenMassCraftMenu: CanOpen[IO, MineStackMassCraftMenu], + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) val EMPHASIZE = s"$UNDERLINE$BOLD" 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 fddb7593ee..d265a6f2b1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/CommonButtons.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/CommonButtons.scala @@ -9,6 +9,8 @@ import com.github.unchama.seichiassist.SkullOwners import com.github.unchama.seichiassist.effects.player.CommonSoundEffects import com.github.unchama.seichiassist.menus.ColorScheme.{clickResultDescription, navigation} import com.github.unchama.seichiassist.menus.stickmenu.{FirstPage, StickMenu} +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI +import org.bukkit.entity.Player /** * メニューUIに頻繁に現れるような[Button]を生成する、または定数として持っているオブジェクト. @@ -33,7 +35,10 @@ object CommonButtons { ) ) - def openStickMenu(implicit canOpenStickMenu: CanOpen[IO, FirstPage.type]): Button = { + def openStickMenu( + implicit canOpenStickMenu: CanOpen[IO, FirstPage.type], + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] + ): Button = { transferButton( new SkullItemStackBuilder(SkullOwners.MHF_ArrowLeft), "木の棒メニューホームへ", 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 e8d1a01e54..126952108e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/RegionMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/RegionMenu.scala @@ -15,7 +15,8 @@ import com.github.unchama.seichiassist.menus.gridregion.GridRegionMenu import com.github.unchama.seichiassist.subsystems.gridregion.GridRegionAPI import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.player.{CommandEffect, FocusedSoundEffect} -import com.github.unchama.util.external.ExternalPlugins +import com.sk89q.worldedit.WorldEdit +import com.sk89q.worldedit.bukkit.BukkitAdapter import org.bukkit.ChatColor._ import org.bukkit.entity.Player import org.bukkit.event.inventory.InventoryType @@ -69,34 +70,34 @@ object RegionMenu extends Menu { val computeButtonToClaimRegion: IO[Button] = for { regionCount <- gridRegionAPI.regionCount(player) } yield { - val selection = ExternalPlugins.getWorldEdit.getSelection(player) - + val session = WorldEdit.getInstance().getSessionManager.get(BukkitAdapter.adapt(player)) + val world = BukkitAdapter.adapt(player.getWorld) + val isSelected = session.isSelectionDefined(world) val playerHasPermission = player.hasPermission("worldguard.region.claim") - val isSelectionNull = selection == null + + val selectionOpt = Option.when(isSelected)(session.getSelection(world)) val selectionHasEnoughSpace = - if (!isSelectionNull) - selection.getLength >= 10 && selection.getWidth >= 10 - else false + selectionOpt.exists(selection => selection.getLength >= 10 && selection.getWidth >= 10) - val canMakeRegion = playerHasPermission && !isSelectionNull && selectionHasEnoughSpace + val canMakeRegion = playerHasPermission && !isSelected && selectionHasEnoughSpace val iconItemStack = { - val lore = { - if (!playerHasPermission) + if (!playerHasPermission) { Seq(s"${RED}このワールドでは", s"${RED}保護を作成できません") - else if (isSelectionNull) + } else if (!isSelected) { Seq(s"${RED}範囲指定されていません", s"${RED}先に木の斧で2か所クリックしてネ") - else if (!selectionHasEnoughSpace) + } else if (!selectionHasEnoughSpace) { Seq(s"${RED}選択された範囲が狭すぎます", s"${RED}一辺当たり最低10ブロック以上にしてネ") - else + } else { Seq(s"$DARK_GREEN${UNDERLINE}範囲指定されています", s"$DARK_GREEN${UNDERLINE}クリックすると保護を作成します") + } } ++ { if (playerHasPermission) Seq( s"${GRAY}Y座標は自動で全範囲保護されます", s"${YELLOW}A new region has been claimed", - s"${YELLOW}named '${getName}_$regionCount'.", + s"${YELLOW}named '${getName}_${regionCount.value}'.", s"${GRAY}と出れば保護設定完了です", s"${RED}赤色で別の英文が出た場合", s"${GRAY}保護の設定に失敗しています", @@ -108,7 +109,7 @@ object RegionMenu extends Menu { } import scala.util.chaining._ - new IconItemStackBuilder(Material.GOLD_AXE) + new IconItemStackBuilder(Material.GOLDEN_AXE) .tap { b => if (canMakeRegion) b.enchanted() } .title(s"$YELLOW$UNDERLINE${BOLD}保護の作成") .lore(lore.toList) @@ -120,7 +121,7 @@ object RegionMenu extends Menu { action.FilteredButtonEffect(ClickEventFilter.LEFT_CLICK)(_ => if (!playerHasPermission) MessageEffect(s"${RED}このワールドでは保護を作成できません") - else if (isSelectionNull) + else if (!isSelected) SequentialEffect( MessageEffect(s"${RED}先に木の斧で範囲を指定してからこのボタンを押してください"), FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 0.5f) @@ -133,7 +134,7 @@ object RegionMenu extends Menu { else SequentialEffect( CommandEffect("/expand vert"), - CommandEffect(s"rg claim ${player.getName}_$regionCount"), + CommandEffect(s"rg claim ${player.getName}_${regionCount.value}"), gridRegionAPI.createAndClaimRegionSelectedOnWorldGuard, CommandEffect("/sel"), FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) @@ -153,7 +154,7 @@ object RegionMenu extends Menu { s"$GREEN④メニューの${YELLOW}金の斧${GREEN}をクリック" ) - val iconItemStack = new IconItemStackBuilder(Material.WOOD_AXE) + val iconItemStack = new IconItemStackBuilder(Material.WOODEN_AXE) .title(s"$YELLOW$UNDERLINE${BOLD}保護設定用の木の斧を召喚") .lore( wandUsage ++ List( 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 f037604ab3..f338920141 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/ServerSwitchMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/ServerSwitchMenu.scala @@ -7,6 +7,7 @@ import com.github.unchama.menuinventory.slot.button.Button import com.github.unchama.menuinventory.slot.button.action.LeftClickButtonEffect import com.github.unchama.menuinventory.{ChestSlotRef, Menu, MenuFrame, MenuSlotLayout} import com.github.unchama.seichiassist.menus.stickmenu.FirstPage +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.targetedeffect._ import com.github.unchama.targetedeffect.player.PlayerEffects._ import org.bukkit.ChatColor._ @@ -53,7 +54,7 @@ object ServerSwitchMenu extends Menu { ) case object EDEN - extends Server(s"$YELLOW${BOLD}エデン", "s2", ChestSlotRef(0, 1), Material.DIAMOND_SPADE) + extends Server(s"$YELLOW${BOLD}エデン", "s2", ChestSlotRef(0, 1), Material.DIAMOND_SHOVEL) case object VALHALLA extends Server(s"$YELLOW${BOLD}ヴァルハラ", "s3", ChestSlotRef(0, 2), Material.DIAMOND_AXE) @@ -71,7 +72,10 @@ object ServerSwitchMenu extends Menu { import com.github.unchama.menuinventory.syntax._ import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.onMainThread - class Environment(implicit val ioCanOpenStickMenu: IO CanOpen FirstPage.type) + class Environment( + implicit val ioCanOpenStickMenu: IO CanOpen FirstPage.type, + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] + ) /** * メニューのサイズとタイトルに関する情報 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 b31e314295..d3180f98b2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/TopLevelRouter.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/TopLevelRouter.scala @@ -15,7 +15,7 @@ import com.github.unchama.seichiassist.menus.home.{ConfirmationMenuEnvironment, import com.github.unchama.seichiassist.menus.minestack.{ CategorizedMineStackMenu, MineStackMainMenu, - MineStackSelectItemColorMenu + MineStackSelectItemKindMenu } import com.github.unchama.seichiassist.menus.nicknames.NickNameMenu import com.github.unchama.seichiassist.menus.ranking.{RankingMenu, RankingRootMenu} @@ -31,6 +31,7 @@ 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.breakskilltargetconfig.BreakSkillTargetConfigAPI +import com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.BreakSuppressionPreferenceAPI import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountData import com.github.unchama.seichiassist.subsystems.discordnotification.DiscordNotificationAPI import com.github.unchama.seichiassist.subsystems.donate.DonatePremiumPointAPI @@ -48,6 +49,7 @@ import com.github.unchama.seichiassist.subsystems.gridregion.GridRegionAPI import com.github.unchama.seichiassist.subsystems.home.HomeReadAPI import com.github.unchama.seichiassist.subsystems.mana.ManaApi import com.github.unchama.seichiassist.subsystems.minestack.MineStackAPI +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.ranking.api.AssortedRankingApi import com.github.unchama.seichiassist.subsystems.ranking.domain.values.{LoginTime, VoteCount} import com.github.unchama.seichiassist.subsystems.sharedinventory.SharedInventoryAPI @@ -102,7 +104,9 @@ object TopLevelRouter { consumeGachaTicketAPI: ConsumeGachaTicketAPI[IO, Player], fairySpeechAPI: FairySpeechAPI[IO, Player], gridRegionAPI: GridRegionAPI[IO, Player, Location], - breakSkillTargetConfigAPI: BreakSkillTargetConfigAPI[IO, Player] + breakSkillTargetConfigAPI: BreakSkillTargetConfigAPI[IO, Player], + breakSuppressionPreferenceAPI: BreakSuppressionPreferenceAPI[IO, Player], + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ): TopLevelRouter[IO] = new TopLevelRouter[IO] { import assortedRankingApi._ @@ -134,9 +138,8 @@ object TopLevelRouter { new AchievementGroupMenu.Environment implicit lazy val passiveSkillMenuEnv: PassiveSkillMenu.Environment = new PassiveSkillMenu.Environment - implicit lazy val mineStackSelectItemColorMenuEnv - : MineStackSelectItemColorMenu.Environment = - new MineStackSelectItemColorMenu.Environment + implicit lazy val mineStackSelectItemColorMenuEnv: MineStackSelectItemKindMenu.Environment = + new MineStackSelectItemKindMenu.Environment implicit lazy val seichiRankingMenuEnv: RankingMenu[SeichiAmountData]#Environment = new RankingMenu.Environment @@ -163,7 +166,7 @@ object TopLevelRouter { implicit lazy val ioCanOpenNickNameMenu: IO CanOpen NickNameMenu.type = _.open - implicit lazy val ioCanOpenSelectItemColorMenu: IO CanOpen MineStackSelectItemColorMenu = + implicit lazy val ioCanOpenSelectItemColorMenu: IO CanOpen MineStackSelectItemKindMenu = _.open implicit lazy val ioCanOpenAchievementGroupMenu: IO CanOpen AchievementGroupMenu = _.open implicit lazy val ioCanOpenHomeConfirmationMenu diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/VoteMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/VoteMenu.scala index 14b414cf4c..90897b0a1c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/VoteMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/VoteMenu.scala @@ -12,6 +12,7 @@ import com.github.unchama.menuinventory.{ChestSlotRef, Menu, MenuFrame, MenuSlot import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.onMainThread import com.github.unchama.seichiassist.menus.stickmenu.FirstPage +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.vote.VoteAPI import com.github.unchama.seichiassist.subsystems.vote.subsystems.fairy.FairyAPI import com.github.unchama.seichiassist.subsystems.vote.subsystems.fairy.domain.property.FairyAppleConsumeStrategy.{ @@ -46,7 +47,8 @@ object VoteMenu extends Menu { implicit val voteAPI: VoteAPI[IO, Player], val fairyAPI: FairyAPI[IO, SyncIO, Player], val ioCanOpenFirstPage: IO CanOpen FirstPage.type, - val fairySpeechAPI: FairySpeechAPI[IO, Player] + val fairySpeechAPI: FairySpeechAPI[IO, Player], + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) /** @@ -149,7 +151,7 @@ object VoteMenu extends Menu { } val showVoteURLButton: Button = Button( - new IconItemStackBuilder(Material.BOOK_AND_QUILL) + new IconItemStackBuilder(Material.WRITABLE_BOOK) .title(s"$YELLOW$UNDERLINE${BOLD}投票ページにアクセス") .lore( List( @@ -181,7 +183,7 @@ object VoteMenu extends Menu { fairySummonCost <- fairyAPI.fairySummonCost(player) } yield { Button( - new IconItemStackBuilder(Material.WATCH) + new IconItemStackBuilder(Material.CLOCK) .title(s"$AQUA$UNDERLINE${BOLD}マナ妖精 時間設定") .lore( List( @@ -288,7 +290,7 @@ object VoteMenu extends Menu { LeftClickButtonEffect { SequentialEffect( FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f), - DeferredEffect(IO(fairySpeechAPI.togglePlaySoundOnSpeech)) + fairySpeechAPI.togglePlaySoundOnSpeech ) } ) 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 587499d69b..fedf88062a 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 @@ -20,6 +20,7 @@ import com.github.unchama.seichiassist.menus.achievement.AchievementCategoryMenu } import com.github.unchama.seichiassist.menus.achievement.group.AchievementGroupMenu import com.github.unchama.seichiassist.menus.{ColorScheme, CommonButtons} +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import org.bukkit.ChatColor._ import org.bukkit.Material import org.bukkit.entity.Player @@ -37,12 +38,12 @@ 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_STAIRS)) case Login => Map( ChestSlotRef(1, 1) -> (PlayTime, Material.COMPASS), ChestSlotRef(1, 3) -> (TotalLogins, Material.BOOK), - ChestSlotRef(1, 5) -> (ConsecutiveLogins, Material.BOOK_AND_QUILL), + ChestSlotRef(1, 5) -> (ConsecutiveLogins, Material.WRITABLE_BOOK), ChestSlotRef(1, 7) -> (Anniversaries, Material.NETHER_STAR) ) case Challenges => @@ -53,8 +54,8 @@ object AchievementCategoryMenu { case Specials => Map( ChestSlotRef(1, 2) -> (OfficialEvent, Material.BLAZE_POWDER), - ChestSlotRef(1, 4) -> (VoteCounts, Material.YELLOW_FLOWER), - ChestSlotRef(1, 6) -> (Secrets, Material.DIAMOND_BARDING) + ChestSlotRef(1, 4) -> (VoteCounts, Material.DANDELION), + ChestSlotRef(1, 6) -> (Secrets, Material.DIAMOND_HORSE_ARMOR) ) } @@ -80,7 +81,8 @@ object AchievementCategoryMenu { class Environment( implicit val ioCanOpenAchievementMainMenu: IO CanOpen AchievementMenu.type, - val ioCanOpenAchievementGroupMenu: IO CanOpen AchievementGroupMenu + val ioCanOpenAchievementGroupMenu: IO CanOpen AchievementGroupMenu, + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) } 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 fe318872ad..332a2c70bd 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 @@ -14,6 +14,7 @@ import com.github.unchama.seichiassist.effects.player.CommonSoundEffects import com.github.unchama.seichiassist.menus.nicknames.NickNameMenu import com.github.unchama.seichiassist.menus.stickmenu.FirstPage import com.github.unchama.seichiassist.menus.{ColorScheme, CommonButtons} +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.vote.VoteAPI import com.github.unchama.targetedeffect.player.FocusedSoundEffect import com.github.unchama.targetedeffect.{SequentialEffect, TargetedEffect} @@ -31,7 +32,8 @@ object AchievementMenu extends Menu { val ioCanOpenCategoryMenu: IO CanOpen AchievementCategoryMenu, val ioOnMainThread: OnMinecraftServerThread[IO], val voteAPI: VoteAPI[IO, Player], - val ioCanOpenNickNameMenu: IO CanOpen NickNameMenu.type + val ioCanOpenNickNameMenu: IO CanOpen NickNameMenu.type, + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) override val frame: MenuFrame = MenuFrame(4.chestRows, s"$DARK_PURPLE${BOLD}実績・二つ名システム") @@ -40,11 +42,11 @@ object AchievementMenu extends Menu { val categoryLayout: Map[Int, AchievementCategoryRepr] = Map( - ChestSlotRef(1, 1) -> (BrokenBlock, Material.GOLD_PICKAXE), + ChestSlotRef(1, 1) -> (BrokenBlock, Material.GOLDEN_PICKAXE), ChestSlotRef(1, 3) -> (Building, Material.GLASS), ChestSlotRef(1, 5) -> (Login, Material.COMPASS), ChestSlotRef(1, 7) -> (Challenges, Material.BLAZE_POWDER), - ChestSlotRef(2, 4) -> (Specials, Material.EYE_OF_ENDER) + ChestSlotRef(2, 4) -> (Specials, Material.ENDER_EYE) ) def buttonFor( @@ -88,7 +90,7 @@ object AchievementMenu extends Menu { categoryLayout.view.mapValues(category => buttonFor(category)).toMap val toggleTitleToPlayerLevelButton = Button( - new IconItemStackBuilder(Material.REDSTONE_TORCH_ON) + new IconItemStackBuilder(Material.REDSTONE_TORCH) .title(ColorScheme.navigation("整地Lvを表示")) .lore( List( 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 a9839f17b5..0d75390bfe 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 @@ -13,6 +13,7 @@ import com.github.unchama.seichiassist.achievement.hierarchy.AchievementGroup._ import com.github.unchama.seichiassist.menus.achievement.AchievementCategoryMenu import com.github.unchama.seichiassist.menus.achievement.group.AchievementGroupMenu.sequentialEntriesIn import com.github.unchama.seichiassist.menus.{ColorScheme, CommonButtons} +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.targetedeffect.TargetedEffect import org.bukkit.entity.Player @@ -20,7 +21,8 @@ object AchievementGroupMenu { class Environment( implicit val ioCanOpenGroupMenu: IO CanOpen AchievementGroupMenu, - val ioCanOpenCategoryMenu: IO CanOpen AchievementCategoryMenu + val ioCanOpenCategoryMenu: IO CanOpen AchievementCategoryMenu, + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) val sequentialEntriesIn: AchievementGroup => List[GroupMenuEntry] = CachedFunction { 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 02a6355957..535d6fc3b7 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 @@ -19,6 +19,7 @@ import com.github.unchama.seichiassist.achievement.{ NicknameMapping, SeichiAchievement } +import com.github.unchama.seichiassist.achievement.hierarchy.AchievementGroup import com.github.unchama.seichiassist.menus.ColorScheme import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.player.FocusedSoundEffect @@ -124,7 +125,13 @@ object AchievementGroupMenuButtons { .playermap(player.getUniqueId) .TitleFlags .addOne(achievement.id) - player.sendMessage(s"実績No${achievement.id}を解除しました!おめでとうございます!") + val displayGroupName = + AchievementGroup + .getGroupNameByEntryId(achievement.id) + .getOrElse("未実装") + player.sendMessage( + s"[${displayGroupName}]実績No${achievement.id}を解除しました!おめでとうございます!" + ) } else { MessageEffect(s"${RED}実績No${achievement.id}は条件を満たしていません。")(player) diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/gridregion/GridRegionMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/gridregion/GridRegionMenu.scala index ed58f828e9..630ee03d79 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/gridregion/GridRegionMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/gridregion/GridRegionMenu.scala @@ -80,7 +80,7 @@ object GridRegionMenu extends Menu { for { currentLengthChangePerClick <- gridRegionAPI.lengthChangePerClick(player) } yield { - val iconItemStack = new IconItemStackBuilder(Material.STAINED_GLASS_PANE, 1) + val iconItemStack = new IconItemStackBuilder(Material.WHITE_STAINED_GLASS_PANE) .title(s"${GREEN}拡張単位の変更") .lore( List( @@ -143,11 +143,15 @@ object GridRegionMenu extends Menu { case HorizontalAxisAlignedSubjectiveDirection.Right => "右へ" } - val stainedGlassPaneDurability = relativeDirection match { - case HorizontalAxisAlignedSubjectiveDirection.Ahead => 14 - case HorizontalAxisAlignedSubjectiveDirection.Left => 10 - case HorizontalAxisAlignedSubjectiveDirection.Behind => 13 - case HorizontalAxisAlignedSubjectiveDirection.Right => 5 + val material = relativeDirection match { + case HorizontalAxisAlignedSubjectiveDirection.Ahead => + Material.RED_STAINED_GLASS_PANE + case HorizontalAxisAlignedSubjectiveDirection.Behind => + Material.GREEN_STAINED_GLASS_PANE + case HorizontalAxisAlignedSubjectiveDirection.Left => + Material.PURPLE_STAINED_GLASS_PANE + case HorizontalAxisAlignedSubjectiveDirection.Right => + Material.YELLOW_STAINED_GLASS_PANE } def updateCurrentRegionShapeTo( @@ -169,10 +173,10 @@ object GridRegionMenu extends Menu { } Button( - new IconItemStackBuilder( - Material.STAINED_GLASS_PANE, - stainedGlassPaneDurability.toShort - ).title(s"$DARK_GREEN${relativeDirectionString}ユニット増やす/減らす").lore(lore).build(), + new IconItemStackBuilder(material) + .title(s"$DARK_GREEN${relativeDirectionString}ユニット増やす/減らす") + .lore(lore) + .build(), LeftClickButtonEffect(updateCurrentRegionShapeTo(expandedShape)), RightClickButtonEffect(updateCurrentRegionShapeTo(contractedShape)) ) @@ -194,7 +198,7 @@ object GridRegionMenu extends Menu { } val resetSettingButton: Button = { - val itemStack = new IconItemStackBuilder(Material.STAINED_GLASS_PANE, 4) + val itemStack = new IconItemStackBuilder(Material.YELLOW_STAINED_GLASS_PANE) .title(s"${RED}全設定リセット") .lore(List(s"$RED${UNDERLINE}取り扱い注意!!")) .build() @@ -225,10 +229,10 @@ object GridRegionMenu extends Menu { s"${GRAY}右方向:${showRegionShapeDimension(shape.right)}", s"${GRAY}左方向:${showRegionShapeDimension(shape.left)}", s"${GRAY}保護ユニット数:$AQUA${shape.regionUnits.count}", - s"${GRAY}保護ユニット上限値:$RED${gridRegionAPI.regionUnitLimit(worldName).limit}" + s"${GRAY}保護ユニット上限値:$RED${gridRegionAPI.regionUnitLimit(worldName).limit.count}" ) - val itemStack = new IconItemStackBuilder(Material.STAINED_GLASS_PANE, 11) + val itemStack = new IconItemStackBuilder(Material.BLUE_STAINED_GLASS_PANE) .title(s"${DARK_GREEN}設定") .lore(lore) .build() @@ -245,7 +249,7 @@ object GridRegionMenu extends Menu { canCreateRegionResult match { case RegionCreationResult.Success => Button( - new IconItemStackBuilder(Material.WOOL, 11) + new IconItemStackBuilder(Material.LIGHT_BLUE_WOOL) .title(s"${GREEN}保護作成") .lore(List(s"${DARK_GREEN}保護作成可能です", s"$RED${UNDERLINE}クリックで作成")) .build(), @@ -257,14 +261,14 @@ object GridRegionMenu extends Menu { ) case RegionCreationResult.WorldProhibitsRegionCreation => Button( - new IconItemStackBuilder(Material.WOOL, 14) + new IconItemStackBuilder(Material.RED_WOOL) .title(s"${RED}保護作成") .lore(List(s"$RED${UNDERLINE}このワールドでは保護を作成できません")) .build() ) case RegionCreationResult.Error => Button( - new IconItemStackBuilder(Material.WOOL, 1) + new IconItemStackBuilder(Material.RED_WOOL) .title(s"${RED}以下の原因により保護の作成できません") .lore( List( diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/home/HomeMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/home/HomeMenu.scala index f899665d95..2af33b9774 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/home/HomeMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/home/HomeMenu.scala @@ -14,6 +14,7 @@ import com.github.unchama.seichiassist.menus.CommonButtons import com.github.unchama.seichiassist.menus.stickmenu.FirstPage import com.github.unchama.seichiassist.subsystems.home.HomeReadAPI import com.github.unchama.seichiassist.subsystems.home.domain.{Home, HomeId} +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.{ManagedWorld, SkullOwners} import com.github.unchama.targetedeffect._ import com.github.unchama.targetedeffect.player.PlayerEffects._ @@ -31,7 +32,8 @@ object HomeMenu { implicit val ioCanOpenHome: IO CanOpen HomeMenu, val ioCanOpenHomeRemoveConfirmationMenu: IO CanOpen HomeRemoveConfirmationMenu, implicit val homeReadAPI: HomeReadAPI[IO], - implicit val asyncShift: NonServerThreadContextShift[IO] + implicit val asyncShift: NonServerThreadContextShift[IO], + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) } @@ -168,8 +170,9 @@ case class HomeMenuButtonComputations(player: Player)( val commandInfo = List(s"$DARK_RED${UNDERLINE}クリックで名称変更", s"${DARK_GRAY}command->[/home name $homeId]") - val coordinates = List(s"$GRAY$worldName x:${Math.floor(location.x)} y:${Math - .floor(location.y)} z:${Math.floor(location.z)}") + val coordinates = List( + s"$GRAY$worldName x:${Math.floor(location.x)} y:${Math.floor(location.y)} z:${Math.floor(location.z)}" + ) nameStatus ++ commandInfo ++ coordinates }) Button( @@ -200,7 +203,7 @@ case class HomeMenuButtonComputations(player: Player)( homeOpt <- homeReadAPI.get(player.getUniqueId, homeId) } yield { Button( - new IconItemStackBuilder(Material.BED) + new IconItemStackBuilder(Material.WHITE_BED) .title(s"$YELLOW$UNDERLINE${BOLD}ホームポイント${homeNumber}を設定") .lore( List( @@ -231,7 +234,7 @@ case class HomeMenuButtonComputations(player: Player)( homeOpt <- homeReadAPI.get(player.getUniqueId, homeId) } yield { Button( - new IconItemStackBuilder(Material.WOOL, 14) + new IconItemStackBuilder(Material.RED_WOOL) .title(s"$RED$UNDERLINE${BOLD}ホームポイント${homeNumber}を削除") .lore( List( @@ -242,7 +245,7 @@ case class HomeMenuButtonComputations(player: Player)( ) .build(), LeftClickButtonEffect { - FocusedSoundEffect(Sound.BLOCK_ENDERCHEST_CLOSE, 1f, 0.1f) + FocusedSoundEffect(Sound.BLOCK_ENDER_CHEST_CLOSE, 1f, 0.1f) SequentialEffect( environment .ioCanOpenHomeRemoveConfirmationMenu @@ -272,7 +275,7 @@ case class HomeChangeConfirmationMenu(changeHomeNumber: Int, homeName: String = val changeButton: Button = Button( - new IconItemStackBuilder(Material.WOOL, durability = 5).title(s"${GREEN}変更する").build(), + new IconItemStackBuilder(Material.LIME_WOOL).title(s"${GREEN}変更する").build(), LeftClickButtonEffect { SequentialEffect( FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f), @@ -284,7 +287,7 @@ case class HomeChangeConfirmationMenu(changeHomeNumber: Int, homeName: String = def cancelButton(implicit environment: Environment): Button = Button( - new IconItemStackBuilder(Material.WOOL, durability = 14).title(s"${RED}変更しない").build(), + new IconItemStackBuilder(Material.RED_WOOL).title(s"${RED}変更しない").build(), LeftClickButtonEffect { FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) environment.ioCanOpenHomeMenu.open(new HomeMenu) @@ -324,7 +327,7 @@ case class HomeRemoveConfirmationMenu(removeHomeNumber: Int, homeName: String = val removeButton: Button = Button( - new IconItemStackBuilder(Material.WOOL, durability = 5).title(s"${GREEN}削除する").build(), + new IconItemStackBuilder(Material.LIME_WOOL).title(s"${GREEN}削除する").build(), LeftClickButtonEffect { SequentialEffect( FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f), @@ -336,7 +339,7 @@ case class HomeRemoveConfirmationMenu(removeHomeNumber: Int, homeName: String = def cancelButton(implicit environment: Environment): Button = Button( - new IconItemStackBuilder(Material.WOOL, durability = 14).title(s"${RED}変更しない").build(), + new IconItemStackBuilder(Material.RED_WOOL).title(s"${RED}変更しない").build(), LeftClickButtonEffect { FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) environment.ioCanOpenHomeMenu.open(new HomeMenu) 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 3df3df405d..e79c4b3fb4 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 @@ -12,6 +12,7 @@ import com.github.unchama.seichiassist.menus.CommonButtons import com.github.unchama.seichiassist.subsystems.gachaprize.GachaPrizeAPI import com.github.unchama.seichiassist.subsystems.minestack.MineStackAPI import com.github.unchama.seichiassist.subsystems.minestack.domain.minestackobject.MineStackObjectCategory +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.targetedeffect.{DeferredEffect, TargetedEffect} import org.bukkit.ChatColor._ import org.bukkit.entity.Player @@ -22,10 +23,11 @@ object CategorizedMineStackMenu { class Environment( implicit val ioCanOpenMineStackMainMenu: IO CanOpen MineStackMainMenu.type, val ioCanOpenCategorizedMenu: IO CanOpen CategorizedMineStackMenu, - val ioCanOpenSelectItemColorMenu: IO CanOpen MineStackSelectItemColorMenu, + val ioCanOpenSelectItemColorMenu: IO CanOpen MineStackSelectItemKindMenu, val onMainThread: OnMinecraftServerThread[IO], val mineStackAPI: MineStackAPI[IO, Player, ItemStack], - implicit val gachaPrizeAPI: GachaPrizeAPI[IO, ItemStack, Player] + implicit val gachaPrizeAPI: GachaPrizeAPI[IO, ItemStack, Player], + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) } @@ -52,7 +54,8 @@ case class CategorizedMineStackMenu(category: MineStackObjectCategory, pageIndex ) private def mineStackMainMenuButtonSection( - implicit ioCanOpenMineStackMainMenu: IO CanOpen MineStackMainMenu.type + implicit ioCanOpenMineStackMainMenu: IO CanOpen MineStackMainMenu.type, + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ): Seq[(Int, Button)] = Seq( ChestSlotRef(5, 0) -> CommonButtons.transferButton( 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 5a2d1ba2fb..19a7bfca12 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 @@ -12,7 +12,7 @@ import com.github.unchama.seichiassist.subsystems.minestack.MineStackAPI import com.github.unchama.seichiassist.subsystems.minestack.domain.minestackobject.{ MineStackObject, MineStackObjectGroup, - MineStackObjectWithColorVariants + MineStackObjectWithKindVariants } import com.github.unchama.seichiassist.util.InventoryOperations.grantItemStacksEffect import com.github.unchama.targetedeffect.commandsender.MessageEffect @@ -38,7 +38,7 @@ private[minestack] case class MineStackButtons(player: Player)( mineStackObjectGroup match { case Left(mineStackObject) => mineStackObject - case Right(MineStackObjectWithColorVariants(representative, _)) => + case Right(MineStackObjectWithKindVariants(representative, _)) => representative } } @@ -69,7 +69,7 @@ private[minestack] case class MineStackButtons(player: Player)( val mineStackObject = mineStackObjectGroup match { case Left(mineStackObject) => mineStackObject - case Right(MineStackObjectWithColorVariants(representative, _)) => + case Right(MineStackObjectWithKindVariants(representative, _)) => representative } @@ -94,17 +94,17 @@ private[minestack] case class MineStackButtons(player: Player)( } setLore { - val itemDetail = List(s"$RESET$GREEN${stackedAmount.formatted("%,d")}個") val operationDetail = if (mineStackObjectGroup.isRight) { - List(s"$RESET${DARK_GREEN}クリックで色選択画面を開きます。") + List(s"$RESET${DARK_GREEN}クリックで種類選択画面を開きます。") } else { List( + s"$RESET$GREEN${String.format("%,d", stackedAmount)}個", s"$RESET$DARK_RED${UNDERLINE}左クリックで1スタック取り出し", s"$RESET$DARK_AQUA${UNDERLINE}右クリックで1個取り出し" ) } - (itemDetail ++ operationDetail).asJava + operationDetail.asJava } setAmount(1) @@ -120,7 +120,7 @@ private[minestack] case class MineStackButtons(player: Player)( oldPage: Int )( implicit onMainThread: OnMinecraftServerThread[IO], - canOpenCategorizedMineStackMenu: IO CanOpen MineStackSelectItemColorMenu + canOpenCategorizedMineStackMenu: IO CanOpen MineStackSelectItemKindMenu ): IO[Button] = RecomputedButton { for { itemStack <- getMineStackObjectIconItemStack(mineStackObjectGroup) @@ -152,7 +152,7 @@ private[minestack] case class MineStackButtons(player: Player)( oldPage: Int )( implicit onMainThread: OnMinecraftServerThread[IO], - canOpenMineStackSelectItemColorMenu: IO CanOpen MineStackSelectItemColorMenu + canOpenMineStackSelectItemColorMenu: IO CanOpen MineStackSelectItemKindMenu ): Kleisli[IO, Player, Unit] = { mineStackObjectGroup match { case Left(mineStackObject) => @@ -168,7 +168,7 @@ private[minestack] case class MineStackButtons(player: Player)( ) case Right(mineStackObjectWithColorVariants) => canOpenMineStackSelectItemColorMenu.open( - MineStackSelectItemColorMenu(mineStackObjectWithColorVariants, oldPage) + MineStackSelectItemKindMenu(mineStackObjectWithColorVariants, oldPage) ) } } 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 dadbab3c2c..f6800b8c2c 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 @@ -13,6 +13,7 @@ import com.github.unchama.seichiassist.subsystems.gachaprize.GachaPrizeAPI import com.github.unchama.seichiassist.subsystems.minestack.MineStackAPI import com.github.unchama.seichiassist.subsystems.minestack.domain.minestackobject.MineStackObjectCategory import com.github.unchama.seichiassist.subsystems.minestack.domain.minestackobject.MineStackObjectCategory._ +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import org.bukkit.ChatColor._ import org.bukkit.Material import org.bukkit.entity.Player @@ -28,7 +29,8 @@ object MineStackMainMenu extends Menu { implicit val ioCanOpenFirstPage: IO CanOpen FirstPage.type, implicit val ioCanOpenCategorizedMineStackMenu: IO CanOpen CategorizedMineStackMenu, implicit val gachaPrizeAPI: GachaPrizeAPI[IO, ItemStack, Player], - implicit val mineStackAPI: MineStackAPI[IO, Player, ItemStack] + implicit val mineStackAPI: MineStackAPI[IO, Player, ItemStack], + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) override val frame: MenuFrame = MenuFrame(6.chestRows, s"$DARK_PURPLE${BOLD}MineStackメインメニュー") @@ -39,8 +41,8 @@ object MineStackMainMenu extends Menu { 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 AGRICULTURAL => Material.WHEAT_SEEDS + case BUILDING => Material.STONE_BRICKS case REDSTONE_AND_TRANSPORTATION => Material.REDSTONE case GACHA_PRIZES => Material.GOLDEN_APPLE } diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/minestack/MineStackSelectItemColorMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/minestack/MineStackSelectItemColorMenu.scala deleted file mode 100644 index 4b150c03a2..0000000000 --- a/src/main/scala/com/github/unchama/seichiassist/menus/minestack/MineStackSelectItemColorMenu.scala +++ /dev/null @@ -1,62 +0,0 @@ -package com.github.unchama.seichiassist.menus.minestack - -import cats.effect.IO -import cats.implicits.toTraverseOps -import com.github.unchama.itemstackbuilder.SkullItemStackBuilder -import com.github.unchama.menuinventory.router.CanOpen -import com.github.unchama.menuinventory.{ChestSlotRef, Menu, MenuFrame, MenuSlotLayout} -import com.github.unchama.seichiassist.SkullOwners -import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.onMainThread -import com.github.unchama.seichiassist.menus.CommonButtons -import com.github.unchama.seichiassist.subsystems.gachaprize.GachaPrizeAPI -import com.github.unchama.seichiassist.subsystems.minestack.MineStackAPI -import com.github.unchama.seichiassist.subsystems.minestack.domain.minestackobject.MineStackObjectWithColorVariants -import eu.timepit.refined.auto._ -import org.bukkit.ChatColor.{BOLD, DARK_BLUE} -import org.bukkit.entity.Player -import org.bukkit.inventory.ItemStack - -object MineStackSelectItemColorMenu { - - class Environment( - implicit val canOpenCategorizedMineStackMenu: CanOpen[IO, CategorizedMineStackMenu], - implicit val mineStackAPI: MineStackAPI[IO, Player, ItemStack], - implicit val gachaPrizeAPI: GachaPrizeAPI[IO, ItemStack, Player] - ) - -} - -case class MineStackSelectItemColorMenu( - group: MineStackObjectWithColorVariants[ItemStack], - oldPage: Int -) extends Menu { - - import com.github.unchama.menuinventory.syntax._ - - override type Environment = MineStackSelectItemColorMenu.Environment - override val frame: MenuFrame = - MenuFrame(6.chestRows, s"$DARK_BLUE${BOLD}MineStack(アイテム色選択)") - - override def computeMenuLayout( - player: Player - )(implicit environment: Environment): IO[MenuSlotLayout] = { - import environment._ - val buttonMapping = (List(group.representative) ++ group.coloredVariants).zipWithIndex.map { - case (inListMineStackObj, index) => - index -> MineStackButtons(player).getMineStackObjectButtonOf(inListMineStackObj) - } ++ List( - ChestSlotRef(5, 0) -> IO( - CommonButtons.transferButton( - new SkullItemStackBuilder(SkullOwners.MHF_ArrowUp), - s"MineStack${oldPage + 1}ページ目へ", - CategorizedMineStackMenu(group.category, oldPage) - ) - ) - ) - for { - mapping <- buttonMapping.traverse(_.sequence) - } yield MenuSlotLayout(mapping: _*) - - } - -} diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/minestack/MineStackSelectItemKindMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/minestack/MineStackSelectItemKindMenu.scala new file mode 100644 index 0000000000..3cc2017e4a --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/menus/minestack/MineStackSelectItemKindMenu.scala @@ -0,0 +1,110 @@ +package com.github.unchama.seichiassist.menus.minestack + +import cats.effect.IO +import cats.implicits.toTraverseOps +import com.github.unchama.itemstackbuilder.{SkullItemStackBuilder, SkullOwnerReference} +import com.github.unchama.menuinventory.router.CanOpen +import com.github.unchama.menuinventory.{ChestSlotRef, Menu, MenuFrame, MenuSlotLayout} +import com.github.unchama.seichiassist.SkullOwners +import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.onMainThread +import com.github.unchama.seichiassist.menus.CommonButtons +import com.github.unchama.seichiassist.subsystems.gachaprize.GachaPrizeAPI +import com.github.unchama.seichiassist.subsystems.minestack.MineStackAPI +import com.github.unchama.seichiassist.subsystems.minestack.domain.minestackobject.MineStackObjectWithKindVariants +import eu.timepit.refined.auto._ +import org.bukkit.ChatColor.{BOLD, DARK_BLUE} +import org.bukkit.entity.Player +import org.bukkit.inventory.ItemStack +import com.github.unchama.menuinventory.slot.button.Button +import com.github.unchama.generic.MapExtra +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI + +object MineStackSelectItemKindMenu { + + class Environment( + implicit val canOpenCategorizedMineStackMenu: CanOpen[IO, CategorizedMineStackMenu], + implicit val canOpenSelectItemKindMenu: CanOpen[IO, MineStackSelectItemKindMenu], + implicit val mineStackAPI: MineStackAPI[IO, Player, ItemStack], + implicit val gachaPrizeAPI: GachaPrizeAPI[IO, ItemStack, Player], + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] + ) + +} + +case class MineStackSelectItemKindMenu( + group: MineStackObjectWithKindVariants[ItemStack], + oldPage: Int, + pageIndex: Int = 0 +) extends Menu { + + import com.github.unchama.menuinventory.syntax._ + + override type Environment = MineStackSelectItemKindMenu.Environment + override val frame: MenuFrame = + MenuFrame(6.chestRows, s"$DARK_BLUE${BOLD}MineStack(アイテム種類選択)") + + /** + * マインスタックオブジェクトボタンを置くセクションの行数 + */ + private val objectSectionRows = 5 + + // ページ操作等のボタンを含むレイアウトセクション + def uiOperationSection( + totalNumberOfPages: Int + )(page: Int)(implicit environment: Environment): Seq[(Int, Button)] = { + import environment._ + + def buttonToTransferTo(pageIndex: Int, skullOwnerReference: SkullOwnerReference): Button = + CommonButtons.transferButton( + new SkullItemStackBuilder(skullOwnerReference), + s"MineStack(アイテム種類選択)${pageIndex + 1}ページ目へ", + MineStackSelectItemKindMenu(group, oldPage, pageIndex) + ) + + val previousPageButtonSection = + MapExtra.when(page > 0)( + Map(ChestSlotRef(5, 7) -> buttonToTransferTo(page - 1, SkullOwners.MHF_ArrowUp)) + ) + + val nextPageButtonSection = + MapExtra.when(page + 1 < totalNumberOfPages)( + Map(ChestSlotRef(5, 8) -> buttonToTransferTo(page + 1, SkullOwners.MHF_ArrowDown)) + ) + + (previousPageButtonSection ++ nextPageButtonSection).toSeq + } + + override def computeMenuLayout( + player: Player + )(implicit environment: Environment): IO[MenuSlotLayout] = { + import environment._ + val mineStackObjectPerPage = objectSectionRows.chestRows.slotCount + + val buttonMapping = (List(group.representative) ++ group.kindVariants) + .slice( + mineStackObjectPerPage * pageIndex, + mineStackObjectPerPage * pageIndex + mineStackObjectPerPage + ) + .zipWithIndex + .map { + case (inListMineStackObj, index) => + index -> MineStackButtons(player).getMineStackObjectButtonOf(inListMineStackObj) + } ++ List( + ChestSlotRef(5, 0) -> IO( + CommonButtons.transferButton( + new SkullItemStackBuilder(SkullOwners.MHF_ArrowUp), + s"MineStack${oldPage + 1}ページ目へ", + CategorizedMineStackMenu(group.category, oldPage) + ) + ) + ) + + val totalNumberOfPages = Math.ceil((group.kindVariants.length + 1) / 45.0).toInt + + for { + mapping <- buttonMapping.traverse(_.sequence) + } yield MenuSlotLayout(mapping ++ uiOperationSection(totalNumberOfPages)(pageIndex): _*) + + } + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/nicknames/NickNameMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/nicknames/NickNameMenu.scala index 79d30d4de4..6304b22081 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/nicknames/NickNameMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/nicknames/NickNameMenu.scala @@ -19,6 +19,7 @@ import com.github.unchama.seichiassist.achievement.Nicknames import com.github.unchama.seichiassist.data.MenuInventoryData import com.github.unchama.seichiassist.menus.CommonButtons import com.github.unchama.seichiassist.menus.achievement.AchievementMenu +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.vote.VoteAPI import com.github.unchama.targetedeffect.{SequentialEffect, UnfocusedEffect} import com.github.unchama.targetedeffect.player.FocusedSoundEffect @@ -33,7 +34,8 @@ object NickNameMenu extends Menu { implicit val ioCanOpenAchievementMenu: IO CanOpen AchievementMenu.type, implicit val layoutPreparationContext: LayoutPreparationContext, implicit val onMinecraftServerThread: OnMinecraftServerThread[IO], - implicit val voteAPI: VoteAPI[IO, Player] + implicit val voteAPI: VoteAPI[IO, Player], + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) override val frame: MenuFrame = MenuFrame(4.chestRows, s"$DARK_PURPLE${BOLD}二つ名組み合わせシステム") 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 8b45a77a6b..f7bfb6baa6 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 @@ -10,6 +10,7 @@ import com.github.unchama.seichiassist.SkullOwners import com.github.unchama.seichiassist.menus.CommonButtons 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.playerheadskin.PlayerHeadSkinAPI 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.{ @@ -25,7 +26,8 @@ object RankingMenu { class Environment[R]( implicit val rankingApi: RankingProvider[IO, R], val ioCanOpenRankingMenuItself: IO CanOpen RankingMenu[R], - val ioCanOpenRankingRootMenu: IO CanOpen RankingRootMenu.type + val ioCanOpenRankingRootMenu: IO CanOpen RankingRootMenu.type, + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) } @@ -157,10 +159,14 @@ case class RankingMenu[R](template: RankingMenuTemplate[R], pageIndex: Int = 0) goBackToStickMenuSection ++ previousPageButtonSection ++ nextPageButtonSection } - private def rankingSection(ranking: Ranking[R]): Seq[(Int, Button)] = { + private def rankingSection( + ranking: Ranking[R] + )(implicit environment: Environment): Seq[(Int, Button)] = { + import environment.playerHeadSkinAPI + def entry(position: Int, record: RankingRecord[R]): Button = { Button( - new SkullItemStackBuilder(record.playerName) + new SkullItemStackBuilder(record.uuid) .title(s"$YELLOW$BOLD${position}位:$WHITE${record.playerName}") .lore(template.recordDataLore(record.value)) .build() @@ -178,7 +184,11 @@ case class RankingMenu[R](template: RankingMenuTemplate[R], pageIndex: Int = 0) } } - private def totalAmountSection(ranking: Ranking[R]): Seq[(Int, Button)] = { + private def totalAmountSection( + ranking: Ranking[R] + )(implicit environment: Environment): Seq[(Int, Button)] = { + import environment.playerHeadSkinAPI + Seq( ChestSlotRef(5, 4) -> Button( 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 9ac4dc2f5f..23e9171b6e 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 @@ -12,6 +12,7 @@ import com.github.unchama.seichiassist.menus.CommonButtons import com.github.unchama.seichiassist.menus.stickmenu.FirstPage 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.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.ranking.domain.values.{LoginTime, VoteCount} import eu.timepit.refined.auto._ import org.bukkit.ChatColor._ @@ -25,7 +26,8 @@ object RankingRootMenu extends Menu { val ioCanOpenSeichiRankingMenu: IO CanOpen RankingMenu[SeichiAmountData], val ioCanOpenBuildRankingMenu: IO CanOpen RankingMenu[BuildAmountData], val ioCanOpenLoginTimeRankingMenu: IO CanOpen RankingMenu[LoginTime], - val ioCanOpenVoteCountRankingMenu: IO CanOpen RankingMenu[VoteCount] + val ioCanOpenVoteCountRankingMenu: IO CanOpen RankingMenu[VoteCount], + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) override val frame: MenuFrame = MenuFrame(4.chestRows, s"$DARK_PURPLE${BOLD}ランキング") 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 bcef47f214..d66da57d1f 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 @@ -19,6 +19,7 @@ import com.github.unchama.seichiassist.seichiskill.effect.{ import com.github.unchama.seichiassist.subsystems.vote.VoteAPI import com.github.unchama.seichiassist.subsystems.vote.domain.EffectPoint import com.github.unchama.seichiassist.subsystems.donate.DonatePremiumPointAPI +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.{SeichiAssist, SkullOwners} import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.player.FocusedSoundEffect @@ -39,7 +40,8 @@ object ActiveSkillEffectMenu extends Menu { val ioCanOpenTransactionHistoryMenu: IO CanOpen PremiumPointTransactionHistoryMenu, val ioOnMainThread: OnMinecraftServerThread[IO], val voteAPI: VoteAPI[IO, Player], - val donateAPI: DonatePremiumPointAPI[IO] + val donateAPI: DonatePremiumPointAPI[IO], + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) override val frame: MenuFrame = MenuFrame(6.chestRows, s"$DARK_PURPLE${BOLD}整地スキルエフェクト選択") @@ -237,7 +239,8 @@ object ActiveSkillEffectMenu extends Menu { ) def goBackToSkillMenuButton( - implicit ioCanOpenActiveSkillMenu: IO CanOpen ActiveSkillMenu.type + implicit ioCanOpenActiveSkillMenu: IO CanOpen ActiveSkillMenu.type, + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ): Button = CommonButtons.transferButton( new SkullItemStackBuilder(SkullOwners.MHF_ArrowLeft), 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 7ad8f10c9b..7f0b7aa1ed 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 @@ -3,7 +3,6 @@ package com.github.unchama.seichiassist.menus.skill import cats.data.Kleisli 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, @@ -28,6 +27,7 @@ import com.github.unchama.seichiassist.seichiskill.assault.AssaultRoutine import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountAPI import com.github.unchama.seichiassist.subsystems.discordnotification.DiscordNotificationAPI import com.github.unchama.seichiassist.subsystems.mana.ManaApi +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.util.SendMessageEffect import com.github.unchama.targetedeffect.SequentialEffect import com.github.unchama.targetedeffect.TargetedEffect.emptyEffect @@ -61,7 +61,8 @@ object ActiveSkillMenu extends Menu { val ioCanOpenActiveSkillEffectMenu: IO CanOpen ActiveSkillEffectMenu.type, val ioCanOpenFirstPage: IO CanOpen FirstPage.type, val ioOnMainThread: OnMinecraftServerThread[IO], - val globalNotification: DiscordNotificationAPI[IO] + val globalNotification: DiscordNotificationAPI[IO], + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) override val frame: MenuFrame = MenuFrame(5.chestRows, s"$DARK_PURPLE${BOLD}整地スキル選択") @@ -155,7 +156,7 @@ object ActiveSkillMenu extends Menu { case skill: ActiveSkill => skill match { case SeichiSkill.DualBreak => - new IconItemStackBuilder(Material.GRASS) + new IconItemStackBuilder(Material.GRASS_BLOCK) case SeichiSkill.TrialBreak => new IconItemStackBuilder(Material.STONE) case SeichiSkill.Explosion => @@ -178,11 +179,11 @@ object ActiveSkillMenu extends Menu { case SeichiSkill.Thunderstorm => new IconItemStackBuilder(Material.MINECART) case SeichiSkill.StarlightBreaker => - new IconItemStackBuilder(Material.STORAGE_MINECART) + new IconItemStackBuilder(Material.CHEST_MINECART) case SeichiSkill.EarthDivide => - new IconItemStackBuilder(Material.POWERED_MINECART) + new IconItemStackBuilder(Material.FURNACE_MINECART) case SeichiSkill.HeavenGaeBolg => - new IconItemStackBuilder(Material.EXPLOSIVE_MINECART) + new IconItemStackBuilder(Material.TNT_MINECART) case SeichiSkill.Decision => new IconItemStackBuilder(Material.HOPPER_MINECART) @@ -210,9 +211,9 @@ object ActiveSkillMenu extends Menu { case SeichiSkill.LavaCondensation => new IconItemStackBuilder(Material.NETHERRACK) case SeichiSkill.MoerakiBoulders => - new IconItemStackBuilder(Material.NETHER_BRICK) + new IconItemStackBuilder(Material.NETHER_BRICKS) case SeichiSkill.Eldfell => - new IconItemStackBuilder(Material.MAGMA) + new IconItemStackBuilder(Material.MAGMA_BLOCK) case SeichiSkill.VenderBlizzard => new IconItemStackBuilder(Material.NETHER_STAR) case SeichiSkill.AssaultArmor => @@ -282,9 +283,7 @@ object ActiveSkillMenu extends Menu { } } - def seichiSkillButton[F[ - _ - ]: ConcurrentEffect: NonServerThreadContextShift: DiscordNotificationAPI]( + def seichiSkillButton[F[_]: ConcurrentEffect: DiscordNotificationAPI]( state: SkillSelectionState, skill: SeichiSkill )(implicit environment: Environment): Button = { @@ -380,12 +379,12 @@ object ActiveSkillMenu extends Menu { .sendPlainText(notificationMessage) .toIO ), - Kleisli.liftF(IO { - SendMessageEffect.sendMessageToEveryoneIgnoringPreference( + Kleisli.liftF( + SendMessageEffect.sendMessageToEveryoneIgnoringPreferenceIO( s"$GOLD$BOLD$notificationMessage" ) - }), - BroadcastSoundEffect(Sound.ENTITY_ENDERDRAGON_DEATH, 1.0f, 1.2f) + ), + BroadcastSoundEffect(Sound.ENTITY_ENDER_DRAGON_DEATH, 1.0f, 1.2f) ) ) } else (unlockedState, emptyEffect) 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 a2034882b7..12e6dfdc8f 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 @@ -13,6 +13,8 @@ import com.github.unchama.seichiassist.menus.stickmenu.FirstPage import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountAPI import com.github.unchama.seichiassist.subsystems.breakskilltargetconfig.BreakSkillTargetConfigAPI import com.github.unchama.seichiassist.subsystems.breakskilltargetconfig.domain.BreakSkillTargetConfigKey +import com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.BreakSuppressionPreferenceAPI +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.targetedeffect._ import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.player.FocusedSoundEffect @@ -33,7 +35,9 @@ object PassiveSkillMenu extends Menu { class Environment( implicit val breakCountApi: BreakCountAPI[IO, SyncIO, Player], implicit val breakSkillTargetConfigAPI: BreakSkillTargetConfigAPI[IO, Player], - val ioCanOpenFirstPage: IO CanOpen FirstPage.type + implicit val breakSuppressionPreferenceAPI: BreakSuppressionPreferenceAPI[IO, Player], + val ioCanOpenFirstPage: IO CanOpen FirstPage.type, + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) /** @@ -60,6 +64,7 @@ object PassiveSkillMenu extends Menu { val dynamicPartComputation = List( ChestSlotRef(0, 0) -> computeToggleMultipleBlockTypeDestructionButton, ChestSlotRef(0, 1) -> computeToggleChestBreakButton, + ChestSlotRef(0, 2) -> computeToggleManaFullyConsumedBreakStopButton, ChestSlotRef(1, 0) -> computeGiganticBerserkButton, ChestSlotRef(1, 1) -> computeToggleNetherQuartzBlockButton ).traverse(_.sequence) @@ -229,6 +234,49 @@ object PassiveSkillMenu extends Menu { ) }) + val computeToggleManaFullyConsumedBreakStopButton: IO[Button] = RecomputedButton(for { + isBreakSuppressionEnabled <- breakSuppressionPreferenceAPI.isBreakSuppressionEnabled( + player + ) + + } yield { + val baseLore = List(s"${YELLOW}マナ切れでブロック破壊を止めるスキル") + val statusLore = if (isBreakSuppressionEnabled) { + List(s"${GREEN}ON (マナが切れるとブロック破壊を止めます。)", s"${DARK_RED}クリックでOFF") + } else { + List(s"${RED}OFF (マナが切れてもブロック破壊を続けます。)", s"${DARK_GREEN}クリックでON") + } + + Button( + new IconItemStackBuilder(Material.LAPIS_LAZULI) + .tap { builder => + if (isBreakSuppressionEnabled) + builder.enchanted() + } + .title(s"$WHITE$UNDERLINE${BOLD}マナ切れでブロック破壊を止めるスキル切り替え") + .lore(baseLore ++ statusLore) + .build(), + LeftClickButtonEffect { + SequentialEffect( + breakSuppressionPreferenceAPI.toggleBreakSuppression, + DeferredEffect(IO { + if (isBreakSuppressionEnabled) { + SequentialEffect( + MessageEffect(s"${RED}マナが切れたらブロック破壊を止めるスキルを無効化しました。"), + FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 0.5f) + ) + } else { + SequentialEffect( + MessageEffect(s"${GREEN}マナが切れたらブロック破壊を止めるスキルを有効化しました。"), + FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) + ) + } + }) + ) + } + ) + }) + val computeGiganticBerserkButton: IO[Button] = RecomputedButton { environment .breakCountApi @@ -260,7 +308,8 @@ object PassiveSkillMenu extends Menu { } else { List( s"${GRAY}MOBの魂を${openerData.giganticBerserk.requiredExpToNextLevel()}回吸収すると更なる力が得られる", - s"$GRAY${openerData.giganticBerserk.exp}/${openerData.giganticBerserk.requiredExpToNextLevel()}" + s"$GRAY${openerData.giganticBerserk.exp}/${openerData.giganticBerserk.requiredExpToNextLevel()}", + s"${GRAY}MOB討伐総数:${openerData.giganticBerserk.totalNumberOfKilledEnemies}" ) } val probability = 100 * openerData.giganticBerserk.manaRegenerationProbability() 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 a28fe5ab69..3b9eaa6f21 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 @@ -13,6 +13,7 @@ import com.github.unchama.seichiassist.SkullOwners import com.github.unchama.seichiassist.menus.CommonButtons import com.github.unchama.seichiassist.subsystems.donate.DonatePremiumPointAPI import com.github.unchama.seichiassist.subsystems.donate.domain.{Obtained, Used} +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import net.md_5.bungee.api.ChatColor._ import org.bukkit.ChatColor.{GOLD, GREEN, RESET} import org.bukkit.Material @@ -23,7 +24,8 @@ object PremiumPointTransactionHistoryMenu { class Environment( implicit val ioCanOpenActiveSkillEffectMenu: IO CanOpen ActiveSkillEffectMenu.type, val ioCanOpenTransactionHistoryMenu: IO CanOpen PremiumPointTransactionHistoryMenu, - val donateAPI: DonatePremiumPointAPI[IO] + val donateAPI: DonatePremiumPointAPI[IO], + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) } @@ -37,13 +39,16 @@ case class PremiumPointTransactionHistoryMenu(pageNumber: Int) extends Menu { override val frame: MenuFrame = MenuFrame(4.chestRows, s"$BLUE${BOLD}プレミアムエフェクト購入履歴") def buttonToTransferTo(pageNumber: Int, skullOwnerReference: SkullOwnerReference)( - implicit ioCanOpenTransactionHistoryMenu: IO CanOpen PremiumPointTransactionHistoryMenu - ): Button = + implicit environment: Environment + ): Button = { + import environment._ + CommonButtons.transferButton( new SkullItemStackBuilder(skullOwnerReference), s"${pageNumber}ページ目へ", PremiumPointTransactionHistoryMenu(pageNumber) ) + } def computeDynamicParts(player: Player)( implicit environment: PremiumPointTransactionHistoryMenu.Environment 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 8724203955..88d3ddd579 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 @@ -8,7 +8,8 @@ import com.github.unchama.menuinventory.router.CanOpen import com.github.unchama.menuinventory.slot.button.action.{ ClickEventFilter, FilteredButtonEffect, - LeftClickButtonEffect + LeftClickButtonEffect, + RightClickButtonEffect } import com.github.unchama.menuinventory.slot.button.{Button, RecomputedButton, action} import com.github.unchama.seichiassist.data.descrptions.PlayerStatsLoreGenerator @@ -44,6 +45,7 @@ import com.github.unchama.seichiassist.subsystems.gachapoint.GachaPointApi import com.github.unchama.seichiassist.subsystems.ranking.api.RankingProvider import com.github.unchama.seichiassist.task.CoolDownTask import com.github.unchama.seichiassist.ManagedWorld._ +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.vote.VoteAPI import com.github.unchama.seichiassist.{SeichiAssist, SkullOwners, util} import com.github.unchama.targetedeffect.TargetedEffect.emptyEffect @@ -94,7 +96,8 @@ object FirstPage extends Menu { val ioCanOpenVoteMenu: IO CanOpen VoteMenu.type, val enderChestAccessApi: AnywhereEnderChestAPI[IO], val gachaTicketAPI: GachaTicketAPI[IO], - val voteAPI: VoteAPI[IO, Player] + val voteAPI: VoteAPI[IO, Player], + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) override val frame: MenuFrame = @@ -165,6 +168,7 @@ object FirstPage extends Menu { private case class ButtonComputations(player: Player)(implicit environment: Environment) { import player._ + import environment._ val computeStatsButton: IO[Button] = RecomputedButton { val openerData = SeichiAssist.playermap(getUniqueId) @@ -279,14 +283,13 @@ object FirstPage extends Menu { val computeRegionMenuButton: IO[Button] = IO { val (buttonLore, effect) = { val world = getWorld - val regionManager = WorldGuardWrapper.getRegionManager(world) - if (regionManager.isEmpty) { + if (!WorldGuardWrapper.canProtectionWorld(world)) { (List(s"${GRAY}このワールドでは土地の保護は行なえません"), LeftClickButtonEffect(emptyEffect)) } else { - val maxRegionCount = WorldGuardWrapper.getMaxRegionCount(player, world) + val maxRegionCount = WorldGuardWrapper.getWorldMaxRegion(world) val currentPlayerRegionCount = - WorldGuardWrapper.getRegionCountOfPlayer(player, world) + WorldGuardWrapper.getMaxRegion(player, world) ( List( @@ -370,7 +373,7 @@ object FirstPage extends Menu { s"$RESET$DARK_GREEN${UNDERLINE}クリックで開く" ) - new IconItemStackBuilder(Material.ENDER_PORTAL_FRAME) + new IconItemStackBuilder(Material.END_PORTAL_FRAME) .title(s"$YELLOW$UNDERLINE${BOLD}4次元ポケットを開く") .lore(loreHeading ++ loreAnnotation) .build() @@ -405,10 +408,10 @@ object FirstPage extends Menu { environment.enderChestAccessApi.openEnderChestOrNotifyInsufficientLevel.flatMap { case Right(_) => // 開くのに成功した場合の音 - FocusedSoundEffect(Sound.BLOCK_GRASS_PLACE, 1.0f, 0.1f) + FocusedSoundEffect(Sound.BLOCK_ENDER_CHEST_OPEN, 1.0f, 1.0f) case Left(_) => // 開くのに失敗した場合の音 - FocusedSoundEffect(Sound.BLOCK_ENDERCHEST_OPEN, 1.0f, 1.0f) + FocusedSoundEffect(Sound.BLOCK_GRASS_PLACE, 1.0f, 0.1f) } ) ) @@ -517,8 +520,11 @@ object FirstPage extends Menu { } val computeGachaTicketButton: IO[Button] = { - val effect: FilteredButtonEffect = LeftClickButtonEffect( - environment.gachaPointApi.receiveBatch + val leftClickEffect: FilteredButtonEffect = LeftClickButtonEffect( + environment.gachaPointApi.receiveLargeBatch + ) + val rightClickEffect: FilteredButtonEffect = RightClickButtonEffect( + environment.gachaPointApi.receiveSmallBatch ) val computeItemStack: IO[ItemStack] = @@ -532,8 +538,10 @@ object FirstPage extends Menu { val requiredToNextTicket = s"$RESET${AQUA}次のガチャ券まで:${point.amountUntilNextGachaTicket.amount}ブロック" + val receiveGachaTicketDescription = + s"$RESET${GRAY}左クリックで最大9st、右クリックで最大1stのガチャ券を受け取ります" - List(gachaTicketStatus, requiredToNextTicket) + List(gachaTicketStatus, requiredToNextTicket, receiveGachaTicketDescription) } new SkullItemStackBuilder(SkullOwners.unchama) @@ -543,7 +551,7 @@ object FirstPage extends Menu { } val computeButton: IO[Button] = computeItemStack.map { itemStack => - Button(itemStack, effect) + Button(itemStack, leftClickEffect, rightClickEffect) } RecomputedButton(computeButton) @@ -616,7 +624,10 @@ object FirstPage extends Menu { ) } - def secondPageButton(implicit ioCanOpenSecondPage: IO CanOpen SecondPage.type): Button = + def secondPageButton( + implicit ioCanOpenSecondPage: IO CanOpen SecondPage.type, + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] + ): Button = CommonButtons.transferButton( new SkullItemStackBuilder(SkullOwners.MHF_ArrowRight), "2ページ目へ", @@ -657,7 +668,7 @@ object FirstPage extends Menu { def homePointMenuButton(implicit ioCanOpenHomeMenu: IO CanOpen HomeMenu): Button = { val iconItemStack = - new IconItemStackBuilder(Material.BED) + new IconItemStackBuilder(Material.WHITE_BED) .title(s"$YELLOW$UNDERLINE${BOLD}ホームメニューを開く") .lore(List(s"$RESET${GRAY}ホームポイントに関するメニュー", s"$RESET$DARK_RED${UNDERLINE}クリックで開く")) .build() @@ -673,7 +684,7 @@ object FirstPage extends Menu { val fastCraftButton: Button = { val iconItemStack = - new IconItemStackBuilder(Material.WORKBENCH) + new IconItemStackBuilder(Material.CRAFTING_TABLE) .title(s"$YELLOW$UNDERLINE${BOLD}FastCraft機能") .lore( List( 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 00963b7a50..3b292fde92 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 @@ -22,6 +22,7 @@ import com.github.unchama.seichiassist.subsystems.gacha.GachaDrawAPI import com.github.unchama.seichiassist.subsystems.gacha.subsystems.consumegachaticket.ConsumeGachaTicketAPI import com.github.unchama.seichiassist.subsystems.gachapoint.GachaPointApi import com.github.unchama.seichiassist.subsystems.gachapoint.domain.gachapoint.GachaPoint +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.seasonalevents.anniversary.Anniversary import com.github.unchama.seichiassist.subsystems.seasonalevents.anniversary.AnniversaryItemData.anniversaryPlayerHead import com.github.unchama.seichiassist.subsystems.seasonalevents.christmas.Christmas @@ -62,7 +63,8 @@ object SecondPage extends Menu { val sharedInventoryAPI: SharedInventoryAPI[IO, Player], val gachaDrawAPI: GachaDrawAPI[IO, Player], val gachaPointAPI: GachaPointApi[IO, SyncIO, Player], - val consumeGachaTicketAPI: ConsumeGachaTicketAPI[IO, Player] + val consumeGachaTicketAPI: ConsumeGachaTicketAPI[IO, Player], + implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) override val frame: MenuFrame = @@ -105,7 +107,8 @@ object SecondPage extends Menu { } private case class ButtonComputations(player: Player)( - implicit sharedInventoryAPI: SharedInventoryAPI[IO, Player] + implicit sharedInventoryAPI: SharedInventoryAPI[IO, Player], + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) { import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.layoutPreparationContext @@ -489,17 +492,18 @@ 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をクリックしてください" + val iconItemStack = + new IconItemStackBuilder(Material.OAK_SIGN) // 1.16から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をクリックしてください" + ) ) - ) - .build() + .build() Button( iconItemStack, @@ -514,7 +518,7 @@ object SecondPage extends Menu { } val appleConversionButton: Button = { - val iconItemStack = new IconItemStackBuilder(Material.GOLDEN_APPLE, durability = 1) + val iconItemStack = new IconItemStackBuilder(Material.GOLDEN_APPLE) .title(s"$YELLOW$UNDERLINE${BOLD}GT景品→椎名林檎変換システム") .lore( List( 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 9f97c27841..304a22c10b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/seichiskill/BlockSearching.scala +++ b/src/main/scala/com/github/unchama/seichiassist/seichiskill/BlockSearching.scala @@ -5,8 +5,10 @@ import com.github.unchama.seichiassist.MaterialSets import com.github.unchama.seichiassist.MaterialSets.BlockBreakableBySkill import com.github.unchama.seichiassist.data.XYZTuple import com.github.unchama.seichiassist.util.BreakUtil +import com.github.unchama.util.external.ExternalPlugins import org.bukkit.Material import org.bukkit.block.Block +import org.bukkit.block.data.Waterlogged import org.bukkit.entity.Player import scala.collection.{Set, mutable} @@ -37,18 +39,35 @@ object BlockSearching { relativeVectors.collect { case XYZTuple(x, y, z) => val targetBlock = referencePoint.getRelative(x, y, z) + val waterloggedMaterials = Set( + Material.WATER, + Material.BUBBLE_COLUMN, + Material.TALL_SEAGRASS, + Material.SEAGRASS, + Material.KELP, + Material.KELP_PLANT + ) - 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)) { + if (targetBlock.getType == Material.LAVA) { + lavaBlocks.add(targetBlock) + } else if ( + waterloggedMaterials.contains(targetBlock.getType) || (targetBlock + .getBlockData + .isInstanceOf[Waterlogged] && ExternalPlugins + .getCoreProtectWrapper + .isNotEditedBlock(targetBlock) && targetBlock + .getBlockData + .asInstanceOf[Waterlogged] + .isWaterlogged) + ) { + waterBlocks.add(targetBlock) + } else { + MaterialSets + .refineBlock(targetBlock, MaterialSets.materials) + .foreach(b => solidBlocks.add(b)) } + } } Result(solidBlocks.toList, waterBlocks.toList, lavaBlocks.toList) @@ -63,10 +82,7 @@ object BlockSearching { 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)) // マテリアルが同一視により等しくなるかどうか blockMaterials.size == 1 || identifications.exists(blockMaterials.subsetOf) 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 d5731b0b78..f5ff56819f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/seichiskill/SkillRange.scala +++ b/src/main/scala/com/github/unchama/seichiassist/seichiskill/SkillRange.scala @@ -41,24 +41,22 @@ object AssaultSkillRange { } case class Water(effectChunkSize: XYZTuple) extends AssaultSkillRange { - override val blockMaterialConversion: Material => Material = { - case Material.WATER => Material.ICE - case x => x + override val blockMaterialConversion: Material => Material = { m => + if (m == Material.WATER || m == Material.BUBBLE_COLUMN) Material.ICE else m } } case class Lava(effectChunkSize: XYZTuple) extends AssaultSkillRange { - override val blockMaterialConversion: Material => Material = { - case Material.LAVA => Material.MAGMA - case x => x + override val blockMaterialConversion: Material => Material = { m => + if (m == Material.LAVA) Material.MAGMA_BLOCK else m } } 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 + override val blockMaterialConversion: Material => Material = { m => + if (m == Material.WATER || m == Material.BUBBLE_COLUMN) Material.ICE + else if (m == Material.LAVA) Material.MAGMA_BLOCK + else m } } 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 190aee7bab..e3a8ac2324 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 @@ -18,6 +18,7 @@ import com.github.unchama.seichiassist.{DefaultEffectEnvironment, MaterialSets, import org.bukkit.ChatColor._ import org.bukkit.enchantments.Enchantment import org.bukkit.entity.Player +import org.bukkit.inventory.meta.Damageable import org.bukkit.{GameMode, Location, Material, Sound} object AssaultRoutine { @@ -141,7 +142,7 @@ object AssaultRoutine { // 減る耐久値の計算 val durability = - (toolToBeUsed.getDurability + + (toolToBeUsed.getItemMeta.asInstanceOf[Damageable].getDamage + BreakUtil.calcDurability( toolToBeUsed.getEnchantmentLevel(Enchantment.DURABILITY), breakTargets @@ -161,7 +162,11 @@ object AssaultRoutine { } // 耐久値を減らす - if (!toolToBeUsed.getItemMeta.isUnbreakable) toolToBeUsed.setDurability(durability) + if (!toolToBeUsed.getItemMeta.isUnbreakable) { + val meta = toolToBeUsed.getItemMeta + meta.asInstanceOf[Damageable].setDamage(durability) + toolToBeUsed.setItemMeta(meta) + } // ブロックを書き換える if (shouldBreakAllBlocks) { @@ -178,7 +183,7 @@ object AssaultRoutine { ) } else { if (shouldRemoveOrCondenseWater) foundWaters.foreach(_.setType(Material.PACKED_ICE)) - if (shouldRemoveOrCondenseLava) foundLavas.foreach(_.setType(Material.MAGMA)) + if (shouldRemoveOrCondenseLava) foundLavas.foreach(_.setType(Material.MAGMA_BLOCK)) } Some(newState) 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 b8b8775ab8..74a13cbd83 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 @@ -22,7 +22,6 @@ import enumeratum.{Enum, EnumEntry} import org.bukkit.ChatColor._ import org.bukkit._ import org.bukkit.entity.{Chicken, Player} -import org.bukkit.material.Wool import scala.util.Random @@ -128,10 +127,12 @@ sealed abstract class ActiveSkillNormalEffect( tool, isSkillDualBreakOrTrialBreak ) - _ <- IO { - explosionLocations.foreach(coordinates => - world.createExplosion(coordinates.toLocation(world), 0f, false) - ) + _ <- ioOnMainThread.runAction { + SyncIO { + explosionLocations.foreach(coordinates => + world.createExplosion(coordinates.toLocation(world), 0f, false) + ) + } } } yield () @@ -248,7 +249,7 @@ object ActiveSkillNormalEffect extends Enum[ActiveSkillNormalEffect] { s"${DARK_RED}メテオ", "隕石を落とす", 100, - Material.FIREBALL + Material.FIRE_CHARGE ) } @@ -277,27 +278,28 @@ sealed abstract class ActiveSkillPremiumEffect( this match { case ActiveSkillPremiumEffect.MAGIC => - val colors = Array(DyeColor.RED, DyeColor.BLUE, DyeColor.YELLOW, DyeColor.GREEN) + val colors = Array( + Material.RED_WOOL, + Material.BLUE_WOOL, + Material.YELLOW_WOOL, + Material.GREEN_WOOL + ) // 破壊するブロックの中心位置 val centerBreak: Location = standard + ((breakArea.begin + breakArea.end) / 2) for { - randomColor <- IO { colors(Random.nextInt(colors.length)) } + randomWool <- IO { colors(Random.nextInt(colors.length)) } _ <- BreakUtil.massBreakBlock( player, breakBlocks, standard, tool, shouldPlayBreakSound = false, - Material.WOOL + randomWool ) _ <- IO { - breakBlocks.foreach { b => - val state = b.getState - state.getData.asInstanceOf[Wool].setColor(randomColor) - state.update() - } + breakBlocks.foreach(_.setType(randomWool)) } period <- IO { if (SeichiAssist.DEBUG) 100 else 10 } @@ -351,7 +353,7 @@ case object ActiveSkillPremiumEffect extends Enum[ActiveSkillPremiumEffect] { s"$RED$UNDERLINE${BOLD}マジック", "鶏が出る手品", 10, - Material.RED_ROSE + Material.POPPY ) } 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 a4b55429a7..dcf189630c 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 @@ -7,6 +7,7 @@ import com.github.unchama.seichiassist.SeichiAssist import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts import com.github.unchama.targetedeffect.player.FocusedSoundEffect import com.github.unchama.util.effect.BukkitResources +import org.bukkit.entity.AbstractArrow.PickupStatus import org.bukkit.entity._ import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.PotionMeta @@ -28,9 +29,10 @@ object ArrowEffects { def normalArrowEffect( implicit mainThread: OnMinecraftServerThread[IO] ): TargetedEffect[Player] = - arrowEffect[Arrow]( + arrowEffect[AbstractArrow]( ProjectileSpawnConfiguration(1.0, (0.0, 1.6, 0.0)), - Some(Sound.ENTITY_ARROW_SHOOT) + Some(Sound.ENTITY_ARROW_SHOOT), + _.setPickupStatus(PickupStatus.DISALLOWED) ) def singleArrowBlizzardEffect( @@ -65,10 +67,13 @@ object ArrowEffects { def singleArrowMeteoEffect( implicit mainThread: OnMinecraftServerThread[IO] ): TargetedEffect[Player] = - arrowEffect[Arrow]( + arrowEffect[AbstractArrow]( ProjectileSpawnConfiguration(1.0, (0.0, 1.6, 0.0)), Some(Sound.ENTITY_ARROW_SHOOT), - _.setGlowing(true) + { arrow => + arrow.setGlowing(true) + arrow.setPickupStatus(PickupStatus.DISALLOWED) + } ) def singleArrowExplosionEffect( 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 index 0a1f997474..f50b67d17e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/anywhereender/System.scala @@ -1,6 +1,5 @@ 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 @@ -25,9 +24,7 @@ object System { import ContextCoercion._ import cats.implicits._ - def wired[F[_]: BreakCountReadAPI[IO, *[_], Player]: Functor: ContextCoercion[*[_], G], G[ - _ - ]: Effect]( + def wired[F[_]: BreakCountReadAPI[IO, *[_], Player]: ContextCoercion[*[_], G], G[_]: Effect]( configuration: SystemConfiguration )(implicit onMainThread: OnMinecraftServerThread[IO]): System[G] = new System[G] { 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 c3cb3d3782..93baa0ae1c 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 @@ -1,7 +1,7 @@ package com.github.unchama.seichiassist.subsystems.autosave import cats.effect.{Sync, Timer} -import com.github.unchama.minecraft.actions.OnMinecraftServerThread +import com.github.unchama.minecraft.actions.{GetConnectedPlayers, OnMinecraftServerThread} import com.github.unchama.seichiassist.subsystems.autosave.application.{ CanNotifySaves, CanSaveWorlds, @@ -12,16 +12,17 @@ import com.github.unchama.seichiassist.subsystems.autosave.bukkit.instances.{ SyncCanNotifyBukkitSaves, SyncCanSaveBukkitWorlds } +import org.bukkit.entity.Player object System { - def backgroundProcess[F[_]: Sync: Timer: OnMinecraftServerThread, G[_]]( + def backgroundProcess[F[_]: Sync: Timer: OnMinecraftServerThread]( + implicit getConnectedPlayers: GetConnectedPlayers[F, Player], configuration: SystemConfiguration ): F[Nothing] = { - implicit val _configuration: SystemConfiguration = configuration - implicit val _canSaveWorlds: CanSaveWorlds[F] = SyncCanSaveBukkitWorlds[F] - implicit val _canNotifySaves: CanNotifySaves[F] = SyncCanNotifyBukkitSaves[F] + implicit val _canNotifySaves: CanNotifySaves[F] = + SyncCanNotifyBukkitSaves[F] WorldSaveRoutine() } 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 88e7cc3950..c86feb9364 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 @@ -1,17 +1,24 @@ package com.github.unchama.seichiassist.subsystems.autosave.bukkit.instances import cats.effect.Sync -import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.onMainThread +import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.subsystems.autosave.application.CanNotifySaves import com.github.unchama.seichiassist.util.SendMessageEffect import org.bukkit.Bukkit +import org.bukkit.entity.Player +import com.github.unchama.minecraft.actions.GetConnectedPlayers object SyncCanNotifyBukkitSaves { - def apply[F[_]: Sync]: CanNotifySaves[F] = (message: String) => - Sync[F].delay { - SendMessageEffect.sendMessageToEveryoneIgnoringPreference(message) - Bukkit.getLogger.info(message) - } + import cats.implicits._ + + def apply[F[_]: Sync: OnMinecraftServerThread]( + implicit getConnectedPlayers: GetConnectedPlayers[F, Player] + ): CanNotifySaves[F] = + (message: String) => + for { + _ <- SendMessageEffect.sendMessageToEveryoneIgnoringPreferenceM[String, F](message) + _ <- Sync[F].delay(Bukkit.getLogger.info(message)) + } yield () } 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 1a1e6188d8..f5c505d5a5 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 @@ -1,6 +1,6 @@ package com.github.unchama.seichiassist.subsystems.autosave.bukkit.instances -import cats.effect.{Sync, SyncIO} +import cats.effect.SyncIO import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.subsystems.autosave.application.CanSaveWorlds import org.bukkit.{Bukkit, World} @@ -10,7 +10,7 @@ import scala.annotation.tailrec object SyncCanSaveBukkitWorlds { - def apply[F[_]: Sync: OnMinecraftServerThread]: CanSaveWorlds[F] = new CanSaveWorlds[F] { + def apply[F[_]: OnMinecraftServerThread]: CanSaveWorlds[F] = new CanSaveWorlds[F] { val save: SyncIO[Unit] = SyncIO { def saveWorld(world: World): Unit = { // WARNを防ぐためMinecraftサーバーデフォルトの自動セーブは無効化 diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/blockliquidstream/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/blockliquidstream/System.scala new file mode 100644 index 0000000000..b6907fe2d2 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/blockliquidstream/System.scala @@ -0,0 +1,15 @@ +package com.github.unchama.seichiassist.subsystems.blockliquidstream + +import com.github.unchama.seichiassist.meta.subsystem.Subsystem +import com.github.unchama.seichiassist.subsystems.blockliquidstream.bukkit.LiquidStreamListener +import org.bukkit.event.Listener + +object System { + + def wired[F[_]]: Subsystem[F] = { + new Subsystem[F] { + override val listeners: Seq[Listener] = Seq(new LiquidStreamListener) + } + } + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/blockliquidstream/bukkit/LiquidStreamListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/blockliquidstream/bukkit/LiquidStreamListener.scala new file mode 100644 index 0000000000..43e9231333 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/blockliquidstream/bukkit/LiquidStreamListener.scala @@ -0,0 +1,28 @@ +package com.github.unchama.seichiassist.subsystems.blockliquidstream.bukkit + +import org.bukkit.block.data.Waterlogged +import org.bukkit.event.block.BlockFromToEvent +import org.bukkit.event.{EventHandler, EventPriority, Listener} +import org.bukkit.block.Block +import com.github.unchama.seichiassist.ManagedWorld._ + +class LiquidStreamListener extends Listener { + + @EventHandler(priority = EventPriority.HIGHEST) + def onBlockMove(event: BlockFromToEvent): Unit = { + event.setCancelled(isLiquidStream(event.getBlock)) + } + + private def isLiquidStream(block: Block): Boolean = { + val world = block.getWorld + + if (world.isNotSeichi) return false + val blockData = block.getBlockData + + blockData match { + case waterlogged: Waterlogged if !block.isLiquid && !waterlogged.isWaterlogged => false + case _ => true + } + } + +} 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 b459659a79..50e547825f 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 @@ -75,6 +75,7 @@ object AchievementCommand { ) ) + // TODO: パーサーを分けるべき def executor[F[_]: ConcurrentEffect]( implicit service: AchievementBookingService[F] ): TabExecutor = ContextualExecutorBuilder @@ -149,7 +150,7 @@ object AchievementCommand { }.combineAll } - execution() + execution().flatMap(_.apply(sender)) } .asNonBlockingTabExecutor() } 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 cf318220e4..b8d8db6729 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 @@ -26,7 +26,6 @@ class JdbcBookedAchievementPersistenceRepository[F[_]](implicit SyncContext: Syn | values (${key.toString}, $achievementId, ${operation.toString})""" .stripMargin .update() - .apply() } } } @@ -49,7 +48,6 @@ class JdbcBookedAchievementPersistenceRepository[F[_]](implicit SyncContext: Syn ) } .toList() - .apply() } } } @@ -64,7 +62,6 @@ class JdbcBookedAchievementPersistenceRepository[F[_]](implicit SyncContext: Syn | where player_uuid = ${key.toString} and completed_at is null""" .stripMargin .update() - .apply() } } } @@ -79,7 +76,6 @@ class JdbcBookedAchievementPersistenceRepository[F[_]](implicit SyncContext: Syn sql"select (uuid) from playerdata where name = $playerName" .map { rs => rs.string("uuid") } .toList() - .apply() .head ) } 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 0eb97809c1..4869d51fe4 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 @@ -5,7 +5,6 @@ import cats.effect.concurrent.Ref import cats.effect.{Effect, Sync} import com.github.unchama.datarepository.KeyedDataRepository import com.github.unchama.fs2.workaround.fs3.Fs3Topic -import com.github.unchama.generic.ContextCoercion import com.github.unchama.generic.effect.EffectExtra import com.github.unchama.seichiassist.subsystems.breakcount.domain.SeichiAmountData import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.SeichiExpAmount @@ -27,9 +26,7 @@ object IncrementSeichiExp { /** * 与えられたデータレポジトリと更新を流すトピックを用いてプレーヤーの整地量を増加させるような 代数を作成する。 */ - def using[F[_]: Sync: ClassifyPlayerWorld[*[_], Player], G[_]: Effect: ContextCoercion[F, *[ - _ - ]], Player]( + def using[F[_]: Sync: ClassifyPlayerWorld[*[_], Player], G[_]: Effect, Player]( dataRepository: KeyedDataRepository[Player, Ref[F, SeichiAmountData]], dataTopic: Fs3Topic[G, Option[(Player, SeichiAmountData)]] ): IncrementSeichiExp[F, Player] = 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 73def02d89..d2b60991e1 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 @@ -17,7 +17,7 @@ case class SeichiExpAmount private (amount: BigDecimal) extends AnyVal { def subtract(a: SeichiExpAmount): SeichiExpAmount = mapAmount(_ - a.amount) - def formatted: String = amount.toLong.formatted("%,d") + def formatted: String = String.format("%,d", amount.longValue) } object SeichiExpAmount { 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 8ef24a54dd..f6be481b3e 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 @@ -23,7 +23,6 @@ class JdbcSeichiAmountDataPersistence[F[_]](implicit F: Sync[F]) ) } .first() - .apply() } } @@ -32,7 +31,6 @@ class JdbcSeichiAmountDataPersistence[F[_]](implicit F: Sync[F]) DB.localTx { implicit session => sql"update playerdata set totalbreaknum = ${value.expAmount.amount} where uuid = ${key.toString}" .update() - .apply() } } 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 b8337299bd..47563698cd 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 @@ -15,20 +15,19 @@ import com.github.unchama.seichiassist.subsystems.breakcount.subsystems.notifica import com.github.unchama.seichiassist.subsystems.discordnotification.DiscordNotificationAPI import com.github.unchama.seichiassist.util.{ LaunchFireWorksEffect, - PlayerSendable, SendMessageEffect, SendSoundEffect } import org.bukkit.ChatColor.{BOLD, GOLD} import org.bukkit.Sound import org.bukkit.entity.Player +import com.github.unchama.seichiassist.util.PlayerSendable._ //FIXME ファイル名とやっていることが違うようになっているので修正するべき。 //例えば、10億の倍数到達時の通知はLevelUp時の通知ではない //また、BukkitNotifyLevelUpなのにdiffの展開やいつメッセージを出すかなどを扱うべきでない。 object BukkitNotifyLevelUp { - import PlayerSendable.forString import cats.implicits._ def apply[F[_]: OnMinecraftServerThread: ConcurrentEffect: DiscordNotificationAPI] @@ -44,17 +43,21 @@ object BukkitNotifyLevelUp { .expAmount .amount >= nextTenBillion ) { + // TODO: ここのSyncIOを剥がせそう OnMinecraftServerThread[F].runAction(SyncIO { val notificationMessage = s"${player.getName}の総整地量が${(newBreakAmount.expAmount.amount / 100000000).toInt}億に到達しました!" - SendMessageEffect.sendMessageToEveryoneIgnoringPreference( - s"$GOLD$BOLD$notificationMessage" - )(forString[IO]) + + SendMessageEffect + .sendMessageToEveryoneIgnoringPreferenceIO(s"$GOLD$BOLD$notificationMessage")( + forString[IO] + ) + .unsafeRunAsyncAndForget() DiscordNotificationAPI[F] .sendPlainText(notificationMessage) .toIO .unsafeRunAsyncAndForget() - SendSoundEffect.sendEverySound(Sound.ENTITY_ENDERDRAGON_DEATH, 1.0f, 1.2f) + SendSoundEffect.sendEverySound(Sound.ENTITY_ENDER_DRAGON_DEATH, 1.0f, 1.2f) }) } else Applicative[F].unit } 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 4d5e1b8ed9..2da81fedcb 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 @@ -73,7 +73,7 @@ object ExpBarSynchronizationRepositoryTemplate { } yield (bossBar, fiberPromise) } - def finalization[G[_]: Sync, F[_]: ConcurrentEffect: ContextCoercion[G, *[_]], Player] + def finalization[G[_]: Sync, F[_]: ConcurrentEffect, Player] : RepositoryFinalization[G, Player, RepositoryValueType[F, Player]] = RepositoryFinalization.withoutAnyPersistence { case (_, (_, fiberPromise)) => 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 5f12a59787..546ea08c18 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 @@ -24,7 +24,6 @@ class JdbcBreakCountBarVisibilityPersistence[F[_]](implicit F: Sync[F]) } } .first() - .apply() } } @@ -33,7 +32,6 @@ class JdbcBreakCountBarVisibilityPersistence[F[_]](implicit F: Sync[F]) DB.localTx { implicit session => sql"update playerdata set expvisible = ${value == BreakCountBarVisibility.Shown} where uuid = ${key.toString}" .update() - .apply() } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakskilltargetconfig/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakskilltargetconfig/System.scala index 08ded6514f..5e2e56eee5 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakskilltargetconfig/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakskilltargetconfig/System.scala @@ -1,7 +1,7 @@ package com.github.unchama.seichiassist.subsystems.breakskilltargetconfig import cats.data.Kleisli -import cats.effect.{Sync, SyncEffect} +import cats.effect.SyncEffect import com.github.unchama.datarepository.bukkit.player.BukkitRepositoryControls import com.github.unchama.generic.ContextCoercion import com.github.unchama.seichiassist.meta.subsystem.Subsystem @@ -21,7 +21,7 @@ object System { import cats.implicits._ - def wired[F[_]: Sync, G[_]: SyncEffect: ContextCoercion[*[_], F]]: G[System[F, Player]] = { + def wired[F[_], G[_]: SyncEffect: ContextCoercion[*[_], F]]: G[System[F, Player]] = { implicit val breakSkillTargetConfigPersistence: BreakSkillTargetConfigPersistence[G] = new JdbcBreakSkillTargetConfigPersistence[G] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakskilltargetconfig/persistence/JdbcBreakSkillTargetConfigPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakskilltargetconfig/persistence/JdbcBreakSkillTargetConfigPersistence.scala index 2ed07fa43d..5270f088e5 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/breakskilltargetconfig/persistence/JdbcBreakSkillTargetConfigPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breakskilltargetconfig/persistence/JdbcBreakSkillTargetConfigPersistence.scala @@ -22,7 +22,6 @@ class JdbcBreakSkillTargetConfigPersistence[F[_]: Sync] } } .toList() - .apply() .flatten .toMap } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/BreakSuppressionPreferenceAPI.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/BreakSuppressionPreferenceAPI.scala new file mode 100644 index 0000000000..9e18059cf1 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/BreakSuppressionPreferenceAPI.scala @@ -0,0 +1,25 @@ +package com.github.unchama.seichiassist.subsystems.breaksuppressionpreference + +import cats.data.Kleisli + +trait BreakSuppressionPreferenceAPI[F[_], Player] { + + /** + * @return 破壊抑制の設定をトグルする作用 + */ + def toggleBreakSuppression: Kleisli[F, Player, Unit] + + /** + * @return 現在の破壊抑制の設定を取得する作用 + */ + def isBreakSuppressionEnabled(player: Player): F[Boolean] + +} + +object BreakSkillTriggerConfigAPI { + + def apply[F[_], Player]( + implicit ev: BreakSuppressionPreferenceAPI[F, Player] + ): BreakSuppressionPreferenceAPI[F, Player] = ev + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/System.scala new file mode 100644 index 0000000000..b62234845e --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/System.scala @@ -0,0 +1,58 @@ +package com.github.unchama.seichiassist.subsystems.breaksuppressionpreference + +import cats.data.Kleisli +import cats.effect.SyncEffect +import com.github.unchama.datarepository.bukkit.player.BukkitRepositoryControls +import com.github.unchama.generic.ContextCoercion +import com.github.unchama.seichiassist.meta.subsystem.Subsystem +import com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.application.repository.BreakSuppressionPreferenceRepositoryDefinition +import com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.domain.BreakSuppressionPreferencePersistence +import com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.persistence.JdbcBreakSuppressionPreferencePersistence +import org.bukkit.entity.Player + +trait System[F[_], Player] extends Subsystem[F] { + val api: BreakSuppressionPreferenceAPI[F, Player] +} + +object System { + + import cats.implicits._ + + def wired[F[_], G[_]: SyncEffect: ContextCoercion[*[_], F]]: G[System[F, Player]] = { + implicit val breakSuppressionPreferencePersistence + : BreakSuppressionPreferencePersistence[G] = + new JdbcBreakSuppressionPreferencePersistence[G] + + for { + breakSuppressionPreferenceRepositoryControls <- BukkitRepositoryControls.createHandles( + BreakSuppressionPreferenceRepositoryDefinition.withContext[G, Player] + ) + } yield { + val breakSuppressionPreferenceRepository = + breakSuppressionPreferenceRepositoryControls.repository + + new System[F, Player] { + override val api: BreakSuppressionPreferenceAPI[F, Player] = + new BreakSuppressionPreferenceAPI[F, Player] { + override def toggleBreakSuppression: Kleisli[F, Player, Unit] = + Kleisli { player => + ContextCoercion( + breakSuppressionPreferenceRepository(player) + .update(_.toggleBreakSuppression()) + ) + } + + override def isBreakSuppressionEnabled(player: Player): F[Boolean] = + ContextCoercion( + breakSuppressionPreferenceRepository(player).get.map(_.doBreakSuppression) + ) + } + + override val managedRepositoryControls: Seq[BukkitRepositoryControls[F, _]] = Seq( + breakSuppressionPreferenceRepositoryControls.coerceFinalizationContextTo[F] + ) + } + } + } + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/application/BreakSuppressionPreferenceRepositoryDefinition.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/application/BreakSuppressionPreferenceRepositoryDefinition.scala new file mode 100644 index 0000000000..8003970bd9 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/application/BreakSuppressionPreferenceRepositoryDefinition.scala @@ -0,0 +1,23 @@ +package com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.application.repository + +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.breaksuppressionpreference.domain.{ + BreakSuppressionPreference, + BreakSuppressionPreferencePersistence +} + +object BreakSuppressionPreferenceRepositoryDefinition { + + def withContext[F[_]: Sync, Player]( + implicit persistence: BreakSuppressionPreferencePersistence[F] + ): RepositoryDefinition[F, Player, Ref[F, BreakSuppressionPreference]] = + RefDictBackedRepositoryDefinition + .usingUuidRefDict[F, Player, BreakSuppressionPreference](persistence)( + BreakSuppressionPreference.initial + ) + .toRefRepository + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/domain/BreakSuppressionPreference.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/domain/BreakSuppressionPreference.scala new file mode 100644 index 0000000000..d0725622f4 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/domain/BreakSuppressionPreference.scala @@ -0,0 +1,24 @@ +package com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.domain + +case class BreakSuppressionPreference(doBreakSuppression: Boolean) { + + /** + * @return 破壊抑制の設定をトグルする + */ + def toggleBreakSuppression(): BreakSuppressionPreference = + this.copy(doBreakSuppression = !this.doBreakSuppression) + + /** + * @return 現在の破壊抑制の設定を取得する + */ + def isBreakSuppressionEnabled: Boolean = doBreakSuppression +} + +object BreakSuppressionPreference { + + /** + * [[BreakSuppressionPreference]]の初期値 + */ + val initial: BreakSuppressionPreference = + BreakSuppressionPreference(doBreakSuppression = false) +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/domain/BreakSuppressionPreferencePersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/domain/BreakSuppressionPreferencePersistence.scala new file mode 100644 index 0000000000..1b2810b0b2 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/domain/BreakSuppressionPreferencePersistence.scala @@ -0,0 +1,8 @@ +package com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.domain + +import com.github.unchama.generic.RefDict + +import java.util.UUID + +trait BreakSuppressionPreferencePersistence[F[_]] + extends RefDict[F, UUID, BreakSuppressionPreference] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/infrastructure/JdbcBreakSuppressionPreferencePersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/infrastructure/JdbcBreakSuppressionPreferencePersistence.scala new file mode 100644 index 0000000000..e1a063f786 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/infrastructure/JdbcBreakSuppressionPreferencePersistence.scala @@ -0,0 +1,34 @@ +package com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.persistence + +import cats.effect.Sync +import com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.domain.{ + BreakSuppressionPreference, + BreakSuppressionPreferencePersistence +} +import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} + +import java.util.UUID + +class JdbcBreakSuppressionPreferencePersistence[F[_]: Sync] + extends BreakSuppressionPreferencePersistence[F] { + + override def read(key: UUID): F[Option[BreakSuppressionPreference]] = Sync[F].delay { + DB.readOnly { implicit session => + sql"SELECT do_break_suppression_due_to_mana FROM player_break_suppression_preference WHERE uuid = ${key.toString}" + .map(_.boolean("do_break_suppression_due_to_mana")) + .single() + .map(BreakSuppressionPreference) + } + } + + override def write(key: UUID, value: BreakSuppressionPreference): F[Unit] = Sync[F].delay { + DB.localTx { implicit session => + val uuid = key.toString + sql"""INSERT INTO player_break_suppression_preference (uuid, do_break_suppression_due_to_mana) + | VALUES ($uuid, ${value.doBreakSuppression}) + | ON DUPLICATE KEY UPDATE + | do_break_suppression_due_to_mana = VALUES(do_break_suppression_due_to_mana) + """.stripMargin.update() + } + } +} 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 107dde5198..9116673b67 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 @@ -16,15 +16,11 @@ trait BuildCountAPI[F[_], G[_], Player] { implicit val incrementBuildExpWhenBuiltByHand: IncrementBuildExpWhenBuiltByHand[G, Player] - implicit val incrementBuildExpWhenBuiltWithSkill: IncrementBuildExpWhenBuiltWithSkill[ - G, - Player - ] - - implicit val playerBuildAmountRepository: KeyedDataRepository[ - Player, - ReadOnlyRef[G, BuildAmountData] - ] + implicit val incrementBuildExpWhenBuiltWithSkill + : IncrementBuildExpWhenBuiltWithSkill[G, Player] + + implicit val playerBuildAmountRepository + : KeyedDataRepository[Player, ReadOnlyRef[G, BuildAmountData]] val buildAmountUpdates: fs2.Stream[F, (Player, 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 b85f72cca1..2c3995209c 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 @@ -1,7 +1,6 @@ package com.github.unchama.seichiassist.subsystems.buildcount import cats.effect.{Clock, ConcurrentEffect, SyncEffect} -import com.github.unchama.concurrent.NonServerThreadContextShift import com.github.unchama.datarepository.KeyedDataRepository import com.github.unchama.datarepository.bukkit.player.BukkitRepositoryControls import com.github.unchama.datarepository.template.RepositoryDefinition @@ -9,7 +8,7 @@ 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.ratelimiting.RateLimiter -import com.github.unchama.minecraft.actions.OnMinecraftServerThread +import com.github.unchama.minecraft.actions.{GetConnectedPlayers, OnMinecraftServerThread} import com.github.unchama.seichiassist.meta.subsystem.Subsystem import com.github.unchama.seichiassist.subsystems.buildcount.application.actions.{ ClassifyPlayerWorld, @@ -53,10 +52,11 @@ object System { def wired[F[ _ - ]: OnMinecraftServerThread: ConcurrentEffect: NonServerThreadContextShift: ErrorLogger: DiscordNotificationAPI, G[ + ]: OnMinecraftServerThread: ConcurrentEffect: ErrorLogger: DiscordNotificationAPI, G[ _ ]: SyncEffect: ContextCoercion[*[_], F]: Clock]( - implicit configuration: Configuration + implicit configuration: Configuration, + getConnectedPlayers: GetConnectedPlayers[F, Player] ): F[System[F, G]] = { implicit val expMultiplier: BuildExpMultiplier = configuration.multipliers implicit val persistence: JdbcBuildAmountDataPersistence[G] = 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 2170bbaeed..aedb816544 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 @@ -5,7 +5,6 @@ import cats.effect.concurrent.Ref import cats.effect.{Effect, Sync} import com.github.unchama.datarepository.KeyedDataRepository import com.github.unchama.fs2.workaround.fs3.Fs3Topic -import com.github.unchama.generic.ContextCoercion import com.github.unchama.generic.effect.EffectExtra import com.github.unchama.generic.ratelimiting.RateLimiter import com.github.unchama.seichiassist.subsystems.buildcount.application.BuildExpMultiplier @@ -31,10 +30,7 @@ object IncrementBuildExpWhenBuiltByHand { implicit ev: IncrementBuildExpWhenBuiltByHand[F, Player] ): IncrementBuildExpWhenBuiltByHand[F, Player] = ev - def using[F[_]: ClassifyPlayerWorld[*[_], Player], G[_]: Effect: ContextCoercion[ - F, - *[_] - ], Player]( + def using[F[_]: ClassifyPlayerWorld[*[_], Player], G[_]: Effect, Player]( rateLimiterRepository: KeyedDataRepository[Player, RateLimiter[F, BuildExpAmount]], dataRepository: KeyedDataRepository[Player, Ref[F, BuildAmountData]], dataTopic: Fs3Topic[G, (Player, BuildAmountData)] 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 a6c5b3311c..2418f1873a 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 @@ -59,8 +59,7 @@ object RateLimiterRepositoryDefinitions { } def finalization[F[_]: Sync: JavaTime, Player: HasUuid]( - implicit config: Configuration, - persistence: BuildAmountRateLimitPersistence[F] + implicit persistence: BuildAmountRateLimitPersistence[F] ): RepositoryFinalization[F, Player, RateLimiter[F, BuildExpAmount]] = RepositoryFinalization.withoutAnyFinalization { case (p, rateLimiter) => 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 bb8a1d12b6..2339238309 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 @@ -24,7 +24,6 @@ class JdbcBuildAmountDataPersistence[F[_]](implicit F: Sync[F]) BuildAmountData(exp) } .first() - .apply() } } @@ -34,7 +33,6 @@ class JdbcBuildAmountDataPersistence[F[_]](implicit F: Sync[F]) sql"update playerdata set build_count = ${value.expAmount.amount} where uuid = ${key.toString}" .stripMargin .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 3f7f1d8046..4e523046ed 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 @@ -1,7 +1,6 @@ package com.github.unchama.seichiassist.subsystems.buildcount.infrastructure import cats.effect.Sync -import com.github.unchama.seichiassist.subsystems.buildcount.application.Configuration import com.github.unchama.seichiassist.subsystems.buildcount.domain.BuildAmountRateLimiterSnapshot import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.BuildExpAmount import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountRateLimitPersistence @@ -10,8 +9,7 @@ import scalikejdbc._ import java.util.UUID class JdbcBuildAmountRateLimitPersistence[SyncContext[_]]( - implicit SyncContext: Sync[SyncContext], - config: Configuration + implicit SyncContext: Sync[SyncContext] ) extends BuildAmountRateLimitPersistence[SyncContext] { override def read(key: UUID): SyncContext[Option[BuildAmountRateLimiterSnapshot]] = @@ -26,7 +24,6 @@ class JdbcBuildAmountRateLimitPersistence[SyncContext[_]]( BuildAmountRateLimiterSnapshot(exp, ldt) } .first() - .apply() } } @@ -40,7 +37,7 @@ class JdbcBuildAmountRateLimitPersistence[SyncContext[_]]( | on duplicate key update | available_permission = ${value.amount.toPlainString}, | record_date = ${value.recordTime} - |""".stripMargin.update().apply() + |""".stripMargin.update() } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/subsystems/notification/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/subsystems/notification/System.scala index 02532af23e..86741a7835 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/subsystems/notification/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/subsystems/notification/System.scala @@ -2,7 +2,7 @@ package com.github.unchama.seichiassist.subsystems.buildcount.subsystems.notific import cats.effect.Concurrent import com.github.unchama.generic.effect.stream.StreamExtra -import com.github.unchama.minecraft.actions.OnMinecraftServerThread +import com.github.unchama.minecraft.actions.{GetConnectedPlayers, OnMinecraftServerThread} import com.github.unchama.seichiassist.subsystems.buildcount.BuildCountAPI import com.github.unchama.seichiassist.subsystems.buildcount.subsystems.notification.application.actions.{ NotifyBuildAmountThreshold, @@ -22,7 +22,7 @@ object System { _ ]: Concurrent: ErrorLogger: OnMinecraftServerThread: DiscordNotificationAPI, G[_], A]( buildCountReadAPI: BuildCountAPI[F, G, Player] - ): F[A] = { + )(implicit getConnectedPlayers: GetConnectedPlayers[F, Player]): F[A] = { val notifyLevelUp: NotifyLevelUp[F, Player] = BukkitNotifyLevelUp[F] val notifyBuildAmountThreshold: NotifyBuildAmountThreshold[F, Player] = BukkitNotifyBuildAmountThreshold[F] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/subsystems/notification/bukkit/actions/BukkitNotifyBuildAmountThreshold.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/subsystems/notification/bukkit/actions/BukkitNotifyBuildAmountThreshold.scala index a2c53c7a15..325279cef1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/subsystems/notification/bukkit/actions/BukkitNotifyBuildAmountThreshold.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/subsystems/notification/bukkit/actions/BukkitNotifyBuildAmountThreshold.scala @@ -1,23 +1,24 @@ package com.github.unchama.seichiassist.subsystems.buildcount.subsystems.notification.bukkit.actions -import cats.effect.{IO, Sync} +import cats.effect.Sync import com.github.unchama.generic.Diff -import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.onMainThread +import com.github.unchama.minecraft.actions.{GetConnectedPlayers, OnMinecraftServerThread} import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountData import com.github.unchama.seichiassist.subsystems.buildcount.subsystems.notification.application.actions.NotifyBuildAmountThreshold import com.github.unchama.seichiassist.subsystems.discordnotification.DiscordNotificationAPI -import com.github.unchama.seichiassist.util.{PlayerSendable, SendMessageEffect, SendSoundEffect} +import com.github.unchama.seichiassist.util.{SendMessageEffect, SendSoundEffect} import org.bukkit.ChatColor.{BOLD, GOLD} import org.bukkit.Sound import org.bukkit.entity.Player object BukkitNotifyBuildAmountThreshold { - import PlayerSendable.forString import cats.implicits._ // TODO: BukkitNotifyLevelUpなのにdiffの展開やいつメッセージを出すかなどを扱うべきでない。 - def apply[F[_]: Sync: DiscordNotificationAPI]: NotifyBuildAmountThreshold[F, Player] = { + def apply[F[_]: Sync: DiscordNotificationAPI: OnMinecraftServerThread: GetConnectedPlayers[*[ + _ + ], Player]]: NotifyBuildAmountThreshold[F, Player] = { new NotifyBuildAmountThreshold[F, Player] { override def ofBuildAmountTo(player: Player)(diff: Diff[BuildAmountData]): F[Unit] = { val Diff(oldBuildAmount, newBuildAmount) = diff @@ -46,11 +47,10 @@ object BukkitNotifyBuildAmountThreshold { val notificationMessage = s"${player.getName}の総建築量が${newBuildAmountDisplay}に到達しました!" - Sync[F].delay { - SendMessageEffect.sendMessageToEveryoneIgnoringPreference( - s"$GOLD$BOLD$notificationMessage" - )(forString[IO]) - SendSoundEffect.sendEverySound(Sound.ENTITY_ENDERDRAGON_DEATH, 1.0f, 1.2f) + SendMessageEffect.sendMessageToEveryoneIgnoringPreferenceM[String, F]( + s"$GOLD$BOLD$notificationMessage" + ) >> Sync[F].delay { + SendSoundEffect.sendEverySound(Sound.ENTITY_ENDER_DRAGON_DEATH, 1.0f, 1.2f) } >> DiscordNotificationAPI[F].sendPlainText(notificationMessage) } else Sync[F].unit } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/subsystems/notification/bukkit/actions/BukkitNotifyLevelUp.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/subsystems/notification/bukkit/actions/BukkitNotifyLevelUp.scala index 69d165e8ee..7695570b28 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/subsystems/notification/bukkit/actions/BukkitNotifyLevelUp.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/buildcount/subsystems/notification/bukkit/actions/BukkitNotifyLevelUp.scala @@ -1,30 +1,29 @@ package com.github.unchama.seichiassist.subsystems.buildcount.subsystems.notification.bukkit.actions -import cats.effect.{IO, Sync} +import cats.effect.Sync import com.github.unchama.generic.Diff -import com.github.unchama.minecraft.actions.OnMinecraftServerThread -import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.onMainThread +import com.github.unchama.minecraft.actions.{GetConnectedPlayers, OnMinecraftServerThread} import com.github.unchama.seichiassist.subsystems.buildcount.domain.explevel.{ BuildAssistExpTable, BuildLevel } import com.github.unchama.seichiassist.subsystems.buildcount.subsystems.notification.application.actions.NotifyLevelUp import com.github.unchama.seichiassist.subsystems.discordnotification.DiscordNotificationAPI -import com.github.unchama.seichiassist.util.SendMessageEffect.sendMessageToEveryoneIgnoringPreference +import com.github.unchama.seichiassist.util.SendMessageEffect.sendMessageToEveryoneIgnoringPreferenceM import com.github.unchama.seichiassist.util.SendSoundEffect.sendEverySound -import com.github.unchama.seichiassist.util.{LaunchFireWorksEffect, PlayerSendable} +import com.github.unchama.seichiassist.util.LaunchFireWorksEffect import org.bukkit.ChatColor.GOLD import org.bukkit.Sound import org.bukkit.entity.Player object BukkitNotifyLevelUp { - import PlayerSendable.forString import cats.implicits._ // TODO: BukkitNotifyLevelUpなのにdiffの展開やいつメッセージを出すかなどを扱うべきでない。 - def apply[F[_]: Sync: OnMinecraftServerThread: DiscordNotificationAPI] - : NotifyLevelUp[F, Player] = { + def apply[F[_]: Sync: OnMinecraftServerThread: DiscordNotificationAPI: GetConnectedPlayers[*[ + _ + ], Player]]: NotifyLevelUp[F, Player] = { new NotifyLevelUp[F, Player] { override def ofBuildLevelTo(player: Player)(diff: Diff[BuildLevel]): F[Unit] = { val Diff(oldLevel, newLevel) = diff @@ -32,11 +31,12 @@ object BukkitNotifyLevelUp { val messageLevelMaxGlobal = s"$GOLD${player.getName}の建築レベルが最大Lvに到達したよ(`・ω・´)" val messageLevelMaxDiscord = s"${player.getName}の建築レベルが最大Lvに到達したよ(`・ω・´)" val messageLevelMaxPlayer = s"${GOLD}最大Lvに到達したよ(`・ω・´)" - Sync[F].delay { - sendMessageToEveryoneIgnoringPreference(messageLevelMaxGlobal)(forString[IO]) - player.sendMessage(messageLevelMaxPlayer) - sendEverySound(Sound.ENTITY_ENDERDRAGON_DEATH, 1.0f, 1.2f) - } >> LaunchFireWorksEffect.launchFireWorks[F]( + + sendMessageToEveryoneIgnoringPreferenceM[String, F](messageLevelMaxGlobal) >> Sync[F] + .delay { + player.sendMessage(messageLevelMaxPlayer) + sendEverySound(Sound.ENTITY_ENDER_DRAGON_DEATH, 1.0f, 1.2f) + } >> LaunchFireWorksEffect.launchFireWorks[F]( player.getLocation ) >> DiscordNotificationAPI[F].sendPlainText(messageLevelMaxDiscord) } else if (oldLevel < newLevel) { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/canceldamagebyfallingblocks/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/canceldamagebyfallingblocks/System.scala new file mode 100644 index 0000000000..0b995a87cb --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/canceldamagebyfallingblocks/System.scala @@ -0,0 +1,15 @@ +package com.github.unchama.seichiassist.subsystems.canceldamagebyfallingblocks + +import com.github.unchama.seichiassist.meta.subsystem.Subsystem +import com.github.unchama.seichiassist.subsystems.canceldamagebyfallingblocks.bukkit.listeners.PlayerDamageByBlockEvents +import org.bukkit.event.Listener + +object System { + + def wired[F[_]]: Subsystem[F] = { + new Subsystem[F] { + override val listeners: Seq[Listener] = Seq(PlayerDamageByBlockEvents) + } + } + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/canceldamagebyfallingblocks/bukkit/listeners/PlayerDamageByBlockEvents.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/canceldamagebyfallingblocks/bukkit/listeners/PlayerDamageByBlockEvents.scala new file mode 100644 index 0000000000..aafe84e885 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/canceldamagebyfallingblocks/bukkit/listeners/PlayerDamageByBlockEvents.scala @@ -0,0 +1,22 @@ +package com.github.unchama.seichiassist.subsystems.canceldamagebyfallingblocks.bukkit.listeners + +import org.bukkit.entity.Player +import org.bukkit.event.entity.EntityDamageEvent +import org.bukkit.event.entity.EntityDamageEvent.DamageCause +import org.bukkit.event.{EventHandler, Listener} +import com.github.unchama.seichiassist.ManagedWorld._ + +object PlayerDamageByBlockEvents extends Listener { + + @EventHandler + def onDamage(e: EntityDamageEvent): Unit = { + e.getEntity match { + // 整地ワールドではブロックの落下ダメージを無効化する + case player: Player + if e.getCause == DamageCause.FALLING_BLOCK && player.getWorld.isSeichi => + e.setDamage(0.0) + case _ => + } + } + +} 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 f56d0bce5e..7ce30513dc 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 @@ -9,15 +9,13 @@ import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountReadAPI import com.github.unchama.seichiassist.subsystems.chatratelimiter.application.ChatRateLimitRepositoryDefinition import com.github.unchama.seichiassist.subsystems.chatratelimiter.bukkit.listeners.RateLimitCheckListener import com.github.unchama.seichiassist.subsystems.chatratelimiter.domain.ObtainChatPermission -import io.chrisdavenport.log4cats.ErrorLogger 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, G[_]: SyncEffect: ContextCoercion[*[_], F]: Timer]( + implicit breakCountAPI: BreakCountReadAPI[F, G, Player] + ): F[Subsystem[F]] = { val repository = ChatRateLimitRepositoryDefinition.inSyncContext[G, Player] for { 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 f4e84a3810..1e078261c2 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 @@ -6,20 +6,18 @@ import com.github.unchama.seichiassist.subsystems.discordnotification.infrastruc DefaultDiscordNotificationSender, WebhookDiscordNotificationSender } -import io.chrisdavenport.log4cats.Logger trait System[F[_]] extends Subsystem[F] { implicit val globalNotification: DiscordNotificationAPI[F] } object System { - 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) + def wired[F[_]: Sync: ContextShift: LiftIO](configuration: SystemConfiguration): System[F] = + new System[F] { + implicit override val globalNotification: DiscordNotificationAPI[F] = { + 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 7950ecd446..5036a6d4e3 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 @@ -3,13 +3,11 @@ package com.github.unchama.seichiassist.subsystems.discordnotification.infrastru import cats.effect.LiftIO import com.github.unchama.seichiassist.SeichiAssist import com.github.unchama.seichiassist.subsystems.discordnotification.DiscordNotificationAPI -import io.chrisdavenport.log4cats.Logger /** * この実装は[[sendPlainText]]が呼ばれるたびに警告をロガーに流す以外は何もしない。 */ -final class DefaultDiscordNotificationSender[F[_]: Logger: LiftIO] - extends DiscordNotificationAPI[F] { +final class DefaultDiscordNotificationSender[F[_]: LiftIO] extends DiscordNotificationAPI[F] { override def sendPlainText(message: String): F[Unit] = { SeichiAssist .instance 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 cbe745cf0e..237981483a 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 @@ -2,7 +2,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 java.io.IOException import java.net.{HttpURLConnection, MalformedURLException, URL} @@ -24,14 +23,14 @@ class WebhookDiscordNotificationSender[F[_]: Sync: ContextShift] private (webhoo import io.circe.generic.auto._ import io.circe.syntax._ val markdownSafeMessage = message - .replaceAllLiterally("\\", "\\\\") - .replaceAllLiterally("_", "\\_") - .replaceAllLiterally("*", "\\*") - .replaceAllLiterally("`", "\\`") - .replaceAllLiterally("|", "\\|") - .replaceAllLiterally("@", "\\@") - .replaceAllLiterally("~", "\\~") - .replaceAllLiterally(":", "\\:") + .replace("\\", "\\\\") + .replace("_", "\\_") + .replace("*", "\\*") + .replace("`", "\\`") + .replace("|", "\\|") + .replace("@", "\\@") + .replace("~", "\\~") + .replace(":", "\\:") val json = WebhookDiscordNotificationSender.PlainMessage(markdownSafeMessage).asJson.noSpaces @@ -76,7 +75,7 @@ object WebhookDiscordNotificationSender { * @return * 初期化に成功した場合はSome、初期化中に特定の例外が送出された場合はNone。マスクされない例外が送出されたときは、再送出する。 */ - def tryCreate[F[_]: Sync: ContextShift: Logger]( + def tryCreate[F[_]: Sync: ContextShift]( webhookURL: String ): Option[WebhookDiscordNotificationSender[F]] = { try { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/donate/bukkit/commands/DonationCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/donate/bukkit/commands/DonationCommand.scala index db33f0340e..63101baebe 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/donate/bukkit/commands/DonationCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/donate/bukkit/commands/DonationCommand.scala @@ -1,6 +1,6 @@ package com.github.unchama.seichiassist.subsystems.donate.bukkit.commands -import cats.effect.ConcurrentEffect.ops.toAllConcurrentEffectOps +import cats.data.Kleisli import cats.effect.{ConcurrentEffect, Sync} import com.github.unchama.contextualexecutor.ContextualExecutor import com.github.unchama.contextualexecutor.builder.{ContextualExecutorBuilder, Parsers} @@ -12,7 +12,7 @@ import com.github.unchama.seichiassist.subsystems.donate.domain.{ PlayerName } import com.github.unchama.targetedeffect.UnfocusedEffect -import com.github.unchama.targetedeffect.commandsender.MessageEffect +import com.github.unchama.targetedeffect.commandsender.{MessageEffect, MessageEffectF} import org.bukkit.ChatColor._ import org.bukkit.command.TabExecutor import shapeless.HNil @@ -31,7 +31,7 @@ class DonationCommand[F[_]: ConcurrentEffect]( .beginConfiguration .thenParse(Parsers.identity) .thenParse(Parsers.integer(MessageEffect(s"${RED}付与するプレミアムエフェクトポイントは整数で指定してください。"))) - .buildWithExecutionF { context => + .buildWithExecutionCSEffect { context => import shapeless.:: val rawName :: rawDonatePoint :: HNil = context.args.parsed @@ -40,29 +40,31 @@ class DonationCommand[F[_]: ConcurrentEffect]( val dateRegex = "[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])".r val dateOpt = context.args.yetToBeParsed.headOption - val isMatchedPattern = dateOpt.forall(date => dateRegex.matches(date)) + val isMatchedPattern = dateOpt.forall(dateRegex.matches) - val eff = for { - date <- Sync[F].delay { + (for { + date <- Kleisli.liftF(Sync[F].delay { dateOpt match { case Some(date) if isMatchedPattern => val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") LocalDate.parse(date, dateTimeFormatter) case _ => LocalDate.now() } - } - _ <- donatePersistence - .addDonatePremiumEffectPoint(playerName, Obtained(donatePoint, date)) - .whenA(isMatchedPattern) + }) + _ <- Kleisli.liftF( + donatePersistence + .addDonatePremiumEffectPoint(playerName, Obtained(donatePoint, date)) + .whenA(isMatchedPattern) + ) } yield { - if (!isMatchedPattern) - MessageEffect(s"${RED}購入日はyyyy-MM-ddの形式で指定してください。") - else - MessageEffect( + if (isMatchedPattern) { + MessageEffectF( s"$GREEN${playerName.name}に${donatePoint.value}のプレミアムエフェクトポイントを付与しました。" ) - } - eff.toIO + } else { + MessageEffectF(s"${RED}購入日はyyyy-MM-ddの形式で指定してください。") + } + }).flatten } private val commandDescriptionExecutor: ContextualExecutor = diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/donate/infrastructure/JdbcDonatePersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/donate/infrastructure/JdbcDonatePersistence.scala index 01fa757d4e..75f2f194e8 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/donate/infrastructure/JdbcDonatePersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/donate/infrastructure/JdbcDonatePersistence.scala @@ -26,7 +26,7 @@ class JdbcDonatePersistence[F[_]: Sync] extends DonatePersistence[F] { | VALUES | ((SELECT uuid FROM playerdata WHERE name = ${playerName.name}), | ${obtainedPremiumEffectPoint.effectPoint.value}, - | ${obtainedPremiumEffectPoint.purchaseDate})""".stripMargin.execute().apply() + | ${obtainedPremiumEffectPoint.purchaseDate})""".stripMargin.execute() } } @@ -38,7 +38,6 @@ class JdbcDonatePersistence[F[_]: Sync] extends DonatePersistence[F] { DB.localTx { implicit session => sql"INSERT INTO donate_usage_history (uuid, effect_name, use_points) VALUES (${uuid.toString}, ${effect.entryName}, ${effect.usePoint})" .execute() - .apply() } } @@ -51,7 +50,7 @@ class JdbcDonatePersistence[F[_]: Sync] extends DonatePersistence[F] { | WHERE uuid = ${uuid.toString}) - ( | SELECT COALESCE(SUM(use_points), 0) AS sum_use_points FROM donate_usage_history | WHERE uuid = ${uuid.toString}) AS currentPremiumEffectPoints - """.stripMargin.map(_.int("currentPremiumEffectPoints")).single().apply() + """.stripMargin.map(_.int("currentPremiumEffectPoints")).single() DonatePremiumEffectPoint(premiumEffectPointsOpt.get) } } @@ -65,8 +64,7 @@ class JdbcDonatePersistence[F[_]: Sync] extends DonatePersistence[F] { .map(rs => Obtained(DonatePremiumEffectPoint(rs.int("get_points")), rs.localDate("timestamp")) ) - .list - .apply() + .list() .toVector } } @@ -83,8 +81,7 @@ class JdbcDonatePersistence[F[_]: Sync] extends DonatePersistence[F] { ActiveSkillPremiumEffect.withName(rs.string("effect_name")) ) ) - .list - .apply() + .list() .toVector } } 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 10cb78c9c2..5d286979de 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 @@ -2,13 +2,16 @@ package com.github.unchama.seichiassist.subsystems.dragonnighttime import cats.effect.{Concurrent, Timer} import com.github.unchama.generic.ContextCoercion +import com.github.unchama.minecraft.actions.{GetConnectedPlayers, OnMinecraftServerThread} import com.github.unchama.seichiassist.subsystems.dragonnighttime.application._ import com.github.unchama.seichiassist.subsystems.dragonnighttime.bukkit.instances._ import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.FastDiggingEffectWriteApi import com.github.unchama.seichiassist.subsystems.mana.ManaApi object System { - def backgroundProcess[F[_]: Concurrent: Timer, G[_]: ContextCoercion[*[_], F], Player]( + def backgroundProcess[F[_]: Concurrent: Timer: OnMinecraftServerThread: GetConnectedPlayers[*[ + _ + ], org.bukkit.entity.Player], G[_]: ContextCoercion[*[_], F], Player]( implicit fastDiggingEffectApi: FastDiggingEffectWriteApi[F, Player], manaApi: ManaApi[F, G, Player] ): F[Nothing] = { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/bukkit/instances/SyncCanBroadcastOnBukkit.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/bukkit/instances/SyncCanBroadcastOnBukkit.scala index aa16c0e760..20c1874748 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/bukkit/instances/SyncCanBroadcastOnBukkit.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/dragonnighttime/bukkit/instances/SyncCanBroadcastOnBukkit.scala @@ -1,15 +1,20 @@ package com.github.unchama.seichiassist.subsystems.dragonnighttime.bukkit.instances import cats.effect.Sync -import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.onMainThread +import com.github.unchama.minecraft.actions.{GetConnectedPlayers, OnMinecraftServerThread} import com.github.unchama.seichiassist.subsystems.dragonnighttime.application.CanBroadcast import com.github.unchama.seichiassist.util.SendMessageEffect import org.bukkit.Bukkit +import org.bukkit.entity.Player object SyncCanBroadcastOnBukkit { - def apply[F[_]: Sync]: CanBroadcast[F] = (message: String) => - Sync[F].delay { - SendMessageEffect.sendMessageToEveryoneIgnoringPreference(message) - Bukkit.getLogger.info(message) - } + import cats.implicits._ + + def apply[F[_]: Sync: OnMinecraftServerThread: GetConnectedPlayers[*[_], Player]] + : CanBroadcast[F] = (message: String) => { + for { + _ <- SendMessageEffect.sendMessageToEveryoneIgnoringPreferenceM[String, F](message) + _ <- Sync[F].delay(Bukkit.getLogger.info(message)) + } yield () + } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/elevator/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/elevator/System.scala new file mode 100644 index 0000000000..edbe85e27b --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/elevator/System.scala @@ -0,0 +1,25 @@ +package com.github.unchama.seichiassist.subsystems.elevator + +import cats.effect.ConcurrentEffect +import com.github.unchama.generic.effect.unsafe.EffectEnvironment +import com.github.unchama.seichiassist.meta.subsystem.Subsystem +import com.github.unchama.seichiassist.subsystems.elevator.application.actions.FindTeleportLocation +import com.github.unchama.seichiassist.subsystems.elevator.bukkit.actions.BukkitFindTeleportLocation +import com.github.unchama.seichiassist.subsystems.elevator.bukkit.listeners.ElevatorEventsListener +import org.bukkit.Location +import org.bukkit.event.Listener + +object System { + + def wired[F[_]: ConcurrentEffect]( + implicit effectEnvironment: EffectEnvironment + ): Subsystem[F] = { + implicit val findTeleportLocation: FindTeleportLocation[F, Location] = + new BukkitFindTeleportLocation[F] + + new Subsystem[F] { + override val listeners: Seq[Listener] = Seq(new ElevatorEventsListener[F]) + } + } + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/elevator/application/actions/FindTeleportLocation.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/elevator/application/actions/FindTeleportLocation.scala new file mode 100644 index 0000000000..35743f4bea --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/elevator/application/actions/FindTeleportLocation.scala @@ -0,0 +1,25 @@ +package com.github.unchama.seichiassist.subsystems.elevator.application.actions + +trait FindTeleportLocation[F[_], Location] { + + /** + * @return `currentLocation`がテレポート元として正しいかどうかを判定する作用 + */ + def currentLocationTeleportFromAsCorrectIs(currentLocation: Location): F[Boolean] + + /** + * @return `targetLocation`がテレポート先として正しいかどうかを判定する作用 + */ + def isTeleportTargetLocation(targetLocation: Location): F[Boolean] + + /** + * @return `currentLocation`より上のテレポート対象となる[[Location]]を探す作用 + */ + def findUpperLocation(currentLocation: Location): F[Option[Location]] + + /** + * @return `currentLocation`より下のテレポート対象となる[[Location]]を探す作用 + */ + def findLowerLocation(currentLocation: Location): F[Option[Location]] + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/elevator/bukkit/actions/BukkitFindTeleportLocation.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/elevator/bukkit/actions/BukkitFindTeleportLocation.scala new file mode 100644 index 0000000000..e1b390f31c --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/elevator/bukkit/actions/BukkitFindTeleportLocation.scala @@ -0,0 +1,61 @@ +package com.github.unchama.seichiassist.subsystems.elevator.bukkit.actions + +import cats.effect.Sync +import com.github.unchama.seichiassist.subsystems.elevator.application.actions.FindTeleportLocation +import org.bukkit.Location +import org.bukkit.Material + +class BukkitFindTeleportLocation[F[_]: Sync] extends FindTeleportLocation[F, Location] { + + override def currentLocationTeleportFromAsCorrectIs(currentLocation: Location): F[Boolean] = + Sync[F].delay { + currentLocation + .clone() + .add(0, -1, 0) + .getBlock + .getType == Material.IRON_BLOCK && currentLocation + .getBlock + .getType == Material.HEAVY_WEIGHTED_PRESSURE_PLATE + } + + override def isTeleportTargetLocation(targetLocation: Location): F[Boolean] = Sync[F].delay { + targetLocation + .clone() + .add(0, -1, 0) + .getBlock + .getType == Material.IRON_BLOCK && targetLocation + .getBlock + .getType == Material.HEAVY_WEIGHTED_PRESSURE_PLATE && targetLocation + .clone() + .add(0, 1, 0) + .getBlock + .getType == Material.AIR + } + + import cats.implicits._ + + override def findUpperLocation(currentLocation: Location): F[Option[Location]] = { + (currentLocation.getY.toInt + 1 until currentLocation.getWorld.getMaxHeight) + .toVector + .map { y => + val location = currentLocation.clone() + location.setY(y) + + location + } + .findM(isTeleportTargetLocation) + } + + override def findLowerLocation(currentLocation: Location): F[Option[Location]] = { + (currentLocation.getY.toInt - 1 until currentLocation.getWorld.getMinHeight by -1) + .toVector + .map { y => + val location = currentLocation.clone() + location.setY(y) + + location + } + .findM(isTeleportTargetLocation) + } + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/elevator/bukkit/listeners/ElevatorEventsListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/elevator/bukkit/listeners/ElevatorEventsListener.scala new file mode 100644 index 0000000000..9bce467fa3 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/elevator/bukkit/listeners/ElevatorEventsListener.scala @@ -0,0 +1,60 @@ +package com.github.unchama.seichiassist.subsystems.elevator.bukkit.listeners + +import cats.effect.{ConcurrentEffect, Sync} +import com.github.unchama.generic.ApplicativeExtra +import com.github.unchama.generic.effect.unsafe.EffectEnvironment +import com.github.unchama.seichiassist.subsystems.elevator.application.actions.FindTeleportLocation +import org.bukkit.Location +import org.bukkit.event.player.{PlayerMoveEvent, PlayerToggleSneakEvent} +import org.bukkit.event.{EventHandler, Listener} + +class ElevatorEventsListener[F[_]: ConcurrentEffect]( + implicit findTeleportLocation: FindTeleportLocation[F, Location], + effectEnvironment: EffectEnvironment +) extends Listener { + + import cats.implicits._ + + @EventHandler + def onJump(e: PlayerMoveEvent): Unit = { + val player = e.getPlayer + val currentLocation = player.getLocation + + if (player.isFlying || e.getFrom.getY >= e.getTo.getY) return + + val teleportEffect = for { + currentLocationIsCorrectTeleportLocation <- findTeleportLocation + .currentLocationTeleportFromAsCorrectIs(currentLocation) + teleportTargetLocation <- ApplicativeExtra.whenAOrElse( + currentLocationIsCorrectTeleportLocation + )(findTeleportLocation.findUpperLocation(currentLocation), None) + _ <- teleportTargetLocation.traverse { location => + Sync[F].delay(player.teleport(location)) + } + } yield () + + effectEnvironment.unsafeRunEffectAsync("エレベータの上昇処理を行う", teleportEffect) + } + + @EventHandler + def onSneak(e: PlayerToggleSneakEvent): Unit = { + val player = e.getPlayer + val currentLocation = player.getLocation + + if (!player.isSneaking) return + + val teleportEffect = for { + currentLocationIsCorrectTeleportLocation <- findTeleportLocation + .currentLocationTeleportFromAsCorrectIs(currentLocation) + teleportTargetLocation <- ApplicativeExtra.whenAOrElse( + currentLocationIsCorrectTeleportLocation + )(findTeleportLocation.findLowerLocation(currentLocation), None) + _ <- teleportTargetLocation.traverse { location => + Sync[F].delay(player.teleport(location)) + } + } yield () + + effectEnvironment.unsafeRunEffectAsync("エレベータの降下処理を行う", teleportEffect) + } + +} 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 5cce9248f3..1776eee635 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 @@ -46,7 +46,7 @@ class ExpBottleStackUsageController[F[_]: Effect, G[_]: SyncEffect]( if ( player.isSneaking && playerInventory.getItemInMainHand != null - && playerInventory.getItemInMainHand.getType == Material.EXP_BOTTLE + && playerInventory.getItemInMainHand.getType == Material.EXPERIENCE_BOTTLE && (action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK) ) { 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 2fe77ede3d..e9ca3fb02b 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 @@ -63,18 +63,14 @@ 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]] } 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 0f9c2d60c1..50009bd66e 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 @@ -2,7 +2,6 @@ package com.github.unchama.seichiassist.subsystems.fastdiggingeffect.application import cats.effect.{ConcurrentEffect, Timer} import com.github.unchama.minecraft.actions.GetConnectedPlayers -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 @@ -21,7 +20,7 @@ object PlayerCountEffectSynchronization { def using[F[_]: ConcurrentEffect: Timer: GetConnectedPlayers[ *[_], Player - ]: GetNetworkConnectionCount, Player: HasUuid]( + ]: GetNetworkConnectionCount, Player]( implicit configuration: Configuration, api: FastDiggingEffectApi[F, Player] ): fs2.Stream[F, Unit] = { 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 174c6976b0..6ce2baee5d 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 @@ -1,12 +1,12 @@ package com.github.unchama.seichiassist.subsystems.fastdiggingeffect.bukkit.actions -import cats.effect.{Sync, SyncIO} +import cats.effect.SyncIO import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.subsystems.fastdiggingeffect.domain.actions.GrantFastDiggingEffect import org.bukkit.entity.Player import org.bukkit.potion.{PotionEffect, PotionEffectType} -class GrantBukkitFastDiggingEffect[F[_]: Sync: OnMinecraftServerThread] +class GrantBukkitFastDiggingEffect[F[_]: OnMinecraftServerThread] extends GrantFastDiggingEffect[F, Player] { override def forTwoSeconds(player: Player)(amount: Int): F[Unit] = { 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 56d8fd3e63..6617c751d2 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 @@ -33,7 +33,6 @@ class JdbcFastDiggingEffectStatsSettingsPersistence[F[_]: Sync] sql"select messageflag from playerdata where uuid = ${key.toString}" .map { rs => booleanToSettings(rs.boolean("messageflag")) } .headOption() - .apply() } } @@ -42,9 +41,7 @@ class JdbcFastDiggingEffectStatsSettingsPersistence[F[_]: Sync] 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() } } } 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 79be1e9472..05f7c83981 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 @@ -45,7 +45,6 @@ class JdbcFastDiggingEffectSuppressionStatePersistence[F[_]: Sync] sql"select effectflag from playerdata where uuid = ${key.toString}" .map { rs => intToSuppressionState(rs.int("effectflag")) } .headOption() - .apply() } } @@ -54,9 +53,7 @@ class JdbcFastDiggingEffectSuppressionStatePersistence[F[_]: Sync] 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() } } 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 7de4971007..47216e0e1f 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 @@ -76,7 +76,7 @@ object System { override val openPocketInventory: Kleisli[F, Player, Unit] = Kleisli { player => Sync[F].delay { // 開く音を再生 - player.playSound(player.getLocation, Sound.BLOCK_ENDERCHEST_OPEN, 1f, 0.1f) + player.playSound(player.getLocation, Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.1f) } >> ContextCoercion { pocketInventoryRepositoryHandles.repository(player)._1.readLatest }.flatMap(inventory => interactInventory.open(inventory)(player)) 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 94dd36e456..137752e861 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 @@ -22,7 +22,7 @@ class OpenPocketInventoryOnPlacingEnderPortalFrame[F[_]: Effect]( val action = event.getAction val hand = event.getHand - if (event.getMaterial != Material.ENDER_PORTAL_FRAME) { + if (event.getMaterial != Material.END_PORTAL_FRAME) { return } 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 30946eca13..b71ac49658 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 @@ -18,7 +18,6 @@ class JdbcBukkitPocketInventoryPersistence[F[_]: Sync] sql"select inventory from playerdata where uuid = ${key.toString}" .map { rs => BukkitSerialization.fromBase64forPocket(rs.string("inventory")) } .headOption() - .apply() } } @@ -26,9 +25,7 @@ class JdbcBukkitPocketInventoryPersistence[F[_]: Sync] 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() } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/System.scala index 5371812d15..c010c31141 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/System.scala @@ -2,7 +2,7 @@ package com.github.unchama.seichiassist.subsystems.gacha import cats.data.Kleisli import cats.effect.ConcurrentEffect -import com.github.unchama.minecraft.actions.OnMinecraftServerThread +import com.github.unchama.minecraft.actions.{GetConnectedPlayers, OnMinecraftServerThread} import com.github.unchama.minecraft.bukkit.algebra.CloneableBukkitItemStack.instance import com.github.unchama.seichiassist.meta.subsystem.Subsystem import com.github.unchama.seichiassist.subsystems.gacha.application.actions.{ @@ -38,9 +38,11 @@ trait System[F[_], Player] extends Subsystem[F] { object System { - def wired[F[_]: ConcurrentEffect: OnMinecraftServerThread]( + def wired[F[_]: ConcurrentEffect: OnMinecraftServerThread: GetConnectedPlayers[ + *[_], + Player + ]: GachaTicketAPI]( implicit gachaPrizeAPI: GachaPrizeAPI[F, ItemStack, Player], - gachaTicketAPI: GachaTicketAPI[F], mineStackAPI: MineStackAPI[F, Player, ItemStack] ): System[F, Player] = { implicit val canBeSignedAsGachaPrize: CanBeSignedAsGachaPrize[ItemStack] = diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/bukkit/GachaCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/bukkit/GachaCommand.scala index 5846f2ebe5..11f4857e1b 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/bukkit/GachaCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/bukkit/GachaCommand.scala @@ -1,7 +1,7 @@ package com.github.unchama.seichiassist.subsystems.gacha.bukkit import cats.data.Kleisli -import cats.effect.{ConcurrentEffect, Effect, Sync} +import cats.effect.{ConcurrentEffect, Sync} import com.github.unchama.contextualexecutor.ContextualExecutor import com.github.unchama.contextualexecutor.builder.ParserResponse.{failWith, succeedWith} import com.github.unchama.contextualexecutor.builder.{ @@ -31,7 +31,7 @@ import eu.timepit.refined.api.Refined import eu.timepit.refined.auto._ import eu.timepit.refined.numeric.{Interval, NonNegative, Positive} import org.bukkit.ChatColor._ -import org.bukkit.command.TabExecutor +import org.bukkit.command.{CommandSender, TabExecutor} import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack import shapeless.HNil @@ -77,7 +77,9 @@ class GachaCommand[F[_]: OnMinecraftServerThread: ConcurrentEffect]( s"$RED/gacha delete-event <イベント名>", "イベントを削除します。(間違ってイベントを作成した時以外は使わないでください。)", s"$RED/gacha list-event", - "イベントの一覧を表示します。" + "イベントの一覧を表示します。", + s"$RED/gacha replace-prize ", + "指定したIDのガチャ景品を手元のアイテムに置き換えます" ) ) ) @@ -95,7 +97,8 @@ class GachaCommand[F[_]: OnMinecraftServerThread: ConcurrentEffect]( "setprob" -> setProbability, "create-event" -> createEvent, "delete-event" -> deleteEvent, - "list-event" -> eventList + "list-event" -> eventList, + "replace-prize" -> replaceGachaPrize ), whenBranchNotFound = Some(printDescriptionExecutor), whenArgInsufficient = Some(printDescriptionExecutor) @@ -154,7 +157,7 @@ class GachaCommand[F[_]: OnMinecraftServerThread: ConcurrentEffect]( case "all" => Kleisli .liftF(gachaTicketAPI.addToAllKnownPlayers(amount)) - .flatMap(_ => MessageEffectF(s"${GREEN}全プレイヤーへガチャ券${amount}枚加算成功")) + .flatMap(_ => MessageEffectF(s"${GREEN}全プレイヤーへガチャ券${amount.value}枚加算成功")) case value => val uuidRegex = "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}".r @@ -187,35 +190,34 @@ class GachaCommand[F[_]: OnMinecraftServerThread: ConcurrentEffect]( MessageEffect("IDは0以上の整数を指定してください。") ) ) - .buildWithExecutionF { context => + .buildWithExecutionCSEffect { context => import shapeless.:: val gachaPrizeId :: shapeless.HNil = context.args.parsed // optional val ownerName = context.args.yetToBeParsed.headOption - for { - gachaPrize <- gachaPrizeAPI.fetch(GachaPrizeId(gachaPrizeId)) - existsGachaPrize = gachaPrize.nonEmpty - _ <- Sync[F].delay { - gachaPrize.foreach { gachaPrize => - val itemStack = ownerName match { - case Some(name) => gachaPrize.materializeWithOwnerSignature(name) - case None => gachaPrize.itemStack + Kleisli + .liftF { + for { + gachaPrize <- gachaPrizeAPI.fetch(GachaPrizeId(gachaPrizeId)) + _ <- gachaPrize.traverse { gachaPrize => + val itemStack = ownerName match { + case Some(name) => gachaPrize.materializeWithOwnerSignature(name) + case None => gachaPrize.itemStack + } + + InventoryOperations.grantItemStacksEffect(itemStack).apply(context.sender) } - - InventoryOperations.grantItemStacksEffect(itemStack).apply(context.sender) - } + } yield gachaPrize + } + .flatMap { + case Some(_) => MessageEffectF("ガチャアイテムを付与しました。") + case None => MessageEffectF("指定されたIDのガチャ景品は存在しません。") } - } yield { - if (existsGachaPrize) - MessageEffect("ガチャアイテムを付与しました。") - else - MessageEffect("指定されたIDのガチャ景品は存在しません。") - } } val add: ContextualExecutor = - playerCommandBuilder.thenParse(probabilityParser).buildWithExecutionF { context => + playerCommandBuilder.thenParse(probabilityParser).buildWithExecutionCSEffect { context => import shapeless.:: val player = context.sender @@ -223,45 +225,46 @@ class GachaCommand[F[_]: OnMinecraftServerThread: ConcurrentEffect]( val eventName = context.args.yetToBeParsed.headOption.map(GachaEventName) val mainHandItem = player.getInventory.getItemInMainHand - for { - events <- gachaPrizeAPI.createdGachaEvents - _ <- gachaPrizeAPI.addGachaPrize( - domain.GachaPrizeTableEntry( - mainHandItem, - GachaProbability(probability), - probability < 0.1, - _, - events.find(gachaEvent => eventName.contains(gachaEvent.eventName)) - ) - ) - } yield MessageEffect(List("ガチャアイテムを追加しました!")) + Kleisli + .liftF[F, CommandSender, Unit] { + for { + events <- gachaPrizeAPI.createdGachaEvents + _ <- gachaPrizeAPI.addGachaPrize( + domain.GachaPrizeTableEntry( + mainHandItem, + GachaProbability(probability), + probability < 0.1, + _, + events.find(gachaEvent => eventName.contains(gachaEvent.eventName)) + ) + ) + } yield () + } + .productR(MessageEffectF("ガチャアイテムを追加しました!")) } val list: ContextualExecutor = - ContextualExecutorBuilder.beginConfiguration.buildWithExecutionF { context => + ContextualExecutorBuilder.beginConfiguration.buildWithExecutionCSEffect { context => val eventName = context.args.yetToBeParsed.headOption.map(GachaEventName) - for { - gachaPrizes <- gachaPrizeAPI.allGachaPrizeList - } yield { - val gachaPrizeInformation = gachaPrizes - .filter { gachaPrize => - if (eventName.isEmpty) gachaPrize.nonGachaEventItem - else - gachaPrize.gachaEvent.map(_.eventName) == eventName - } + Kleisli.liftF(gachaPrizeAPI.allGachaPrizeList).flatMap { gachaPrizes => + val eventGachaPrizes = gachaPrizes.filter { gachaPrize => + if (eventName.isEmpty) gachaPrize.nonGachaEventItem + else + gachaPrize.gachaEvent.map(_.eventName) == eventName + } + + val gachaPrizeInformation = eventGachaPrizes .sortBy(_.id.id) .map { gachaPrize => val itemStack = gachaPrize.itemStack val probability = gachaPrize.probability.value - s"${gachaPrize.id.id}|${itemStack.getType.toString}/${itemStack - .getItemMeta - .getDisplayName}$RESET|${itemStack.getAmount}|$probability(${probability * 100}%)" + s"${gachaPrize.id.id}|${itemStack.getType.toString}/${itemStack.getItemMeta.getDisplayName}$RESET|${itemStack.getAmount}|$probability(${probability * 100}%)" } .toList - val totalProbability = gachaPrizes.map(_.probability.value).sum - MessageEffect( + val totalProbability = eventGachaPrizes.map(_.probability.value).sum + MessageEffectF( List(s"${RED}アイテム番号|アイテム名|アイテム数|出現確率") ++ gachaPrizeInformation ++ List( s"${RED}合計確率: $totalProbability(${totalProbability * 100}%)", s"${RED}合計確率は100%以内に収まるようにしてください。" @@ -279,15 +282,15 @@ class GachaCommand[F[_]: OnMinecraftServerThread: ConcurrentEffect]( MessageEffect("IDは正の値を指定してください。") ) ) - .buildWithExecutionF { context => + .buildWithExecutionCSEffect { context => val gachaId = GachaPrizeId(context.args.parsed.head) - for { - didRemoveGachaPrize <- gachaPrizeAPI.removeByGachaPrizeId(gachaId) - } yield { - if (didRemoveGachaPrize) - MessageEffect(List("ガチャアイテムを削除しました")) - else - MessageEffect("指定されたIDのガチャ景品が存在しないため、ガチャアイテムを削除できませんでした。") + Kleisli.liftF(gachaPrizeAPI.removeByGachaPrizeId(gachaId)).flatMap { + didRemoveGachaPrize => + if (didRemoveGachaPrize) { + MessageEffectF("ガチャアイテムを削除しました") + } else { + MessageEffectF("指定されたIDのガチャ景品が存在しないため、ガチャアイテムを削除できませんでした。") + } } } @@ -302,53 +305,56 @@ class GachaCommand[F[_]: OnMinecraftServerThread: ConcurrentEffect]( MessageEffect("数は1~64で指定してください。") ) ) - .buildWithExecutionF { context => + .buildWithExecutionCSEffect { context => import shapeless.:: val targetId :: amount :: HNil = context.args.parsed - for { - currentGachaPrize <- gachaPrizeAPI.fetch(targetId) - oldItemStack <- currentGachaPrize.traverse { prize => - gachaPrizeAPI - .upsertGachaPrize( - prize.copy(itemStack = prize.itemStack.tap(_.setAmount(amount))) - ) - .as(Some(prize.itemStack)) - } - } yield { - oldItemStack match { + + Kleisli + .liftF(for { + currentGachaPrize <- gachaPrizeAPI.fetch(targetId) + oldItemStack <- currentGachaPrize.traverse { prize => + gachaPrizeAPI + .upsertGachaPrize( + prize.copy(itemStack = prize.itemStack.tap(_.setAmount(amount))) + ) + .as(Some(prize.itemStack)) + } + } yield oldItemStack) + .flatMap { case Some(itemStack) => - MessageEffect( + MessageEffectF( s"${targetId.id}|${itemStack.get.getType.toString}/${itemStack.get.getItemMeta.getDisplayName}${RESET}のアイテム数を${amount}個に変更しました。" ) case None => - MessageEffect("指定されたIDのガチャ景品が存在しないため、アイテム数が変更できませんでした。") + MessageEffectF("指定されたIDのガチャ景品が存在しないため、アイテム数が変更できませんでした。") } - } } val setProbability: ContextualExecutor = ContextualExecutorBuilder .beginConfiguration .thenParse(gachaPrizeIdExistsParser) .thenParse(probabilityParser) - .buildWithExecutionF { context => + .buildWithExecutionCSEffect { context => import shapeless.:: val targetId :: newProb :: HNil = context.args.parsed - for { - currentGachaPrize <- gachaPrizeAPI.fetch(targetId) - probabilityChange <- currentGachaPrize.traverse { gachaPrize => - gachaPrizeAPI.upsertGachaPrize( - gachaPrize.copy(probability = GachaProbability(newProb)) - ) - } + + (for { + currentGachaPrize <- Kleisli.liftF(gachaPrizeAPI.fetch(targetId)) + probabilityChange <- Kleisli.liftF(currentGachaPrize.traverse { gachaPrize => + gachaPrizeAPI + .upsertGachaPrize(gachaPrize.copy(probability = GachaProbability(newProb))) + }) itemStack = currentGachaPrize.map(_.itemStack) } yield { - if (probabilityChange.nonEmpty) - MessageEffect( + if (probabilityChange.nonEmpty) { + MessageEffectF( s"${targetId.id}|${itemStack.get.getType.toString}/${itemStack.get.getItemMeta.getDisplayName}${RESET}の確率を$newProb(${newProb * 100}%)に変更しました。" ) - else - MessageEffect("指定されたIDのガチャ景品は存在しません。") - } + } else { + MessageEffectF("指定されたIDのガチャ景品は存在しません。") + } + }).flatten + } val createEvent: ContextualExecutor = @@ -357,7 +363,7 @@ class GachaCommand[F[_]: OnMinecraftServerThread: ConcurrentEffect]( .thenParse(Parsers.identity) .thenParse(Parsers.identity) .thenParse(Parsers.identity) - .buildWithExecutionF { context => + .buildWithExecutionCSEffect { context => import shapeless.:: val e :: startDate :: endDate :: HNil = context.args.parsed val eventName = GachaEventName(e) @@ -366,38 +372,43 @@ class GachaCommand[F[_]: OnMinecraftServerThread: ConcurrentEffect]( val dateRegex = "[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])".r if (!dateRegex.matches(startDate) || !dateRegex.matches(endDate)) { - Effect[F].pure(MessageEffect(s"${RED}開始日/終了日はyyyy-MM-ddの形式で指定してください。")) + MessageEffectF(s"${RED}開始日/終了日はyyyy-MM-ddの形式で指定してください。") } else if (eventName.name.length > 30) { - Effect[F].pure(MessageEffect(s"${RED}イベント名は30字以内で指定してください。")) - } else { - for { - existsEvent <- gachaPrizeAPI.existsGachaEvent(eventName) - _ <- gachaPrizeAPI - .createGachaEvent( - GachaEvent( - eventName, - LocalDate.parse(startDate, dateTimeFormatter), - LocalDate.parse(endDate, dateTimeFormatter) - ) + MessageEffectF(s"${RED}イベント名は30字以内で指定してください。") + } else + { + for { + existsEvent <- Kleisli.liftF(gachaPrizeAPI.existsGachaEvent(eventName)) + _ <- Kleisli.liftF( + gachaPrizeAPI + .createGachaEvent( + GachaEvent( + eventName, + LocalDate.parse(startDate, dateTimeFormatter), + LocalDate.parse(endDate, dateTimeFormatter) + ) + ) + .unlessA(existsEvent) ) - .unlessA(existsEvent) - } yield { - if (existsEvent) MessageEffect(s"${RED}指定された名前のイベントが既に存在します。") - else MessageEffect(s"${AQUA}イベントを作成しました。") - } - } + } yield { + if (existsEvent) MessageEffectF(s"${RED}指定された名前のイベントが既に存在します。") + else MessageEffectF(s"${AQUA}イベントを作成しました。") + } + }.flatten + } val deleteEvent: ContextualExecutor = ContextualExecutorBuilder .beginConfiguration .thenParse(Parsers.identity) - .buildWithExecutionF { context => + .buildWithExecutionCSEffect { context => val eventName = GachaEventName(context.args.parsed.head) - for { - _ <- gachaPrizeAPI.deleteGachaEvent(eventName) - } yield MessageEffect(s"ガチャイベント: ${eventName.name}を削除しました。") + + Kleisli.liftF(gachaPrizeAPI.deleteGachaEvent(eventName)).flatMap { _ => + MessageEffectF(s"ガチャイベント: ${eventName.name}を削除しました。") + } } private def toTimeString(localDate: LocalDate): String = { @@ -406,16 +417,34 @@ class GachaCommand[F[_]: OnMinecraftServerThread: ConcurrentEffect]( } val eventList: ContextualExecutor = - ContextualExecutorBuilder.beginConfiguration.buildWithExecutionF { _ => - for { - events <- gachaPrizeAPI.createdGachaEvents - } yield { + ContextualExecutorBuilder.beginConfiguration.buildWithExecutionCSEffect { _ => + Kleisli.liftF(gachaPrizeAPI.createdGachaEvents).flatMap { events => val messages = "イベント名 | 開始日 | 終了日" +: events.map { event => s"${event.eventName.name} | ${toTimeString(event.startDate)} | ${toTimeString(event.endDate)}" } - MessageEffect(messages.toList) + + MessageEffectF(messages.toList) } } + + val replaceGachaPrize: ContextualExecutor = + playerCommandBuilder.thenParse(gachaPrizeIdExistsParser).buildWithExecutionCSEffect { + context => + import shapeless.:: + val targetId :: HNil = context.args.parsed + + Kleisli + .liftF[F, CommandSender, Unit] { + for { + gachaPrize <- gachaPrizeAPI.fetch(targetId) + mainHandItem <- Sync[F].delay(context.sender.getInventory.getItemInMainHand) + _ <- gachaPrize.traverse { prize => + gachaPrizeAPI.upsertGachaPrize(prize.copy(itemStack = mainHandItem)) + } + } yield () + } + .productR(MessageEffectF("ガチャ景品を置き換えました。")) + } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/bukkit/actions/BukkitDrawGacha.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/bukkit/actions/BukkitDrawGacha.scala index 4277428df4..2306118995 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/bukkit/actions/BukkitDrawGacha.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/bukkit/actions/BukkitDrawGacha.scala @@ -1,7 +1,7 @@ package com.github.unchama.seichiassist.subsystems.gacha.bukkit.actions -import cats.effect.{IO, Sync} -import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.onMainThread +import cats.effect.{LiftIO, Sync} +import com.github.unchama.minecraft.actions.{GetConnectedPlayers, OnMinecraftServerThread} import com.github.unchama.seichiassist.subsystems.gacha.application.actions.{ DrawGacha, GrantGachaPrize @@ -11,24 +11,28 @@ import com.github.unchama.seichiassist.subsystems.gachaprize.GachaPrizeAPI import com.github.unchama.seichiassist.subsystems.gachaprize.domain.GachaRarity._ import com.github.unchama.seichiassist.util.SendMessageEffect.sendMessageToEveryone import com.github.unchama.seichiassist.util._ +import net.md_5.bungee.api.chat.hover.content.Text import net.md_5.bungee.api.chat.{HoverEvent, TextComponent} import org.bukkit.ChatColor._ import org.bukkit.Sound import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack -class BukkitDrawGacha[F[_]: Sync]( +class BukkitDrawGacha[ + F[_]: LiftIO: Sync: OnMinecraftServerThread: GetConnectedPlayers[*[_], Player] +]( implicit gachaPrizeAPI: GachaPrizeAPI[F, ItemStack, Player], lotteryOfGachaItems: LotteryOfGachaItems[F, ItemStack], grantGachaPrize: GrantGachaPrize[F, ItemStack, Player] ) extends DrawGacha[F, Player] { - import PlayerSendable._ import cats.implicits._ import scala.jdk.CollectionConverters._ override def draw(player: Player, count: Int): F[Unit] = { + implicitly[PlayerSendable[TextComponent, F]] + for { currentGachaPrizes <- gachaPrizeAPI.listOfNow gachaPrizes <- lotteryOfGachaItems.runLottery(count, currentGachaPrizes) @@ -49,7 +53,7 @@ class BukkitDrawGacha[F[_]: Sync]( val localizedEnchantmentList = prizeItem.getItemMeta.getEnchants.asScala.toSeq.map { case (enchantment, level) => - s"$GRAY${EnchantNameToJapanese.getEnchantName(enchantment.getName, level)}" + s"$GRAY${EnchantNameToJapanese.getEnchantName(enchantment.getKey.toString, level)}" } import scala.util.chaining._ @@ -62,13 +66,11 @@ class BukkitDrawGacha[F[_]: Sync]( setHoverEvent { new HoverEvent( HoverEvent.Action.SHOW_TEXT, - Array( - new TextComponent( - s" ${prizeItem.getItemMeta.getDisplayName}\n" + - ListFormatters.getDescFormat(localizedEnchantmentList.toList) + - ListFormatters - .getDescFormat(prizeItem.getItemMeta.getLore.asScala.toList) - ) + new Text( + s" ${prizeItem.getItemMeta.getDisplayName}\n" + + ListFormatters.getDescFormat(localizedEnchantmentList.toList) + + ListFormatters + .getDescFormat(prizeItem.getItemMeta.getLore.asScala.toList) ) ) } @@ -76,16 +78,15 @@ class BukkitDrawGacha[F[_]: Sync]( Sync[F].delay { player.sendMessage(s"${RED}おめでとう!!!!!Gigantic☆大当たり!$additionalMessage") player.spigot().sendMessage(message) - sendMessageToEveryone(s"$GOLD${player.getName}がガチャでGigantic☆大当たり!")( - forString[IO] - ) - sendMessageToEveryone(message)(forTextComponent[IO]) + } >> Sync[F].delay { SendSoundEffect.sendEverySoundWithoutIgnore( - Sound.ENTITY_ENDERDRAGON_DEATH, + Sound.ENTITY_ENDER_DRAGON_DEATH, 0.5f, 2f ) - } + } >> sendMessageToEveryone[String, F]( + s"$GOLD${player.getName}がガチャでGigantic☆大当たり!" + ) case GachaRarity.Big => Sync[F].delay { player.playSound(player.getLocation, Sound.ENTITY_WITHER_SPAWN, 0.8f, 1f) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/bukkit/actions/BukkitGrantGachaPrize.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/bukkit/actions/BukkitGrantGachaPrize.scala index a020a2faa3..2d7afa8cf0 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/bukkit/actions/BukkitGrantGachaPrize.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/bukkit/actions/BukkitGrantGachaPrize.scala @@ -28,18 +28,15 @@ class BukkitGrantGachaPrize[F[_]: Sync: OnMinecraftServerThread]( Kleisli { player => for { currentAutoMineStackState <- mineStackAPI.autoMineStack(player) - results <- prizes.traverse { gachaPrize => - val itemStack = gachaPrize.itemStack - whenAOrElse(currentAutoMineStackState)( - mineStackAPI - .mineStackRepository - .tryIntoMineStack(player, itemStack, itemStack.getAmount) - .map(hasBeenStoredInMineStack => gachaPrize -> hasBeenStoredInMineStack), - gachaPrize -> false - ) - } - } yield results.collect { - case (gachaPrize, result) if !result => gachaPrize + itemStacks <- Sync[F].delay(prizes.map(_.materializeWithOwnerSignature(player.getName))) + intoFailedItemStacksAndSuccessItemStacks <- whenAOrElse(currentAutoMineStackState)( + mineStackAPI.mineStackRepository.tryIntoMineStack(player, itemStacks), + (itemStacks, Vector.empty) + ) + } yield prizes.filter { prize => + intoFailedItemStacksAndSuccessItemStacks + ._1 + .exists(_.isSimilar(prize.materializeWithOwnerSignature(player.getName))) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/domain/LotteryOfGachaItems.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/domain/LotteryOfGachaItems.scala index da1d282802..9c3b74d30d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/domain/LotteryOfGachaItems.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/domain/LotteryOfGachaItems.scala @@ -2,8 +2,8 @@ package com.github.unchama.seichiassist.subsystems.gacha.domain import cats.effect.Sync import com.github.unchama.seichiassist.subsystems.gachaprize.domain.{ - GachaPrizeTableEntry, GachaPrizeId, + GachaPrizeTableEntry, GachaProbability, StaticGachaPrizeFactory } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/subsystems/gachaticket/infrastructure/JdbcGachaTicketFromAdminTeamRepository.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/subsystems/gachaticket/infrastructure/JdbcGachaTicketFromAdminTeamRepository.scala index c5a181a819..3b4c44426a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/subsystems/gachaticket/infrastructure/JdbcGachaTicketFromAdminTeamRepository.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gacha/subsystems/gachaticket/infrastructure/JdbcGachaTicketFromAdminTeamRepository.scala @@ -26,7 +26,6 @@ class JdbcGachaTicketFromAdminTeamRepository[F[_]: Sync: NonServerThreadContextS DB.localTx { implicit session => sql"update playerdata set numofsorryforbug = numofsorryforbug + ${amount.value}" .execute() - .apply() } } } @@ -42,8 +41,7 @@ class JdbcGachaTicketFromAdminTeamRepository[F[_]: Sync: NonServerThreadContextS DB.localTx { implicit session => val affectedRows = sql"UPDATE playerdata SET numofsorryforbug = numofsorryforbug + ${amount.value} WHERE name = ${playerName.name}" - .update - .apply() + .update() getReceiptResult(affectedRows) } @@ -62,7 +60,6 @@ class JdbcGachaTicketFromAdminTeamRepository[F[_]: Sync: NonServerThreadContextS val affectedRows = sql"UPDATE playerdata SET numofsorryforbug = numofsorryforbug + ${amount.value} WHERE uuid = ${uuid.toString}" .update() - .apply() getReceiptResult(affectedRows) } @@ -76,7 +73,6 @@ class JdbcGachaTicketFromAdminTeamRepository[F[_]: Sync: NonServerThreadContextS sql"SELECT numofsorryforbug FROM playerdata WHERE uuid = ${uuid.toString} FOR UPDATE" .map(_.int("numofsorryforbug")) .toList() - .apply() .headOption .getOrElse(0) @@ -87,7 +83,6 @@ class JdbcGachaTicketFromAdminTeamRepository[F[_]: Sync: NonServerThreadContextS if (updatedAmount >= 0) { sql"UPDATE playerdata SET numofsorryforbug = numofsorryforbug - $receiveAmount WHERE uuid = ${uuid.toString}" .execute() - .apply() } GachaTicketAmount(receiveAmount) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/GachaPointApi.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/GachaPointApi.scala index 3549b054c7..e7153e2692 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/GachaPointApi.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachapoint/GachaPointApi.scala @@ -15,7 +15,12 @@ trait GachaPointApi[F[_], G[_], Player] { /** * プレーヤーのガチャポイントをガチャ券に変換して一括で受け取る作用。 */ - val receiveBatch: Kleisli[F, Player, Unit] + val receiveLargeBatch: Kleisli[F, Player, Unit] + + /** + * プレーヤーのガチャポイントをガチャ券に変換して一括で受け取る作用。 + */ + val receiveSmallBatch: Kleisli[F, Player, Unit] /** * プレーヤーのガチャポイントを増やす作用。 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 ca33d5777e..7138ed0d5a 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 @@ -11,7 +11,7 @@ import com.github.unchama.generic.ContextCoercion import com.github.unchama.generic.effect.EffectExtra import com.github.unchama.generic.effect.concurrent.ReadOnlyRef import com.github.unchama.generic.effect.stream.StreamExtra -import com.github.unchama.minecraft.actions.{GetConnectedPlayers, OnMinecraftServerThread} +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.gachapoint.application.process.AddSeichiExpAsGachaPoint @@ -20,6 +20,7 @@ import com.github.unchama.seichiassist.subsystems.gachapoint.bukkit.GrantBukkitG import com.github.unchama.seichiassist.subsystems.gachapoint.domain.GrantGachaTicketToAPlayer import com.github.unchama.seichiassist.subsystems.gachapoint.domain.gachapoint.GachaPoint import com.github.unchama.seichiassist.subsystems.gachapoint.infrastructure.JdbcGachaPointPersistence +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import io.chrisdavenport.log4cats.ErrorLogger import org.bukkit.entity.Player @@ -34,11 +35,12 @@ object System { import cats.effect.implicits._ import cats.implicits._ - def wired[F[_]: ConcurrentEffect: Timer: GetConnectedPlayers[*[_], Player]: ErrorLogger, G[ + def wired[F[_]: ConcurrentEffect: Timer: ErrorLogger, G[_]: SyncEffect: ContextCoercion[*[ _ - ]: SyncEffect: ContextCoercion[*[_], F]]( - breakCountReadAPI: BreakCountReadAPI[F, G, Player] - )(implicit ioOnMainThread: OnMinecraftServerThread[IO]): G[System[F, G, Player]] = { + ], F]](breakCountReadAPI: BreakCountReadAPI[F, G, Player])( + implicit ioOnMainThread: OnMinecraftServerThread[IO], + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] + ): G[System[F, G, Player]] = { import com.github.unchama.minecraft.bukkit.algebra.BukkitPlayerHasUuid.instance val gachaPointPersistence = new JdbcGachaPointPersistence[G] @@ -81,11 +83,18 @@ object System { ReadOnlyRef.fromRef(value.pointRef) ) - override val receiveBatch: Kleisli[F, Player, Unit] = Kleisli { player => + override val receiveLargeBatch: Kleisli[F, Player, Unit] = Kleisli { player => gachaPointRepositoryControlsRepository .lift(player) - .traverse { value => value.semaphore.tryBatchTransaction } - .as(()) + .traverse { _.semaphore.tryLargeBatchTransaction } + .void + } + + override val receiveSmallBatch: Kleisli[F, Player, Unit] = Kleisli { player => + gachaPointRepositoryControlsRepository + .lift(player) + .traverse { _.semaphore.trySmallBatchTransaction } + .void } override def addGachaPoint(point: GachaPoint): Kleisli[F, Player, Unit] = 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 d70733c303..f12b5e671f 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 @@ -3,7 +3,6 @@ package com.github.unchama.seichiassist.subsystems.gachapoint.application.proces import cats.Applicative import cats.effect.concurrent.Ref import com.github.unchama.datarepository.KeyedDataRepository -import com.github.unchama.minecraft.algebra.HasUuid import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.SeichiExpAmount import com.github.unchama.seichiassist.subsystems.gachapoint.domain.gachapoint.GachaPoint @@ -11,7 +10,7 @@ object AddSeichiExpAsGachaPoint { import cats.implicits._ - def stream[F[_]: Applicative, Player: HasUuid]( + def stream[F[_]: Applicative, Player]( refRepository: KeyedDataRepository[Player, Ref[F, GachaPoint]] )(seichiExpStream: fs2.Stream[F, (Player, SeichiExpAmount)]): fs2.Stream[F, Unit] = seichiExpStream.evalMap { 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 2e42af0aa2..bd0791ed2b 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 @@ -4,6 +4,7 @@ import cats.effect.{IO, LiftIO} import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.subsystems.gachaprize.bukkit.factories.BukkitGachaSkullData import com.github.unchama.seichiassist.subsystems.gachapoint.domain.GrantGachaTicketToAPlayer +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.util.InventoryOperations import com.github.unchama.targetedeffect.SequentialEffect import com.github.unchama.targetedeffect.TargetedEffect.emptyEffect @@ -14,7 +15,8 @@ import org.bukkit.Sound import org.bukkit.entity.Player case class GrantBukkitGachaTicketToAPlayer[F[_]: LiftIO](player: Player)( - implicit ioOnMainThread: OnMinecraftServerThread[IO] + implicit ioOnMainThread: OnMinecraftServerThread[IO], + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) extends GrantGachaTicketToAPlayer[F] { override def give(count: Int): F[Unit] = { 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 b34aba3816..fc2281ceae 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 @@ -18,15 +18,24 @@ class BatchUsageSemaphore[F[_]: FlatMap, G[_]: ContextCoercion[*[_], F]]( import cats.implicits._ /** - * バッチでのガチャポイント変換を行い、 [[BatchUsageSemaphore.usageInterval]]の間使用不可にする作用。 + * 576個(= 64 * 9スタック)のバッチでガチャポイント変換を行い、 [[BatchUsageSemaphore.usageInterval]]の間使用不可にする作用。 */ - def tryBatchTransaction: F[Unit] = + def tryLargeBatchTransaction: F[Unit] = recoveringSemaphore.tryUse { ContextCoercion { - gachaPointRef.modify { point => point.useInBatch.asTuple } + gachaPointRef.modify { _.useInLargeBatch.asTuple } }.flatTap(grantAction.give) }(BatchUsageSemaphore.usageInterval) + /** + * 64個(= 64 * 1スタック)のバッチでガチャポイント変換を行い、 [[BatchUsageSemaphore.usageInterval]]の間使用不可にする作用。 + */ + def trySmallBatchTransaction: F[Unit] = + recoveringSemaphore.tryUse { + ContextCoercion { + gachaPointRef.modify { _.useInSmallBatch.asTuple } + }.flatTap(grantAction.give) + }(BatchUsageSemaphore.usageInterval) } object BatchUsageSemaphore { 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 06f7c62021..acbb80be42 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 @@ -5,7 +5,7 @@ import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.Seichi /** * ガチャポイントとはプレーヤーが持つ「消費可能な整地経験量」である。 * - * プレーヤーは576個(= 64 * 9スタック)のバッチにてガチャポイントをガチャ券に交換できる。 + * プレーヤーは576個(= 64 * 9スタック)のバッチもしくは、64個(1スタック)のバッチにてガチャポイントをガチャ券に交換できる。 */ case class GachaPoint(exp: SeichiExpAmount) { @@ -18,8 +18,8 @@ case class GachaPoint(exp: SeichiExpAmount) { /** * ガチャポイントをバッチでガチャ券に変換した際のポイントの変化を計算する。 */ - lazy val useInBatch: GachaPoint.Usage = { - val ticketCount = availableTickets.min(GachaPoint.batchSize).toInt + private def useInBatch(batchSize: BatchSize): GachaPoint.Usage = { + val ticketCount = availableTickets.min(batchSize.value).toInt val expToUse = GachaPoint.perGachaTicket.exp.amount * ticketCount val remaining = GachaPoint.ofNonNegative(exp.amount - expToUse) @@ -27,6 +27,16 @@ case class GachaPoint(exp: SeichiExpAmount) { GachaPoint.Usage(remaining, ticketCount) } + /** + * ガチャポイントを576個(= 64 * 9スタック)のバッチでガチャ券に変換した際のポイントの変化を計算する。 + */ + lazy val useInLargeBatch: GachaPoint.Usage = useInBatch(GachaPoint.largeBatchSize) + + /** + * ガチャポイントを64個(= 64 * 1スタック)のバッチでガチャ券に変換した際のポイントの変化を計算する。 + */ + lazy val useInSmallBatch: GachaPoint.Usage = useInBatch(GachaPoint.smallBatchSize) + /** * 次にガチャ券を利用できるようになるまでに必要な整地経験値量 */ @@ -42,6 +52,10 @@ case class GachaPoint(exp: SeichiExpAmount) { def subtract(point: GachaPoint): GachaPoint = GachaPoint(exp.subtract(point.exp)) } +case class BatchSize(value: Int) { + require(value > 0, "batch size must be positive") +} + object GachaPoint { def ofNonNegative(x: BigDecimal): GachaPoint = GachaPoint(SeichiExpAmount.ofNonNegative(x)) @@ -54,15 +68,28 @@ object GachaPoint { * 変換にて得られるガチャ券の総数 */ case class Usage(remainingGachaPoint: GachaPoint, gachaTicketCount: Int) { - require(gachaTicketCount <= GachaPoint.batchSize, "usage must not exceed batch size") + require( + gachaTicketCount <= GachaPoint.maxBatchSize.value, + "usage must not exceed max batch size" + ) def asTuple: (GachaPoint, Int) = (remainingGachaPoint, gachaTicketCount) } + /** + * ガチャ券を576個(= 64 * 9スタック)のバッチでガチャポイントに変換する際のバッチサイズ + */ + final val largeBatchSize = BatchSize(9 * 64) + + /** + * ガチャ券を64個(= 64 * 1スタック)のバッチでガチャポイントに変換する際のバッチサイズ + */ + final val smallBatchSize = BatchSize(64) + /** * ガチャ券へのポイント交換にて一度に得られるガチャ券の上限 */ - final val batchSize = 9 * 64 + final val maxBatchSize = largeBatchSize /** * ガチャポイントの初期値 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 405d324c9d..b4d3c217d2 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 @@ -21,7 +21,6 @@ class JdbcGachaPointPersistence[F[_]: Sync] extends GachaPointPersistence[F] { sql"select gachapoint from playerdata where uuid = ${key.toString}" .map { rs => decode(rs.bigInt("gachapoint")) } .headOption() - .apply() } } @@ -29,7 +28,6 @@ class JdbcGachaPointPersistence[F[_]: Sync] extends GachaPointPersistence[F] { DB.localTx { implicit session => sql"update playerdata set gachapoint = ${encode(value)} where uuid = ${key.toString}" .update() - .apply() } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/bukkit/factories/BukkitGachaSkullData.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/bukkit/factories/BukkitGachaSkullData.scala index 816a4acda3..e5a46b08c5 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/bukkit/factories/BukkitGachaSkullData.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/bukkit/factories/BukkitGachaSkullData.scala @@ -1,76 +1,39 @@ package com.github.unchama.seichiassist.subsystems.gachaprize.bukkit.factories -import com.github.unchama.seichiassist.util.ItemMetaFactory +import com.github.unchama.itemstackbuilder.SkullItemStackBuilder +import com.github.unchama.seichiassist.SkullOwners +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import org.bukkit.ChatColor._ -import org.bukkit.Material import org.bukkit.inventory.ItemStack +import cats.effect.IO +import org.bukkit.entity.Player object BukkitGachaSkullData { - import scala.jdk.CollectionConverters._ - import scala.util.chaining._ - /** * ノーマルガチャ券 */ - val gachaSkull: ItemStack = - new ItemStack(Material.SKULL_ITEM, 1).tap { skull => - import skull._ - setDurability(3) - setItemMeta { - ItemMetaFactory.SKULL.getValue.tap { meta => - import meta._ - setDisplayName(s"$YELLOW${BOLD}ガチャ券") - setLore { - List(s"$RESET${GREEN}右クリックで使えます").asJava - } - - // 参加したことのないプレーヤーはgetOfflinePlayerでデータが取れないのでこうするしか無い - setOwner("unchama") - } - } - } + def gachaSkull(implicit playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player]): ItemStack = + new SkullItemStackBuilder(SkullOwners.unchama) + .title(s"$YELLOW${BOLD}ガチャ券") + .lore(List(s"$RESET${GREEN}右クリックで使えます")) + .build() /** * 投票報酬のガチャ券 */ - val gachaForVoting: ItemStack = - new ItemStack(Material.SKULL_ITEM, 1).tap { itemStack => - import itemStack._ - setDurability(3) - setItemMeta { - ItemMetaFactory.SKULL.getValue.tap { meta => - import meta._ - setDisplayName(s"$YELLOW${BOLD}ガチャ券") - setLore { - List(s"$RESET${GREEN}右クリックで使えます", s"$RESET${LIGHT_PURPLE}投票ありがとナス♡").asJava - } - - // 参加したことのないプレーヤーはgetOfflinePlayerでデータが取れないのでこうするしか無い - setOwner("unchama") - } - } - } + def gachaForVoting(implicit playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player]): ItemStack = + new SkullItemStackBuilder(SkullOwners.unchama) + .title(s"$YELLOW${BOLD}ガチャ券") + .lore(List(s"$RESET${GREEN}右クリックで使えます", s"$RESET${LIGHT_PURPLE}投票ありがとナス♡")) + .build() /** * ガチャ景品(当たり・大当たり)とガチャ券の交換システムで手に入るガチャ券 */ - val gachaForExchanging: ItemStack = { - new ItemStack(Material.SKULL_ITEM, 1).tap { itemStack => - import itemStack._ - setDurability(3) - setItemMeta { - ItemMetaFactory.SKULL.getValue.tap { meta => - import meta._ - setDisplayName(s"$YELLOW${BOLD}ガチャ券") - setLore { - List(s"$RESET${GREEN}右クリックで使えます", s"$RESET${GRAY}ガチャ景品と交換しました。").asJava - } - - // 参加したことのないプレーヤーはgetOfflinePlayerでデータが取れないのでこうするしか無い - setOwner("unchama") - } - } - } - } + def gachaForExchanging(implicit playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player]): ItemStack = + new SkullItemStackBuilder(SkullOwners.unchama) + .title(s"$YELLOW${BOLD}ガチャ券") + .lore(List(s"$RESET${GREEN}右クリックで使えます", s"$RESET${GRAY}ガチャ景品と交換しました。")) + .build() } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/bukkit/factories/BukkitStaticGachaPrizeFactory.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/bukkit/factories/BukkitStaticGachaPrizeFactory.scala index e15fd197ad..e4c082226f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/bukkit/factories/BukkitStaticGachaPrizeFactory.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/bukkit/factories/BukkitStaticGachaPrizeFactory.scala @@ -28,10 +28,10 @@ object BukkitStaticGachaPrizeFactory extends StaticGachaPrizeFactory[ItemStack] setItemMeta(meta) } - override val expBottle: ItemStack = new ItemStack(Material.EXP_BOTTLE, 20) + override val expBottle: ItemStack = new ItemStack(Material.EXPERIENCE_BOTTLE, 20) override val mineHeadItem: ItemStack = - new ItemStack(Material.CARROT_STICK, 1, 1.toShort).tap { itemStack => + new ItemStack(Material.CARROT_ON_A_STICK, 1).tap { itemStack => import itemStack._ val meta = getItemMeta meta.setDisplayName(s"${DARK_RED}死神の鎌") diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/domain/GachaRarity.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/domain/GachaRarity.scala index c5983cb395..02871e6ec8 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/domain/GachaRarity.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/domain/GachaRarity.scala @@ -38,11 +38,15 @@ object GachaRarity { override def values: IndexedSeq[GachaRarity] = findValues def of[ItemStack](gachaPrize: GachaPrizeTableEntry[ItemStack]): GachaRarity = + fromGachaProbability(gachaPrize.probability) + + def fromGachaProbability(gachaProbability: GachaProbability): GachaRarity = { GachaRarity .values - .filter { rarity => rarity.probabilityUpperLimit.value > gachaPrize.probability.value } + .filter(rarity => rarity.probabilityUpperLimit.value > gachaProbability.value) .minByOption(_.probabilityUpperLimit.value) .getOrElse(GachaRingoOrExpBottle) + } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/infrastructure/JdbcGachaEventPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/infrastructure/JdbcGachaEventPersistence.scala index e38215dc90..345ccc945f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/infrastructure/JdbcGachaEventPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/infrastructure/JdbcGachaEventPersistence.scala @@ -17,13 +17,13 @@ class JdbcGachaEventPersistence[F[_]: Sync] extends GachaEventPersistence[F] { sql"""INSERT INTO gacha_events | (event_name, event_start_time, event_end_time) VALUES | (${gachaEvent.eventName.name}, ${gachaEvent.startDate}, ${gachaEvent.endDate}) - """.stripMargin.execute().apply() + """.stripMargin.execute() } } override def deleteGachaEvent(eventName: GachaEventName): F[Unit] = Sync[F].delay { DB.localTx { implicit session => - sql"DELETE FROM gacha_events WHERE event_name = ${eventName.name}".execute().apply() + sql"DELETE FROM gacha_events WHERE event_name = ${eventName.name}".execute() } } @@ -38,7 +38,6 @@ class JdbcGachaEventPersistence[F[_]: Sync] extends GachaEventPersistence[F] { ) } .toList() - .apply() .toVector } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/infrastructure/JdbcGachaPrizeListPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/infrastructure/JdbcGachaPrizeListPersistence.scala index bf9356bcb1..34f0e7ea4d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/infrastructure/JdbcGachaPrizeListPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gachaprize/infrastructure/JdbcGachaPrizeListPersistence.scala @@ -48,7 +48,6 @@ class JdbcGachaPrizeListPersistence[F[_]: Sync, ItemStack: Cloneable]( .merge } .toList() - .apply() .toVector } } @@ -61,7 +60,6 @@ class JdbcGachaPrizeListPersistence[F[_]: Sync, ItemStack: Cloneable]( sql"SELECT id FROM gacha_events WHERE event_name = ${gachaEvent.eventName.name}" .map(_.int("id")) .single() - .apply() } val serializedItemStack = serializeAndDeserialize.serialize(gachaPrize.itemStack) @@ -76,13 +74,13 @@ class JdbcGachaPrizeListPersistence[F[_]: Sync, ItemStack: Cloneable]( | ON DUPLICATE KEY UPDATE | probability = ${gachaPrize.probability.value}, | itemstack = $serializedItemStack - """.stripMargin.execute().apply() + """.stripMargin.execute() } } override def removeGachaPrize(gachaPrizeId: GachaPrizeId): F[Unit] = Sync[F].delay { DB.localTx { implicit session => - sql"DELETE FROM gachadata WHERE id = ${gachaPrizeId.id}".execute().apply() + sql"DELETE FROM gachadata WHERE id = ${gachaPrizeId.id}".execute() } } @@ -93,7 +91,7 @@ class JdbcGachaPrizeListPersistence[F[_]: Sync, ItemStack: Cloneable]( sql"""INSERT INTO gachadata (probability, itemstack, event_id) | (SELECT probability, itemstack, (SELECT id FROM gacha_events WHERE event_name = $eventName) FROM gachadata | WHERE event_id IS NULL) - """.stripMargin.execute().apply() + """.stripMargin.execute() } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gridregion/bukkit/BukkitRegionOperations.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gridregion/bukkit/BukkitRegionOperations.scala index 9ab2c05318..ec789febc2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gridregion/bukkit/BukkitRegionOperations.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gridregion/bukkit/BukkitRegionOperations.scala @@ -8,6 +8,8 @@ import com.github.unchama.seichiassist.subsystems.gridregion.domain.CardinalDire import com.github.unchama.seichiassist.subsystems.gridregion.domain.HorizontalAxisAlignedSubjectiveDirection.Ahead import com.github.unchama.seichiassist.subsystems.gridregion.domain._ import com.github.unchama.util.external.{WorldEditWrapper, WorldGuardWrapper} +import com.sk89q.worldedit.math.BlockVector3 +import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion import org.bukkit.Location import org.bukkit.entity.Player @@ -72,21 +74,16 @@ class BukkitRegionOperations[F[_]: Sync]( override def tryCreatingSelectedWorldGuardRegion(player: Player): F[Unit] = for { regionCount <- regionCountRepository(player).get + wgManager = WorldGuardWrapper.getRegionManager(player.getWorld) + selection = WorldEditWrapper.getSelection(player) + regionName = s"${player.getName}_${regionCount.value}" + region = new ProtectedCuboidRegion( + regionName, + BlockVector3.at(selection.getBlockX, -64, selection.getBlockZ), + BlockVector3.at(selection.getBlockX, 320, selection.getBlockZ) + ) regionCreateResult <- Sync[F].delay { - WorldEditWrapper - .getSelection(player) - .map { selection => - val regionName = s"${player.getName}_${regionCount.value}" - - WorldGuardWrapper.tryCreateRegion( - regionName, - player, - player.getWorld, - selection.getNativeMinimumPoint.toBlockVector, - selection.getNativeMaximumPoint.toBlockVector - ) - } - .getOrElse(()) + wgManager.addRegion(region) } _ <- regionCountRepository(player).update(_.increment) } yield regionCreateResult @@ -103,27 +100,18 @@ class BukkitRegionOperations[F[_]: Sync]( result <- if (!SeichiAssist.seichiAssistConfig.isGridProtectionEnabled(world)) { Sync[F].pure(RegionCreationResult.WorldProhibitsRegionCreation) - } else if (selection.isEmpty || wgManager.isEmpty) { + } else if (regionCount.value >= WorldGuardWrapper.getWorldMaxRegion(player.getWorld)) { Sync[F].pure(RegionCreationResult.Error) } else { Sync[F].delay { - val regions = WorldGuardWrapper.getApplicableRegionCount( - world, - s"${player.getName}_${regionCount.value}", - selection.get.getNativeMinimumPoint.toBlockVector, - selection.get.getNativeMaximumPoint.toBlockVector - ) - if (regions != 0) { + wgManager.getApplicableRegions(selection) + val maxRegionCount = WorldGuardWrapper.getWorldMaxRegion(world) + val regionCountPerPlayer = WorldGuardWrapper.getNumberOfRegions(player, world) + + if (maxRegionCount >= 0 && regionCountPerPlayer >= maxRegionCount) { RegionCreationResult.Error } else { - val maxRegionCount = WorldGuardWrapper.getMaxRegionCount(player, world) - val regionCountPerPlayer = WorldGuardWrapper.getRegionCountOfPlayer(player, world) - - if (maxRegionCount >= 0 && regionCountPerPlayer >= maxRegionCount) { - RegionCreationResult.Error - } else { - RegionCreationResult.Success - } + RegionCreationResult.Success } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gridregion/domain/RegionShapeSelectionState.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gridregion/domain/RegionShapeSelectionState.scala index da0a661d4f..ef30ba2922 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gridregion/domain/RegionShapeSelectionState.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gridregion/domain/RegionShapeSelectionState.scala @@ -3,7 +3,7 @@ package com.github.unchama.seichiassist.subsystems.gridregion.domain import cats.effect.Sync import cats.effect.concurrent.Ref -class RegionShapeSelectionState[F[_]: Sync] private ( +class RegionShapeSelectionState[F[_]] private ( private val regionUnitsReference: Ref[F, SubjectiveRegionShape] ) { def currentShape: F[SubjectiveRegionShape] = regionUnitsReference.get diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gridregion/infrastructure/JdbcRegionCountPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gridregion/infrastructure/JdbcRegionCountPersistence.scala index 5516824da7..d90f4149c9 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gridregion/infrastructure/JdbcRegionCountPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gridregion/infrastructure/JdbcRegionCountPersistence.scala @@ -13,7 +13,6 @@ class JdbcRegionCountPersistence[F[_]: Sync] extends RegionCountPersistence[F] { DB.localTx { implicit session => sql"UPDATE playerdata SET rgnum = ${regionCount.value} WHERE uuid = ${uuid.toString}" .execute() - .apply() } } @@ -22,7 +21,6 @@ class JdbcRegionCountPersistence[F[_]: Sync] extends RegionCountPersistence[F] { sql"SELECT rgnum FROM playerdata WHERE uuid = ${uuid.toString}" .map(_.int("rgnum")) .single() - .apply() .map(num => RegionCount(num)) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/gridregion/infrastructure/JdbcRegionTemplatePersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/gridregion/infrastructure/JdbcRegionTemplatePersistence.scala index d5eb163761..c28eea4480 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/gridregion/infrastructure/JdbcRegionTemplatePersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/gridregion/infrastructure/JdbcRegionTemplatePersistence.scala @@ -33,7 +33,6 @@ class JdbcRegionTemplatePersistence[F[_]: Sync] extends RegionTemplatePersistenc RegionTemplate(id, regionUnits) } .toList() - .apply() .toVector } } @@ -56,7 +55,7 @@ class JdbcRegionTemplatePersistence[F[_]: Sync] extends RegionTemplatePersistenc | right_length = ${value.shape.right.rul} | behind_length = ${value.shape.behind.rul} | left_length = ${value.shape.left.rul} - """.stripMargin.execute().apply() + """.stripMargin.execute() } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/home/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/home/System.scala index 206e487bbd..3512ce47c7 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/home/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/home/System.scala @@ -1,6 +1,6 @@ package com.github.unchama.seichiassist.subsystems.home -import cats.effect.{ConcurrentEffect, SyncEffect} +import cats.effect.ConcurrentEffect import com.github.unchama.concurrent.NonServerThreadContextShift import com.github.unchama.generic.ContextCoercion import com.github.unchama.minecraft.actions.OnMinecraftServerThread @@ -26,7 +26,7 @@ trait System[F[_]] extends Subsystem[F] { object System { def wired[F[_]: OnMinecraftServerThread: ConcurrentEffect: NonServerThreadContextShift, G[ _ - ]: SyncEffect: ContextCoercion[*[_], F]]( + ]: ContextCoercion[*[_], F]]( implicit breakCountReadAPI: BreakCountReadAPI[F, G, Player], buildCountReadAPI: BuildCountAPI[F, G, Player] ): System[F] = { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/home/bukkit/command/HomeCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/home/bukkit/command/HomeCommand.scala index 5f963be5f9..fda7d28c29 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/home/bukkit/command/HomeCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/home/bukkit/command/HomeCommand.scala @@ -1,8 +1,9 @@ package com.github.unchama.seichiassist.subsystems.home.bukkit.command import cats.Monad +import cats.data.Kleisli import cats.effect.implicits._ -import cats.effect.{ConcurrentEffect, Effect, IO, SyncEffect} +import cats.effect.{ConcurrentEffect, IO} import com.github.unchama.chatinterceptor.CancellationReason.Overridden import com.github.unchama.chatinterceptor.ChatInterceptionScope import com.github.unchama.concurrent.NonServerThreadContextShift @@ -32,7 +33,7 @@ class HomeCommand[F[ _ ]: OnMinecraftServerThread: ConcurrentEffect: NonServerThreadContextShift: HomeAPI, G[ _ -]: SyncEffect: ContextCoercion[*[_], F]]( +]: ContextCoercion[*[_], F]]( implicit scope: ChatInterceptionScope, breakCountReadAPI: BreakCountReadAPI[F, G, Player], buildCountReadAPI: BuildCountAPI[F, G, Player] @@ -86,11 +87,9 @@ class HomeCommand[F[ private def listExecutor() = { // locationの座標は負の無限大方向へ切り捨て(Debug画面のBlock:で表示される座標と同じ丸め方) def toBlockPos(pos: Double) = pos.floor.toInt - playerCommandBuilder.buildWithExecutionF { context => + playerCommandBuilder.buildWithExecutionCSEffect { context => val player = context.sender - val eff = for { - homeMap <- HomeReadAPI[F].list(player.getUniqueId) - } yield { + Kleisli.liftF(HomeReadAPI[F].list(player.getUniqueId)).flatMap { homeMap => val title = s"${RED}登録ホームポイント一覧:" val messages = title +: homeMap.toList.sortBy(_._1.value).map { case (homeId, home) => @@ -100,9 +99,8 @@ class HomeCommand[F[ ManagedWorld.fromName(worldName).map(_.japaneseName).getOrElse(worldName) f"${YELLOW}ID ${homeId.value}%2d $displayWorldName(${toBlockPos(x)}, ${toBlockPos(y)}, ${toBlockPos(z)}): $displayHomeName" } - MessageEffect(messages) + MessageEffectF(messages) } - eff.toIO } } @@ -129,35 +127,36 @@ class HomeCommand[F[ } private def warpExecutor = - argsAndSenderConfiguredBuilder.buildWithExecutionF { context => + argsAndSenderConfiguredBuilder.buildWithExecutionCSEffect { context => val homeId = HomeId(context.args.parsed.head) val player = context.sender val eff = for { - maxAvailableHomeCount <- Home.maxAvailableHomeCountF(player) - isHomeAvailable = maxAvailableHomeCount >= homeId.value - _ <- NonServerThreadContextShift[F].shift - homeLocation <- HomeReadAPI[F].get(player.getUniqueId, homeId) + maxAvailableHomeCount <- Kleisli.liftF(Home.maxAvailableHomeCountF(player)) + _ <- Kleisli.liftF(NonServerThreadContextShift[F].shift) + homeLocation <- Kleisli.liftF(HomeReadAPI[F].get(player.getUniqueId, homeId)) } yield { + val isHomeAvailable = maxAvailableHomeCount >= homeId.value + if (isHomeAvailable) - homeLocation.fold(MessageEffect(s"ホームポイント${homeId}が設定されてません"))(home => { + homeLocation.fold(MessageEffectF[F](s"ホームポイント${homeId}が設定されてません")) { home => val location = home.location LocationCodec .toBukkitLocation(location) .fold( - MessageEffect( + MessageEffectF[F]( List(s"${RED}ホームポイントへのワープに失敗しました", s"${RED}登録先のワールドが削除された可能性があります") ) )(bukkitLocation => - TeleportEffect.to[F](bukkitLocation).mapK(Effect.toIOK[F]) >> - MessageEffect(s"ホームポイント${homeId}にワープしました") + TeleportEffect.to[F](bukkitLocation) >> + MessageEffectF[F](s"ホームポイント${homeId}にワープしました") ) - }) + } else - MessageEffect(s"ホームポイント${homeId}は現在のレベルでは使用できません") + MessageEffectF[F](s"ホームポイント${homeId}は現在のレベルでは使用できません") } - eff.toIO + eff.flatten } def setHomeExecutor(): ContextualExecutor = diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/home/domain/Home.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/home/domain/Home.scala index 2e4146ce0c..fb47de08d3 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/home/domain/Home.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/home/domain/Home.scala @@ -1,6 +1,6 @@ package com.github.unchama.seichiassist.subsystems.home.domain -import cats.effect.{ConcurrentEffect, SyncEffect} +import cats.effect.ConcurrentEffect import cats.implicits._ import com.github.unchama.generic.ContextCoercion import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountReadAPI @@ -30,10 +30,9 @@ object Home { /** * プレイヤーの現在レベル(整地レベル、建築レベル)で利用可能なホームポイント数を取得する作用 */ - def maxAvailableHomeCountF[F[_]: ConcurrentEffect, G[_]: SyncEffect: ContextCoercion[ - *[_], - F - ], Player](player: Player)( + def maxAvailableHomeCountF[F[_]: ConcurrentEffect, G[_]: ContextCoercion[*[_], F], Player]( + player: Player + )( implicit breakCountReadAPI: BreakCountReadAPI[F, G, Player], buildCountReadAPI: BuildCountAPI[F, G, Player] ): F[Int] = { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/home/infrastructure/JdbcHomePersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/home/infrastructure/JdbcHomePersistence.scala index f52c8e430a..d6906e49d9 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/home/infrastructure/JdbcHomePersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/home/infrastructure/JdbcHomePersistence.scala @@ -36,7 +36,7 @@ class JdbcHomePersistence[F[_]: Sync: NonServerThreadContextShift] extends HomeP | location_z = $z, | pitch = $pitch, | yaw = $yaw, - | world_name = $worldName""".stripMargin.update().apply() + | world_name = $worldName""".stripMargin.update() } } @@ -67,7 +67,6 @@ class JdbcHomePersistence[F[_]: Sync: NonServerThreadContextShift] extends HomeP ) .stripMargin .list() - .apply() }.toMap } @@ -78,7 +77,7 @@ class JdbcHomePersistence[F[_]: Sync: NonServerThreadContextShift] extends HomeP sql"""delete from seichiassist.home | where server_id = $serverId | and player_uuid = ${ownerUuid.toString} - | and id = ${id.value - 1}""".stripMargin.execute().apply() + | and id = ${id.value - 1}""".stripMargin.execute() } } } 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 257ef27139..01274ae312 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 @@ -1,10 +1,9 @@ package com.github.unchama.seichiassist.subsystems.itemmigration -import cats.effect.{ConcurrentEffect, ContextShift, IO, Sync, SyncEffect, SyncIO} +import cats.effect.{ConcurrentEffect, IO, Sync, SyncEffect, SyncIO} import com.github.unchama.datarepository.bukkit.player.BukkitRepositoryControls import com.github.unchama.datarepository.template.RepositoryDefinition import com.github.unchama.generic.ContextCoercion -import com.github.unchama.generic.effect.unsafe.EffectEnvironment import com.github.unchama.itemmigration.application.ItemMigrationStateRepositoryDefinitions import com.github.unchama.itemmigration.bukkit.controllers.player.PlayerItemMigrationController import com.github.unchama.itemmigration.service.ItemMigrationService @@ -32,9 +31,8 @@ object System { import cats.implicits._ - def wired[F[_]: ConcurrentEffect: ContextShift, G[_]: SyncEffect: ContextCoercion[*[_], F]]( - implicit effectEnvironment: EffectEnvironment, - logger: Logger + def wired[F[_]: ConcurrentEffect, G[_]: SyncEffect: ContextCoercion[*[_], F]]( + implicit logger: Logger ): G[System[F]] = for { migrations <- Sync[G].delay { implicit val syncIOUuidRepository: UuidRepository[SyncIO] = 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 db43855190..cc797a51fa 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 @@ -1,7 +1,6 @@ package com.github.unchama.seichiassist.subsystems.itemmigration.controllers import cats.effect.{Sync, SyncEffect, SyncIO} -import com.github.unchama.generic.effect.unsafe.EffectEnvironment import com.github.unchama.itemmigration.domain.ItemMigrations import com.github.unchama.itemmigration.service.ItemMigrationService import com.github.unchama.seichiassist.subsystems.itemmigration.infrastructure.loggers.PersistedItemsMigrationSlf4jLogger @@ -11,8 +10,7 @@ import org.slf4j.Logger import scalikejdbc.DB case class DatabaseMigrationController[F[_]: SyncEffect](migrations: ItemMigrations)( - implicit effectEnvironment: EffectEnvironment, - logger: Logger + implicit logger: Logger ) { lazy val runDatabaseMigration: F[Unit] = Sync[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 fc00e4b536..a79a461429 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 @@ -3,7 +3,6 @@ package com.github.unchama.seichiassist.subsystems.itemmigration.infrastructure. 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 @@ -33,7 +32,6 @@ object JdbcBackedUuidRepository { (rs.string("name").toLowerCase, UUID.fromString(rs.string("uuid"))) } .list() - .apply() .toMap } @@ -44,9 +42,7 @@ object JdbcBackedUuidRepository { } } - def initializeInstanceIn[F[_]: Sync, G[_]: Applicative]( - implicit logger: Logger - ): F[UuidRepository[G]] = { + def initializeInstanceIn[F[_]: Sync, G[_]: Applicative]: F[UuidRepository[G]] = { import cats.implicits._ for { @@ -56,6 +52,6 @@ object JdbcBackedUuidRepository { } } - def initializeInstance[F[_]: Sync](implicit logger: Logger): F[UuidRepository[F]] = + def initializeInstance[F[_]: Sync](): 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 70907fb507..4beb513c0f 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 @@ -21,25 +21,24 @@ class PersistedItemsMigrationVersionRepository[F[_]](implicit dbSession: DBSessi 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"set autocommit=0".update() // ロックを取得するときは利用するテーブルすべてをロックしなければならない sql"lock tables seichiassist.item_migration_on_database write, seichiassist.playerdata write" .update() - .apply() // このリソースを使用する際にはロックが取れているというのを保証すればよいため、リソースの実体は無くて良い () }) { case (_, ExitCase.Completed) => F.delay { - sql"commit".update().apply() - sql"unlock tables".update().apply() + sql"commit".update() + sql"unlock tables".update() } case _ => F.delay { - sql"rollback".update().apply() - sql"unlock tables".update().apply() + sql"rollback".update() + sql"unlock tables".update() } } } @@ -52,8 +51,7 @@ class PersistedItemsMigrationVersionRepository[F[_]](implicit dbSession: DBSessi sql""" select version_string from seichiassist.item_migration_on_database """.map { rs => rs.string("version_string") } - .list - .apply() + .list() .flatMap(ItemMigrationVersionNumber.fromString) .toSet } 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 4c1e273b18..a4f8454a58 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 @@ -33,8 +33,7 @@ class PlayerItemsMigrationVersionRepository[F[_]](serverId: String)(implicit F: 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() + .list() .flatMap(ItemMigrationVersionNumber.fromString) .toSet 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 d925e4c87a..e2c55554be 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 @@ -32,8 +32,7 @@ class WorldLevelItemsMigrationVersionRepository[F[_]](serverId: String)(implicit sql""" select version_string from seichiassist.item_migration_in_server_world_levels where server_id = $serverId """.map { rs => rs.string("version_string") } - .list - .apply() + .list() .flatMap(ItemMigrationVersionNumber.fromString) .toSet } 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 f02354f219..7b4c5e9ade 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 @@ -46,7 +46,6 @@ class SeichiAssistPersistedItems[F[_]](implicit dBSession: DBSession, F: Sync[F] val triples = sql"select uuid, shareinv, inventory from seichiassist.playerdata" .map { rs => (rs.string("uuid"), rs.stringOpt("shareinv"), rs.stringOpt("inventory")) } .list() - .apply() val batchParam: Seq[Seq[String]] = triples.map { case (uuid, shareinv, inventory) => 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 c58bcd791a..38b3b37b9c 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 @@ -6,7 +6,7 @@ import com.github.unchama.itemmigration.domain.{ItemMigration, ItemMigrationVers import com.github.unchama.seichiassist.subsystems.itemmigration.domain.minecraft.UuidRepository import com.github.unchama.seichiassist.subsystems.itemmigration.migrations.V1_0_0_MigrateMebiusToNewCodec.OldBukkitMebiusItemStackCodec.OldMebiusRawProperty import com.github.unchama.seichiassist.subsystems.mebius.bukkit.codec.BukkitMebiusItemStackCodec.NBTTagConstants -import de.tr7zw.itemnbtapi.NBTItem +import de.tr7zw.nbtapi.NBTItem import org.bukkit.ChatColor._ import org.bukkit.Material import org.bukkit.inventory.ItemStack @@ -24,7 +24,6 @@ object V1_0_0_MigrateMebiusToNewCodec { // noinspection DuplicatedCode object OldBukkitMebiusItemStackCodec { - import de.tr7zw.itemnbtapi.NBTItem import org.bukkit.inventory.ItemStack private val mebiusLoreHead = 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 f4d1bced70..fec6d6914d 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 @@ -36,7 +36,6 @@ object V1_1_0_AddUnbreakableToNarutoRemake { itemStack.clone().tap { clone => import clone._ - setDurability(0.toShort) setItemMeta { getItemMeta.tap { itemMeta => import itemMeta._ diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/joinandquitmessenger/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/joinandquitmessenger/System.scala new file mode 100644 index 0000000000..3ce22378fd --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/joinandquitmessenger/System.scala @@ -0,0 +1,15 @@ +package com.github.unchama.seichiassist.subsystems.joinandquitmessenger + +import com.github.unchama.seichiassist.meta.subsystem.Subsystem +import com.github.unchama.seichiassist.subsystems.joinandquitmessenger.bukkit.JoinAndQuitListeners +import org.bukkit.event.Listener + +object System { + + def wired[F[_]]: Subsystem[F] = { + new Subsystem[F] { + override val listeners: Seq[Listener] = Seq(new JoinAndQuitListeners) + } + } + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/joinandquitmessenger/bukkit/JoinAndQuitListeners.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/joinandquitmessenger/bukkit/JoinAndQuitListeners.scala new file mode 100644 index 0000000000..5b19ae5551 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/joinandquitmessenger/bukkit/JoinAndQuitListeners.scala @@ -0,0 +1,20 @@ +package com.github.unchama.seichiassist.subsystems.joinandquitmessenger.bukkit + +import com.github.unchama.seichiassist.subsystems.joinandquitmessenger.domain.Messages +import org.bukkit.ChatColor +import org.bukkit.event.{EventHandler, Listener} +import org.bukkit.event.player.{PlayerJoinEvent, PlayerQuitEvent} + +class JoinAndQuitListeners extends Listener { + + @EventHandler + def onJoin(event: PlayerJoinEvent): Unit = { + event.setJoinMessage(s"${ChatColor.GRAY}${Messages.joinMessage(event.getPlayer.getName)}") + } + + @EventHandler + def onQuit(event: PlayerQuitEvent): Unit = { + event.setQuitMessage(Messages.quitMessage(event.getPlayer.getName)) + } + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/joinandquitmessenger/domain/Messages.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/joinandquitmessenger/domain/Messages.scala new file mode 100644 index 0000000000..0759f2ab00 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/joinandquitmessenger/domain/Messages.scala @@ -0,0 +1,10 @@ +package com.github.unchama.seichiassist.subsystems.joinandquitmessenger.domain + +object Messages { + + def joinMessage(playerName: String): String = s"$playerName がログインしました" + + // Note: 整地鯖では退出メッセージを表示しない + def quitMessage(playerName: String): String = "" + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/lastquit/bukkit/commands/LastQuitCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/lastquit/bukkit/commands/LastQuitCommand.scala index 6abbe4235b..1cb66dc606 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/lastquit/bukkit/commands/LastQuitCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/lastquit/bukkit/commands/LastQuitCommand.scala @@ -1,14 +1,14 @@ package com.github.unchama.seichiassist.subsystems.lastquit.bukkit.commands -import cats.effect.ConcurrentEffect.ops.toAllConcurrentEffectOps -import cats.effect.{ConcurrentEffect, IO} +import cats.data.Kleisli +import cats.effect.ConcurrentEffect import com.github.unchama.contextualexecutor.builder.{ContextualExecutorBuilder, Parsers} import com.github.unchama.seichiassist.infrastructure.minecraft.{ JdbcLastSeenNameToUuid, LastSeenNameToUuidError } import com.github.unchama.seichiassist.subsystems.lastquit.LastQuitAPI -import com.github.unchama.targetedeffect.commandsender.MessageEffect +import com.github.unchama.targetedeffect.commandsender.MessageEffectF import org.bukkit.ChatColor.RED import org.bukkit.command.TabExecutor @@ -21,30 +21,30 @@ class LastQuitCommand[F[_]: ConcurrentEffect](implicit lastQuitAPI: LastQuitAPI[ val executor: TabExecutor = ContextualExecutorBuilder .beginConfiguration .thenParse(Parsers.identity) - .buildWithExecutionF { context => + .buildWithExecutionCSEffect { context => val playerName = context.args.parsed.head - for { - uuidEither <- new JdbcLastSeenNameToUuid[IO].of(playerName) - lastQuit <- uuidEither.traverse(uuid => lastQuitAPI.get(uuid).toIO) + (for { + uuidEither <- Kleisli.liftF(new JdbcLastSeenNameToUuid[F].of(playerName)) + lastQuit <- Kleisli.liftF(uuidEither.traverse(uuid => lastQuitAPI.get(uuid))) } yield { lastQuit match { case Left(error) => error match { case LastSeenNameToUuidError.MultipleFound => - MessageEffect(s"${RED}指定された名前のプレイヤーが複数見つかりました。") + MessageEffectF(s"${RED}指定された名前のプレイヤーが複数見つかりました。") case LastSeenNameToUuidError.NotFound => - MessageEffect(s"${RED}指定された名前のプレイヤーが見つかりませんでした。") + MessageEffectF(s"${RED}指定された名前のプレイヤーが見つかりませんでした。") } case Right(lastQuitTimeOpt) => lastQuitTimeOpt match { case Some(lastQuitTime) => val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm") - MessageEffect( + MessageEffectF( s"${playerName}の最終ログアウト日時:${lastQuitTime.dateTime.format(dateTimeFormatter)}" ) case None => - MessageEffect( + MessageEffectF( List( s"${RED}最終ログアウト日時の照会に失敗しました。", s"${RED}プレイヤー名が変更されていないか確認してください。", @@ -53,7 +53,7 @@ class LastQuitCommand[F[_]: ConcurrentEffect](implicit lastQuitAPI: LastQuitAPI[ ) } } - } + }).flatten } .asNonBlockingTabExecutor() diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/lastquit/infrastructure/JdbcLastQuitPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/lastquit/infrastructure/JdbcLastQuitPersistence.scala index 7fd796338e..406f4a599a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/lastquit/infrastructure/JdbcLastQuitPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/lastquit/infrastructure/JdbcLastQuitPersistence.scala @@ -13,9 +13,7 @@ class JdbcLastQuitPersistence[F[_]: Sync] extends LastQuitPersistence[F] { override def updateLastQuitNow(uuid: UUID): F[Unit] = Sync[F].delay { DB.localTx { implicit session => - sql"UPDATE playerdata SET lastquit = NOW() WHERE uuid = ${uuid.toString}" - .execute() - .apply() + sql"UPDATE playerdata SET lastquit = NOW() WHERE uuid = ${uuid.toString}".execute() } } @@ -26,7 +24,6 @@ class JdbcLastQuitPersistence[F[_]: Sync] extends LastQuitPersistence[F] { sql"SELECT lastquit FROM playerdata WHERE uuid = ${uuid.toString}" .map(_.localDateTime("lastquit")) .toList() - .apply() .headOption lastQuitDateTime.map(LastQuitDateTime) } 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 d56585e8a0..5cc675b0be 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 @@ -2,7 +2,7 @@ package com.github.unchama.seichiassist.subsystems.mana.application.process import cats.effect.concurrent.Ref import cats.implicits._ -import cats.{Functor, Monad} +import cats.Monad import com.github.unchama.datarepository.KeyedDataRepository import com.github.unchama.generic.{ContextCoercion, Diff} import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountReadAPI @@ -13,7 +13,7 @@ import com.github.unchama.seichiassist.subsystems.mana.domain.LevelCappedManaAmo */ object RefillToCap { - def using[F[_]: Functor, G[_]: Monad: ContextCoercion[*[_], F], Player]( + def using[F[_], G[_]: Monad: ContextCoercion[*[_], F], Player]( repository: KeyedDataRepository[Player, Ref[G, LevelCappedManaAmount]] )(implicit breakCountReadAPI: BreakCountReadAPI[F, G, Player]): fs2.Stream[F, Unit] = { breakCountReadAPI.seichiStarLevelUpdates.evalMap { 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 4329e87385..1c9dd48735 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 @@ -1,7 +1,7 @@ package com.github.unchama.seichiassist.subsystems.mana.application.process import cats.effect.concurrent.Ref -import cats.{Functor, Monad} +import cats.Monad import com.github.unchama.datarepository.KeyedDataRepository import com.github.unchama.generic.{ContextCoercion, Diff} import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountReadAPI @@ -11,7 +11,7 @@ object UpdateManaCaps { import cats.implicits._ - def using[F[_]: Functor, G[_]: Monad: ContextCoercion[*[_], F], Player]( + def using[F[_], G[_]: Monad: ContextCoercion[*[_], F], Player]( repository: KeyedDataRepository[Player, Ref[G, LevelCappedManaAmount]] )(implicit breakCountReadAPI: BreakCountReadAPI[F, G, Player]): fs2.Stream[F, Unit] = breakCountReadAPI.seichiLevelUpdates.evalMap { 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 ded6a9a728..67e818eb29 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 @@ -28,6 +28,11 @@ trait ManaManipulation[F[_]] { */ def tryAcquire(amount: ManaAmount): F[Option[ManaAmount]] + /** + * `amount` だけマナを消費することが可能ならば `true`、そうでないなら `false` を返す作用 + */ + def canAcquire(amount: ManaAmount): F[Boolean] + } object ManaManipulation { @@ -54,6 +59,12 @@ object ManaManipulation { } } } + override def canAcquire(amount: ManaAmount): F[Boolean] = { + for { + multiplier <- dragonNightTimeMultiplierRef.get + original <- ref.get + } yield original.tryUse(amount)(multiplier).isDefined + } } } 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 2c27080658..b016a77dfa 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 @@ -16,16 +16,13 @@ class JdbcManaAmountPersistence[F[_]](implicit F: Sync[F]) extends ManaAmountPer sql"select mana from playerdata where uuid = ${key.toString}" .map { rs => ManaAmount(rs.double("mana")) } .first() - .apply() } } override def write(key: UUID, value: ManaAmount): F[Unit] = F.delay { DB.localTx { implicit session => - sql"update playerdata set mana = ${value.value} where uuid = ${key.toString}" - .update() - .apply() + sql"update playerdata set mana = ${value.value} where uuid = ${key.toString}".update() } } 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 16b3b21273..90ac7b627c 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 @@ -16,10 +16,12 @@ import com.github.unchama.seichiassist.subsystems.managedfly.application._ import com.github.unchama.seichiassist.subsystems.managedfly.application.repository.ActiveSessionReferenceRepositoryDefinition import com.github.unchama.seichiassist.subsystems.managedfly.bukkit.BukkitPlayerFlyStatusManipulation import com.github.unchama.seichiassist.subsystems.managedfly.bukkit.controllers.BukkitFlyCommand +import com.github.unchama.seichiassist.subsystems.managedfly.bukkit.listeners.BukkitPlayerStatusChangeListener import com.github.unchama.seichiassist.subsystems.managedfly.domain.PlayerFlyStatus import com.github.unchama.seichiassist.subsystems.managedfly.infrastructure.JdbcFlyDurationPersistenceRepository import org.bukkit.command.TabExecutor import org.bukkit.entity.Player +import org.bukkit.event.Listener /** * NOTE: このサブシステム(managedfly)は本来BuildAssist側に属するが、 @@ -75,6 +77,9 @@ object System { : Seq[BukkitRepositoryControls[AsyncContext, _]] = Seq(controls.coerceFinalizationContextTo[AsyncContext]) + override val listeners: Seq[Listener] = + Seq(new BukkitPlayerStatusChangeListener[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/ActiveSessionReference.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/application/ActiveSessionReference.scala index 720fc0ea2d..2037fab461 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,9 +8,7 @@ import com.github.unchama.seichiassist.subsystems.managedfly.domain.{NotFlying, /** * プレーヤーの飛行セッションの参照 */ -class ActiveSessionReference[AsyncContext[_]: ConcurrentEffect, SyncContext[ - _ -]: Sync: ContextCoercion[*[_], AsyncContext]]( +class ActiveSessionReference[AsyncContext[_]: ConcurrentEffect, SyncContext[_]: Sync]( private val sessionMutexRef: Mutex[AsyncContext, SyncContext, Option[ ActiveSession[AsyncContext, SyncContext] ]] 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 83a5187dd1..d9911a265c 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 @@ -1,7 +1,7 @@ package com.github.unchama.seichiassist.subsystems.managedfly.bukkit import cats.data.Kleisli -import cats.effect.{Concurrent, Sync, SyncIO, Timer} +import cats.effect.{Concurrent, Sync, SyncIO} import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.subsystems.idletime.IdleTimeAPI import com.github.unchama.seichiassist.subsystems.managedfly.application._ @@ -10,9 +10,7 @@ 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]( +class BukkitPlayerFlyStatusManipulation[AsyncContext[_]: Concurrent: OnMinecraftServerThread]( implicit configuration: SystemConfiguration, idleTimeAPI: IdleTimeAPI[AsyncContext, Player] ) extends PlayerFlyStatusManipulation[Kleisli[AsyncContext, Player, *]] { @@ -92,7 +90,7 @@ class BukkitPlayerFlyStatusManipulation[AsyncContext[ private val sendMessages: List[String] => Kleisli[AsyncContext, Player, Unit] = { messages => Kleisli { player => Sync[AsyncContext].delay { - player.sendMessage(messages.toArray) + player.sendMessage(messages.mkString("\n")) } } } 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 6e222d6196..71aa2a38b1 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 @@ -1,6 +1,7 @@ package com.github.unchama.seichiassist.subsystems.managedfly.bukkit.controllers -import cats.effect.{ConcurrentEffect, IO, SyncEffect, SyncIO, Timer} +import cats.data.Kleisli +import cats.effect.{ConcurrentEffect, IO, SyncEffect, SyncIO} import com.github.unchama.contextualexecutor.ContextualExecutor import com.github.unchama.contextualexecutor.builder.Parsers import com.github.unchama.contextualexecutor.executors.BranchedExecutor @@ -16,7 +17,7 @@ import com.github.unchama.seichiassist.subsystems.managedfly.domain.{ RemainingFlyDuration } import com.github.unchama.targetedeffect.TargetedEffect -import com.github.unchama.targetedeffect.commandsender.MessageEffect +import com.github.unchama.targetedeffect.commandsender.{MessageEffect, MessageEffectF} import eu.timepit.refined.api.Refined import eu.timepit.refined.numeric.Positive import org.bukkit.ChatColor._ @@ -53,7 +54,7 @@ object BukkitFlyCommand { import cats.effect.implicits._ import cats.implicits._ - def startEndlessCommand[F[_]: ConcurrentEffect: Timer, G[_]: SyncEffect]( + private def startEndlessCommand[F[_]: ConcurrentEffect, G[_]: SyncEffect]( implicit sessionReferenceRepository: KeyedDataRepository[Player, ActiveSessionReference[F, G]], factory: ActiveSessionFactory[F, Player] @@ -66,7 +67,7 @@ object BukkitFlyCommand { } yield TargetedEffect.emptyEffect } - def addCommand[F[_]: ConcurrentEffect: Timer, G[_]: SyncEffect]( + private def addCommand[F[_]: ConcurrentEffect, G[_]: SyncEffect]( implicit sessionReferenceRepository: KeyedDataRepository[Player, ActiveSessionReference[F, G]], factory: ActiveSessionFactory[F, Player] @@ -91,24 +92,22 @@ object BukkitFlyCommand { } yield TargetedEffect.emptyEffect } - def finishCommand[F[_]: ConcurrentEffect, G[_]]( + private def finishCommand[F[_]: ConcurrentEffect, G[_]]( implicit sessionReferenceRepository: KeyedDataRepository[Player, ActiveSessionReference[F, G]] ): ContextualExecutor = - BuilderTemplates.playerCommandBuilder.buildWithExecutionF { context => - for { - sessionStopped <- - sessionReferenceRepository(context.sender).stopAnyRunningSession - } yield { - if (sessionStopped) { - MessageEffect(s"${GREEN}fly効果を停止しました。") - } else { - MessageEffect(s"${GREEN}fly効果は現在OFFです。") - } + BuilderTemplates.playerCommandBuilder.buildWithExecutionCSEffect { context => + Kleisli.liftF(sessionReferenceRepository(context.sender).stopAnyRunningSession).flatMap { + sessionStopped => + if (sessionStopped) { + MessageEffectF(s"${GREEN}fly効果を停止しました。") + } else { + MessageEffectF(s"${GREEN}fly効果は現在OFFです。") + } } } - def executor[F[_]: ConcurrentEffect: Timer, G[_]: SyncEffect]( + def executor[F[_]: ConcurrentEffect, G[_]: SyncEffect]( implicit sessionReferenceRepository: KeyedDataRepository[Player, ActiveSessionReference[F, G]], factory: ActiveSessionFactory[F, Player] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/bukkit/listeners/BukkitPlayerStatusChangeListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/bukkit/listeners/BukkitPlayerStatusChangeListener.scala new file mode 100644 index 0000000000..0aae33467f --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/managedfly/bukkit/listeners/BukkitPlayerStatusChangeListener.scala @@ -0,0 +1,52 @@ +package com.github.unchama.seichiassist.subsystems.managedfly.bukkit.listeners + +import cats.data.Kleisli +import cats.effect.{ConcurrentEffect, SyncEffect, SyncIO} +import com.github.unchama.datarepository.KeyedDataRepository +import com.github.unchama.seichiassist.subsystems.managedfly.application.{ + ActiveSessionReference, + PlayerFlyStatusManipulation +} +import org.bukkit.entity.Player +import org.bukkit.event.player.{PlayerChangedWorldEvent, PlayerRespawnEvent} +import org.bukkit.event.{EventHandler, Listener} + +class BukkitPlayerStatusChangeListener[F[_]: ConcurrentEffect, G[_]: SyncEffect]( + implicit + sessionReferenceRepository: KeyedDataRepository[Player, ActiveSessionReference[F, G]], + playerFlyStatusManipulation: PlayerFlyStatusManipulation[Kleisli[F, Player, *]] +) extends Listener { + + import cats.effect.implicits._ + + @EventHandler + def onWorldChange(event: PlayerChangedWorldEvent): Unit = { + val player = event.getPlayer + + val program = for { + currentStatus <- sessionReferenceRepository(player) + .getLatestFlyStatus + .runSync[SyncIO] + .toIO + _ <- playerFlyStatusManipulation.synchronizeFlyStatus(currentStatus)(player).toIO + } yield () + + program.unsafeRunAsyncAndForget() + } + + @EventHandler + def onPlayerRespawn(event: PlayerRespawnEvent): Unit = { + val player = event.getPlayer + + val program = for { + currentStatus <- sessionReferenceRepository(player) + .getLatestFlyStatus + .runSync[SyncIO] + .toIO + _ <- playerFlyStatusManipulation.synchronizeFlyStatus(currentStatus)(player).toIO + } yield () + + program.unsafeRunAsyncAndForget() + } + +} 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 bdb1d8bedc..4c49f1824f 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 @@ -23,7 +23,7 @@ class JdbcFlyDurationPersistenceRepository[SyncContext[_]]( sql""" insert into fly_status_cache values (${key.toString}, $serializedDuration) on duplicate key update remaining_fly_minutes = $serializedDuration - """.stripMargin.update().apply() + """.stripMargin.update() } } @@ -33,7 +33,7 @@ class JdbcFlyDurationPersistenceRepository[SyncContext[_]]( sql""" select remaining_fly_minutes from fly_status_cache where player_uuid = ${key.toString} - """.map { rs => rs.int("remaining_fly_minutes") }.headOption().apply().map { + """.map { rs => rs.int("remaining_fly_minutes") }.headOption().map { case 0 => None case -1 => Some(RemainingFlyDuration.Infinity) case n if n > 0 => Some(RemainingFlyDuration.PositiveMinutes.fromPositive(n)) 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 937eb6606a..69e71cfff5 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 @@ -7,7 +7,7 @@ object BukkitMebiusAppearanceMaterialCodec { private val appearanceThresholds = List( 1 -> Material.LEATHER_HELMET, - 5 -> Material.GOLD_HELMET, + 5 -> Material.GOLDEN_HELMET, 10 -> Material.CHAINMAIL_HELMET, 20 -> Material.IRON_HELMET, 25 -> Material.DIAMOND_HELMET 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 226a5afa8e..e0295419e8 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 @@ -3,7 +3,7 @@ package com.github.unchama.seichiassist.subsystems.mebius.bukkit.codec import com.github.unchama.seichiassist.subsystems.mebius.domain.property._ import com.github.unchama.seichiassist.subsystems.mebius.domain.resources.MebiusTalks import com.github.unchama.seichiassist.subsystems.seasonalevents.christmas.ChristmasItemData -import de.tr7zw.itemnbtapi.NBTItem +import de.tr7zw.nbtapi.NBTItem import org.bukkit.ChatColor._ import org.bukkit.Material import org.bukkit.entity.Player @@ -119,19 +119,19 @@ object BukkitMebiusItemStackCodec { * * を満足する。 */ - def materialize(property: MebiusProperty, damageValue: Short): ItemStack = { + def materialize(property: MebiusProperty): ItemStack = { val material = property.forcedMaterial match { case MebiusForcedMaterial.None => BukkitMebiusAppearanceMaterialCodec.appearanceMaterialAt(property.level) case MebiusForcedMaterial.Leather => Material.LEATHER_HELMET // 革のヘルメット case MebiusForcedMaterial.Iron => Material.IRON_HELMET // 鉄のヘルメット case MebiusForcedMaterial.Chain => Material.CHAINMAIL_HELMET // チェーンのヘルメット - case MebiusForcedMaterial.Gold => Material.GOLD_HELMET // 金のヘルメット + case MebiusForcedMaterial.Gold => Material.GOLDEN_HELMET // 金のヘルメット } import scala.util.chaining._ - val item = new ItemStack(material, 1, damageValue) + val item = new ItemStack(material, 1) item.setItemMeta { item.getItemMeta.tap { meta => 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 e15664d7fc..49195bf506 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 @@ -64,8 +64,7 @@ class MebiusCommandExecutorProvider( SequentialEffect( UnfocusedEffect { player.getInventory.setHelmet { - BukkitMebiusItemStackCodec - .materialize(newProperty, damageValue = helmet.getDurability) + BukkitMebiusItemStackCodec.materialize(newProperty) } }, additionalEffectsOnModification(newProperty) @@ -117,7 +116,7 @@ class MebiusCommandExecutorProvider( if (property.level.isMaximum) { val newProperty = property.toggleForcedMaterial val newItem = - BukkitMebiusItemStackCodec.materialize(newProperty, mainHand.getDurability) + BukkitMebiusItemStackCodec.materialize(newProperty) val newMaterialName = newProperty.forcedMaterial match { case MebiusForcedMaterial.None => "ダイヤモンド" 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 8298ce99c5..778a3562d8 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 @@ -44,13 +44,13 @@ class BukkitMebiusSpeechGateway(player: Player)(implicit timer: Timer[IO]) val effect = strength match { case MebiusSpeechStrength.Medium => playSoundsInSequence( - RepeatedEffect(3)(FocusedSoundEffect(Sound.BLOCK_NOTE_HARP, 2.0f, 1.0f)), - RepeatedEffect(3)(FocusedSoundEffect(Sound.BLOCK_NOTE_HARP, 2.0f, 1.5f)) + RepeatedEffect(3)(FocusedSoundEffect(Sound.BLOCK_NOTE_BLOCK_HARP, 2.0f, 1.0f)), + RepeatedEffect(3)(FocusedSoundEffect(Sound.BLOCK_NOTE_BLOCK_HARP, 2.0f, 1.5f)) ) 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_BLOCK_HARP, 2.0f, 1.5f)), + RepeatedEffect(5)(FocusedSoundEffect(Sound.BLOCK_NOTE_BLOCK_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 37b7facbca..6faf7eccae 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 @@ -51,7 +51,7 @@ class MebiusDropTrialListener[G[_]: ChristmasEventsAPI: RandomEffect: SyncEffect ) val mebius = - BukkitMebiusItemStackCodec.materialize(droppedMebiusProperty, damageValue = 0.toShort) + BukkitMebiusItemStackCodec.materialize(droppedMebiusProperty) player.sendMessage(s"$RESET$YELLOW${BOLD}おめでとうございます。採掘中にMEBIUSを発見しました。") player.sendMessage(s"$RESET$YELLOW${BOLD}MEBIUSはプレイヤーと共に成長するヘルメットです。") 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 1fdff31a1e..5bb0ad78db 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 @@ -21,6 +21,7 @@ import org.bukkit.event.block.BlockBreakEvent import org.bukkit.event.entity.{EntityDamageByEntityEvent, EntityDeathEvent} import org.bukkit.event.player.PlayerItemBreakEvent import org.bukkit.event.{EventHandler, EventPriority, Listener} +import org.bukkit.inventory.meta.Damageable class MebiusInteractionResponder( implicit serviceRepository: PlayerDataRepository[MebiusSpeechService[SyncIO]], @@ -40,32 +41,37 @@ class MebiusInteractionResponder( val speechService = serviceRepository(player) - val messageProgram = if (helmet.getDurability >= helmet.getType.getMaxDurability - 10) { - MebiusMessages.onDamageBreaking.pickOne[SyncIO].flatMap { message => - // 耐久閾値を超えていたら破損警告 - speechService.tryMakingSpeech( - mebiusProperty, - MebiusSpeech( - message.interpolate(mebiusProperty.ownerNickname), - MebiusSpeechStrength.Medium + val messageProgram = + if ( + helmet.getItemMeta.asInstanceOf[Damageable].getDamage >= helmet + .getType + .getMaxDurability - 10 + ) { + MebiusMessages.onDamageBreaking.pickOne[SyncIO].flatMap { message => + // 耐久閾値を超えていたら破損警告 + speechService.tryMakingSpeech( + mebiusProperty, + 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 _ => @@ -98,7 +104,7 @@ class MebiusInteractionResponder( MessageEffect( s"${BukkitMebiusItemStackCodec.displayNameOfMaterializedItem(property)}${RESET}が旅立ちました。" ), - FocusedSoundEffect(Sound.ENTITY_ENDERDRAGON_DEATH, 1.0f, 0.1f) + FocusedSoundEffect(Sound.ENTITY_ENDER_DRAGON_DEATH, 1.0f, 0.1f) ).run(player) } ) 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 1505585cb9..3dd1de4d2f 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 @@ -39,7 +39,7 @@ class MebiusLevelUpTrialListener( if (newMebiusProperty != oldMebiusProperty) { player.getInventory.setHelmet { - BukkitMebiusItemStackCodec.materialize(newMebiusProperty, damageValue = 0) + BukkitMebiusItemStackCodec.materialize(newMebiusProperty) } import cats.implicits._ 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 83f684de10..b22544bae9 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 @@ -1,6 +1,6 @@ package com.github.unchama.seichiassist.subsystems.mebius.bukkit.listeners -import cats.effect.{Effect, IO, SyncIO, Timer} +import cats.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 @@ -16,7 +16,7 @@ import org.bukkit.event.{EventHandler, EventPriority, Listener} import java.util.concurrent.TimeUnit import scala.concurrent.duration.FiniteDuration -class MebiusPlayerJoinGreeter[F[_]: Effect]( +class MebiusPlayerJoinGreeter[F[_]]( implicit effectEnvironment: EffectEnvironment, speechServiceRepository: PlayerDataRepository[MebiusSpeechService[SyncIO]], timer: Timer[IO] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/bukkit/BukkitMineStackObjectList.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/bukkit/BukkitMineStackObjectList.scala index 5700a0c451..7ad705b0c6 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/bukkit/BukkitMineStackObjectList.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/bukkit/BukkitMineStackObjectList.scala @@ -6,6 +6,7 @@ import cats.effect.concurrent.Ref import com.github.unchama.minecraft.bukkit.algebra.CloneableBukkitItemStack._ import com.github.unchama.minecraft.objects.MinecraftMaterial import com.github.unchama.seichiassist.subsystems.gachaprize.GachaPrizeAPI +import com.github.unchama.seichiassist.subsystems.gachaprize.domain.CanBeSignedAsGachaPrize import com.github.unchama.seichiassist.subsystems.minestack.domain.minestackobject.MineStackObject.{ MineStackObjectByItemStack, MineStackObjectByMaterial @@ -15,6 +16,8 @@ import com.github.unchama.seichiassist.subsystems.minestack.domain.minestackobje import org.bukkit.Material import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.meta.PotionMeta +import org.bukkit.potion.{PotionData, PotionType} class BukkitMineStackObjectList[F[_]: Sync]( implicit gachaPrizeAPI: GachaPrizeAPI[F, ItemStack, Player], @@ -29,567 +32,1063 @@ class BukkitMineStackObjectList[F[_]: Sync]( // 採掘可能ブロック private val minestacklistmine: List[MineStackObjectGroup[ItemStack]] = leftElems( - MineStackObjectByMaterial(ORES, "coal_ore", "石炭鉱石", Material.COAL_ORE, 0), - MineStackObjectByMaterial(ORES, "coal", "石炭", Material.COAL, 0), - MineStackObjectByMaterial(ORES, "coal_block", "石炭ブロック", Material.COAL_BLOCK, 0), - MineStackObjectByMaterial(ORES, "coal_1", "木炭", Material.COAL, 1), - MineStackObjectByMaterial(ORES, "iron_ore", "鉄鉱石", Material.IRON_ORE, 0), - MineStackObjectByMaterial(ORES, "iron_ingot", "鉄インゴット", Material.IRON_INGOT, 0), - MineStackObjectByMaterial(ORES, "iron_block", "鉄ブロック", Material.IRON_BLOCK, 0), - MineStackObjectByMaterial(ORES, "quartz_ore", "ネザー水晶鉱石", Material.QUARTZ_ORE, 0), - MineStackObjectByMaterial(ORES, "quartz", "ネザー水晶", Material.QUARTZ, 0), - MineStackObjectByMaterial(ORES, "gold_ore", "金鉱石", Material.GOLD_ORE, 0), - MineStackObjectByMaterial(ORES, "gold_ingot", "金インゴット", Material.GOLD_INGOT, 0), - MineStackObjectByMaterial(ORES, "gold_block", "金ブロック", Material.GOLD_BLOCK, 0), - MineStackObjectByMaterial(ORES, "redstone_ore", "レッドストーン鉱石", Material.REDSTONE_ORE, 0), - MineStackObjectByMaterial(ORES, "lapis_ore", "ラピスラズリ鉱石", Material.LAPIS_ORE, 0), - MineStackObjectByMaterial(ORES, "lapis_lazuli", "ラピスラズリ", Material.INK_SACK, 4), - MineStackObjectByMaterial(ORES, "lapis_block", "ラピスラズリブロック", Material.LAPIS_BLOCK, 0), - MineStackObjectByMaterial(ORES, "diamond_ore", "ダイヤモンド鉱石", Material.DIAMOND_ORE, 0), - MineStackObjectByMaterial(ORES, "diamond", "ダイヤモンド", Material.DIAMOND, 0), - MineStackObjectByMaterial(ORES, "diamond_block", "ダイヤモンドブロック", Material.DIAMOND_BLOCK, 0), - MineStackObjectByMaterial(ORES, "emerald_ore", "エメラルド鉱石", Material.EMERALD_ORE, 0), - MineStackObjectByMaterial(ORES, "emerald", "エメラルド", Material.EMERALD, 0), - MineStackObjectByMaterial(ORES, "emerald_block", "エメラルドブロック", Material.EMERALD_BLOCK, 0) + MineStackObjectByMaterial(ORES, "coal_ore", "石炭鉱石", Material.COAL_ORE), + MineStackObjectByMaterial(ORES, "coal", "石炭", Material.COAL), + MineStackObjectByMaterial(ORES, "coal_block", "石炭ブロック", Material.COAL_BLOCK), + MineStackObjectByMaterial(ORES, "coal_1", "木炭", Material.CHARCOAL), + MineStackObjectByMaterial(ORES, "iron_ore", "鉄鉱石", Material.IRON_ORE), + MineStackObjectByMaterial(ORES, "iron_ingot", "鉄インゴット", Material.IRON_INGOT), + MineStackObjectByMaterial(ORES, "iron_block", "鉄ブロック", Material.IRON_BLOCK), + MineStackObjectByMaterial(ORES, "quartz_ore", "ネザークォーツ鉱石", Material.NETHER_QUARTZ_ORE), + MineStackObjectByMaterial(ORES, "quartz", "ネザークォーツ", Material.QUARTZ), + MineStackObjectByMaterial(ORES, "gold_ore", "金鉱石", Material.GOLD_ORE), + MineStackObjectByMaterial(ORES, "gold_ingot", "金インゴット", Material.GOLD_INGOT), + MineStackObjectByMaterial(ORES, "gold_block", "金ブロック", Material.GOLD_BLOCK), + MineStackObjectByMaterial(ORES, "redstone_ore", "レッドストーン鉱石", Material.REDSTONE_ORE), + MineStackObjectByMaterial(ORES, "lapis_ore", "ラピスラズリ鉱石", Material.LAPIS_ORE), + MineStackObjectByMaterial(ORES, "lapis_lazuli", "ラピスラズリ", Material.LAPIS_LAZULI), + MineStackObjectByMaterial(ORES, "lapis_block", "ラピスラズリブロック", Material.LAPIS_BLOCK), + MineStackObjectByMaterial(ORES, "diamond_ore", "ダイヤモンド鉱石", Material.DIAMOND_ORE), + MineStackObjectByMaterial(ORES, "diamond", "ダイヤモンド", Material.DIAMOND), + MineStackObjectByMaterial(ORES, "diamond_block", "ダイヤモンドブロック", Material.DIAMOND_BLOCK), + MineStackObjectByMaterial(ORES, "emerald_ore", "エメラルド鉱石", Material.EMERALD_ORE), + MineStackObjectByMaterial(ORES, "emerald", "エメラルド", Material.EMERALD), + MineStackObjectByMaterial(ORES, "emerald_block", "エメラルドブロック", Material.EMERALD_BLOCK), + MineStackObjectByMaterial(ORES, "copper_ore", "銅鉱石", Material.COPPER_ORE), + MineStackObjectByMaterial(ORES, "copper_ingot", "銅インゴット", Material.COPPER_INGOT), + MineStackObjectByMaterial(ORES, "copper_block", "銅ブロック", Material.COPPER_BLOCK), + MineStackObjectByMaterial(ORES, "deepslate_coal_ore", "深層石炭鉱石", Material.DEEPSLATE_COAL_ORE), + MineStackObjectByMaterial(ORES, "deepslate_iron_ore", "深層鉄鉱石", Material.DEEPSLATE_IRON_ORE), + MineStackObjectByMaterial(ORES, "deepslate_gold_ore", "深層金鉱石", Material.DEEPSLATE_GOLD_ORE), + MineStackObjectByMaterial(ORES, "deepslate_redstone_ore", "深層レッドストーン鉱石", Material.DEEPSLATE_REDSTONE_ORE), + MineStackObjectByMaterial(ORES, "deepslate_emerald_ore", "深層エメラルド鉱石", Material.DEEPSLATE_EMERALD_ORE), + MineStackObjectByMaterial(ORES, "deepslate_lapis_ore", "深層ラピスラズリ鉱石", Material.DEEPSLATE_LAPIS_ORE), + MineStackObjectByMaterial(ORES, "deepslate_diamond_ore", "深層ダイヤモンド鉱石", Material.DEEPSLATE_DIAMOND_ORE), + MineStackObjectByMaterial(ORES, "deepslate_copper_ore", "深層銅鉱石", Material.DEEPSLATE_COPPER_ORE), + MineStackObjectByMaterial(ORES, "nether_gold_ore", "ネザー金鉱石", Material.NETHER_GOLD_ORE), + MineStackObjectByMaterial(ORES, "ancient_debris", "古代の残骸", Material.ANCIENT_DEBRIS), + MineStackObjectByMaterial(ORES, "raw_copper_block", "銅の原石ブロック", Material.RAW_COPPER_BLOCK), + MineStackObjectByMaterial(ORES, "raw_iron_block", "鉄の原石ブロック", Material.RAW_IRON_BLOCK), + MineStackObjectByMaterial(ORES, "raw_gold_block", "金の原石ブロック", Material.RAW_GOLD_BLOCK), + MineStackObjectByMaterial(ORES, "amethyst_block", "アメジストブロック", Material.AMETHYST_BLOCK), + MineStackObjectByMaterial(ORES, "netherite_block", "ネザライトブロック", Material.NETHERITE_BLOCK), + MineStackObjectByMaterial(ORES, "amethyst_shard", "アメジストの欠片", Material.AMETHYST_SHARD), + MineStackObjectByMaterial(ORES, "raw_copper", "銅の原石", Material.RAW_COPPER), + MineStackObjectByMaterial(ORES, "raw_iron", "鉄の原石", Material.RAW_IRON), + MineStackObjectByMaterial(ORES, "raw_gold", "金の原石", Material.RAW_GOLD), + MineStackObjectByMaterial(ORES, "netherite_ingot", "ネザライトインゴット", Material.NETHERITE_INGOT), + MineStackObjectByMaterial(ORES, "netherite_scrap", "ネザライトの欠片", Material.NETHERITE_SCRAP), + MineStackObjectByMaterial(ORES, "small_amethyst_bud", "小さなアメジストの芽", Material.SMALL_AMETHYST_BUD), + MineStackObjectByMaterial(ORES, "medium_amethyst_bud", "中くらいのアメジストの芽", Material.MEDIUM_AMETHYST_BUD), + MineStackObjectByMaterial(ORES, "large_amethyst_bud", "大きなアメジストの芽", Material.LARGE_AMETHYST_BUD), + MineStackObjectByMaterial(ORES, "amethyst_cluster", "アメジストの塊", Material.AMETHYST_CLUSTER), + MineStackObjectByMaterial(ORES, "iron_nugget", "鉄塊", Material.IRON_NUGGET), + MineStackObjectByMaterial(ORES, "waxed_copper_block", "錆止めされた銅ブロック", Material.WAXED_COPPER_BLOCK), + MineStackObjectByMaterial(ORES, "waxed_exposed_copper", "錆止めされた風化した銅", Material.WAXED_EXPOSED_COPPER), + MineStackObjectByMaterial(ORES, "waxed_weathered_copper", "錆止めされた錆びた銅", Material.WAXED_WEATHERED_COPPER), + MineStackObjectByMaterial(ORES, "waxed_oxidized_copper", "錆止めされた酸化した銅", Material.WAXED_OXIDIZED_COPPER), + MineStackObjectByMaterial(ORES, "waxed_cut_copper", "錆止めされた切り込み入りの銅", Material.WAXED_CUT_COPPER), + MineStackObjectByMaterial(ORES, "waxed_exposed_cut_copper", "錆止めされた風化した切り込み入りの銅", Material.WAXED_EXPOSED_CUT_COPPER), + MineStackObjectByMaterial(ORES, "waxed_weathered_cut_copper", "錆止めされた錆びた切り込み入りの銅", Material.WAXED_WEATHERED_CUT_COPPER), + MineStackObjectByMaterial(ORES, "waxed_oxidized_cut_copper", "錆止めされた酸化した切り込み入りの銅", Material.WAXED_OXIDIZED_CUT_COPPER), + MineStackObjectByMaterial(ORES, "exposed_copper", "風化した銅", Material.EXPOSED_COPPER), + MineStackObjectByMaterial(ORES, "weathered_copper", "錆びた銅", Material.WEATHERED_COPPER), + MineStackObjectByMaterial(ORES, "oxidized_copper", "酸化した銅", Material.OXIDIZED_COPPER), + MineStackObjectByMaterial(ORES, "cut_copper", "切り込み入りの銅", Material.CUT_COPPER), + MineStackObjectByMaterial(ORES, "exposed_cut_copper", "風化した切り込み入りの銅", Material.EXPOSED_CUT_COPPER), + MineStackObjectByMaterial(ORES, "weathered_cut_copper", "錆びた切り込み入りの銅", Material.WEATHERED_CUT_COPPER), + MineStackObjectByMaterial(ORES, "oxidized_cut_copper", "酸化した切り込み入りの銅", Material.OXIDIZED_CUT_COPPER), ) + import scala.util.chaining._ + // モンスター+動物ドロップ - private val minestacklistdrop: List[Either[MineStackObject[ItemStack], MineStackObjectWithColorVariants[ItemStack]]] = leftElems( - MineStackObjectByMaterial(MOB_DROP, "ender_pearl", "エンダーパール", Material.ENDER_PEARL, 0), - MineStackObjectByMaterial(MOB_DROP, "ender_eye", "エンダーアイ", Material.EYE_OF_ENDER, 0), - MineStackObjectByMaterial(MOB_DROP, "slime_ball", "スライムボール", Material.SLIME_BALL, 0), - MineStackObjectByMaterial(MOB_DROP, "slime", "スライムブロック", Material.SLIME_BLOCK, 0), - MineStackObjectByMaterial(MOB_DROP, "rotten_flesh", "腐った肉", Material.ROTTEN_FLESH, 0), - MineStackObjectByMaterial(MOB_DROP, "bone", "骨", Material.BONE, 0), - MineStackObjectByMaterial(MOB_DROP, "sulphur", "火薬", Material.SULPHUR, 0), - MineStackObjectByMaterial(MOB_DROP, "arrow", "矢", Material.ARROW, 0), - MineStackObjectByMaterial(MOB_DROP, "tipped_arrow", "鈍化の矢", Material.TIPPED_ARROW, 0), - MineStackObjectByMaterial(MOB_DROP, "spider_eye", "蜘蛛の目", Material.SPIDER_EYE, 0), - MineStackObjectByMaterial(MOB_DROP, "string", "糸", Material.STRING, 0), - MineStackObjectByMaterial(MOB_DROP, "name_tag", "名札", Material.NAME_TAG, 0), - MineStackObjectByMaterial(MOB_DROP, "lead", "リード", Material.LEASH, 0), - MineStackObjectByMaterial(MOB_DROP, "glass_bottle", "ガラス瓶", Material.GLASS_BOTTLE, 0), - MineStackObjectByMaterial(MOB_DROP, "gold_nugget", "金塊", Material.GOLD_NUGGET, 0), - MineStackObjectByMaterial(MOB_DROP, "blaze_rod", "ブレイズロッド", Material.BLAZE_ROD, 0), - MineStackObjectByMaterial(MOB_DROP, "blaze_powder", "ブレイズパウダー", Material.BLAZE_POWDER, 0), - MineStackObjectByMaterial(MOB_DROP, "ghast_tear", "ガストの涙", Material.GHAST_TEAR, 0), - MineStackObjectByMaterial(MOB_DROP, "magma_cream", "マグマクリーム", Material.MAGMA_CREAM, 0), - MineStackObjectByMaterial(MOB_DROP, "prismarine_shard", "プリズマリンの欠片", Material.PRISMARINE_SHARD, 0), - MineStackObjectByMaterial(MOB_DROP, "prismarine_crystals", "プリズマリンクリスタル", Material.PRISMARINE_CRYSTALS, 0), - MineStackObjectByMaterial(MOB_DROP, "feather", "羽", Material.FEATHER, 0), - MineStackObjectByMaterial(MOB_DROP, "leather", "革", Material.LEATHER, 0), - MineStackObjectByMaterial(MOB_DROP, "rabbit_hide", "ウサギの皮", Material.RABBIT_HIDE, 0), - MineStackObjectByMaterial(MOB_DROP, "rabbit_foot", "ウサギの足", Material.RABBIT_FOOT, 0), - MineStackObjectByMaterial(MOB_DROP, "dragon_egg", "エンドラの卵", Material.DRAGON_EGG, 0), - MineStackObjectByMaterial(MOB_DROP, "shulker_shell", "シュルカーの殻", Material.SHULKER_SHELL, 0), - MineStackObjectByMaterial(MOB_DROP, "totem_of_undying", "不死のトーテム", Material.TOTEM, 0), - MineStackObjectByMaterial(MOB_DROP, "dragon_head", "エンダードラゴンの頭", Material.SKULL_ITEM, 5), - MineStackObjectByMaterial(MOB_DROP, "wither_skeleton_skull", "ウィザースケルトンの頭", Material.SKULL_ITEM, 1), - MineStackObjectByMaterial(MOB_DROP, "stick", "棒", Material.STICK, 0) + private val minestacklistdrop: List[MineStackObjectGroup[ItemStack]] = leftElems( + MineStackObjectByMaterial(MOB_DROP, "ender_pearl", "エンダーパール", Material.ENDER_PEARL), + MineStackObjectByMaterial(MOB_DROP, "ender_eye", "エンダーアイ", Material.ENDER_EYE), + MineStackObjectByMaterial(MOB_DROP, "slime_ball", "スライムボール", Material.SLIME_BALL), + MineStackObjectByMaterial(MOB_DROP, "slime", "スライムブロック", Material.SLIME_BLOCK), + MineStackObjectByMaterial(MOB_DROP, "rotten_flesh", "腐った肉", Material.ROTTEN_FLESH), + MineStackObjectByMaterial(MOB_DROP, "bone", "骨", Material.BONE), + MineStackObjectByMaterial(MOB_DROP, "sulphur", "火薬", Material.GUNPOWDER), + MineStackObjectByMaterial(MOB_DROP, "arrow", "矢", Material.ARROW), + MineStackObjectByMaterial(MOB_DROP, "spider_eye", "クモの目", Material.SPIDER_EYE), + MineStackObjectByItemStack(MOB_DROP, "tipped_arrow", Some("鈍化の矢"), hasNameLore = false, new ItemStack(Material.TIPPED_ARROW).tap { itemStack => + val meta = itemStack.getItemMeta.asInstanceOf[PotionMeta] + meta.setBasePotionData(new PotionData(PotionType.SLOWNESS)) + itemStack.setItemMeta(meta) + }), + MineStackObjectByMaterial(MOB_DROP, "string", "糸", Material.STRING), + MineStackObjectByMaterial(MOB_DROP, "name_tag", "名札", Material.NAME_TAG), + MineStackObjectByMaterial(MOB_DROP, "lead", "リード", Material.LEAD), + MineStackObjectByMaterial(MOB_DROP, "glass_bottle", "ガラス瓶", Material.GLASS_BOTTLE), + MineStackObjectByMaterial(MOB_DROP, "gold_nugget", "金塊", Material.GOLD_NUGGET), + MineStackObjectByMaterial(MOB_DROP, "blaze_rod", "ブレイズロッド", Material.BLAZE_ROD), + MineStackObjectByMaterial(MOB_DROP, "blaze_powder", "ブレイズパウダー", Material.BLAZE_POWDER), + MineStackObjectByMaterial(MOB_DROP, "ghast_tear", "ガストの涙", Material.GHAST_TEAR), + MineStackObjectByMaterial(MOB_DROP, "magma_cream", "マグマクリーム", Material.MAGMA_CREAM), + MineStackObjectByMaterial(MOB_DROP, "prismarine_shard", "プリズマリンの欠片", Material.PRISMARINE_SHARD), + MineStackObjectByMaterial(MOB_DROP, "prismarine_crystals", "プリズマリンクリスタル", Material.PRISMARINE_CRYSTALS), + MineStackObjectByMaterial(MOB_DROP, "feather", "羽根", Material.FEATHER), + MineStackObjectByMaterial(MOB_DROP, "leather", "革", Material.LEATHER), + MineStackObjectByMaterial(MOB_DROP, "rabbit_hide", "ウサギの皮", Material.RABBIT_HIDE), + MineStackObjectByMaterial(MOB_DROP, "rabbit_foot", "ウサギの足", Material.RABBIT_FOOT), + MineStackObjectByMaterial(MOB_DROP, "dragon_egg", "ドラゴンの卵", Material.DRAGON_EGG), + MineStackObjectByMaterial(MOB_DROP, "shulker_shell", "シュルカーの殻", Material.SHULKER_SHELL), + MineStackObjectByMaterial(MOB_DROP, "totem_of_undying", "不死のトーテム", Material.TOTEM_OF_UNDYING), + MineStackObjectByMaterial(MOB_DROP, "dragon_head", "ドラゴンの頭", Material.DRAGON_HEAD), + MineStackObjectByMaterial(MOB_DROP, "wither_skeleton_skull", "ウィザースケルトンの頭蓋骨", Material.WITHER_SKELETON_SKULL), + MineStackObjectByMaterial(MOB_DROP, "stick", "棒", Material.STICK), + MineStackObjectByMaterial(MOB_DROP, "scute", "カメのウロコ", Material.SCUTE), + MineStackObjectByMaterial(MOB_DROP, "fermented_spider_eye", "発酵したクモの目", Material.FERMENTED_SPIDER_EYE), + MineStackObjectByMaterial(MOB_DROP, "golden_carrot", "金のニンジン", Material.GOLDEN_CARROT), + MineStackObjectByMaterial(MOB_DROP, "skeleton_skull", "スケルトンの頭蓋骨", Material.SKELETON_SKULL), + MineStackObjectByMaterial(MOB_DROP, "zombie_head", "ゾンビの頭", Material.ZOMBIE_HEAD), + MineStackObjectByMaterial(MOB_DROP, "creeper_head", "クリーパーの頭", Material.CREEPER_HEAD), + MineStackObjectByMaterial(MOB_DROP, "nether_star", "ネザースター", Material.NETHER_STAR), + MineStackObjectByMaterial(MOB_DROP, "dragon_breath", "ドラゴンブレス", Material.DRAGON_BREATH), + MineStackObjectByMaterial(MOB_DROP, "phantom_membrane", "ファントムの皮膜", Material.PHANTOM_MEMBRANE), + MineStackObjectByMaterial(MOB_DROP, "nautilus_shell", "オウムガイの殻", Material.NAUTILUS_SHELL), + MineStackObjectByMaterial(MOB_DROP, "heart_of_the_sea", "海洋の心", Material.HEART_OF_THE_SEA), + MineStackObjectByMaterial(MOB_DROP, "ink_sack0", "イカスミ", Material.INK_SAC), + MineStackObjectByMaterial(MOB_DROP, "glow_ink_sac", "輝くイカスミ", Material.GLOW_INK_SAC), + ) ++ rightElems( + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(MOB_DROP, "music_disc_otherside", "レコード", Material.MUSIC_DISC_OTHERSIDE), + List( + MineStackObjectByMaterial(MOB_DROP, "music_disc_pigstep", "レコード", Material.MUSIC_DISC_PIGSTEP), + MineStackObjectByMaterial(MOB_DROP,"record_13","レコード",Material.MUSIC_DISC_13), + MineStackObjectByMaterial(MOB_DROP,"record_cat","レコード",Material.MUSIC_DISC_CAT), + MineStackObjectByMaterial(MOB_DROP,"record_blocks","レコード",Material.MUSIC_DISC_BLOCKS), + MineStackObjectByMaterial(MOB_DROP,"record_chirp","レコード",Material.MUSIC_DISC_CHIRP), + MineStackObjectByMaterial(MOB_DROP,"record_far","レコード",Material.MUSIC_DISC_FAR), + MineStackObjectByMaterial(MOB_DROP,"record_mall","レコード",Material.MUSIC_DISC_MALL), + MineStackObjectByMaterial(MOB_DROP,"record_mellohi","レコード",Material.MUSIC_DISC_MELLOHI), + MineStackObjectByMaterial(MOB_DROP,"record_stal","レコード",Material.MUSIC_DISC_STAL), + MineStackObjectByMaterial(MOB_DROP,"record_strad","レコード",Material.MUSIC_DISC_STRAD), + MineStackObjectByMaterial(MOB_DROP,"record_ward","レコード",Material.MUSIC_DISC_WARD), + MineStackObjectByMaterial(MOB_DROP,"record_11","レコード",Material.MUSIC_DISC_11), + MineStackObjectByMaterial(MOB_DROP,"record_wait","レコード",Material.MUSIC_DISC_WAIT), + ) + ) ) // 採掘で入手可能な農業系ブロック private val minestacklistfarm: List[MineStackObjectGroup[ItemStack]] = leftElems( - MineStackObjectByMaterial(AGRICULTURAL, "seeds", "種", Material.SEEDS, 0), - MineStackObjectByMaterial(AGRICULTURAL, "apple", "リンゴ", Material.APPLE, 0), - MineStackObjectByMaterial(AGRICULTURAL, "long_grass1", "草", Material.LONG_GRASS, 1), - MineStackObjectByMaterial(AGRICULTURAL, "long_grass2", "シダ", Material.LONG_GRASS, 2), - MineStackObjectByMaterial(AGRICULTURAL, "dead_bush", "枯れ木", Material.DEAD_BUSH, 0), - MineStackObjectByMaterial(AGRICULTURAL, "cactus", "サボテン", Material.CACTUS, 0), - MineStackObjectByMaterial(AGRICULTURAL, "vine", "ツタ", Material.VINE, 0), - MineStackObjectByMaterial(AGRICULTURAL, "water_lily", "スイレンの葉", Material.WATER_LILY, 0), - MineStackObjectByMaterial(AGRICULTURAL, "yellow_flower", "タンポポ", Material.YELLOW_FLOWER, 0), - MineStackObjectByMaterial(AGRICULTURAL, "red_rose0", "ポピー", Material.RED_ROSE, 0), - MineStackObjectByMaterial(AGRICULTURAL, "red_rose1", "ヒスイラン", Material.RED_ROSE, 1), - MineStackObjectByMaterial(AGRICULTURAL, "red_rose2", "アリウム", Material.RED_ROSE, 2), - MineStackObjectByMaterial(AGRICULTURAL, "red_rose3", "ヒナソウ", Material.RED_ROSE, 3), - MineStackObjectByMaterial(AGRICULTURAL, "red_rose4", "赤色のチューリップ", Material.RED_ROSE, 4), - MineStackObjectByMaterial(AGRICULTURAL, "red_rose5", "橙色のチューリップ", Material.RED_ROSE, 5), - MineStackObjectByMaterial(AGRICULTURAL, "red_rose6", "白色のチューリップ", Material.RED_ROSE, 6), - MineStackObjectByMaterial(AGRICULTURAL, "red_rose7", "桃色のチューリップ", Material.RED_ROSE, 7), - MineStackObjectByMaterial(AGRICULTURAL, "red_rose8", "フランスギク", Material.RED_ROSE, 8), - MineStackObjectByMaterial(AGRICULTURAL, "leaves", "オークの葉", Material.LEAVES, 0), - MineStackObjectByMaterial(AGRICULTURAL, "leaves1", "マツの葉", Material.LEAVES, 1), - MineStackObjectByMaterial(AGRICULTURAL, "leaves2", "シラカバの葉", Material.LEAVES, 2), - MineStackObjectByMaterial(AGRICULTURAL, "leaves3", "ジャングルの葉", Material.LEAVES, 3), - MineStackObjectByMaterial(AGRICULTURAL, "leaves_2", "アカシアの葉", Material.LEAVES_2, 0), - MineStackObjectByMaterial(AGRICULTURAL, "leaves_21", "ダークオークの葉", Material.LEAVES_2, 1), - MineStackObjectByMaterial(AGRICULTURAL, "double_plant0", "ヒマワリ", Material.DOUBLE_PLANT, 0), - MineStackObjectByMaterial(AGRICULTURAL, "double_plant1", "ライラック", Material.DOUBLE_PLANT, 1), - MineStackObjectByMaterial(AGRICULTURAL, "double_plant2", "高い草", Material.DOUBLE_PLANT, 2), - MineStackObjectByMaterial(AGRICULTURAL, "double_plant3", "大きなシダ", Material.DOUBLE_PLANT, 3), - MineStackObjectByMaterial(AGRICULTURAL, "double_plant4", "バラの低木", Material.DOUBLE_PLANT, 4), - MineStackObjectByMaterial(AGRICULTURAL, "double_plant5", "ボタン", Material.DOUBLE_PLANT, 5), - MineStackObjectByMaterial(AGRICULTURAL, "sugar_cane", "サトウキビ", Material.SUGAR_CANE, 0), - MineStackObjectByMaterial(AGRICULTURAL, "pumpkin", "カボチャ", Material.PUMPKIN, 0), - MineStackObjectByMaterial(AGRICULTURAL, "ink_sack3", "カカオ豆", Material.INK_SACK, 3), - MineStackObjectByMaterial(AGRICULTURAL, "huge_mushroom_1", "キノコ", Material.HUGE_MUSHROOM_1, 0), - MineStackObjectByMaterial(AGRICULTURAL, "huge_mushroom_2", "キノコ", Material.HUGE_MUSHROOM_2, 0), - MineStackObjectByMaterial(AGRICULTURAL, "melon", "スイカ", Material.MELON, 0), - MineStackObjectByMaterial(AGRICULTURAL, "melon_block", "スイカ", Material.MELON_BLOCK, 0), - MineStackObjectByMaterial(AGRICULTURAL, "brown_mushroom", "キノコ", Material.BROWN_MUSHROOM, 0), - MineStackObjectByMaterial(AGRICULTURAL, "red_mushroom", "キノコ", Material.RED_MUSHROOM, 0), - MineStackObjectByMaterial(AGRICULTURAL, "sapling", "オークの苗木", Material.SAPLING, 0), - MineStackObjectByMaterial(AGRICULTURAL, "sapling1", "マツの苗木", Material.SAPLING, 1), - MineStackObjectByMaterial(AGRICULTURAL, "sapling2", "シラカバの苗木", Material.SAPLING, 2), - MineStackObjectByMaterial(AGRICULTURAL, "sapling3", "ジャングルの苗木", Material.SAPLING, 3), - MineStackObjectByMaterial(AGRICULTURAL, "sapling4", "アカシアの苗木", Material.SAPLING, 4), - MineStackObjectByMaterial(AGRICULTURAL, "sapling5", "ダークオークの苗木", Material.SAPLING, 5), - MineStackObjectByMaterial(AGRICULTURAL, "beetroot", "ビートルート", Material.BEETROOT, 0), - MineStackObjectByMaterial(AGRICULTURAL, "beetroot_seeds", "ビートルートの種", Material.BEETROOT_SEEDS, 0), - MineStackObjectByMaterial(AGRICULTURAL, "carrot_item", "ニンジン", Material.CARROT_ITEM, 0), - MineStackObjectByMaterial(AGRICULTURAL, "potato_item", "ジャガイモ", Material.POTATO_ITEM, 0), - MineStackObjectByMaterial(AGRICULTURAL, "poisonous_potato", "青くなったジャガイモ", Material.POISONOUS_POTATO, 0), - MineStackObjectByMaterial(AGRICULTURAL, "wheat", "小麦", Material.WHEAT, 0), - MineStackObjectByMaterial(AGRICULTURAL, "pumpkin_seeds", "カボチャの種", Material.PUMPKIN_SEEDS, 0), - MineStackObjectByMaterial(AGRICULTURAL, "melon_seeds", "スイカの種", Material.MELON_SEEDS, 0), - MineStackObjectByMaterial(AGRICULTURAL, "nether_stalk", "ネザーウォート", Material.NETHER_STALK, 0), - MineStackObjectByMaterial(AGRICULTURAL, "chorus_fruit", "コーラスフルーツ", Material.CHORUS_FRUIT, 0), - MineStackObjectByMaterial(AGRICULTURAL, "chorus_flower", "コーラスフラワー", Material.CHORUS_FLOWER, 0), - MineStackObjectByMaterial(AGRICULTURAL, "popped_chorus_fruit", "焼いたコーラスフルーツ", Material.CHORUS_FRUIT_POPPED, 0), - MineStackObjectByMaterial(AGRICULTURAL, "egg", "卵", Material.EGG, 0), - MineStackObjectByMaterial(AGRICULTURAL, "pork", "生の豚肉", Material.PORK, 0), - MineStackObjectByMaterial(AGRICULTURAL, "cooked_porkchop", "焼き豚", Material.GRILLED_PORK, 0), - MineStackObjectByMaterial(AGRICULTURAL, "raw_chicken", "生の鶏肉", Material.RAW_CHICKEN, 0), - MineStackObjectByMaterial(AGRICULTURAL, "cooked_chicken", "焼き鳥", Material.COOKED_CHICKEN, 0), - MineStackObjectByMaterial(AGRICULTURAL, "mutton", "生の羊肉", Material.MUTTON, 0), - MineStackObjectByMaterial(AGRICULTURAL, "cooked_mutton", "焼いた羊肉", Material.COOKED_MUTTON, 0), - MineStackObjectByMaterial(AGRICULTURAL, "raw_beef", "生の牛肉", Material.RAW_BEEF, 0), - MineStackObjectByMaterial(AGRICULTURAL, "cooked_beaf", "ステーキ", Material.COOKED_BEEF, 0), - MineStackObjectByMaterial(AGRICULTURAL, "rabbit", "生の兎肉", Material.RABBIT, 0), - MineStackObjectByMaterial(AGRICULTURAL, "cooked_rabbit", "焼き兎肉", Material.COOKED_RABBIT, 0), - MineStackObjectByMaterial(AGRICULTURAL, "raw_fish0", "生魚", Material.RAW_FISH, 0), - MineStackObjectByMaterial(AGRICULTURAL, "cooked_fish0", "焼き魚", Material.COOKED_FISH, 0), - MineStackObjectByMaterial(AGRICULTURAL, "raw_fish1", "生鮭", Material.RAW_FISH, 1), - MineStackObjectByMaterial(AGRICULTURAL, "cooked_fish1", "焼き鮭", Material.COOKED_FISH, 1), - MineStackObjectByMaterial(AGRICULTURAL, "raw_fish2", "クマノミ", Material.RAW_FISH, 2), - MineStackObjectByMaterial(AGRICULTURAL, "raw_fish3", "フグ", Material.RAW_FISH, 3), - MineStackObjectByMaterial(AGRICULTURAL, "bread", "パン", Material.BREAD, 0), - MineStackObjectByMaterial(AGRICULTURAL, "sugar", "砂糖", Material.SUGAR, 0), - MineStackObjectByMaterial(AGRICULTURAL, "baked_potato", "ベイクドポテト", Material.BAKED_POTATO, 0), - MineStackObjectByMaterial(AGRICULTURAL, "cake", "ケーキ", Material.CAKE, 0), - MineStackObjectByMaterial(AGRICULTURAL, "mushroom_stew", "キノコシチュー", Material.MUSHROOM_SOUP, 0), - MineStackObjectByMaterial(AGRICULTURAL, "rabbit_stew", "ウサギシチュー", Material.RABBIT_STEW, 0), - MineStackObjectByMaterial(AGRICULTURAL, "beetroot_soup", "ビートルートスープ", Material.BEETROOT_SOUP, 0), - MineStackObjectByMaterial(AGRICULTURAL, "bowl", "ボウル", Material.BOWL, 0), - MineStackObjectByMaterial(AGRICULTURAL, "milk_bucket", "牛乳", Material.MILK_BUCKET, 0) + MineStackObjectByMaterial(AGRICULTURAL, "seeds", "小麦の種", Material.WHEAT_SEEDS), + MineStackObjectByMaterial(AGRICULTURAL, "apple", "リンゴ", Material.APPLE), + MineStackObjectByMaterial(AGRICULTURAL, "long_grass2", "シダ", Material.FERN), + MineStackObjectByMaterial(AGRICULTURAL, "dead_bush", "枯れ木", Material.DEAD_BUSH), + MineStackObjectByMaterial(AGRICULTURAL, "cactus", "サボテン", Material.CACTUS), + MineStackObjectByMaterial(AGRICULTURAL, "vine", "ツタ", Material.VINE), + MineStackObjectByMaterial(AGRICULTURAL, "water_lily", "スイレンの葉", Material.LILY_PAD), + MineStackObjectByMaterial(AGRICULTURAL, "yellow_flower", "タンポポ", Material.DANDELION), + MineStackObjectByMaterial(AGRICULTURAL, "red_rose0", "ポピー", Material.POPPY), + MineStackObjectByMaterial(AGRICULTURAL, "red_rose1", "ヒスイラン", Material.BLUE_ORCHID), + MineStackObjectByMaterial(AGRICULTURAL, "red_rose2", "アリウム", Material.ALLIUM), + MineStackObjectByMaterial(AGRICULTURAL, "red_rose3", "ヒナソウ", Material.AZURE_BLUET), + MineStackObjectByMaterial(AGRICULTURAL, "red_rose4", "赤色のチューリップ", Material.RED_TULIP), + MineStackObjectByMaterial(AGRICULTURAL, "red_rose5", "橙色のチューリップ", Material.ORANGE_TULIP), + MineStackObjectByMaterial(AGRICULTURAL, "red_rose6", "白色のチューリップ", Material.WHITE_TULIP), + MineStackObjectByMaterial(AGRICULTURAL, "red_rose7", "桃色のチューリップ", Material.PINK_TULIP), + MineStackObjectByMaterial(AGRICULTURAL, "red_rose8", "フランスギク", Material.OXEYE_DAISY), + MineStackObjectByMaterial(AGRICULTURAL, "leaves", "オークの葉", Material.OAK_LEAVES), + MineStackObjectByMaterial(AGRICULTURAL, "leaves1", "トウヒの葉", Material.SPRUCE_LEAVES), + MineStackObjectByMaterial(AGRICULTURAL, "leaves2", "シラカバの葉", Material.BIRCH_LEAVES), + MineStackObjectByMaterial(AGRICULTURAL, "leaves3", "ジャングルの葉", Material.JUNGLE_LEAVES), + MineStackObjectByMaterial(AGRICULTURAL, "leaves_2", "アカシアの葉", Material.ACACIA_LEAVES), + MineStackObjectByMaterial(AGRICULTURAL, "leaves_21", "ダークオークの葉", Material.DARK_OAK_LEAVES), + MineStackObjectByMaterial(AGRICULTURAL, "double_plant0", "ヒマワリ", Material.SUNFLOWER), + MineStackObjectByMaterial(AGRICULTURAL, "double_plant1", "ライラック", Material.LILAC), + MineStackObjectByMaterial(AGRICULTURAL, "double_plant2", "背の高い草", Material.TALL_GRASS), + MineStackObjectByMaterial(AGRICULTURAL, "double_plant3", "大きなシダ", Material.LARGE_FERN), + MineStackObjectByMaterial(AGRICULTURAL, "double_plant4", "バラの低木", Material.ROSE_BUSH), + MineStackObjectByMaterial(AGRICULTURAL, "double_plant5", "ボタン", Material.PEONY), + MineStackObjectByMaterial(AGRICULTURAL, "sugar_cane", "サトウキビ", Material.SUGAR_CANE), + MineStackObjectByMaterial(AGRICULTURAL, "pumpkin", "カボチャ", Material.PUMPKIN), + MineStackObjectByMaterial(AGRICULTURAL, "ink_sack3", "カカオ豆", Material.COCOA_BEANS), + MineStackObjectByMaterial(AGRICULTURAL, "huge_mushroom_1", "茶色のキノコ", Material.BROWN_MUSHROOM), + MineStackObjectByMaterial(AGRICULTURAL, "huge_mushroom_2", "赤色のキノコ", Material.RED_MUSHROOM), + MineStackObjectByMaterial(AGRICULTURAL, "melon", "スイカの薄切り", Material.MELON_SLICE), + MineStackObjectByMaterial(AGRICULTURAL, "melon_block", "スイカ", Material.MELON), + MineStackObjectByMaterial(AGRICULTURAL, "sapling", "オークの苗木", Material.OAK_SAPLING), + MineStackObjectByMaterial(AGRICULTURAL, "sapling1", "トウヒの苗木", Material.SPRUCE_SAPLING), + MineStackObjectByMaterial(AGRICULTURAL, "sapling2", "シラカバの苗木", Material.BIRCH_SAPLING), + MineStackObjectByMaterial(AGRICULTURAL, "sapling3", "ジャングルの苗木", Material.JUNGLE_SAPLING), + MineStackObjectByMaterial(AGRICULTURAL, "sapling4", "アカシアの苗木", Material.ACACIA_SAPLING), + MineStackObjectByMaterial(AGRICULTURAL, "sapling5", "ダークオークの苗木", Material.DARK_OAK_SAPLING), + MineStackObjectByMaterial(AGRICULTURAL, "beetroot", "ビートルート", Material.BEETROOT), + MineStackObjectByMaterial(AGRICULTURAL, "beetroot_seeds", "ビートルートの種", Material.BEETROOT_SEEDS), + MineStackObjectByMaterial(AGRICULTURAL, "carrot_item", "ニンジン", Material.CARROT), + MineStackObjectByMaterial(AGRICULTURAL, "potato_item", "ジャガイモ", Material.POTATO), + MineStackObjectByMaterial(AGRICULTURAL, "poisonous_potato", "青くなったジャガイモ", Material.POISONOUS_POTATO), + MineStackObjectByMaterial(AGRICULTURAL, "wheat", "小麦", Material.WHEAT), + MineStackObjectByMaterial(AGRICULTURAL, "pumpkin_seeds", "カボチャの種", Material.PUMPKIN_SEEDS), + MineStackObjectByMaterial(AGRICULTURAL, "melon_seeds", "スイカの種", Material.MELON_SEEDS), + MineStackObjectByMaterial(AGRICULTURAL, "nether_stalk", "ネザーウォート", Material.NETHER_WART), + MineStackObjectByMaterial(AGRICULTURAL, "chorus_fruit", "コーラスフルーツ", Material.CHORUS_FRUIT), + MineStackObjectByMaterial(AGRICULTURAL, "chorus_flower", "コーラスフラワー", Material.CHORUS_FLOWER), + MineStackObjectByMaterial(AGRICULTURAL, "popped_chorus_fruit", "焼いたコーラスフルーツ", Material.POPPED_CHORUS_FRUIT), + MineStackObjectByMaterial(AGRICULTURAL, "egg", "卵", Material.EGG), + MineStackObjectByMaterial(AGRICULTURAL, "pork", "生の豚肉", Material.PORKCHOP), + MineStackObjectByMaterial(AGRICULTURAL, "cooked_porkchop", "焼き豚", Material.COOKED_PORKCHOP), + MineStackObjectByMaterial(AGRICULTURAL, "raw_chicken", "生の鶏肉", Material.CHICKEN), + MineStackObjectByMaterial(AGRICULTURAL, "cooked_chicken", "焼き鳥", Material.COOKED_CHICKEN), + MineStackObjectByMaterial(AGRICULTURAL, "mutton", "生の羊肉", Material.MUTTON), + MineStackObjectByMaterial(AGRICULTURAL, "cooked_mutton", "焼き羊肉", Material.COOKED_MUTTON), + MineStackObjectByMaterial(AGRICULTURAL, "raw_beef", "生の牛肉", Material.BEEF), + MineStackObjectByMaterial(AGRICULTURAL, "cooked_beaf", "ステーキ", Material.COOKED_BEEF), + MineStackObjectByMaterial(AGRICULTURAL, "rabbit", "生の兎肉", Material.RABBIT), + MineStackObjectByMaterial(AGRICULTURAL, "cooked_rabbit", "焼き兎肉", Material.COOKED_RABBIT), + MineStackObjectByMaterial(AGRICULTURAL, "raw_fish0", "生鱈", Material.COD), + MineStackObjectByMaterial(AGRICULTURAL, "cooked_fish0", "焼き鱈", Material.COOKED_COD), + MineStackObjectByMaterial(AGRICULTURAL, "raw_fish1", "生鮭", Material.SALMON), + MineStackObjectByMaterial(AGRICULTURAL, "cooked_fish1", "焼き鮭", Material.COOKED_SALMON), + MineStackObjectByMaterial(AGRICULTURAL, "raw_fish2", "熱帯魚", Material.TROPICAL_FISH), + MineStackObjectByMaterial(AGRICULTURAL, "raw_fish3", "フグ", Material.PUFFERFISH), + MineStackObjectByMaterial(AGRICULTURAL, "bread", "パン", Material.BREAD), + MineStackObjectByMaterial(AGRICULTURAL, "sugar", "砂糖", Material.SUGAR), + MineStackObjectByMaterial(AGRICULTURAL, "baked_potato", "ベイクドポテト", Material.BAKED_POTATO), + MineStackObjectByMaterial(AGRICULTURAL, "cake", "ケーキ", Material.CAKE), + MineStackObjectByMaterial(AGRICULTURAL, "mushroom_stew", "キノコシチュー", Material.MUSHROOM_STEW), + MineStackObjectByMaterial(AGRICULTURAL, "rabbit_stew", "ウサギシチュー", Material.RABBIT_STEW), + MineStackObjectByMaterial(AGRICULTURAL, "beetroot_soup", "ビートルートスープ", Material.BEETROOT_SOUP), + MineStackObjectByMaterial(AGRICULTURAL, "bowl", "ボウル", Material.BOWL), + MineStackObjectByMaterial(AGRICULTURAL, "milk_bucket", "ミルク入りバケツ", Material.MILK_BUCKET), + MineStackObjectByMaterial(AGRICULTURAL, "kelp", "コンブ", Material.KELP), + MineStackObjectByMaterial(AGRICULTURAL, "azalea_leaves", "ツツジの葉", Material.AZALEA_LEAVES), + MineStackObjectByMaterial(AGRICULTURAL, "flowering_azalea_leaves", "開花したツツジの葉", Material.FLOWERING_AZALEA_LEAVES), + MineStackObjectByMaterial(AGRICULTURAL, "carved_pumpkin", "くり抜かれたカボチャ", Material.CARVED_PUMPKIN), + MineStackObjectByMaterial(AGRICULTURAL, "dried_kelp_block", "乾燥した昆布ブロック", Material.DRIED_KELP_BLOCK), + MineStackObjectByMaterial(AGRICULTURAL, "cookie", "クッキー", Material.COOKIE), + MineStackObjectByMaterial(AGRICULTURAL, "dried_kelp", "乾燥した昆布", Material.DRIED_KELP), + MineStackObjectByMaterial(AGRICULTURAL, "glistering_melon_slice", "きらめくスイカの薄切り", Material.GLISTERING_MELON_SLICE), + MineStackObjectByMaterial(AGRICULTURAL, "pumpkin_pie", "パンプキンパイ", Material.PUMPKIN_PIE), + MineStackObjectByMaterial(AGRICULTURAL, "dye_15", "骨粉", Material.BONE_MEAL), + MineStackObjectByMaterial(AGRICULTURAL, "grass_block", "草", Material.GRASS), + MineStackObjectByMaterial(AGRICULTURAL, "azalea", "ツツジ", Material.AZALEA), + MineStackObjectByMaterial(AGRICULTURAL, "flowering_azalea", "開花したツツジ", Material.FLOWERING_AZALEA), + MineStackObjectByMaterial(AGRICULTURAL, "seagrass", "海草", Material.SEAGRASS), + MineStackObjectByMaterial(AGRICULTURAL, "sea_pickle", "シーピクルス", Material.SEA_PICKLE), + MineStackObjectByMaterial(AGRICULTURAL, "cornflower", "ヤグルマギク", Material.CORNFLOWER), + MineStackObjectByMaterial(AGRICULTURAL, "lily_of_the_valley", "スズラン", Material.LILY_OF_THE_VALLEY), + MineStackObjectByMaterial(AGRICULTURAL, "wither_rose", "ウィザーローズ", Material.WITHER_ROSE), + MineStackObjectByMaterial(AGRICULTURAL, "spore_blossom", "胞子の花", Material.SPORE_BLOSSOM), + MineStackObjectByMaterial(AGRICULTURAL, "crimson_fungus", "真紅のキノコ", Material.CRIMSON_FUNGUS), + MineStackObjectByMaterial(AGRICULTURAL, "warped_fungus", "歪んだキノコ", Material.WARPED_FUNGUS), + MineStackObjectByMaterial(AGRICULTURAL, "crimson_roots", "真紅の根", Material.CRIMSON_ROOTS), + MineStackObjectByMaterial(AGRICULTURAL, "warped_roots", "歪んだ根", Material.WARPED_ROOTS), + MineStackObjectByMaterial(AGRICULTURAL, "nether_sprouts", "ネザースプラウト", Material.NETHER_SPROUTS), + MineStackObjectByMaterial(AGRICULTURAL, "weeping_vines", "しだれツタ", Material.WEEPING_VINES), + MineStackObjectByMaterial(AGRICULTURAL, "twisting_vines", "ねじれツタ", Material.TWISTING_VINES), + MineStackObjectByMaterial(AGRICULTURAL, "big_dripleaf", "大きなドリップリーフ", Material.BIG_DRIPLEAF), + MineStackObjectByMaterial(AGRICULTURAL, "small_dripleaf", "小さなドリップリーフ", Material.SMALL_DRIPLEAF), + MineStackObjectByMaterial(AGRICULTURAL, "bamboo", "竹", Material.BAMBOO), + MineStackObjectByMaterial(AGRICULTURAL, "glow_lichen", "ヒカリゴケ", Material.GLOW_LICHEN), + MineStackObjectByMaterial(AGRICULTURAL, "sweet_berries", "スイートベリー", Material.SWEET_BERRIES), + MineStackObjectByMaterial(AGRICULTURAL, "glow_berries", "グロウベリー", Material.GLOW_BERRIES), + MineStackObjectByMaterial(AGRICULTURAL, "hanging_roots", "垂れ根", Material.HANGING_ROOTS), ) // 建築系ブロック private val minestacklistbuild: List[MineStackObjectGroup[ItemStack]] = leftElems( - MineStackObjectByMaterial(BUILDING, "log", "オークの原木", Material.LOG, 0), - MineStackObjectByMaterial(BUILDING, "wood", "オークの木材", Material.WOOD, 0), - MineStackObjectByMaterial(BUILDING, "wood_step0", "オークの木材ハーフブロック", Material.WOOD_STEP, 0), - MineStackObjectByMaterial(BUILDING, "oak_stairs", "オークの木の階段", Material.WOOD_STAIRS, 0), - MineStackObjectByMaterial(BUILDING, "fence", "オークのフェンス", Material.FENCE, 0), - MineStackObjectByMaterial(BUILDING, "log1", "マツの原木", Material.LOG, 1), - MineStackObjectByMaterial(BUILDING, "wood_1", "マツの木材", Material.WOOD, 1), - MineStackObjectByMaterial(BUILDING, "wood_step1", "マツの木材ハーフブロック", Material.WOOD_STEP, 1), - MineStackObjectByMaterial(BUILDING, "spruce_stairs", "マツの木の階段", Material.SPRUCE_WOOD_STAIRS, 0), - MineStackObjectByMaterial(BUILDING, "spruce_fence", "マツのフェンス", Material.SPRUCE_FENCE, 0), - MineStackObjectByMaterial(BUILDING, "log2", "シラカバの原木", Material.LOG, 2), - MineStackObjectByMaterial(BUILDING, "wood_2", "シラカバの木材", Material.WOOD, 2), - MineStackObjectByMaterial(BUILDING, "wood_step2", "シラカバの木材ハーフブロック", Material.WOOD_STEP, 2), - MineStackObjectByMaterial(BUILDING, "birch_stairs", "シラカバの木の階段", Material.BIRCH_WOOD_STAIRS, 0), - MineStackObjectByMaterial(BUILDING, "birch_fence", "シラカバのフェンス", Material.BIRCH_FENCE, 0), - MineStackObjectByMaterial(BUILDING, "log3", "ジャングルの原木", Material.LOG, 3), - MineStackObjectByMaterial(BUILDING, "wood_3", "ジャングルの木材", Material.WOOD, 3), - MineStackObjectByMaterial(BUILDING, "wood_step3", "ジャングルの木材ハーフブロック", Material.WOOD_STEP, 3), - MineStackObjectByMaterial(BUILDING, "jungle_stairs", "ジャングルの木の階段", Material.JUNGLE_WOOD_STAIRS, 0), - MineStackObjectByMaterial(BUILDING, "jungle_fence", "ジャングルのフェンス", Material.JUNGLE_FENCE, 0), - MineStackObjectByMaterial(BUILDING, "log_2", "アカシアの原木", Material.LOG_2, 0), - MineStackObjectByMaterial(BUILDING, "wood_4", "アカシアの木材", Material.WOOD, 4), - MineStackObjectByMaterial(BUILDING, "wood_step4", "アカシアの木材ハーフブロック", Material.WOOD_STEP, 4), - MineStackObjectByMaterial(BUILDING, "acacia_stairs", "アカシアの木の階段", Material.ACACIA_STAIRS, 0), - MineStackObjectByMaterial(BUILDING, "acacia_fence", "アカシアのフェンス", Material.ACACIA_FENCE, 0), - MineStackObjectByMaterial(BUILDING, "log_21", "ダークオークの原木", Material.LOG_2, 1), - MineStackObjectByMaterial(BUILDING, "wood_5", "ダークオークの木材", Material.WOOD, 5), - MineStackObjectByMaterial(BUILDING, "wood_step5", "ダークオークの木材ハーフブロック", Material.WOOD_STEP, 5), - MineStackObjectByMaterial(BUILDING, "dark_oak_stairs", "ダークオークの木の階段", Material.DARK_OAK_STAIRS, 0), - MineStackObjectByMaterial(BUILDING, "dark_oak_fence", "ダークオークのフェンス", Material.DARK_OAK_FENCE, 0), - MineStackObjectByMaterial(BUILDING, "cobblestone", "丸石", Material.COBBLESTONE, 0), - MineStackObjectByMaterial(BUILDING, "step3", "丸石ハーフブロック", Material.STEP, 3), - MineStackObjectByMaterial(BUILDING, "stone_stairs", "丸石の階段", Material.COBBLESTONE_STAIRS, 0), - MineStackObjectByMaterial(BUILDING, "cobblestone_wall_0", "丸石の壁", Material.COBBLE_WALL, 0), - MineStackObjectByMaterial(BUILDING, "mossy_cobblestone", "苔石", Material.MOSSY_COBBLESTONE, 0), - MineStackObjectByMaterial(BUILDING, "cobblestone_wall_1", "苔石の壁", Material.COBBLE_WALL, 1), - MineStackObjectByMaterial(BUILDING, "stone", "石", Material.STONE, 0), - MineStackObjectByMaterial(BUILDING, "step0", "石ハーフブロック", Material.STEP, 0), - MineStackObjectByMaterial(BUILDING, "smooth_brick0", "石レンガ", Material.SMOOTH_BRICK, 0), - MineStackObjectByMaterial(BUILDING, "step5", "石レンガハーフブロック", Material.STEP, 5), - MineStackObjectByMaterial(BUILDING, "smooth_stairs", "石レンガの階段", Material.SMOOTH_STAIRS, 0), - MineStackObjectByMaterial(BUILDING, "smooth_brick3", "模様入り石レンガ", Material.SMOOTH_BRICK, 3), - MineStackObjectByMaterial(BUILDING, "smooth_brick1", "苔石レンガ", Material.SMOOTH_BRICK, 1), - MineStackObjectByMaterial(BUILDING, "smooth_brick2", "ひびの入った石レンガ", Material.SMOOTH_BRICK, 2), - MineStackObjectByMaterial(BUILDING, "sand", "砂", Material.SAND, 0), - MineStackObjectByMaterial(BUILDING, "sandstone", "砂岩", Material.SANDSTONE, 0), - MineStackObjectByMaterial(BUILDING, "step1", "砂岩ハーフブロック", Material.STEP, 1), - MineStackObjectByMaterial(BUILDING, "standstone_stairs", "砂岩の階段", Material.SANDSTONE_STAIRS, 0), - MineStackObjectByMaterial(BUILDING, "sandstone1", "模様入りの砂岩", Material.SANDSTONE, 1), - MineStackObjectByMaterial(BUILDING, "sandstone2", "なめらかな砂岩", Material.SANDSTONE, 2), - MineStackObjectByMaterial(BUILDING, "red_sand", "赤い砂", Material.SAND, 1), - MineStackObjectByMaterial(BUILDING, "red_sandstone", "赤い砂岩", Material.RED_SANDSTONE, 0), - MineStackObjectByMaterial(BUILDING, "stone_slab20", "赤い砂岩ハーフブロック", Material.STONE_SLAB2, 0), - MineStackObjectByMaterial(BUILDING, "red_sandstone_stairs", "赤い砂岩の階段", Material.RED_SANDSTONE_STAIRS, 0), - MineStackObjectByMaterial(BUILDING, "red_sandstone1", "模様入りの赤い砂岩", Material.RED_SANDSTONE, 1), - MineStackObjectByMaterial(BUILDING, "red_sandstone2", "なめらかな赤い砂岩", Material.RED_SANDSTONE, 2), - MineStackObjectByMaterial(BUILDING, "clay_ball", "粘土", Material.CLAY_BALL, 0), - MineStackObjectByMaterial(BUILDING, "clay", "粘土(ブロック)", Material.CLAY, 0), - MineStackObjectByMaterial(BUILDING, "brick_item", "レンガ", Material.CLAY_BRICK, 0), - MineStackObjectByMaterial(BUILDING, "brick", "レンガ(ブロック)", Material.BRICK, 0), - MineStackObjectByMaterial(BUILDING, "step4", "レンガハーフブロック", Material.STEP, 4), - MineStackObjectByMaterial(BUILDING, "brick_stairs", "レンガの階段", Material.BRICK_STAIRS, 0), - MineStackObjectByMaterial(BUILDING, "quartz_block", "ネザー水晶ブロック", Material.QUARTZ_BLOCK, 0), - MineStackObjectByMaterial(BUILDING, "step7", "ネザー水晶ハーフブロック", Material.STEP, 7), - MineStackObjectByMaterial(BUILDING, "quartz_stairs", "ネザー水晶の階段", Material.QUARTZ_STAIRS, 0), - MineStackObjectByMaterial(BUILDING, "quartz_block1", "模様入りネザー水晶ブロック", Material.QUARTZ_BLOCK, 1), - MineStackObjectByMaterial(BUILDING, "quartz_block2", "柱状ネザー水晶ブロック", Material.QUARTZ_BLOCK, 2), - MineStackObjectByMaterial(BUILDING, "netherrack", "ネザーラック", Material.NETHERRACK, 0), - MineStackObjectByMaterial(BUILDING, "nether_brick_item", "ネザーレンガ", Material.NETHER_BRICK_ITEM, 0), - MineStackObjectByMaterial(BUILDING, "nether_brick", "ネザーレンガ(ブロック)", Material.NETHER_BRICK, 0), - MineStackObjectByMaterial(BUILDING, "step6", "ネザーレンガハーフブロック", Material.STEP, 6), - MineStackObjectByMaterial(BUILDING, "nether_brick_stairs", "ネザーレンガの階段", Material.NETHER_BRICK_STAIRS, 0), - MineStackObjectByMaterial(BUILDING, "nether_brick_fence", "ネザーレンガのフェンス", Material.NETHER_FENCE, 0), - MineStackObjectByMaterial(BUILDING, "red_nether_brick", "赤いネザーレンガ", Material.RED_NETHER_BRICK, 0), - MineStackObjectByMaterial(BUILDING, "nether_wart_block", "ネザ-ウォートブロック", Material.NETHER_WART_BLOCK, 0), - MineStackObjectByMaterial(BUILDING, "ender_stone", "エンドストーン", Material.ENDER_STONE, 0), - MineStackObjectByMaterial(BUILDING, "end_bricks", "エンドストーンレンガ", Material.END_BRICKS, 0), - MineStackObjectByMaterial(BUILDING, "purpur_block", "プルプァブロック", Material.PURPUR_BLOCK, 0), - MineStackObjectByMaterial(BUILDING, "purpur_pillar", "柱状プルプァブロック", Material.PURPUR_PILLAR, 0), - MineStackObjectByMaterial(BUILDING, "purpur_slab", "プルプァハーフブロック", Material.PURPUR_SLAB, 0), - MineStackObjectByMaterial(BUILDING, "purpur_stairs", "プルプァの階段", Material.PURPUR_STAIRS, 0), - MineStackObjectByMaterial(BUILDING, "prismarine0", "プリズマリン", Material.PRISMARINE, 0), - MineStackObjectByMaterial(BUILDING, "prismarine1", "プリズマリンレンガ", Material.PRISMARINE, 1), - MineStackObjectByMaterial(BUILDING, "prismarine2", "ダークプリズマリン", Material.PRISMARINE, 2), - MineStackObjectByMaterial(BUILDING, "sea_lantern", "シーランタン", Material.SEA_LANTERN, 0), - MineStackObjectByMaterial(BUILDING, "granite", "花崗岩", Material.STONE, 1), - MineStackObjectByMaterial(BUILDING, "polished_granite", "磨かれた花崗岩", Material.STONE, 2), - MineStackObjectByMaterial(BUILDING, "diorite", "閃緑岩", Material.STONE, 3), - MineStackObjectByMaterial(BUILDING, "polished_diorite", "磨かれた閃緑岩", Material.STONE, 4), - MineStackObjectByMaterial(BUILDING, "andesite", "安山岩", Material.STONE, 5), - MineStackObjectByMaterial(BUILDING, "polished_andesite", "磨かれた安山岩", Material.STONE, 6), - MineStackObjectByMaterial(BUILDING, "dirt", "土", Material.DIRT, 0), - MineStackObjectByMaterial(BUILDING, "grass", "草ブロック", Material.GRASS, 0), - MineStackObjectByMaterial(BUILDING, "gravel", "砂利", Material.GRAVEL, 0), - MineStackObjectByMaterial(BUILDING, "flint", "火打石", Material.FLINT, 0), - MineStackObjectByMaterial(BUILDING, "flint_and_steel", "火打石と打ち金", Material.FLINT_AND_STEEL, 0), - MineStackObjectByMaterial(BUILDING, "dirt1", "粗い土", Material.DIRT, 1), - MineStackObjectByMaterial(BUILDING, "dirt2", "ポドゾル", Material.DIRT, 2), - MineStackObjectByMaterial(BUILDING, "snow_block", "雪", Material.SNOW_BLOCK, 0), - MineStackObjectByMaterial(BUILDING, "snow_layer", "雪タイル", Material.SNOW, 0), - MineStackObjectByMaterial(BUILDING, "snow_ball", "雪玉", Material.SNOW_BALL, 0), - MineStackObjectByMaterial(BUILDING, "ice", "氷", Material.ICE, 0), - MineStackObjectByMaterial(BUILDING, "packed_ice", "氷塊", Material.PACKED_ICE, 0), - MineStackObjectByMaterial(BUILDING, "mycel", "菌糸", Material.MYCEL, 0), - MineStackObjectByMaterial(BUILDING, "bone_block", "骨ブロック", Material.BONE_BLOCK, 0), - MineStackObjectByMaterial(BUILDING, "sponge", "スポンジ", Material.SPONGE, 0), - MineStackObjectByMaterial(BUILDING, "wet_sponge", "濡れたスポンジ", Material.SPONGE, 1), - MineStackObjectByMaterial(BUILDING, "soul_sand", "ソウルサンド", Material.SOUL_SAND, 0), - MineStackObjectByMaterial(BUILDING, "magma", "マグマブロック", Material.MAGMA, 0), - MineStackObjectByMaterial(BUILDING, "obsidian", "黒曜石", Material.OBSIDIAN, 0), - MineStackObjectByMaterial(BUILDING, "glowstone_dust", "グロウストーンダスト", Material.GLOWSTONE_DUST, 0), - MineStackObjectByMaterial(BUILDING, "glowstone", "グロウストーン", Material.GLOWSTONE, 0), - MineStackObjectByMaterial(BUILDING, "torch", "松明", Material.TORCH, 0), - MineStackObjectByMaterial(BUILDING, "jack_o_lantern", "ジャック・オ・ランタン", Material.JACK_O_LANTERN, 0), - MineStackObjectByMaterial(BUILDING, "end_rod", "エンドロッド", Material.END_ROD, 0), - MineStackObjectByMaterial(BUILDING, "bucket", "バケツ", Material.BUCKET, 0), - MineStackObjectByMaterial(BUILDING, "water_bucket", "水入りバケツ", Material.WATER_BUCKET, 0), - MineStackObjectByMaterial(BUILDING, "lava_bucket", "溶岩入りバケツ", Material.LAVA_BUCKET, 0), - MineStackObjectByMaterial(BUILDING, "web", "クモの巣", Material.WEB, 0), - MineStackObjectByMaterial(BUILDING, "rails", "レール", Material.RAILS, 0), - MineStackObjectByMaterial(BUILDING, "furnace", "かまど", Material.FURNACE, 0), - MineStackObjectByMaterial(BUILDING, "chest", "チェスト", Material.CHEST, 0), - MineStackObjectByMaterial(BUILDING, "book", "本", Material.BOOK, 0), - MineStackObjectByMaterial(BUILDING, "bookshelf", "本棚", Material.BOOKSHELF, 0), - MineStackObjectByMaterial(BUILDING, "iron_bars", "鉄格子", Material.IRON_FENCE, 0), - MineStackObjectByMaterial(BUILDING, "anvil", "金床", Material.ANVIL, 0), - MineStackObjectByMaterial(BUILDING, "cauldron", "大釜", Material.CAULDRON_ITEM, 0), - MineStackObjectByMaterial(BUILDING, "brewing_stand", "醸造台", Material.BREWING_STAND_ITEM, 0), - MineStackObjectByMaterial(BUILDING, "flower_pot", "植木鉢", Material.FLOWER_POT_ITEM, 0), - MineStackObjectByMaterial(BUILDING, "hay_block", "干し草の俵", Material.HAY_BLOCK, 0), - MineStackObjectByMaterial(BUILDING, "ladder", "はしご", Material.LADDER, 0), - MineStackObjectByMaterial(BUILDING, "sign", "看板", Material.SIGN, 0), - MineStackObjectByMaterial(BUILDING, "item_frame", "額縁", Material.ITEM_FRAME, 0), - MineStackObjectByMaterial(BUILDING, "painting", "絵画", Material.PAINTING, 0), - MineStackObjectByMaterial(BUILDING, "beacon", "ビーコン", Material.BEACON, 0), - MineStackObjectByMaterial(BUILDING, "armor_stand", "アーマースタンド", Material.ARMOR_STAND, 0), - MineStackObjectByMaterial(BUILDING, "end_crystal", "エンドクリスタル", Material.END_CRYSTAL, 0), - MineStackObjectByMaterial(BUILDING, "enchanting_table", "エンチャントテーブル", Material.ENCHANTMENT_TABLE, 0), - MineStackObjectByMaterial(BUILDING, "jukebox", "ジュークボックス", Material.JUKEBOX, 0), - MineStackObjectByMaterial(BUILDING, "hard_clay", "テラコッタ", Material.HARD_CLAY, 0), - MineStackObjectByMaterial(BUILDING, "workbench", "作業台", Material.WORKBENCH, 0) + MineStackObjectByMaterial(BUILDING, "cobblestone", "丸石", Material.COBBLESTONE), + MineStackObjectByMaterial(BUILDING, "mossy_cobblestone", "苔むした丸石", Material.MOSSY_COBBLESTONE), + MineStackObjectByMaterial(BUILDING, "stone", "石", Material.STONE), + MineStackObjectByMaterial(BUILDING, "smooth_brick0", "石レンガ", Material.STONE_BRICKS), + MineStackObjectByMaterial(BUILDING, "smooth_brick3", "模様入りの石レンガ", Material.CHISELED_STONE_BRICKS), + MineStackObjectByMaterial(BUILDING, "smooth_brick1", "苔むした石レンガ", Material.MOSSY_STONE_BRICKS), + MineStackObjectByMaterial(BUILDING, "smooth_brick2", "ひび割れた石レンガ", Material.CRACKED_STONE_BRICKS), + MineStackObjectByMaterial(BUILDING, "sand", "砂", Material.SAND), + MineStackObjectByMaterial(BUILDING, "sandstone", "砂岩", Material.SANDSTONE), + MineStackObjectByMaterial(BUILDING, "sandstone1", "模様入りの砂岩", Material.CHISELED_SANDSTONE), + MineStackObjectByMaterial(BUILDING, "sandstone2", "滑らかな砂岩", Material.SMOOTH_SANDSTONE), + MineStackObjectByMaterial(BUILDING, "red_sand", "赤い砂", Material.RED_SAND), + MineStackObjectByMaterial(BUILDING, "red_sandstone", "赤い砂岩", Material.RED_SANDSTONE), + MineStackObjectByMaterial(BUILDING, "red_sandstone1", "模様入りの赤い砂岩", Material.CHISELED_RED_SANDSTONE), + MineStackObjectByMaterial(BUILDING, "red_sandstone2", "滑らかな赤い砂岩", Material.SMOOTH_RED_SANDSTONE), + MineStackObjectByMaterial(BUILDING, "clay_ball", "粘土玉", Material.CLAY_BALL), + MineStackObjectByMaterial(BUILDING, "clay", "粘土", Material.CLAY), + MineStackObjectByMaterial(BUILDING, "brick_item", "レンガ", Material.BRICK), + MineStackObjectByMaterial(BUILDING, "brick", "レンガ(ブロック)", Material.BRICKS), + MineStackObjectByMaterial(BUILDING, "quartz_block", "クォーツブロック", Material.QUARTZ_BLOCK), + MineStackObjectByMaterial(BUILDING, "quartz_block1", "模様入りのクォーツブロック", Material.CHISELED_QUARTZ_BLOCK), + MineStackObjectByMaterial(BUILDING, "quartz_block2", "クォーツの柱", Material.QUARTZ_PILLAR), + MineStackObjectByMaterial(BUILDING, "netherrack", "ネザーラック", Material.NETHERRACK), + MineStackObjectByMaterial(BUILDING, "nether_brick_item", "ネザーレンガ", Material.NETHER_BRICK), + MineStackObjectByMaterial(BUILDING, "nether_brick", "ネザーレンガ(ブロック)", Material.NETHER_BRICKS), + MineStackObjectByMaterial(BUILDING, "red_nether_brick", "赤いネザーレンガ", Material.RED_NETHER_BRICKS), + MineStackObjectByMaterial(BUILDING, "nether_wart_block", "ネザ-ウォートブロック", Material.NETHER_WART_BLOCK), + MineStackObjectByMaterial(BUILDING, "ender_stone", "エンドストーン", Material.END_STONE), + MineStackObjectByMaterial(BUILDING, "end_bricks", "エンドストーンレンガ", Material.END_STONE_BRICKS), + MineStackObjectByMaterial(BUILDING, "purpur_block", "プルプァブロック", Material.PURPUR_BLOCK), + MineStackObjectByMaterial(BUILDING, "purpur_pillar", "プルプァの柱", Material.PURPUR_PILLAR), + MineStackObjectByMaterial(BUILDING, "prismarine0", "プリズマリン", Material.PRISMARINE), + MineStackObjectByMaterial(BUILDING, "prismarine1", "プリズマリンレンガ", Material.PRISMARINE_BRICKS), + MineStackObjectByMaterial(BUILDING, "prismarine2", "ダークプリズマリン", Material.DARK_PRISMARINE), + MineStackObjectByMaterial(BUILDING, "sea_lantern", "シーランタン", Material.SEA_LANTERN), + MineStackObjectByMaterial(BUILDING, "granite", "花崗岩", Material.GRANITE), + MineStackObjectByMaterial(BUILDING, "polished_granite", "磨かれた花崗岩", Material.POLISHED_GRANITE), + MineStackObjectByMaterial(BUILDING, "diorite", "閃緑岩", Material.DIORITE), + MineStackObjectByMaterial(BUILDING, "polished_diorite", "磨かれた閃緑岩", Material.POLISHED_DIORITE), + MineStackObjectByMaterial(BUILDING, "andesite", "安山岩", Material.ANDESITE), + MineStackObjectByMaterial(BUILDING, "polished_andesite", "磨かれた安山岩", Material.POLISHED_ANDESITE), + MineStackObjectByMaterial(BUILDING, "dirt", "土", Material.DIRT), + MineStackObjectByMaterial(BUILDING, "grass", "草ブロック", Material.GRASS_BLOCK), + MineStackObjectByMaterial(BUILDING, "gravel", "砂利", Material.GRAVEL), + MineStackObjectByMaterial(BUILDING, "flint", "火打石", Material.FLINT), + MineStackObjectByMaterial(BUILDING, "flint_and_steel", "火打石と打ち金", Material.FLINT_AND_STEEL), + MineStackObjectByMaterial(BUILDING, "dirt1", "粗い土", Material.COARSE_DIRT), + MineStackObjectByMaterial(BUILDING, "dirt2", "ポドゾル", Material.PODZOL), + MineStackObjectByMaterial(BUILDING, "snow_block", "雪ブロック", Material.SNOW_BLOCK), + MineStackObjectByMaterial(BUILDING, "snow_layer", "雪", Material.SNOW), + MineStackObjectByMaterial(BUILDING, "snow_ball", "雪玉", Material.SNOWBALL), + MineStackObjectByMaterial(BUILDING, "ice", "氷", Material.ICE), + MineStackObjectByMaterial(BUILDING, "packed_ice", "氷塊", Material.PACKED_ICE), + MineStackObjectByMaterial(BUILDING, "mycel", "菌糸", Material.MYCELIUM), + MineStackObjectByMaterial(BUILDING, "bone_block", "骨ブロック", Material.BONE_BLOCK), + MineStackObjectByMaterial(BUILDING, "sponge", "スポンジ", Material.SPONGE), + MineStackObjectByMaterial(BUILDING, "wet_sponge", "濡れたスポンジ", Material.WET_SPONGE), + MineStackObjectByMaterial(BUILDING, "soul_sand", "ソウルサンド", Material.SOUL_SAND), + MineStackObjectByMaterial(BUILDING, "magma", "マグマブロック", Material.MAGMA_BLOCK), + MineStackObjectByMaterial(BUILDING, "obsidian", "黒曜石", Material.OBSIDIAN), + MineStackObjectByMaterial(BUILDING, "glowstone_dust", "グロウストーンダスト", Material.GLOWSTONE_DUST), + MineStackObjectByMaterial(BUILDING, "glowstone", "グロウストーン", Material.GLOWSTONE), + MineStackObjectByMaterial(BUILDING, "torch", "松明", Material.TORCH), + MineStackObjectByMaterial(BUILDING, "jack_o_lantern", "ジャック・オ・ランタン", Material.JACK_O_LANTERN), + MineStackObjectByMaterial(BUILDING, "end_rod", "エンドロッド", Material.END_ROD), + MineStackObjectByMaterial(BUILDING, "bucket", "バケツ", Material.BUCKET), + MineStackObjectByMaterial(BUILDING, "water_bucket", "水入りバケツ", Material.WATER_BUCKET), + MineStackObjectByMaterial(BUILDING, "lava_bucket", "溶岩入りバケツ", Material.LAVA_BUCKET), + MineStackObjectByMaterial(BUILDING, "web", "クモの巣", Material.COBWEB), + MineStackObjectByMaterial(BUILDING, "rails", "レール", Material.RAIL), + MineStackObjectByMaterial(BUILDING, "furnace", "かまど", Material.FURNACE), + MineStackObjectByMaterial(BUILDING, "chest", "チェスト", Material.CHEST), + MineStackObjectByMaterial(BUILDING, "book", "本", Material.BOOK), + MineStackObjectByMaterial(BUILDING, "bookshelf", "本棚", Material.BOOKSHELF), + MineStackObjectByMaterial(BUILDING, "iron_bars", "鉄格子", Material.IRON_BARS), + MineStackObjectByMaterial(BUILDING, "anvil", "金床", Material.ANVIL), + MineStackObjectByMaterial(BUILDING, "cauldron", "大釜", Material.CAULDRON), + MineStackObjectByMaterial(BUILDING, "brewing_stand", "醸造台", Material.BREWING_STAND), + MineStackObjectByMaterial(BUILDING, "flower_pot", "植木鉢", Material.FLOWER_POT), + MineStackObjectByMaterial(BUILDING, "hay_block", "干草の俵", Material.HAY_BLOCK), + MineStackObjectByMaterial(BUILDING, "ladder", "はしご", Material.LADDER), + MineStackObjectByMaterial(BUILDING, "item_frame", "額縁", Material.ITEM_FRAME), + MineStackObjectByMaterial(BUILDING, "painting", "絵画", Material.PAINTING), + MineStackObjectByMaterial(BUILDING, "beacon", "ビーコン", Material.BEACON), + MineStackObjectByMaterial(BUILDING, "armor_stand", "防具立て", Material.ARMOR_STAND), + MineStackObjectByMaterial(BUILDING, "end_crystal", "エンドクリスタル", Material.END_CRYSTAL), + MineStackObjectByMaterial(BUILDING, "enchanting_table", "エンチャントテーブル", Material.ENCHANTING_TABLE), + MineStackObjectByMaterial(BUILDING, "jukebox", "ジュークボックス", Material.JUKEBOX), + MineStackObjectByMaterial(BUILDING, "workbench", "作業台", Material.CRAFTING_TABLE), + MineStackObjectByMaterial(BUILDING, "deepslate", "深層岩", Material.DEEPSLATE), + MineStackObjectByMaterial(BUILDING, "cobbled_deepslate", "深層岩の丸石", Material.COBBLED_DEEPSLATE), + MineStackObjectByMaterial(BUILDING, "polished_deepslate", "磨かれた深層岩", Material.POLISHED_DEEPSLATE), + MineStackObjectByMaterial(BUILDING, "calcite", "方解石", Material.CALCITE), + MineStackObjectByMaterial(BUILDING, "tuff", "凝灰岩", Material.TUFF), + MineStackObjectByMaterial(BUILDING, "dripstone_block", "鍾乳石ブロック", Material.DRIPSTONE_BLOCK), + MineStackObjectByMaterial(BUILDING, "rooted_dirt", "根付いた土", Material.ROOTED_DIRT), + MineStackObjectByMaterial(BUILDING, "crimson_nylium", "真紅のナイリウム", Material.CRIMSON_NYLIUM), + MineStackObjectByMaterial(BUILDING, "warped_nylium", "歪んだナイリウム", Material.WARPED_NYLIUM), + MineStackObjectByMaterial(BUILDING, "warped_wart_block", "歪んだウォートブロック", Material.WARPED_WART_BLOCK), + MineStackObjectByMaterial(BUILDING, "tinted_glass", "遮光ガラス", Material.TINTED_GLASS), + MineStackObjectByMaterial(BUILDING, "cut_sandstone", "研がれた砂岩", Material.CUT_SANDSTONE), + MineStackObjectByMaterial(BUILDING, "moss_carpet", "苔のカーペット", Material.MOSS_CARPET), + MineStackObjectByMaterial(BUILDING, "moss_block", "苔ブロック", Material.MOSS_BLOCK), + MineStackObjectByMaterial(BUILDING, "smooth_quartz", "滑らかなクォーツブロック", Material.SMOOTH_QUARTZ), + MineStackObjectByMaterial(BUILDING, "smooth_stone", "滑らかな石", Material.SMOOTH_STONE), + MineStackObjectByMaterial(BUILDING, "soul_soil", "ソウルソイル", Material.SOUL_SOIL), + MineStackObjectByMaterial(BUILDING, "basalt", "玄武岩", Material.BASALT), + MineStackObjectByMaterial(BUILDING, "polished_basalt", "磨かれた玄武岩", Material.POLISHED_BASALT), + MineStackObjectByMaterial(BUILDING, "smooth_basalt", "滑らかな玄武岩", Material.SMOOTH_BASALT), + MineStackObjectByMaterial(BUILDING, "soul_torch", "魂の松明", Material.SOUL_TORCH), + MineStackObjectByMaterial(BUILDING, "deepslate_bricks", "深層岩レンガ", Material.DEEPSLATE_BRICKS), + MineStackObjectByMaterial(BUILDING, "cracked_deepslate_bricks", "ひび割れた深層岩レンガ", Material.CRACKED_DEEPSLATE_BRICKS), + MineStackObjectByMaterial(BUILDING, "deepslate_tiles", "深層岩タイル", Material.DEEPSLATE_TILES), + MineStackObjectByMaterial(BUILDING, "cracked_deepslate_tiles", "ひび割れた深層岩タイル", Material.CRACKED_DEEPSLATE_TILES), + MineStackObjectByMaterial(BUILDING, "chiseled_deepslate", "模様入りの深層岩", Material.CHISELED_DEEPSLATE), + MineStackObjectByMaterial(BUILDING, "brown_mushroom_block", "茶色のキノコブロック", Material.BROWN_MUSHROOM_BLOCK), + MineStackObjectByMaterial(BUILDING, "red_mushroom_block", "赤色のキノコブロック", Material.RED_MUSHROOM_BLOCK), + MineStackObjectByMaterial(BUILDING, "mushroom_stem", "キノコの柄", Material.MUSHROOM_STEM), + MineStackObjectByMaterial(BUILDING, "chain", "鎖", Material.CHAIN), + MineStackObjectByMaterial(BUILDING, "cracked_nether_bricks", "ひび割れたネザーレンガ", Material.CRACKED_NETHER_BRICKS), + MineStackObjectByMaterial(BUILDING, "chiseled_nether_bricks", "模様入りのネザーレンガ", Material.CHISELED_NETHER_BRICKS), + MineStackObjectByMaterial(BUILDING, "quartz_bricks", "クォーツレンガ", Material.QUARTZ_BRICKS), + MineStackObjectByMaterial(BUILDING, "cut_red_sandstone", "研がれた赤い砂岩", Material.CUT_RED_SANDSTONE), + MineStackObjectByMaterial(BUILDING, "turtle_egg", "カメの卵", Material.TURTLE_EGG), + MineStackObjectByMaterial(BUILDING, "blue_ice", "青氷", Material.BLUE_ICE), + MineStackObjectByMaterial(BUILDING, "conduit", "コンジット", Material.CONDUIT), + MineStackObjectByMaterial(BUILDING, "scaffolding", "足場", Material.SCAFFOLDING), + MineStackObjectByMaterial(BUILDING, "honey_block", "ハチミツブロック", Material.HONEY_BLOCK), + MineStackObjectByMaterial(BUILDING, "loom", "機織り機", Material.LOOM), + MineStackObjectByMaterial(BUILDING, "composter", "コンポスター", Material.COMPOSTER), + MineStackObjectByMaterial(BUILDING, "barrel", "樽", Material.BARREL), + MineStackObjectByMaterial(BUILDING, "smoker", "燻製器", Material.SMOKER), + MineStackObjectByMaterial(BUILDING, "blast_furnace", "溶鉱炉", Material.BLAST_FURNACE), + MineStackObjectByMaterial(BUILDING, "cartography_table", "製図台", Material.CARTOGRAPHY_TABLE), + MineStackObjectByMaterial(BUILDING, "fletching_table", "矢細工台", Material.FLETCHING_TABLE), + MineStackObjectByMaterial(BUILDING, "grindstone", "砥石", Material.GRINDSTONE), + MineStackObjectByMaterial(BUILDING, "smithing_table", "鍛冶台", Material.SMITHING_TABLE), + MineStackObjectByMaterial(BUILDING, "stonecutter", "石切台", Material.STONECUTTER), + MineStackObjectByMaterial(BUILDING, "bell", "鐘", Material.BELL), + MineStackObjectByMaterial(BUILDING, "lantern", "ランタン", Material.LANTERN), + MineStackObjectByMaterial(BUILDING, "soul_lantern", "魂のランタン", Material.SOUL_LANTERN), + MineStackObjectByMaterial(BUILDING, "campfire", "焚き火", Material.CAMPFIRE), + MineStackObjectByMaterial(BUILDING, "soul_campfire", "魂の焚き火", Material.SOUL_CAMPFIRE), + MineStackObjectByMaterial(BUILDING, "shroomlight", "シュルームライト", Material.SHROOMLIGHT), + MineStackObjectByMaterial(BUILDING, "honeycomb", "ハニカム", Material.HONEYCOMB), + MineStackObjectByMaterial(BUILDING, "beehive", "養蜂箱", Material.BEEHIVE), + MineStackObjectByMaterial(BUILDING, "honey_bottle", "ハチミツ入りの瓶", Material.HONEY_BOTTLE), + MineStackObjectByMaterial(BUILDING, "honeycomb_block", "ハニカムブロック", Material.HONEYCOMB_BLOCK), + MineStackObjectByMaterial(BUILDING, "lodestone", "ロードストーン", Material.LODESTONE), + MineStackObjectByMaterial(BUILDING, "crying_obsidian", "泣く黒曜石", Material.CRYING_OBSIDIAN), + MineStackObjectByMaterial(BUILDING, "blackstone", "ブラックストーン", Material.BLACKSTONE), + MineStackObjectByMaterial(BUILDING, "gilded_blackstone", "きらめくブラックストーン", Material.GILDED_BLACKSTONE), + MineStackObjectByMaterial(BUILDING, "polished_blackstone", "磨かれたブラックストーン", Material.POLISHED_BLACKSTONE), + MineStackObjectByMaterial(BUILDING, "chiseled_polished_blackstone", "模様入りの磨かれたブラックストーン", Material.CHISELED_POLISHED_BLACKSTONE), + MineStackObjectByMaterial(BUILDING, "polished_blackstone_bricks", "磨かれたブラックストーンレンガ", Material.POLISHED_BLACKSTONE_BRICKS), + MineStackObjectByMaterial(BUILDING, "cracked_polished_blackstone_bricks", "ひび割れたブラックストーンレンガ", Material.CRACKED_POLISHED_BLACKSTONE_BRICKS), + MineStackObjectByMaterial(BUILDING, "respawn_anchor", "リスポーンアンカー", Material.RESPAWN_ANCHOR), + MineStackObjectByMaterial(BUILDING, "pointed_dripstone", "鍾乳石", Material.POINTED_DRIPSTONE), + MineStackObjectByMaterial(BUILDING, "powder_snow_bucket", "粉雪入りバケツ", Material.POWDER_SNOW_BUCKET), + MineStackObjectByMaterial(BUILDING, "fire_charge", "ファイヤーチャージ", Material.FIRE_CHARGE), + MineStackObjectByMaterial(BUILDING, "writable_book", "本と羽根ペン", Material.WRITABLE_BOOK), + MineStackObjectByMaterial(BUILDING, "glow_item_frame", "輝く額縁", Material.GLOW_ITEM_FRAME), ) ++ rightElems( - MineStackObjectWithColorVariants( - MineStackObjectByMaterial(BUILDING, "bed", "白色のベッド", Material.BED, 0), - List( - MineStackObjectByMaterial(BUILDING, "bed_1", "橙色のベッド", Material.BED, 1), - MineStackObjectByMaterial(BUILDING, "bed_2", "赤紫色のベッド", Material.BED, 2), - MineStackObjectByMaterial(BUILDING, "bed_3", "空色のベッド", Material.BED, 3), - MineStackObjectByMaterial(BUILDING, "bed_4", "黄色のベッド", Material.BED, 4), - MineStackObjectByMaterial(BUILDING, "bed_5", "黄緑色のベッド", Material.BED, 5), - MineStackObjectByMaterial(BUILDING, "bed_6", "桃色のベッド", Material.BED, 6), - MineStackObjectByMaterial(BUILDING, "bed_7", "灰色のベッド", Material.BED, 7), - MineStackObjectByMaterial(BUILDING, "bed_8", "薄灰色のベッド", Material.BED, 8), - MineStackObjectByMaterial(BUILDING, "bed_9", "青緑色のベッド", Material.BED, 9), - MineStackObjectByMaterial(BUILDING, "bed_10", "紫色のベッド", Material.BED, 10), - MineStackObjectByMaterial(BUILDING, "bed_11", "青色のベッド", Material.BED, 11), - MineStackObjectByMaterial(BUILDING, "bed_12", "茶色のベッド", Material.BED, 12), - MineStackObjectByMaterial(BUILDING, "bed_13", "緑色のベッド", Material.BED, 13), - MineStackObjectByMaterial(BUILDING, "bed_14", "赤色のベッド", Material.BED, 14), - MineStackObjectByMaterial(BUILDING, "bed_15", "黒色のベッド", Material.BED, 15) + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "log", "オークの原木", Material.OAK_LOG), + List( + MineStackObjectByMaterial(BUILDING, "log1", "トウヒの原木", Material.SPRUCE_LOG), + MineStackObjectByMaterial(BUILDING, "log2", "シラカバの原木", Material.BIRCH_LOG), + MineStackObjectByMaterial(BUILDING, "log3", "ジャングルの原木", Material.JUNGLE_LOG), + MineStackObjectByMaterial(BUILDING, "log_2", "アカシアの原木", Material.ACACIA_LOG), + MineStackObjectByMaterial(BUILDING, "log_21", "ダークオークの原木", Material.DARK_OAK_LOG), + MineStackObjectByMaterial(BUILDING, "crimson_stem", "真紅の幹", Material.CRIMSON_STEM), + MineStackObjectByMaterial(BUILDING, "warped_stem", "歪んだ幹", Material.WARPED_STEM), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "stripped_oak_log", "樹皮を剥いだオークの原木", Material.STRIPPED_OAK_LOG), + List( + MineStackObjectByMaterial(BUILDING, "stripped_spruce_log", "樹皮を剥いだトウヒの原木", Material.STRIPPED_SPRUCE_LOG), + MineStackObjectByMaterial(BUILDING, "stripped_birch_log", "樹皮を剥いだシラカバの原木", Material.STRIPPED_BIRCH_LOG), + MineStackObjectByMaterial(BUILDING, "stripped_jungle_log", "樹皮を剥いだジャングルの原木", Material.STRIPPED_JUNGLE_LOG), + MineStackObjectByMaterial(BUILDING, "stripped_acacia_log", "樹皮を剥いだアカシアの原木", Material.STRIPPED_ACACIA_LOG), + MineStackObjectByMaterial(BUILDING, "stripped_dark_oak_log", "樹皮を剥いだダークオークの原木", Material.STRIPPED_DARK_OAK_LOG), + MineStackObjectByMaterial(BUILDING, "stripped_crimson_stem", "表皮を剥いだ真紅の幹", Material.STRIPPED_CRIMSON_STEM), + MineStackObjectByMaterial(BUILDING, "stripped_warped_stem", "表皮を剥いだ歪んだ幹", Material.STRIPPED_WARPED_STEM), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "stripped_oak_wood", "樹皮を剥いだオークの木", Material.STRIPPED_OAK_WOOD), + List( + MineStackObjectByMaterial(BUILDING, "stripped_spruce_wood", "樹皮を剥いだトウヒの木", Material.STRIPPED_SPRUCE_WOOD), + MineStackObjectByMaterial(BUILDING, "stripped_birch_wood", "樹皮を剥いだシラカバの木", Material.STRIPPED_BIRCH_WOOD), + MineStackObjectByMaterial(BUILDING, "stripped_jungle_wood", "樹皮を剥いだジャングルの木", Material.STRIPPED_JUNGLE_WOOD), + MineStackObjectByMaterial(BUILDING, "stripped_acacia_wood", "樹皮を剥いだアカシアの木", Material.STRIPPED_ACACIA_WOOD), + MineStackObjectByMaterial(BUILDING, "stripped_dark_oak_wood", "樹皮を剥いだダークオークの木", Material.STRIPPED_DARK_OAK_WOOD), + MineStackObjectByMaterial(BUILDING, "stripped_crimson_hyphae", "表皮を剥いだ真紅の菌糸", Material.STRIPPED_CRIMSON_HYPHAE), + MineStackObjectByMaterial(BUILDING, "stripped_warped_hyphae", "表皮を剥いだ歪んだ菌糸", Material.STRIPPED_WARPED_HYPHAE), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "wood", "オークの木", Material.OAK_WOOD), + List( + MineStackObjectByMaterial(BUILDING, "wood_1", "トウヒの木", Material.SPRUCE_WOOD), + MineStackObjectByMaterial(BUILDING, "wood_2", "シラカバの木", Material.BIRCH_WOOD), + MineStackObjectByMaterial(BUILDING, "wood_3", "ジャングルの木", Material.JUNGLE_WOOD), + MineStackObjectByMaterial(BUILDING, "wood_4", "アカシアの木", Material.ACACIA_WOOD), + MineStackObjectByMaterial(BUILDING, "wood_5", "ダークオークの木", Material.DARK_OAK_WOOD), + MineStackObjectByMaterial(BUILDING, "crimson_hyphae", "真紅の菌糸", Material.CRIMSON_HYPHAE), + MineStackObjectByMaterial(BUILDING, "warped_hyphae", "歪んだ菌糸", Material.WARPED_HYPHAE), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "oak_planks", "オークの板材", Material.OAK_PLANKS), + List( + MineStackObjectByMaterial(BUILDING, "spruce_planks", "トウヒの板材", Material.SPRUCE_PLANKS), + MineStackObjectByMaterial(BUILDING, "birch_planks", "シラカバの板材", Material.BIRCH_PLANKS), + MineStackObjectByMaterial(BUILDING, "jungle_planks", "ジャングルの板材", Material.JUNGLE_PLANKS), + MineStackObjectByMaterial(BUILDING, "acacia_planks", "アカシアの板材", Material.ACACIA_PLANKS), + MineStackObjectByMaterial(BUILDING, "dark_oak_planks", "ダークオークの板材", Material.DARK_OAK_PLANKS), + MineStackObjectByMaterial(BUILDING, "crimson_planks", "真紅の板材", Material.CRIMSON_PLANKS), + MineStackObjectByMaterial(BUILDING, "warped_planks", "歪んだ板材", Material.WARPED_PLANKS), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "wood_step0", "オークのハーフブロック", Material.OAK_SLAB), + List( + MineStackObjectByMaterial(BUILDING, "wood_step1", "トウヒのハーフブロック", Material.SPRUCE_SLAB), + MineStackObjectByMaterial(BUILDING, "wood_step2", "シラカバのハーフブロック", Material.BIRCH_SLAB), + MineStackObjectByMaterial(BUILDING, "wood_step3", "ジャングルのハーフブロック", Material.JUNGLE_SLAB), + MineStackObjectByMaterial(BUILDING, "wood_step4", "アカシアのハーフブロック", Material.ACACIA_SLAB), + MineStackObjectByMaterial(BUILDING, "wood_step5", "ダークオークのハーフブロック", Material.DARK_OAK_SLAB), + MineStackObjectByMaterial(BUILDING, "step3", "丸石のハーフブロック", Material.COBBLESTONE_SLAB), + MineStackObjectByMaterial(BUILDING, "step0", "石のハーフブロック", Material.STONE_SLAB), + MineStackObjectByMaterial(BUILDING, "step5", "石レンガのハーフブロック", Material.STONE_BRICK_SLAB), + MineStackObjectByMaterial(BUILDING, "step1", "砂岩のハーフブロック", Material.SANDSTONE_SLAB), + MineStackObjectByMaterial(BUILDING, "stone_slab20", "赤い砂岩のハーフブロック", Material.RED_SANDSTONE_SLAB), + MineStackObjectByMaterial(BUILDING, "step4", "レンガのハーフブロック", Material.BRICK_SLAB), + MineStackObjectByMaterial(BUILDING, "step7", "クォーツのハーフブロック", Material.QUARTZ_SLAB), + MineStackObjectByMaterial(BUILDING, "step6", "ネザーレンガのハーフブロック", Material.NETHER_BRICK_SLAB), + MineStackObjectByMaterial(BUILDING, "purpur_slab", "プルプァのハーフブロック", Material.PURPUR_SLAB), + MineStackObjectByMaterial(BUILDING, "cut_copper_slab", "切り込み入りの銅のハーフブロック", Material.CUT_COPPER_SLAB), + MineStackObjectByMaterial(BUILDING, "exposed_cut_copper_slab", "風化した切り込み入りの銅のハーフブロック", Material.EXPOSED_CUT_COPPER_SLAB), + MineStackObjectByMaterial(BUILDING, "weathered_cut_copper_slab", "錆びた切り込み入りの銅のハーフブロック", Material.WEATHERED_CUT_COPPER_SLAB), + MineStackObjectByMaterial(BUILDING, "oxidized_cut_copper_slab", "酸化した切り込み入りの銅のハーフブロック", Material.OXIDIZED_CUT_COPPER_SLAB), + MineStackObjectByMaterial(BUILDING, "waxed_cut_copper_slab", "錆止めされた切り込み入りの銅のハーフブロック", Material.WAXED_CUT_COPPER_SLAB), + MineStackObjectByMaterial(BUILDING, "waxed_exposed_cut_copper_slab", "錆止めされた風化した切り込み入りの銅のハーフブロック", Material.WAXED_EXPOSED_CUT_COPPER_SLAB), + MineStackObjectByMaterial(BUILDING, "waxed_weathered_cut_copper_slab", "錆止めされた錆びた切り込み入りの銅のハーフブロック", Material.WAXED_WEATHERED_CUT_COPPER_SLAB), + MineStackObjectByMaterial(BUILDING, "waxed_oxidized_cut_copper_slab", "錆止めされた酸化した切り込み入りの銅のハーフブロック", Material.WAXED_OXIDIZED_CUT_COPPER_SLAB), + MineStackObjectByMaterial(BUILDING, "crimson_slab", "真紅のハーフブロック", Material.CRIMSON_SLAB), + MineStackObjectByMaterial(BUILDING, "warped_slab", "歪んだハーフブロック", Material.WARPED_SLAB), + MineStackObjectByMaterial(BUILDING, "smooth_stone_slab", "滑らかな石のハーフブロック", Material.SMOOTH_STONE_SLAB), + MineStackObjectByMaterial(BUILDING, "cut_sandstone_slab", "研がれた砂岩のハーフブロック", Material.CUT_SANDSTONE_SLAB), + MineStackObjectByMaterial(BUILDING, "cut_red_sandstone_slab", "研がれた赤い砂岩のハーフブロック", Material.CUT_RED_SANDSTONE_SLAB), + MineStackObjectByMaterial(BUILDING, "prismarine_slab", "プリズマリンのハーフブロック", Material.PRISMARINE_SLAB), + MineStackObjectByMaterial(BUILDING, "prismarine_brick_slab", "プリズマリンレンガのハーフブロック", Material.PRISMARINE_BRICK_SLAB), + MineStackObjectByMaterial(BUILDING, "dark_prismarine_slab", "ダークプリズマリンのハーフブロック", Material.DARK_PRISMARINE_SLAB), + MineStackObjectByMaterial(BUILDING, "polished_granite_slab", "磨かれた花崗岩のハーフブロック", Material.POLISHED_GRANITE_SLAB), + MineStackObjectByMaterial(BUILDING, "smooth_red_sandstone_slab", "滑らかな赤い砂岩のハーフブロック", Material.SMOOTH_RED_SANDSTONE_SLAB), + MineStackObjectByMaterial(BUILDING, "mossy_stone_brick_slab", "苔むした石レンガのハーフブロック", Material.MOSSY_STONE_BRICK_SLAB), + MineStackObjectByMaterial(BUILDING, "polished_diorite_slab", "磨かれた閃緑岩のハーフブロック", Material.POLISHED_DIORITE_SLAB), + MineStackObjectByMaterial(BUILDING, "mossy_cobblestone_slab", "苔むした丸石のハーフブロック", Material.MOSSY_COBBLESTONE_SLAB), + MineStackObjectByMaterial(BUILDING, "end_stone_brick_slab", "エンドストーンレンガのハーフブロック", Material.END_STONE_BRICK_SLAB), + MineStackObjectByMaterial(BUILDING, "smooth_sandstone_slab", "滑らかな砂岩のハーフブロック", Material.SMOOTH_SANDSTONE_SLAB), + MineStackObjectByMaterial(BUILDING, "smooth_quartz_slab", "滑らかなクォーツのハーフブロック", Material.SMOOTH_QUARTZ_SLAB), + MineStackObjectByMaterial(BUILDING, "granite_slab", "花崗岩のハーフブロック", Material.GRANITE_SLAB), + MineStackObjectByMaterial(BUILDING, "andesite_slab", "安山岩のハーフブロック", Material.ANDESITE_SLAB), + MineStackObjectByMaterial(BUILDING, "red_nether_brick_slab", "赤いネザーレンガのハーフブロック", Material.RED_NETHER_BRICK_SLAB), + MineStackObjectByMaterial(BUILDING, "polished_andesite_slab", "磨かれた安山岩のハーフブロック", Material.POLISHED_ANDESITE_SLAB), + MineStackObjectByMaterial(BUILDING, "diorite_slab", "閃緑岩のハーフブロック", Material.DIORITE_SLAB), + MineStackObjectByMaterial(BUILDING, "cobbled_deepslate_slab", "深層岩の丸石のハーフブロック", Material.COBBLED_DEEPSLATE_SLAB), + MineStackObjectByMaterial(BUILDING, "polished_deepslate_slab", "磨かれた深層岩のハーフブロック", Material.POLISHED_DEEPSLATE_SLAB), + MineStackObjectByMaterial(BUILDING, "deepslate_brick_slab", "深層岩レンガのハーフブロック", Material.DEEPSLATE_BRICK_SLAB), + MineStackObjectByMaterial(BUILDING, "deepslate_tile_slab", "深層岩タイルのハーフブロック", Material.DEEPSLATE_TILE_SLAB), + MineStackObjectByMaterial(BUILDING, "blackstone_slab", "ブラックストーンのハーフブロック", Material.BLACKSTONE_SLAB), + MineStackObjectByMaterial(BUILDING, "polished_blackstone_slab", "磨かれたブラックストーンのハーフブロック", Material.POLISHED_BLACKSTONE_SLAB), + MineStackObjectByMaterial(BUILDING, "polished_blackstone_brick_slab", "磨かれたブラックストーンレンガのハーフブロック", Material.POLISHED_BLACKSTONE_BRICK_SLAB), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "oak_stairs", "オークの階段", Material.OAK_STAIRS), + List( + MineStackObjectByMaterial(BUILDING, "spruce_stairs", "トウヒの階段", Material.SPRUCE_STAIRS), + MineStackObjectByMaterial(BUILDING, "birch_stairs", "シラカバの階段", Material.BIRCH_STAIRS), + MineStackObjectByMaterial(BUILDING, "jungle_stairs", "ジャングルの階段", Material.JUNGLE_STAIRS), + MineStackObjectByMaterial(BUILDING, "acacia_stairs", "アカシアの階段", Material.ACACIA_STAIRS), + MineStackObjectByMaterial(BUILDING, "dark_oak_stairs", "ダークオークの階段", Material.DARK_OAK_STAIRS), + MineStackObjectByMaterial(BUILDING, "stone_stairs", "丸石の階段", Material.COBBLESTONE_STAIRS), + MineStackObjectByMaterial(BUILDING, "smooth_stairs", "石レンガの階段", Material.STONE_BRICK_STAIRS), + MineStackObjectByMaterial(BUILDING, "standstone_stairs", "砂岩の階段", Material.SANDSTONE_STAIRS), + MineStackObjectByMaterial(BUILDING, "red_sandstone_stairs", "赤い砂岩の階段", Material.RED_SANDSTONE_STAIRS), + MineStackObjectByMaterial(BUILDING, "brick_stairs", "レンガの階段", Material.BRICK_STAIRS), + MineStackObjectByMaterial(BUILDING, "quartz_stairs", "クォーツの階段", Material.QUARTZ_STAIRS), + MineStackObjectByMaterial(BUILDING, "nether_brick_stairs", "ネザーレンガの階段", Material.NETHER_BRICK_STAIRS), + MineStackObjectByMaterial(BUILDING, "purpur_stairs", "プルプァの階段", Material.PURPUR_STAIRS), + MineStackObjectByMaterial(BUILDING, "cut_copper_stairs", "切り込み入りの銅の階段", Material.CUT_COPPER_STAIRS), + MineStackObjectByMaterial(BUILDING, "exposed_cut_copper_stairs", "風化した切り込み入りの銅の階段", Material.EXPOSED_CUT_COPPER_STAIRS), + MineStackObjectByMaterial(BUILDING, "weathered_cut_copper_stairs", "錆びた切り込み入りの銅の階段", Material.WEATHERED_CUT_COPPER_STAIRS), + MineStackObjectByMaterial(BUILDING, "oxidized_cut_copper_stairs", "酸化した切り込み入りの銅の階段", Material.OXIDIZED_CUT_COPPER_STAIRS), + MineStackObjectByMaterial(BUILDING, "waxed_cut_copper_stairs", "錆止めされた切り込み入りの銅の階段", Material.WAXED_CUT_COPPER_STAIRS), + MineStackObjectByMaterial(BUILDING, "waxed_exposed_cut_copper_stairs", "錆止めされた風化した切り込み入りの銅の階段", Material.WAXED_EXPOSED_CUT_COPPER_STAIRS), + MineStackObjectByMaterial(BUILDING, "waxed_weathered_cut_copper_stairs", "錆止めされた錆びた切り込み入りの銅の階段", Material.WAXED_WEATHERED_CUT_COPPER_STAIRS), + MineStackObjectByMaterial(BUILDING, "waxed_oxidized_cut_copper_stairs", "錆止めされた酸化した切り込み入りの銅の階段", Material.WAXED_OXIDIZED_CUT_COPPER_STAIRS), + MineStackObjectByMaterial(BUILDING, "crimson_stairs", "真紅の階段", Material.CRIMSON_STAIRS), + MineStackObjectByMaterial(BUILDING, "prismarine_stairs", "プリズマリンの階段", Material.PRISMARINE_STAIRS), + MineStackObjectByMaterial(BUILDING, "prismarine_brick_stairs", "プリズマリンレンガの階段", Material.PRISMARINE_BRICK_STAIRS), + MineStackObjectByMaterial(BUILDING, "dark_prismarine_stairs", "ダークプリズマリンの階段", Material.DARK_PRISMARINE_STAIRS), + MineStackObjectByMaterial(BUILDING, "polished_granite_stairs", "磨かれた花崗岩の階段", Material.POLISHED_GRANITE_STAIRS), + MineStackObjectByMaterial(BUILDING, "smooth_red_sandstone_stairs", "滑らかな赤い砂岩の階段", Material.SMOOTH_RED_SANDSTONE_STAIRS), + MineStackObjectByMaterial(BUILDING, "mossy_stone_brick_stairs", "苔むした石レンガの階段", Material.MOSSY_STONE_BRICK_STAIRS), + MineStackObjectByMaterial(BUILDING, "polished_diorite_stairs", "磨かれた閃緑岩の階段", Material.POLISHED_DIORITE_STAIRS), + MineStackObjectByMaterial(BUILDING, "mossy_cobblestone_stairs", "苔むした丸石の階段", Material.MOSSY_COBBLESTONE_STAIRS), + MineStackObjectByMaterial(BUILDING, "end_stone_brick_stairs", "エンドストーンレンガの階段", Material.END_STONE_BRICK_STAIRS), + MineStackObjectByMaterial(BUILDING, "stone_stairs", "石の階段", Material.STONE_STAIRS), + MineStackObjectByMaterial(BUILDING, "smooth_sandstone_stairs", "滑らかな砂岩の階段", Material.SMOOTH_SANDSTONE_STAIRS), + MineStackObjectByMaterial(BUILDING, "smooth_quartz_stairs", "滑らかなクォーツの階段", Material.SMOOTH_QUARTZ_STAIRS), + MineStackObjectByMaterial(BUILDING, "granite_stairs", "花崗岩の階段", Material.GRANITE_STAIRS), + MineStackObjectByMaterial(BUILDING, "andesite_stairs", "安山岩の階段", Material.ANDESITE_STAIRS), + MineStackObjectByMaterial(BUILDING, "red_nether_brick_stairs", "赤いネザーレンガの階段", Material.RED_NETHER_BRICK_STAIRS), + MineStackObjectByMaterial(BUILDING, "polished_andesite_stairs", "磨かれた安山岩の階段", Material.POLISHED_ANDESITE_STAIRS), + MineStackObjectByMaterial(BUILDING, "diorite_stairs", "閃緑岩の階段", Material.DIORITE_STAIRS), + MineStackObjectByMaterial(BUILDING, "cobbled_deepslate_stairs", "深層岩の丸石の階段", Material.COBBLED_DEEPSLATE_STAIRS), + MineStackObjectByMaterial(BUILDING, "polished_deepslate_stairs", "磨かれた深層岩の階段", Material.POLISHED_DEEPSLATE_STAIRS), + MineStackObjectByMaterial(BUILDING, "deepslate_brick_stairs", "深層岩レンガの階段", Material.DEEPSLATE_BRICK_STAIRS), + MineStackObjectByMaterial(BUILDING, "deepslate_tile_stairs", "深層岩タイルの階段", Material.DEEPSLATE_TILE_STAIRS), + MineStackObjectByMaterial(BUILDING, "blackstone_stairs", "ブラックストーンの階段", Material.BLACKSTONE_STAIRS), + MineStackObjectByMaterial(BUILDING, "polished_blackstone_stairs", "磨かれたブラックストーンの階段", Material.POLISHED_BLACKSTONE_STAIRS), + MineStackObjectByMaterial(BUILDING, "polished_blackstone_brick_stairs", "磨かれたブラックストーンレンガの階段", Material.POLISHED_BLACKSTONE_BRICK_STAIRS), + MineStackObjectByMaterial(BUILDING, "warped_stairs", "歪んだ階段", Material.WARPED_STAIRS), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "brick_wall", "レンガの塀", Material.BRICK_WALL), + List( + MineStackObjectByMaterial(BUILDING, "cobblestone_wall_0", "丸石の塀", Material.COBBLESTONE_WALL), + MineStackObjectByMaterial(BUILDING, "cobblestone_wall_1", "苔むした丸石の塀", Material.MOSSY_COBBLESTONE_WALL), + MineStackObjectByMaterial(BUILDING, "prismarine_wall", "プリズマリンの塀", Material.PRISMARINE_WALL), + MineStackObjectByMaterial(BUILDING, "red_sandstone_wall", "赤い砂岩の塀", Material.RED_SANDSTONE_WALL), + MineStackObjectByMaterial(BUILDING, "mossy_stone_brick_wall", "苔むした石レンガの塀", Material.MOSSY_STONE_BRICK_WALL), + MineStackObjectByMaterial(BUILDING, "granite_wall", "花崗岩の塀", Material.GRANITE_WALL), + MineStackObjectByMaterial(BUILDING, "stone_brick_wall", "石レンガの塀", Material.STONE_BRICK_WALL), + MineStackObjectByMaterial(BUILDING, "nether_brick_wall", "ネザーレンガの塀", Material.NETHER_BRICK_WALL), + MineStackObjectByMaterial(BUILDING, "andesite_wall", "安山岩の塀", Material.ANDESITE_WALL), + MineStackObjectByMaterial(BUILDING, "red_nether_brick_wall", "赤いネザーレンガの塀", Material.RED_NETHER_BRICK_WALL), + MineStackObjectByMaterial(BUILDING, "sandstone_wall", "砂岩の塀", Material.SANDSTONE_WALL), + MineStackObjectByMaterial(BUILDING, "end_stone_brick_wall", "エンドストーンレンガの塀", Material.END_STONE_BRICK_WALL), + MineStackObjectByMaterial(BUILDING, "diorite_wall", "閃緑岩の塀", Material.DIORITE_WALL), + MineStackObjectByMaterial(BUILDING, "blackstone_wall", "ブラックストーンの塀", Material.BLACKSTONE_WALL), + MineStackObjectByMaterial(BUILDING, "polished_blackstone_wall", "磨かれたブラックストーンの塀", Material.POLISHED_BLACKSTONE_WALL), + MineStackObjectByMaterial(BUILDING, "polished_blackstone_brick_wall", "磨かれたブラックストーンレンガの塀", Material.POLISHED_BLACKSTONE_BRICK_WALL), + MineStackObjectByMaterial(BUILDING, "cobbled_deepslate_wall", "深層岩の丸石の塀", Material.COBBLED_DEEPSLATE_WALL), + MineStackObjectByMaterial(BUILDING, "polished_deepslate_wall", "磨かれた深層岩の塀", Material.POLISHED_DEEPSLATE_WALL), + MineStackObjectByMaterial(BUILDING, "deepslate_brick_wall", "深層岩レンガの塀", Material.DEEPSLATE_BRICK_WALL), + MineStackObjectByMaterial(BUILDING, "deepslate_tile_wall", "深層岩タイルの塀", Material.DEEPSLATE_TILE_WALL), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "fence", "オークのフェンス", Material.OAK_FENCE), + List( + MineStackObjectByMaterial(BUILDING, "spruce_fence", "トウヒのフェンス", Material.SPRUCE_FENCE), + MineStackObjectByMaterial(BUILDING, "birch_fence", "シラカバのフェンス", Material.BIRCH_FENCE), + MineStackObjectByMaterial(BUILDING, "jungle_fence", "ジャングルのフェンス", Material.JUNGLE_FENCE), + MineStackObjectByMaterial(BUILDING, "acacia_fence", "アカシアのフェンス", Material.ACACIA_FENCE), + MineStackObjectByMaterial(BUILDING, "dark_oak_fence", "ダークオークのフェンス", Material.DARK_OAK_FENCE), + MineStackObjectByMaterial(BUILDING, "nether_brick_fence", "ネザーレンガのフェンス", Material.NETHER_BRICK_FENCE), + MineStackObjectByMaterial(BUILDING, "crimson_fence", "真紅のフェンス", Material.CRIMSON_FENCE), + MineStackObjectByMaterial(BUILDING, "warped_fence", "歪んだフェンス", Material.WARPED_FENCE), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "dead_tube_coral_fan", "死んだクダウチワサンゴ", Material.DEAD_TUBE_CORAL_FAN), + List( + MineStackObjectByMaterial(BUILDING, "dead_brain_coral_fan", "死んだノウウチワサンゴ", Material.DEAD_BRAIN_CORAL_FAN), + MineStackObjectByMaterial(BUILDING, "dead_bubble_coral_fan", "死んだミズタマウチワサンゴ", Material.DEAD_BUBBLE_CORAL_FAN), + MineStackObjectByMaterial(BUILDING, "dead_fire_coral_fan", "死んだミレポラウチワサンゴ", Material.DEAD_FIRE_CORAL_FAN), + MineStackObjectByMaterial(BUILDING, "dead_horn_coral_fan", "死んだシカツノウチワサンゴ", Material.DEAD_HORN_CORAL_FAN), + MineStackObjectByMaterial(BUILDING, "dead_brain_coral", "死んだノウサンゴ", Material.DEAD_BRAIN_CORAL), + MineStackObjectByMaterial(BUILDING, "dead_bubble_coral", "死んだミズタマサンゴ", Material.DEAD_BUBBLE_CORAL), + MineStackObjectByMaterial(BUILDING, "dead_fire_coral", "死んだミレポラサンゴ", Material.DEAD_FIRE_CORAL), + MineStackObjectByMaterial(BUILDING, "dead_horn_coral", "死んだシカツノサンゴ", Material.DEAD_HORN_CORAL), + MineStackObjectByMaterial(BUILDING, "dead_tube_coral", "死んだクダサンゴ", Material.DEAD_TUBE_CORAL), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "tube_coral_block", "クダサンゴブロック", Material.TUBE_CORAL_BLOCK), + List( + MineStackObjectByMaterial(BUILDING, "brain_coral_block", "ノウサンゴブロック", Material.BRAIN_CORAL_BLOCK), + MineStackObjectByMaterial(BUILDING, "bubble_coral_block", "ミズタマサンゴブロック", Material.BUBBLE_CORAL_BLOCK), + MineStackObjectByMaterial(BUILDING, "fire_coral_block", "ミレポラサンゴブロック", Material.FIRE_CORAL_BLOCK), + MineStackObjectByMaterial(BUILDING, "horn_coral_block", "シカツノサンゴブロック", Material.HORN_CORAL_BLOCK), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "dead_tube_coral_block", "死んだクダサンゴブロック", Material.DEAD_TUBE_CORAL_BLOCK), + List( + MineStackObjectByMaterial(BUILDING, "dead_brain_coral_block", "死んだノウサンゴブロック", Material.DEAD_BRAIN_CORAL_BLOCK), + MineStackObjectByMaterial(BUILDING, "dead_bubble_coral_block", "死んだミズタマサンゴブロック", Material.DEAD_BUBBLE_CORAL_BLOCK), + MineStackObjectByMaterial(BUILDING, "dead_fire_coral_block", "死んだミレポラサンゴブロック", Material.DEAD_FIRE_CORAL_BLOCK), + MineStackObjectByMaterial(BUILDING, "dead_horn_coral_block", "死んだシカツノサンゴブロック", Material.DEAD_HORN_CORAL_BLOCK), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "tube_coral", "クダサンゴ", Material.TUBE_CORAL), + List( + MineStackObjectByMaterial(BUILDING, "brain_coral", "ノウサンゴ", Material.BRAIN_CORAL), + MineStackObjectByMaterial(BUILDING, "bubble_coral", "ミズタマサンゴ", Material.BUBBLE_CORAL), + MineStackObjectByMaterial(BUILDING, "fire_coral", "ミレポラサンゴ", Material.FIRE_CORAL), + MineStackObjectByMaterial(BUILDING, "horn_coral", "シカツノサンゴ", Material.HORN_CORAL), + MineStackObjectByMaterial(BUILDING, "tube_coral_fan", "クダウチワサンゴ", Material.TUBE_CORAL_FAN), + MineStackObjectByMaterial(BUILDING, "brain_coral_fan", "ノウウチワサンゴ", Material.BRAIN_CORAL_FAN), + MineStackObjectByMaterial(BUILDING, "bubble_coral_fan", "ミズタマウチワサンゴ", Material.BUBBLE_CORAL_FAN), + MineStackObjectByMaterial(BUILDING, "fire_coral_fan", "ミレポラウチワサンゴ", Material.FIRE_CORAL_FAN), + MineStackObjectByMaterial(BUILDING, "horn_coral_fan", "シカツノウチワサンゴ", Material.HORN_CORAL_FAN), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "flower_banner_pattern", "旗の模様", Material.FLOWER_BANNER_PATTERN), + List( + MineStackObjectByMaterial(BUILDING, "creeper_banner_pattern", "旗の模様", Material.CREEPER_BANNER_PATTERN), + MineStackObjectByMaterial(BUILDING, "skull_banner_pattern", "旗の模様", Material.SKULL_BANNER_PATTERN), + MineStackObjectByMaterial(BUILDING, "mojang_banner_pattern", "旗の模様", Material.MOJANG_BANNER_PATTERN), + MineStackObjectByMaterial(BUILDING, "globe_banner_pattern", "旗の模様", Material.GLOBE_BANNER_PATTERN), + MineStackObjectByMaterial(BUILDING, "piglin_banner_pattern", "旗の模様", Material.PIGLIN_BANNER_PATTERN), ) ), - MineStackObjectWithColorVariants( - MineStackObjectByMaterial(BUILDING, "stained_clay", "白色のテラコッタ", Material.STAINED_CLAY, 0), - List( - MineStackObjectByMaterial(BUILDING, "stained_clay1", "橙色のテラコッタ", Material.STAINED_CLAY, 1), - MineStackObjectByMaterial(BUILDING, "stained_clay2", "赤紫色のテラコッタ", Material.STAINED_CLAY, 2), - MineStackObjectByMaterial(BUILDING, "stained_clay3", "空色のテラコッタ", Material.STAINED_CLAY, 3), - MineStackObjectByMaterial(BUILDING, "stained_clay4", "黄色のテラコッタ", Material.STAINED_CLAY, 4), - MineStackObjectByMaterial(BUILDING, "stained_clay5", "黄緑色のテラコッタ", Material.STAINED_CLAY, 5), - MineStackObjectByMaterial(BUILDING, "stained_clay6", "桃色のテラコッタ", Material.STAINED_CLAY, 6), - MineStackObjectByMaterial(BUILDING, "stained_clay7", "灰色のテラコッタ", Material.STAINED_CLAY, 7), - MineStackObjectByMaterial(BUILDING, "stained_clay8", "薄灰色のテラコッタ", Material.STAINED_CLAY, 8), - MineStackObjectByMaterial(BUILDING, "stained_clay9", "青緑色のテラコッタ", Material.STAINED_CLAY, 9), - MineStackObjectByMaterial(BUILDING, "stained_clay10", "紫色のテラコッタ", Material.STAINED_CLAY, 10), - MineStackObjectByMaterial(BUILDING, "stained_clay11", "青色のテラコッタ", Material.STAINED_CLAY, 11), - MineStackObjectByMaterial(BUILDING, "stained_clay12", "茶色のテラコッタ", Material.STAINED_CLAY, 12), - MineStackObjectByMaterial(BUILDING, "stained_clay13", "緑色のテラコッタ", Material.STAINED_CLAY, 13), - MineStackObjectByMaterial(BUILDING, "stained_clay14", "赤色のテラコッタ", Material.STAINED_CLAY, 14), - MineStackObjectByMaterial(BUILDING, "stained_clay15", "黒色のテラコッタ", Material.STAINED_CLAY, 15) + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "white_banner", "白色の旗", Material.WHITE_BANNER), + List( + MineStackObjectByMaterial(BUILDING, "orange_banner", "橙色の旗", Material.ORANGE_BANNER), + MineStackObjectByMaterial(BUILDING, "magenta_banner", "赤紫色の旗", Material.MAGENTA_BANNER), + MineStackObjectByMaterial(BUILDING, "light_blue_banner", "空色の旗", Material.LIGHT_BLUE_BANNER), + MineStackObjectByMaterial(BUILDING, "yellow_banner", "黄色の旗", Material.YELLOW_BANNER), + MineStackObjectByMaterial(BUILDING, "lime_banner", "黄緑色の旗", Material.LIME_BANNER), + MineStackObjectByMaterial(BUILDING, "pink_banner", "桃色の旗", Material.PINK_BANNER), + MineStackObjectByMaterial(BUILDING, "gray_banner", "灰色の旗", Material.GRAY_BANNER), + MineStackObjectByMaterial(BUILDING, "light_gray_banner", "薄灰色の旗", Material.LIGHT_GRAY_BANNER), + MineStackObjectByMaterial(BUILDING, "cyan_banner", "青緑色の旗", Material.CYAN_BANNER), + MineStackObjectByMaterial(BUILDING, "purple_banner", "紫色の旗", Material.PURPLE_BANNER), + MineStackObjectByMaterial(BUILDING, "blue_banner", "青色の旗", Material.BLUE_BANNER), + MineStackObjectByMaterial(BUILDING, "brown_banner", "茶色の旗", Material.BROWN_BANNER), + MineStackObjectByMaterial(BUILDING, "green_banner", "緑色の旗", Material.GREEN_BANNER), + MineStackObjectByMaterial(BUILDING, "red_banner", "赤色の旗", Material.RED_BANNER), + MineStackObjectByMaterial(BUILDING, "black_banner", "黒色の旗", Material.BLACK_BANNER), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "sign", "オークの看板", Material.OAK_SIGN), + List( + MineStackObjectByMaterial(BUILDING, "spruce_sign", "トウヒの看板", Material.SPRUCE_SIGN), + MineStackObjectByMaterial(BUILDING, "birch_sign", "シラカバの看板", Material.BIRCH_SIGN), + MineStackObjectByMaterial(BUILDING, "jungle_sign", "ジャングルの看板", Material.JUNGLE_SIGN), + MineStackObjectByMaterial(BUILDING, "acacia_sign", "アカシアの看板", Material.ACACIA_SIGN), + MineStackObjectByMaterial(BUILDING, "dark_oak_sign", "ダークオークの看板", Material.DARK_OAK_SIGN), + MineStackObjectByMaterial(BUILDING, "crimson_sign", "真紅の看板", Material.CRIMSON_SIGN), + MineStackObjectByMaterial(BUILDING, "warped_sign", "歪んだ看板", Material.WARPED_SIGN), ) ), - MineStackObjectWithColorVariants( - MineStackObjectByMaterial(BUILDING, "concrete", "白色のコンクリート", Material.CONCRETE, 0), - List( - MineStackObjectByMaterial(BUILDING, "concrete1", "橙色のコンクリート", Material.CONCRETE, 1), - MineStackObjectByMaterial(BUILDING, "concrete2", "赤紫色のコンクリート", Material.CONCRETE, 2), - MineStackObjectByMaterial(BUILDING, "concrete3", "空色のコンクリート", Material.CONCRETE, 3), - MineStackObjectByMaterial(BUILDING, "concrete4", "黄色のコンクリート", Material.CONCRETE, 4), - MineStackObjectByMaterial(BUILDING, "concrete5", "黄緑色のコンクリート", Material.CONCRETE, 5), - MineStackObjectByMaterial(BUILDING, "concrete6", "桃色のコンクリート", Material.CONCRETE, 6), - MineStackObjectByMaterial(BUILDING, "concrete7", "灰色のコンクリート", Material.CONCRETE, 7), - MineStackObjectByMaterial(BUILDING, "concrete8", "薄灰色のコンクリート", Material.CONCRETE, 8), - MineStackObjectByMaterial(BUILDING, "concrete9", "青緑色のコンクリート", Material.CONCRETE, 9), - MineStackObjectByMaterial(BUILDING, "concrete10", "紫色のコンクリート", Material.CONCRETE, 10), - MineStackObjectByMaterial(BUILDING, "concrete11", "青色のコンクリート", Material.CONCRETE, 11), - MineStackObjectByMaterial(BUILDING, "concrete12", "茶色のコンクリート", Material.CONCRETE, 12), - MineStackObjectByMaterial(BUILDING, "concrete13", "緑色のコンクリート", Material.CONCRETE, 13), - MineStackObjectByMaterial(BUILDING, "concrete14", "赤色のコンクリート", Material.CONCRETE, 14), - MineStackObjectByMaterial(BUILDING, "concrete15", "黒色のコンクリート", Material.CONCRETE, 15) + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "candle", "ろうそく", Material.CANDLE), + List( + MineStackObjectByMaterial(BUILDING, "white_candle", "白色のろうそく", Material.WHITE_CANDLE), + MineStackObjectByMaterial(BUILDING, "orange_candle", "橙色のろうそく", Material.ORANGE_CANDLE), + MineStackObjectByMaterial(BUILDING, "magenta_candle", "赤紫色のろうそく", Material.MAGENTA_CANDLE), + MineStackObjectByMaterial(BUILDING, "light_blue_candle", "空色のろうそく", Material.LIGHT_BLUE_CANDLE), + MineStackObjectByMaterial(BUILDING, "yellow_candle", "黄色のろうそく", Material.YELLOW_CANDLE), + MineStackObjectByMaterial(BUILDING, "lime_candle", "黄緑色のろうそく", Material.LIME_CANDLE), + MineStackObjectByMaterial(BUILDING, "pink_candle", "桃色のろうそく", Material.PINK_CANDLE), + MineStackObjectByMaterial(BUILDING, "gray_candle", "灰色のろうそく", Material.GRAY_CANDLE), + MineStackObjectByMaterial(BUILDING, "light_gray_candle", "薄灰色のろうそく", Material.LIGHT_GRAY_CANDLE), + MineStackObjectByMaterial(BUILDING, "cyan_candle", "青緑色のろうそく", Material.CYAN_CANDLE), + MineStackObjectByMaterial(BUILDING, "purple_candle", "紫色のろうそく", Material.PURPLE_CANDLE), + MineStackObjectByMaterial(BUILDING, "blue_candle", "青色のろうそく", Material.BLUE_CANDLE), + MineStackObjectByMaterial(BUILDING, "brown_candle", "茶色のろうそく", Material.BROWN_CANDLE), + MineStackObjectByMaterial(BUILDING, "green_candle", "緑色のろうそく", Material.GREEN_CANDLE), + MineStackObjectByMaterial(BUILDING, "red_candle", "赤色のろうそく", Material.RED_CANDLE), + MineStackObjectByMaterial(BUILDING, "black_candle", "黒色のろうそく", Material.BLACK_CANDLE), ) ), - MineStackObjectWithColorVariants( - MineStackObjectByMaterial(BUILDING, "concrete_powder", "白色のコンクリートパウダー", Material.CONCRETE_POWDER, 0), - List( - MineStackObjectByMaterial(BUILDING, "concrete_powder1", "橙色のコンクリートパウダー", Material.CONCRETE_POWDER, 1), - MineStackObjectByMaterial(BUILDING, "concrete_powder2", "赤紫色のコンクリートパウダー", Material.CONCRETE_POWDER, 2), - MineStackObjectByMaterial(BUILDING, "concrete_powder3", "空色のコンクリートパウダー", Material.CONCRETE_POWDER, 3), - MineStackObjectByMaterial(BUILDING, "concrete_powder4", "黄色のコンクリートパウダー", Material.CONCRETE_POWDER, 4), - MineStackObjectByMaterial(BUILDING, "concrete_powder5", "黄緑色のコンクリートパウダー", Material.CONCRETE_POWDER, 5), - MineStackObjectByMaterial(BUILDING, "concrete_powder6", "桃色のコンクリートパウダー", Material.CONCRETE_POWDER, 6), - MineStackObjectByMaterial(BUILDING, "concrete_powder7", "灰色のコンクリートパウダー", Material.CONCRETE_POWDER, 7), - MineStackObjectByMaterial(BUILDING, "concrete_powder8", "薄灰色のコンクリートパウダー", Material.CONCRETE_POWDER, 8), - MineStackObjectByMaterial(BUILDING, "concrete_powder9", "青緑色のコンクリートパウダー", Material.CONCRETE_POWDER, 9), - MineStackObjectByMaterial(BUILDING, "concrete_powder10", "紫色のコンクリートパウダー", Material.CONCRETE_POWDER, 10), - MineStackObjectByMaterial(BUILDING, "concrete_powder11", "青色のコンクリートパウダー", Material.CONCRETE_POWDER, 11), - MineStackObjectByMaterial(BUILDING, "concrete_powder12", "茶色のコンクリートパウダー", Material.CONCRETE_POWDER, 12), - MineStackObjectByMaterial(BUILDING, "concrete_powder13", "緑色のコンクリートパウダー", Material.CONCRETE_POWDER, 13), - MineStackObjectByMaterial(BUILDING, "concrete_powder14", "赤色のコンクリートパウダー", Material.CONCRETE_POWDER, 14), - MineStackObjectByMaterial(BUILDING, "concrete_powder15", "黒色のコンクリートパウダー", Material.CONCRETE_POWDER, 15) + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "bed", "白色のベッド", Material.WHITE_BED), + List( + MineStackObjectByMaterial(BUILDING, "bed_1", "橙色のベッド", Material.ORANGE_BED), + MineStackObjectByMaterial(BUILDING, "bed_2", "赤紫色のベッド", Material.MAGENTA_BED), + MineStackObjectByMaterial(BUILDING, "bed_3", "空色のベッド", Material.LIGHT_BLUE_BED), + MineStackObjectByMaterial(BUILDING, "bed_4", "黄色のベッド", Material.YELLOW_BED), + MineStackObjectByMaterial(BUILDING, "bed_5", "黄緑色のベッド", Material.LIME_BED), + MineStackObjectByMaterial(BUILDING, "bed_6", "桃色のベッド", Material.PINK_BED), + MineStackObjectByMaterial(BUILDING, "bed_7", "灰色のベッド", Material.GRAY_BED), + MineStackObjectByMaterial(BUILDING, "bed_8", "薄灰色のベッド", Material.LIGHT_GRAY_BED), + MineStackObjectByMaterial(BUILDING, "bed_9", "青緑色のベッド", Material.CYAN_BED), + MineStackObjectByMaterial(BUILDING, "bed_10", "紫色のベッド", Material.PURPLE_BED), + MineStackObjectByMaterial(BUILDING, "bed_11", "青色のベッド", Material.BLUE_BED), + MineStackObjectByMaterial(BUILDING, "bed_12", "茶色のベッド", Material.BROWN_BED), + MineStackObjectByMaterial(BUILDING, "bed_13", "緑色のベッド", Material.GREEN_BED), + MineStackObjectByMaterial(BUILDING, "bed_14", "赤色のベッド", Material.RED_BED), + MineStackObjectByMaterial(BUILDING, "bed_15", "黒色のベッド", Material.BLACK_BED) ) ), - MineStackObjectWithColorVariants( - MineStackObjectByMaterial(BUILDING, "white_glazed_terracotta", "白色の彩釉テラコッタ", Material.WHITE_GLAZED_TERRACOTTA, 0), - List( - MineStackObjectByMaterial(BUILDING, "orange_glazed_terracotta", "橙色の彩釉テラコッタ", Material.ORANGE_GLAZED_TERRACOTTA, 0), - MineStackObjectByMaterial(BUILDING, "magenta_glazed_terracotta", "赤紫色の彩釉テラコッタ", Material.MAGENTA_GLAZED_TERRACOTTA, 0), - MineStackObjectByMaterial(BUILDING, "light_blue_glazed_terracotta", "空色の彩釉テラコッタ", Material.LIGHT_BLUE_GLAZED_TERRACOTTA, 0), - MineStackObjectByMaterial(BUILDING, "yellow_glazed_terracotta", "黄色の彩釉テラコッタ", Material.YELLOW_GLAZED_TERRACOTTA, 0), - MineStackObjectByMaterial(BUILDING, "lime_glazed_terracotta", "黄緑色の彩釉テラコッタ", Material.LIME_GLAZED_TERRACOTTA, 0), - MineStackObjectByMaterial(BUILDING, "pink_glazed_terracotta", "桃色の彩釉テラコッタ", Material.PINK_GLAZED_TERRACOTTA, 0), - MineStackObjectByMaterial(BUILDING, "gray_glazed_terracotta", "灰色の彩釉テラコッタ", Material.GRAY_GLAZED_TERRACOTTA, 0), - MineStackObjectByMaterial(BUILDING, "silver_glazed_terracotta", "薄灰色の彩釉テラコッタ", Material.SILVER_GLAZED_TERRACOTTA, 0), - MineStackObjectByMaterial(BUILDING, "cyan_glazed_terracotta", "青緑色の彩釉テラコッタ", Material.CYAN_GLAZED_TERRACOTTA, 0), - MineStackObjectByMaterial(BUILDING, "purple_glazed_terracotta", "紫色の彩釉テラコッタ", Material.PURPLE_GLAZED_TERRACOTTA, 0), - MineStackObjectByMaterial(BUILDING, "blue_glazed_terracotta", "青色の彩釉テラコッタ", Material.BLUE_GLAZED_TERRACOTTA, 0), - MineStackObjectByMaterial(BUILDING, "brown_glazed_terracotta", "茶色の彩釉テラコッタ", Material.BROWN_GLAZED_TERRACOTTA, 0), - MineStackObjectByMaterial(BUILDING, "green_glazed_terracotta", "緑色の彩釉テラコッタ", Material.GREEN_GLAZED_TERRACOTTA, 0), - MineStackObjectByMaterial(BUILDING, "red_glazed_terracotta", "赤色の彩釉テラコッタ", Material.RED_GLAZED_TERRACOTTA, 0), - MineStackObjectByMaterial(BUILDING, "black_glazed_terracotta", "黒色の彩釉テラコッタ", Material.BLACK_GLAZED_TERRACOTTA, 0) + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "hard_clay", "テラコッタ", Material.TERRACOTTA), + List( + MineStackObjectByMaterial(BUILDING, "stained_clay", "白色のテラコッタ", Material.WHITE_TERRACOTTA), + MineStackObjectByMaterial(BUILDING, "stained_clay1", "橙色のテラコッタ", Material.ORANGE_TERRACOTTA), + MineStackObjectByMaterial(BUILDING, "stained_clay2", "赤紫色のテラコッタ", Material.MAGENTA_TERRACOTTA), + MineStackObjectByMaterial(BUILDING, "stained_clay3", "空色のテラコッタ", Material.LIGHT_BLUE_TERRACOTTA), + MineStackObjectByMaterial(BUILDING, "stained_clay4", "黄色のテラコッタ", Material.YELLOW_TERRACOTTA), + MineStackObjectByMaterial(BUILDING, "stained_clay5", "黄緑色のテラコッタ", Material.LIME_TERRACOTTA), + MineStackObjectByMaterial(BUILDING, "stained_clay6", "桃色のテラコッタ", Material.PINK_TERRACOTTA), + MineStackObjectByMaterial(BUILDING, "stained_clay7", "灰色のテラコッタ", Material.GRAY_TERRACOTTA), + MineStackObjectByMaterial(BUILDING, "stained_clay8", "薄灰色のテラコッタ", Material.LIGHT_GRAY_TERRACOTTA), + MineStackObjectByMaterial(BUILDING, "stained_clay9", "青緑色のテラコッタ", Material.CYAN_TERRACOTTA), + MineStackObjectByMaterial(BUILDING, "stained_clay10", "紫色のテラコッタ", Material.PURPLE_TERRACOTTA), + MineStackObjectByMaterial(BUILDING, "stained_clay11", "青色のテラコッタ", Material.BLUE_TERRACOTTA), + MineStackObjectByMaterial(BUILDING, "stained_clay12", "茶色のテラコッタ", Material.BROWN_TERRACOTTA), + MineStackObjectByMaterial(BUILDING, "stained_clay13", "緑色のテラコッタ", Material.GREEN_TERRACOTTA), + MineStackObjectByMaterial(BUILDING, "stained_clay14", "赤色のテラコッタ", Material.RED_TERRACOTTA), + MineStackObjectByMaterial(BUILDING, "stained_clay15", "黒色のテラコッタ", Material.BLACK_TERRACOTTA) ) ), - MineStackObjectWithColorVariants( - MineStackObjectByMaterial(BUILDING, "wool_0", "白色の羊毛", Material.WOOL, 0), - List( - MineStackObjectByMaterial(BUILDING, "wool_1", "橙色の羊毛", Material.WOOL, 1), - MineStackObjectByMaterial(BUILDING, "wool_2", "赤紫色の羊毛", Material.WOOL, 2), - MineStackObjectByMaterial(BUILDING, "wool_3", "空色の羊毛", Material.WOOL, 3), - MineStackObjectByMaterial(BUILDING, "wool_4", "黄色の羊毛", Material.WOOL, 4), - MineStackObjectByMaterial(BUILDING, "wool_5", "黄緑色の羊毛", Material.WOOL, 5), - MineStackObjectByMaterial(BUILDING, "wool_6", "桃色の羊毛", Material.WOOL, 6), - MineStackObjectByMaterial(BUILDING, "wool_7", "灰色の羊毛", Material.WOOL, 7), - MineStackObjectByMaterial(BUILDING, "wool_8", "薄灰色の羊毛", Material.WOOL, 8), - MineStackObjectByMaterial(BUILDING, "wool_9", "青緑色の羊毛", Material.WOOL, 9), - MineStackObjectByMaterial(BUILDING, "wool_10", "紫色の羊毛", Material.WOOL, 10), - MineStackObjectByMaterial(BUILDING, "wool_11", "青色の羊毛", Material.WOOL, 11), - MineStackObjectByMaterial(BUILDING, "wool_12", "茶色の羊毛", Material.WOOL, 12), - MineStackObjectByMaterial(BUILDING, "wool_13", "緑色の羊毛", Material.WOOL, 13), - MineStackObjectByMaterial(BUILDING, "wool_14", "赤色の羊毛", Material.WOOL, 14), - MineStackObjectByMaterial(BUILDING, "wool_15", "黒色の羊毛", Material.WOOL, 15) + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "concrete", "白色のコンクリート", Material.WHITE_CONCRETE), + List( + MineStackObjectByMaterial(BUILDING, "concrete1", "橙色のコンクリート", Material.ORANGE_CONCRETE), + MineStackObjectByMaterial(BUILDING, "concrete2", "赤紫色のコンクリート", Material.MAGENTA_CONCRETE), + MineStackObjectByMaterial(BUILDING, "concrete3", "空色のコンクリート", Material.LIGHT_BLUE_CONCRETE), + MineStackObjectByMaterial(BUILDING, "concrete4", "黄色のコンクリート", Material.YELLOW_CONCRETE), + MineStackObjectByMaterial(BUILDING, "concrete5", "黄緑色のコンクリート", Material.LIME_CONCRETE), + MineStackObjectByMaterial(BUILDING, "concrete6", "桃色のコンクリート", Material.PINK_CONCRETE), + MineStackObjectByMaterial(BUILDING, "concrete7", "灰色のコンクリート", Material.GRAY_CONCRETE), + MineStackObjectByMaterial(BUILDING, "concrete8", "薄灰色のコンクリート", Material.LIGHT_GRAY_CONCRETE), + MineStackObjectByMaterial(BUILDING, "concrete9", "青緑色のコンクリート", Material.CYAN_CONCRETE), + MineStackObjectByMaterial(BUILDING, "concrete10", "紫色のコンクリート", Material.PURPLE_CONCRETE), + MineStackObjectByMaterial(BUILDING, "concrete11", "青色のコンクリート", Material.BLUE_CONCRETE), + MineStackObjectByMaterial(BUILDING, "concrete12", "茶色のコンクリート", Material.BROWN_CONCRETE), + MineStackObjectByMaterial(BUILDING, "concrete13", "緑色のコンクリート", Material.GREEN_CONCRETE), + MineStackObjectByMaterial(BUILDING, "concrete14", "赤色のコンクリート", Material.RED_CONCRETE), + MineStackObjectByMaterial(BUILDING, "concrete15", "黒色のコンクリート", Material.BLACK_CONCRETE) ) ), - MineStackObjectWithColorVariants( - MineStackObjectByMaterial(BUILDING, "carpet_0", "白色のカーペット", Material.CARPET, 0), - List( - MineStackObjectByMaterial(BUILDING, "carpet_1", "橙色のカーペット", Material.CARPET, 1), - MineStackObjectByMaterial(BUILDING, "carpet_2", "赤紫色のカーペット", Material.CARPET, 2), - MineStackObjectByMaterial(BUILDING, "carpet_3", "空色のカーペット", Material.CARPET, 3), - MineStackObjectByMaterial(BUILDING, "carpet_4", "黄色のカーペット", Material.CARPET, 4), - MineStackObjectByMaterial(BUILDING, "carpet_5", "黄緑色のカーペット", Material.CARPET, 5), - MineStackObjectByMaterial(BUILDING, "carpet_6", "桃色のカーペット", Material.CARPET, 6), - MineStackObjectByMaterial(BUILDING, "carpet_7", "灰色のカーペット", Material.CARPET, 7), - MineStackObjectByMaterial(BUILDING, "carpet_8", "薄灰色のカーペット", Material.CARPET, 8), - MineStackObjectByMaterial(BUILDING, "carpet_9", "青緑色のカーペット", Material.CARPET, 9), - MineStackObjectByMaterial(BUILDING, "carpet_10", "紫色のカーペット", Material.CARPET, 10), - MineStackObjectByMaterial(BUILDING, "carpet_11", "青色のカーペット", Material.CARPET, 11), - MineStackObjectByMaterial(BUILDING, "carpet_12", "茶色のカーペット", Material.CARPET, 12), - MineStackObjectByMaterial(BUILDING, "carpet_13", "緑色のカーペット", Material.CARPET, 13), - MineStackObjectByMaterial(BUILDING, "carpet_14", "赤色のカーペット", Material.CARPET, 14), - MineStackObjectByMaterial(BUILDING, "carpet_15", "黒色のカーペット", Material.CARPET, 15) + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "concrete_powder", "白色のコンクリートパウダー", Material.WHITE_CONCRETE_POWDER), + List( + MineStackObjectByMaterial(BUILDING, "concrete_powder1", "橙色のコンクリートパウダー", Material.ORANGE_CONCRETE_POWDER), + MineStackObjectByMaterial(BUILDING, "concrete_powder2", "赤紫色のコンクリートパウダー", Material.MAGENTA_CONCRETE_POWDER), + MineStackObjectByMaterial(BUILDING, "concrete_powder3", "空色のコンクリートパウダー", Material.LIGHT_BLUE_CONCRETE_POWDER), + MineStackObjectByMaterial(BUILDING, "concrete_powder4", "黄色のコンクリートパウダー", Material.YELLOW_CONCRETE_POWDER), + MineStackObjectByMaterial(BUILDING, "concrete_powder5", "黄緑色のコンクリートパウダー", Material.LIME_CONCRETE_POWDER), + MineStackObjectByMaterial(BUILDING,"concrete_powder6","桃色のコンクリートパウダー",Material.PINK_CONCRETE_POWDER), + MineStackObjectByMaterial(BUILDING,"concrete_powder7","灰色のコンクリートパウダー",Material.GRAY_CONCRETE_POWDER), + MineStackObjectByMaterial(BUILDING,"concrete_powder8","薄灰色のコンクリートパウダー",Material.LIGHT_GRAY_CONCRETE_POWDER), + MineStackObjectByMaterial(BUILDING,"concrete_powder9","青緑色のコンクリートパウダー",Material.CYAN_CONCRETE_POWDER), + MineStackObjectByMaterial(BUILDING,"concrete_powder10","紫色のコンクリートパウダー",Material.PURPLE_CONCRETE_POWDER), + MineStackObjectByMaterial(BUILDING,"concrete_powder11","青色のコンクリートパウダー",Material.BLUE_CONCRETE_POWDER), + MineStackObjectByMaterial(BUILDING,"concrete_powder12","茶色のコンクリートパウダー",Material.BROWN_CONCRETE_POWDER), + MineStackObjectByMaterial(BUILDING,"concrete_powder13","緑色のコンクリートパウダー",Material.GREEN_CONCRETE_POWDER), + MineStackObjectByMaterial(BUILDING,"concrete_powder14","赤色のコンクリートパウダー",Material.RED_CONCRETE_POWDER), + MineStackObjectByMaterial(BUILDING,"concrete_powder15","黒色のコンクリートパウダー",Material.BLACK_CONCRETE_POWDER) ) ), - MineStackObjectWithColorVariants( - MineStackObjectByMaterial(BUILDING, "glass", "ガラス", Material.GLASS, 0), - List( - MineStackObjectByMaterial(BUILDING, "stained_glass_0", "白色の色付きガラス", Material.STAINED_GLASS, 0), - MineStackObjectByMaterial(BUILDING, "stained_glass_1", "橙色の色付きガラス", Material.STAINED_GLASS, 1), - MineStackObjectByMaterial(BUILDING, "stained_glass_2", "赤紫色の色付きガラス", Material.STAINED_GLASS, 2), - MineStackObjectByMaterial(BUILDING, "stained_glass_3", "空色の色付きガラス", Material.STAINED_GLASS, 3), - MineStackObjectByMaterial(BUILDING, "stained_glass_4", "黄色の色付きガラス", Material.STAINED_GLASS, 4), - MineStackObjectByMaterial(BUILDING, "stained_glass_5", "黄緑色の色付きガラス", Material.STAINED_GLASS, 5), - MineStackObjectByMaterial(BUILDING, "stained_glass_6", "桃色の色付きガラス", Material.STAINED_GLASS, 6), - MineStackObjectByMaterial(BUILDING, "stained_glass_7", "灰色の色付きガラス", Material.STAINED_GLASS, 7), - MineStackObjectByMaterial(BUILDING, "stained_glass_8", "薄灰色の色付きガラス", Material.STAINED_GLASS, 8), - MineStackObjectByMaterial(BUILDING, "stained_glass_9", "青緑色の色付きガラス", Material.STAINED_GLASS, 9), - MineStackObjectByMaterial(BUILDING, "stained_glass_10", "紫色の色付きガラス", Material.STAINED_GLASS, 10), - MineStackObjectByMaterial(BUILDING, "stained_glass_11", "青色の色付きガラス", Material.STAINED_GLASS, 11), - MineStackObjectByMaterial(BUILDING, "stained_glass_12", "茶色の色付きガラス", Material.STAINED_GLASS, 12), - MineStackObjectByMaterial(BUILDING, "stained_glass_13", "緑色の色付きガラス", Material.STAINED_GLASS, 13), - MineStackObjectByMaterial(BUILDING, "stained_glass_14", "赤色の色付きガラス", Material.STAINED_GLASS, 14), - MineStackObjectByMaterial(BUILDING, "stained_glass_15", "黒色の色付きガラス", Material.STAINED_GLASS, 15) + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING,"white_glazed_terracotta","白色の彩釉テラコッタ",Material.WHITE_GLAZED_TERRACOTTA), + List( + MineStackObjectByMaterial(BUILDING,"orange_glazed_terracotta","橙色の彩釉テラコッタ",Material.ORANGE_GLAZED_TERRACOTTA), + MineStackObjectByMaterial(BUILDING,"magenta_glazed_terracotta","赤紫色の彩釉テラコッタ",Material.MAGENTA_GLAZED_TERRACOTTA), + MineStackObjectByMaterial(BUILDING,"light_blue_glazed_terracotta","空色の彩釉テラコッタ",Material.LIGHT_BLUE_GLAZED_TERRACOTTA), + MineStackObjectByMaterial(BUILDING,"yellow_glazed_terracotta","黄色の彩釉テラコッタ",Material.YELLOW_GLAZED_TERRACOTTA), + MineStackObjectByMaterial(BUILDING,"lime_glazed_terracotta","黄緑色の彩釉テラコッタ",Material.LIME_GLAZED_TERRACOTTA), + MineStackObjectByMaterial(BUILDING,"pink_glazed_terracotta","桃色の彩釉テラコッタ",Material.PINK_GLAZED_TERRACOTTA), + MineStackObjectByMaterial(BUILDING,"gray_glazed_terracotta","灰色の彩釉テラコッタ",Material.GRAY_GLAZED_TERRACOTTA), + MineStackObjectByMaterial(BUILDING,"silver_glazed_terracotta","薄灰色の彩釉テラコッタ",Material.LIGHT_GRAY_GLAZED_TERRACOTTA), + MineStackObjectByMaterial(BUILDING,"cyan_glazed_terracotta","青緑色の彩釉テラコッタ",Material.CYAN_GLAZED_TERRACOTTA), + MineStackObjectByMaterial(BUILDING,"purple_glazed_terracotta","紫色の彩釉テラコッタ",Material.PURPLE_GLAZED_TERRACOTTA), + MineStackObjectByMaterial(BUILDING,"blue_glazed_terracotta","青色の彩釉テラコッタ",Material.BLUE_GLAZED_TERRACOTTA), + MineStackObjectByMaterial(BUILDING,"brown_glazed_terracotta","茶色の彩釉テラコッタ",Material.BROWN_GLAZED_TERRACOTTA), + MineStackObjectByMaterial(BUILDING,"green_glazed_terracotta","緑色の彩釉テラコッタ",Material.GREEN_GLAZED_TERRACOTTA), + MineStackObjectByMaterial(BUILDING,"red_glazed_terracotta","赤色の彩釉テラコッタ",Material.RED_GLAZED_TERRACOTTA), + MineStackObjectByMaterial(BUILDING,"black_glazed_terracotta","黒色の彩釉テラコッタ",Material.BLACK_GLAZED_TERRACOTTA) ) ), - MineStackObjectWithColorVariants( - MineStackObjectByMaterial(BUILDING, "glass_panel", "板ガラス", Material.THIN_GLASS, 0), - List( - MineStackObjectByMaterial(BUILDING, "glass_panel_0", "白色の色付きガラス板", Material.STAINED_GLASS_PANE, 0), - MineStackObjectByMaterial(BUILDING, "glass_panel_1", "橙色の色付きガラス板", Material.STAINED_GLASS_PANE, 1), - MineStackObjectByMaterial(BUILDING, "glass_panel_2", "赤紫色の色付きガラス板", Material.STAINED_GLASS_PANE, 2), - MineStackObjectByMaterial(BUILDING, "glass_panel_3", "空色の色付きガラス板", Material.STAINED_GLASS_PANE, 3), - MineStackObjectByMaterial(BUILDING, "glass_panel_4", "黄色の色付きガラス板", Material.STAINED_GLASS_PANE, 4), - MineStackObjectByMaterial(BUILDING, "glass_panel_5", "黄緑色の色付きガラス板", Material.STAINED_GLASS_PANE, 5), - MineStackObjectByMaterial(BUILDING, "glass_panel_6", "桃色の色付きガラス板", Material.STAINED_GLASS_PANE, 6), - MineStackObjectByMaterial(BUILDING, "glass_panel_7", "灰色の色付きガラス板", Material.STAINED_GLASS_PANE, 7), - MineStackObjectByMaterial(BUILDING, "glass_panel_8", "薄灰色の色付きガラス板", Material.STAINED_GLASS_PANE, 8), - MineStackObjectByMaterial(BUILDING, "glass_panel_9", "青緑色の色付きガラス板", Material.STAINED_GLASS_PANE, 9), - MineStackObjectByMaterial(BUILDING, "glass_panel_10", "紫色の色付きガラス板", Material.STAINED_GLASS_PANE, 10), - MineStackObjectByMaterial(BUILDING, "glass_panel_11", "青色の色付きガラス板", Material.STAINED_GLASS_PANE, 11), - MineStackObjectByMaterial(BUILDING, "glass_panel_12", "茶色の色付きガラス板", Material.STAINED_GLASS_PANE, 12), - MineStackObjectByMaterial(BUILDING, "glass_panel_13", "緑色の色付きガラス板", Material.STAINED_GLASS_PANE, 13), - MineStackObjectByMaterial(BUILDING, "glass_panel_14", "赤色の色付きガラス板", Material.STAINED_GLASS_PANE, 14), - MineStackObjectByMaterial(BUILDING, "glass_panel_15", "黒色の色付きガラス板", Material.STAINED_GLASS_PANE, 15) + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "wool_0", "白色の羊毛", Material.WHITE_WOOL), + List( + MineStackObjectByMaterial(BUILDING, "wool_1", "橙色の羊毛", Material.ORANGE_WOOL), + MineStackObjectByMaterial(BUILDING, "wool_2", "赤紫色の羊毛", Material.MAGENTA_WOOL), + MineStackObjectByMaterial(BUILDING, "wool_3", "空色の羊毛", Material.LIGHT_BLUE_WOOL), + MineStackObjectByMaterial(BUILDING, "wool_4", "黄色の羊毛", Material.YELLOW_WOOL), + MineStackObjectByMaterial(BUILDING, "wool_5", "黄緑色の羊毛", Material.LIME_WOOL), + MineStackObjectByMaterial(BUILDING, "wool_6", "桃色の羊毛", Material.PINK_WOOL), + MineStackObjectByMaterial(BUILDING, "wool_7", "灰色の羊毛", Material.GRAY_WOOL), + MineStackObjectByMaterial(BUILDING, "wool_8", "薄灰色の羊毛", Material.LIGHT_GRAY_WOOL), + MineStackObjectByMaterial(BUILDING, "wool_9", "青緑色の羊毛", Material.CYAN_WOOL), + MineStackObjectByMaterial(BUILDING, "wool_10", "紫色の羊毛", Material.PURPLE_WOOL), + MineStackObjectByMaterial(BUILDING, "wool_11", "青色の羊毛", Material.BLUE_WOOL), + MineStackObjectByMaterial(BUILDING, "wool_12", "茶色の羊毛", Material.BROWN_WOOL), + MineStackObjectByMaterial(BUILDING, "wool_13", "緑色の羊毛", Material.GREEN_WOOL), + MineStackObjectByMaterial(BUILDING, "wool_14", "赤色の羊毛", Material.RED_WOOL), + MineStackObjectByMaterial(BUILDING, "wool_15", "黒色の羊毛", Material.BLACK_WOOL) ) ), - MineStackObjectWithColorVariants( - MineStackObjectByMaterial(BUILDING, "dye_1", "赤色の染料", Material.INK_SACK, 1), - List( - MineStackObjectByMaterial(BUILDING, "dye_2", "緑色の染料", Material.INK_SACK, 2), - MineStackObjectByMaterial(BUILDING, "dye_5", "紫色の染料", Material.INK_SACK, 5), - MineStackObjectByMaterial(BUILDING, "dye_6", "青緑色の染料", Material.INK_SACK, 6), - MineStackObjectByMaterial(BUILDING, "dye_7", "薄灰色の染料", Material.INK_SACK, 7), - MineStackObjectByMaterial(BUILDING, "dye_8", "灰色の染料", Material.INK_SACK, 8), - MineStackObjectByMaterial(BUILDING, "dye_9", "桃色の染料", Material.INK_SACK, 9), - MineStackObjectByMaterial(BUILDING, "dye_10", "黄緑色の染料", Material.INK_SACK, 10), - MineStackObjectByMaterial(BUILDING, "dye_11", "黄色の染料", Material.INK_SACK, 11), - MineStackObjectByMaterial(BUILDING, "dye_12", "空色の染料", Material.INK_SACK, 12), - MineStackObjectByMaterial(BUILDING, "dye_13", "赤紫色の染料", Material.INK_SACK, 13), - MineStackObjectByMaterial(BUILDING, "dye_14", "橙色の染料", Material.INK_SACK, 14), - MineStackObjectByMaterial(BUILDING, "dye_15", "骨粉", Material.INK_SACK, 15), - MineStackObjectByMaterial(BUILDING, "ink_sack0", "イカスミ", Material.INK_SACK, 0) + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "carpet_0", "白色のカーペット", Material.WHITE_CARPET), + List( + MineStackObjectByMaterial(BUILDING, "carpet_1", "橙色のカーペット", Material.ORANGE_CARPET), + MineStackObjectByMaterial(BUILDING, "carpet_2", "赤紫色のカーペット", Material.MAGENTA_CARPET), + MineStackObjectByMaterial(BUILDING, "carpet_3", "空色のカーペット", Material.LIGHT_BLUE_CARPET), + MineStackObjectByMaterial(BUILDING, "carpet_4", "黄色のカーペット", Material.YELLOW_CARPET), + MineStackObjectByMaterial(BUILDING, "carpet_5", "黄緑色のカーペット", Material.LIME_CARPET), + MineStackObjectByMaterial(BUILDING, "carpet_6", "桃色のカーペット", Material.PINK_CARPET), + MineStackObjectByMaterial(BUILDING, "carpet_7", "灰色のカーペット", Material.GRAY_CARPET), + MineStackObjectByMaterial(BUILDING, "carpet_8", "薄灰色のカーペット", Material.LIGHT_GRAY_CARPET), + MineStackObjectByMaterial(BUILDING, "carpet_9", "青緑色のカーペット", Material.CYAN_CARPET), + MineStackObjectByMaterial(BUILDING, "carpet_10", "紫色のカーペット", Material.PURPLE_CARPET), + MineStackObjectByMaterial(BUILDING, "carpet_11", "青色のカーペット", Material.BLUE_CARPET), + MineStackObjectByMaterial(BUILDING, "carpet_12", "茶色のカーペット", Material.BROWN_CARPET), + MineStackObjectByMaterial(BUILDING, "carpet_13", "緑色のカーペット", Material.GREEN_CARPET), + MineStackObjectByMaterial(BUILDING, "carpet_14", "赤色のカーペット", Material.RED_CARPET), + MineStackObjectByMaterial(BUILDING, "carpet_15", "黒色のカーペット", Material.BLACK_CARPET) + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "glass", "ガラス",Material.GLASS), + List( + MineStackObjectByMaterial(BUILDING, "stained_glass_0", "白色の色付きガラス", Material.WHITE_STAINED_GLASS), + MineStackObjectByMaterial(BUILDING, "stained_glass_1", "橙色の色付きガラス", Material.ORANGE_STAINED_GLASS), + MineStackObjectByMaterial(BUILDING, "stained_glass_2", "赤紫色の色付きガラス", Material.MAGENTA_STAINED_GLASS), + MineStackObjectByMaterial(BUILDING, "stained_glass_3", "空色の色付きガラス", Material.LIGHT_BLUE_STAINED_GLASS), + MineStackObjectByMaterial(BUILDING, "stained_glass_4", "黄色の色付きガラス", Material.YELLOW_STAINED_GLASS), + MineStackObjectByMaterial(BUILDING, "stained_glass_5", "黄緑色の色付きガラス", Material.LIME_STAINED_GLASS), + MineStackObjectByMaterial(BUILDING, "stained_glass_6", "桃色の色付きガラス", Material.PINK_STAINED_GLASS), + MineStackObjectByMaterial(BUILDING, "stained_glass_7", "灰色の色付きガラス", Material.GRAY_STAINED_GLASS), + MineStackObjectByMaterial(BUILDING, "stained_glass_8", "薄灰色の色付きガラス", Material.LIGHT_GRAY_STAINED_GLASS), + MineStackObjectByMaterial(BUILDING, "stained_glass_9", "青緑色の色付きガラス", Material.CYAN_STAINED_GLASS), + MineStackObjectByMaterial(BUILDING, "stained_glass_10", "紫色の色付きガラス", Material.PURPLE_STAINED_GLASS), + MineStackObjectByMaterial(BUILDING, "stained_glass_11", "青色の色付きガラス", Material.BLUE_STAINED_GLASS), + MineStackObjectByMaterial(BUILDING, "stained_glass_12", "茶色の色付きガラス", Material.BROWN_STAINED_GLASS), + MineStackObjectByMaterial(BUILDING, "stained_glass_13", "緑色の色付きガラス", Material.GREEN_STAINED_GLASS), + MineStackObjectByMaterial(BUILDING, "stained_glass_14", "赤色の色付きガラス", Material.RED_STAINED_GLASS), + MineStackObjectByMaterial(BUILDING, "stained_glass_15", "黒色の色付きガラス", Material.BLACK_STAINED_GLASS) + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "glass_panel", "ガラス板", Material.GLASS_PANE), + List( + MineStackObjectByMaterial(BUILDING,"glass_panel_0","白色の色付きガラス板",Material.WHITE_STAINED_GLASS_PANE), + MineStackObjectByMaterial(BUILDING,"glass_panel_1","橙色の色付きガラス板",Material.ORANGE_STAINED_GLASS_PANE), + MineStackObjectByMaterial(BUILDING,"glass_panel_2","赤紫色の色付きガラス板",Material.MAGENTA_STAINED_GLASS_PANE), + MineStackObjectByMaterial(BUILDING,"glass_panel_3","空色の色付きガラス板",Material.LIGHT_BLUE_STAINED_GLASS_PANE), + MineStackObjectByMaterial(BUILDING,"glass_panel_4","黄色の色付きガラス板",Material.YELLOW_STAINED_GLASS_PANE), + MineStackObjectByMaterial(BUILDING,"glass_panel_5","黄緑色の色付きガラス板",Material.LIME_STAINED_GLASS_PANE), + MineStackObjectByMaterial(BUILDING,"glass_panel_6","桃色の色付きガラス板",Material.PINK_STAINED_GLASS_PANE), + MineStackObjectByMaterial(BUILDING,"glass_panel_7","灰色の色付きガラス板",Material.GRAY_STAINED_GLASS_PANE), + MineStackObjectByMaterial(BUILDING,"glass_panel_8","薄灰色の色付きガラス板",Material.LIGHT_GRAY_STAINED_GLASS_PANE), + MineStackObjectByMaterial(BUILDING,"glass_panel_9","青緑色の色付きガラス板",Material.CYAN_STAINED_GLASS_PANE), + MineStackObjectByMaterial(BUILDING,"glass_panel_10","紫色の色付きガラス板",Material.PURPLE_STAINED_GLASS_PANE), + MineStackObjectByMaterial(BUILDING,"glass_panel_11","青色の色付きガラス板",Material.BLUE_STAINED_GLASS_PANE), + MineStackObjectByMaterial(BUILDING,"glass_panel_12","茶色の色付きガラス板",Material.BROWN_STAINED_GLASS_PANE), + MineStackObjectByMaterial(BUILDING,"glass_panel_13","緑色の色付きガラス板",Material.GREEN_STAINED_GLASS_PANE), + MineStackObjectByMaterial(BUILDING,"glass_panel_14","赤色の色付きガラス板",Material.RED_STAINED_GLASS_PANE), + MineStackObjectByMaterial(BUILDING,"glass_panel_15","黒色の色付きガラス板",Material.BLACK_STAINED_GLASS_PANE) + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(BUILDING, "dye_1", "赤色の染料", Material.RED_DYE), + List( + MineStackObjectByMaterial(BUILDING, "dye_2", "緑色の染料", Material.GREEN_DYE), + MineStackObjectByMaterial(BUILDING, "dye_5", "紫色の染料", Material.PURPLE_DYE), + MineStackObjectByMaterial(BUILDING, "dye_6", "青緑色の染料", Material.CYAN_DYE), + MineStackObjectByMaterial(BUILDING, "dye_7", "薄灰色の染料", Material.LIGHT_GRAY_DYE), + MineStackObjectByMaterial(BUILDING, "dye_8", "灰色の染料", Material.GRAY_DYE), + MineStackObjectByMaterial(BUILDING, "dye_9", "桃色の染料", Material.PINK_DYE), + MineStackObjectByMaterial(BUILDING, "dye_10", "黄緑色の染料", Material.LIME_DYE), + MineStackObjectByMaterial(BUILDING, "dye_11", "黄色の染料", Material.YELLOW_DYE), + MineStackObjectByMaterial(BUILDING, "dye_12", "空色の染料", Material.LIGHT_BLUE_DYE), + MineStackObjectByMaterial(BUILDING, "dye_13", "赤紫色の染料", Material.MAGENTA_DYE), + MineStackObjectByMaterial(BUILDING, "dye_14", "橙色の染料", Material.ORANGE_DYE), + MineStackObjectByMaterial(BUILDING, "white_dye", "白色の染料", Material.WHITE_DYE), + MineStackObjectByMaterial(BUILDING, "blue_dye", "青色の染料", Material.BLUE_DYE), + MineStackObjectByMaterial(BUILDING, "brown_dye", "茶色の染料", Material.BROWN_DYE), + MineStackObjectByMaterial(BUILDING, "black_dye", "黒色の染料", Material.BLACK_DYE), ) ) ) // レッドストーン系ブロック private val minestacklistrs: List[MineStackObjectGroup[ItemStack]] = leftElems( - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "redstone", "レッドストーン", Material.REDSTONE, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "stone_button", "石のボタン", Material.STONE_BUTTON, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "wood_button", "木のボタン", Material.WOOD_BUTTON, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "stone_plate", "石の感圧版", Material.STONE_PLATE, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "wood_plate", "木の感圧版", Material.WOOD_PLATE, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "fence_gate", "オークのフェンスゲート", Material.FENCE_GATE, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "spruce_fence_gate", "マツのフェンスゲート", Material.SPRUCE_FENCE_GATE, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "birch_fence_gate", "シラカバのフェンスゲート", Material.BIRCH_FENCE_GATE, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "jungle_fence_gate", "ジャングルのフェンスゲート", Material.JUNGLE_FENCE_GATE, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "dark_oak_fence_gate", "ダークオークのフェンスゲート", Material.DARK_OAK_FENCE_GATE, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "acacia_fence_gate", "アカシアのフェンスゲート", Material.ACACIA_FENCE_GATE, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "redstone_block", "レッドストーンブロック", Material.REDSTONE_BLOCK, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "lever", "レバー", Material.LEVER, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "redstone_torch_on", "レッドストーントーチ", Material.REDSTONE_TORCH_ON, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "trap_door", "木のトラップドア", Material.TRAP_DOOR, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "iron_trapdoor", "鉄のトラップドア", Material.IRON_TRAPDOOR, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "gold_plate", "重量感圧版 (軽) ", Material.GOLD_PLATE, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "iron_plate", "重量感圧版 (重) ", Material.IRON_PLATE, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "wood_door", "オークのドア", Material.WOOD_DOOR, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "spruce_door_item", "マツのドア", Material.SPRUCE_DOOR_ITEM, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "birch_door_item", "シラカバのドア", Material.BIRCH_DOOR_ITEM, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "jungle_door_item", "ジャングルのドア", Material.JUNGLE_DOOR_ITEM, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "acacia_door_item", "アカシアのドア", Material.ACACIA_DOOR_ITEM, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "dark_oak_door_item", "ダークオークのドア", Material.DARK_OAK_DOOR_ITEM, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "note_block", "音符ブロック", Material.NOTE_BLOCK, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "redstone_lamp_off", "レッドストーンランプ", Material.REDSTONE_LAMP_OFF, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "tripwire_hook", "トリップワイヤーフック", Material.TRIPWIRE_HOOK, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "dropper", "ドロッパー", Material.DROPPER, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "piston_sticky_base", "粘着ピストン", Material.PISTON_STICKY_BASE, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "piston_base", "ピストン", Material.PISTON_BASE, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "tnt", "TNT", Material.TNT, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "trapped_chest", "トラップチェスト", Material.TRAPPED_CHEST, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "daylight_detector", "日照センサー", Material.DAYLIGHT_DETECTOR, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "iron_door", "鉄のドア", Material.IRON_DOOR, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "diode", "レッドストーンリピーター", Material.DIODE, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "dispenser", "ディスペンサー", Material.DISPENSER, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "hopper", "ホッパー", Material.HOPPER, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "redstone_comparator", "レッドストーンコンパレーター", Material.REDSTONE_COMPARATOR, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "powered_rail", "パワードレール", Material.POWERED_RAIL, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "detector_rail", "ディテクターレール", Material.DETECTOR_RAIL, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "activator_rail", "アクティベーターレール", Material.ACTIVATOR_RAIL, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "boat", "オークのボート", Material.BOAT, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "spruce_boat", "マツのボート", Material.BOAT_SPRUCE, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "birch_boat", "シラカバのボート", Material.BOAT_BIRCH, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "jungle_boat", "ジャングルのボート", Material.BOAT_JUNGLE, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "acacia_boat", "アカシアのボート", Material.BOAT_ACACIA, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "dark_oak_boat", "ダークオークのボート", Material.BOAT_DARK_OAK, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "saddle", "サドル", Material.SADDLE, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "minecart", "トロッコ", Material.MINECART, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "chest_minecart", "チェスト付きトロッコ", Material.STORAGE_MINECART, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "furnace_minecart", "かまど付きトロッコ", Material.POWERED_MINECART, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "hopper_minecart", "ホッパー付きトロッコ", Material.HOPPER_MINECART, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "iron_horse_armor", "鉄の馬鎧", Material.IRON_BARDING, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "golden_horse_armor", "金の馬鎧", Material.GOLD_BARDING, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "diamond_horse_armor", "ダイヤの馬鎧", Material.DIAMOND_BARDING, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "record_13", "レコード", Material.GOLD_RECORD, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "record_cat", "レコード", Material.GREEN_RECORD, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "record_blocks", "レコード", Material.RECORD_3, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "record_chirp", "レコード", Material.RECORD_4, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "record_far", "レコード", Material.RECORD_5, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "record_mall", "レコード", Material.RECORD_6, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "record_mellohi", "レコード", Material.RECORD_7, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "record_stal", "レコード", Material.RECORD_8, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "record_strad", "レコード", Material.RECORD_9, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "record_ward", "レコード", Material.RECORD_10, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "record_11", "レコード", Material.RECORD_11, 0), - MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "record_wait", "レコード", Material.RECORD_12, 0) + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"redstone","レッドストーンダスト",Material.REDSTONE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"redstone_block","レッドストーンブロック",Material.REDSTONE_BLOCK), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "lever", "レバー", Material.LEVER), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"redstone_torch_on","レッドストーントーチ",Material.REDSTONE_TORCH), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"note_block","音符ブロック",Material.NOTE_BLOCK), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"redstone_lamp_off","レッドストーンランプ",Material.REDSTONE_LAMP), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"tripwire_hook","トリップワイヤーフック",Material.TRIPWIRE_HOOK), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "dropper", "ドロッパー", Material.DROPPER), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"piston_sticky_base","粘着ピストン",Material.STICKY_PISTON), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"piston_base","ピストン",Material.PISTON), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "tnt", "TNT", Material.TNT), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"trapped_chest","トラップチェスト",Material.TRAPPED_CHEST), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"daylight_detector","日照センサー",Material.DAYLIGHT_DETECTOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"diode","レッドストーンリピーター",Material.REPEATER), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"dispenser","ディスペンサー",Material.DISPENSER), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "hopper", "ホッパー", Material.HOPPER), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"redstone_comparator","レッドストーンコンパレーター",Material.COMPARATOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"powered_rail","パワードレール",Material.POWERED_RAIL), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"detector_rail","ディテクターレール",Material.DETECTOR_RAIL), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"activator_rail","アクティベーターレール",Material.ACTIVATOR_RAIL), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "saddle", "鞍", Material.SADDLE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "minecart", "トロッコ", Material.MINECART), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"chest_minecart","チェスト付きトロッコ",Material.CHEST_MINECART), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"furnace_minecart","かまど付きトロッコ",Material.FURNACE_MINECART), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"hopper_minecart","ホッパー付きトロッコ",Material.HOPPER_MINECART), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"iron_horse_armor","鉄の馬鎧",Material.IRON_HORSE_ARMOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"golden_horse_armor","金の馬鎧",Material.GOLDEN_HORSE_ARMOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"diamond_horse_armor","ダイヤモンドの馬鎧",Material.DIAMOND_HORSE_ARMOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "lectern", "書見台", Material.LECTERN), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "target", "的", Material.TARGET), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "lightning_rod", "避雷針", Material.LIGHTNING_ROD), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "clock", "時計", Material.CLOCK), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "spyglass", "望遠鏡", Material.SPYGLASS), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "leather_horse_armor", "革の馬鎧", Material.LEATHER_HORSE_ARMOR), + ) ++ rightElems( + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"wood_button","オークのボタン",Material.OAK_BUTTON), + List( + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"stone_button","石のボタン",Material.STONE_BUTTON), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "spruce_button", "トウヒのボタン", Material.SPRUCE_BUTTON), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "birch_button", "シラカバのボタン", Material.BIRCH_BUTTON), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "jungle_button", "ジャングルのボタン", Material.JUNGLE_BUTTON), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "acacia_button", "アカシアのボタン", Material.ACACIA_BUTTON), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "dark_oak_button", "ダークオークのボタン", Material.DARK_OAK_BUTTON), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "polished_blackstone_button", "磨かれたブラックストーンのボタン", Material.POLISHED_BLACKSTONE_BUTTON), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "crimson_button", "真紅のボタン", Material.CRIMSON_BUTTON), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "warped_button", "歪んだボタン", Material.WARPED_BUTTON), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"wood_door","オークのドア",Material.OAK_DOOR), + List( + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"spruce_door_item","トウヒのドア",Material.SPRUCE_DOOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"birch_door_item","シラカバのドア",Material.BIRCH_DOOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"jungle_door_item","ジャングルのドア",Material.JUNGLE_DOOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"acacia_door_item","アカシアのドア",Material.ACACIA_DOOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"dark_oak_door_item","ダークオークのドア",Material.DARK_OAK_DOOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"iron_door","鉄のドア",Material.IRON_DOOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "crimson_door", "真紅のドア", Material.CRIMSON_DOOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "warped_door", "歪んだドア", Material.WARPED_DOOR), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"trap_door","オークのトラップドア",Material.OAK_TRAPDOOR), + List( + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"iron_trapdoor","鉄のトラップドア",Material.IRON_TRAPDOOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "spruce_trapdoor", "トウヒのトラップドア", Material.SPRUCE_TRAPDOOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "birch_trapdoor", "シラカバのトラップドア", Material.BIRCH_TRAPDOOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "jungle_trapdoor", "ジャングルのトラップドア", Material.JUNGLE_TRAPDOOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "acacia_trapdoor", "アカシアのトラップドア", Material.ACACIA_TRAPDOOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "dark_oak_trapdoor", "ダークオークのトラップドア", Material.DARK_OAK_TRAPDOOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "crimson_trapdoor", "真紅のトラップドア", Material.CRIMSON_TRAPDOOR), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "warped_trapdoor", "歪んだトラップドア", Material.WARPED_TRAPDOOR), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"fence_gate","オークのフェンスゲート",Material.OAK_FENCE_GATE), + List( + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"spruce_fence_gate","トウヒのフェンスゲート",Material.SPRUCE_FENCE_GATE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"birch_fence_gate","シラカバのフェンスゲート",Material.BIRCH_FENCE_GATE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"jungle_fence_gate","ジャングルのフェンスゲート",Material.JUNGLE_FENCE_GATE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"dark_oak_fence_gate","ダークオークのフェンスゲート",Material.DARK_OAK_FENCE_GATE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"acacia_fence_gate","アカシアのフェンスゲート",Material.ACACIA_FENCE_GATE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "crimson_fence_gate", "真紅のフェンスゲート", Material.CRIMSON_FENCE_GATE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "warped_fence_gate", "歪んだフェンスゲート", Material.WARPED_FENCE_GATE), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"wood_plate","オークの感圧板",Material.OAK_PRESSURE_PLATE), + List( + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"stone_plate","石の感圧板",Material.STONE_PRESSURE_PLATE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "polished_blackstone_pressure_plate", "磨かれたブラックストーンの感圧板", Material.POLISHED_BLACKSTONE_PRESSURE_PLATE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "spruce_pressure_plate", "トウヒの感圧板", Material.SPRUCE_PRESSURE_PLATE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "birch_pressure_plate", "シラカバの感圧板", Material.BIRCH_PRESSURE_PLATE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "jungle_pressure_plate", "ジャングルの感圧板", Material.JUNGLE_PRESSURE_PLATE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "acacia_pressure_plate", "アカシアの感圧板", Material.ACACIA_PRESSURE_PLATE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "dark_oak_pressure_plate", "ダークオークの感圧板", Material.DARK_OAK_PRESSURE_PLATE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "crimson_pressure_plate", "真紅の感圧板", Material.CRIMSON_PRESSURE_PLATE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "warped_pressure_plate", "歪んだ感圧板", Material.WARPED_PRESSURE_PLATE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"gold_plate","軽量用感圧板",Material.LIGHT_WEIGHTED_PRESSURE_PLATE), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"iron_plate","重量用感圧板",Material.HEAVY_WEIGHTED_PRESSURE_PLATE), + ) + ), + MineStackObjectWithKindVariants( + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION, "boat", "オークのボート", Material.OAK_BOAT), + List( + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"spruce_boat","トウヒのボート",Material.SPRUCE_BOAT), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"birch_boat","シラカバのボート",Material.BIRCH_BOAT), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"jungle_boat","ジャングルのボート",Material.JUNGLE_BOAT), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"acacia_boat","アカシアのボート",Material.ACACIA_BOAT), + MineStackObjectByMaterial(REDSTONE_AND_TRANSPORTATION,"dark_oak_boat","ダークオークのボート",Material.DARK_OAK_BOAT), + ) + ) ) /** @@ -597,7 +1096,7 @@ class BukkitMineStackObjectList[F[_]: Sync]( */ private val minestackBuiltinGachaPrizes: List[MineStackObjectGroup[ItemStack]] = leftElems( MineStackObjectByItemStack(GACHA_PRIZES, "gachaimo", None, hasNameLore = true, gachaPrizeAPI.staticGachaPrizeFactory.gachaRingo), - MineStackObjectByItemStack(GACHA_PRIZES, "exp_bottle", Some("エンチャントの瓶"), hasNameLore = false, new ItemStack(Material.EXP_BOTTLE, 1)) + MineStackObjectByItemStack(GACHA_PRIZES, "exp_bottle", Some("エンチャントの瓶"), hasNameLore = false, new ItemStack(Material.EXPERIENCE_BOTTLE)) ) // @formatter:on @@ -640,7 +1139,7 @@ class BukkitMineStackObjectList[F[_]: Sync]( override def allMineStackObjects: F[Vector[MineStackObject[ItemStack]]] = allMineStackGroups.map(_.flatMap { case Left(mineStackObject: MineStackObject[ItemStack]) => List(mineStackObject) - case Right(group) => List(group.representative) ++ group.coloredVariants + case Right(group) => List(group.representative) ++ group.kindVariants }.toVector) override def getAllObjectGroupsInCategory( @@ -656,37 +1155,36 @@ class BukkitMineStackObjectList[F[_]: Sync]( allMineStackGroups.map(_.filter { group => categoryOf(group) == category }) } - override def findBySignedItemStack( - itemStack: ItemStack, + override def findBySignedItemStacks( + itemStacks: Vector[ItemStack], player: Player - ): F[Option[MineStackObject[ItemStack]]] = for { - gachaPrizes <- gachaPrizeAPI.gachaPrizesWhenGachaEventsIsNotHolding - canBeSignedAsGachaPrize = gachaPrizeAPI.canBeSignedAsGachaPrize - foundGachaPrizeOpt = gachaPrizes.find { gachaPrize => - if (gachaPrize.signOwner) { - gachaPrize - .materializeWithOwnerSignature(player.getName)(canBeSignedAsGachaPrize) - .isSimilar(itemStack) - } else { - gachaPrize.itemStack.isSimilar(itemStack) + ): F[Vector[(ItemStack, Option[MineStackObject[ItemStack]])]] = { + for { + gachaPrizes <- gachaPrizeAPI.gachaPrizesWhenGachaEventsIsNotHolding + mineStackObjects <- allMineStackObjects + } yield { + implicit val canBeSignedAsGachaPrize: CanBeSignedAsGachaPrize[ItemStack] = + gachaPrizeAPI.canBeSignedAsGachaPrize + + val signedItemStacks = gachaPrizes.map { gachaPrize => + gachaPrize.materializeWithOwnerSignature(player.getName) -> gachaPrize.itemStack } - } - isGachaPrize = foundGachaPrizeOpt.nonEmpty - mineStackObjects <- allMineStackObjects - } yield { - // ItemStackのLoreはnullの可能性がある - val isSignedItemStack = - Option(itemStack.getItemMeta.getLore).exists(_.contains("所有者:")) - if (isGachaPrize && foundGachaPrizeOpt.get.signOwner) { - mineStackObjects.find { mineStackObject => - foundGachaPrizeOpt.get.itemStack.isSimilar(mineStackObject.itemStack) + + itemStacks.map { _itemStack => + signedItemStacks.find(_._1.isSimilar(_itemStack)) match { + case Some((_, notSignedItemStack)) => + _itemStack -> mineStackObjects.find(_.itemStack.isSimilar(notSignedItemStack)) + case None => + mineStackObjects.find(_.itemStack.isSimilar(_itemStack)) match { + case Some(value) + if value.category != GACHA_PRIZES || minestackBuiltinGachaPrizes.exists( + _.swap.contains(value) + ) => + _itemStack -> Some(value) + case _ => _itemStack -> None + } + } } - } else if (isSignedItemStack) { - // 所有者名が違うとガチャ景品として認識しないが、違ったらそもそも見つかっていない - // 記名が入っていないアイテムは収納できてしまうが仕様 - None - } else { - mineStackObjects.find(_.itemStack.isSimilar(itemStack)) } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/bukkit/BukkitMineStackRepository.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/bukkit/BukkitMineStackRepository.scala index 80e77fded1..5e6763f417 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/bukkit/BukkitMineStackRepository.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/bukkit/BukkitMineStackRepository.scala @@ -66,9 +66,38 @@ class BukkitMineStackRepository[F[_]: Sync]( ) } - override def tryIntoMineStack(player: Player, itemStack: ItemStack, amount: Int): F[Boolean] = + override def tryIntoMineStack( + player: Player, + itemStack: ItemStack, + amount: Int + ): F[Boolean] = { + for { + foundMineStackObject <- mineStackObjectList.findBySignedItemStacks( + Vector(itemStack), + player + ) + _ <- foundMineStackObject.head.traverse { + case Some(mineStackObject) => + addStackedAmountOf(player, mineStackObject, amount) + case None => Sync[F].unit + } + } yield foundMineStackObject.head._2.isDefined + } + + override def tryIntoMineStack( + player: Player, + itemStacks: Vector[ItemStack] + ): F[(Vector[ItemStack], Vector[ItemStack])] = { for { - foundMineStackObject <- mineStackObjectList.findBySignedItemStack(itemStack, player) - _ <- foundMineStackObject.traverse(addStackedAmountOf(player, _, amount)) - } yield foundMineStackObject.isDefined + mineStackObjects <- mineStackObjectList.findBySignedItemStacks(itemStacks, player) + _ <- mineStackObjects.traverse { + case (itemStack, Some(mineStackObject)) => + addStackedAmountOf(player, mineStackObject, itemStack.getAmount) + case _ => Sync[F].unit + } + } yield mineStackObjects.partitionMap { + case (itemStack, Some(_)) => Right(itemStack) + case (itemStack, None) => Left(itemStack) + } + } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/bukkit/PlayerPickupItemListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/bukkit/PlayerPickupItemListener.scala index b97140073a..e9d998d394 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/bukkit/PlayerPickupItemListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/bukkit/PlayerPickupItemListener.scala @@ -37,9 +37,9 @@ class PlayerPickupItemListener[F[_]: ConcurrentEffect, G[_]: ContextCoercion[*[_ currentAutoMineStackState <- ContextCoercion( mineStackSettingRepository(player).isAutoCollectionTurnedOn ) - isSucceedTryIntoMineStack <- whenAOrElse(currentAutoMineStackState)( - mineStackRepository.tryIntoMineStack(player, itemStack, itemStack.getAmount), - false + intoSucceedItemStacksAndFailedItemStacks <- whenAOrElse(currentAutoMineStackState)( + mineStackRepository.tryIntoMineStack(player, Vector(itemStack)), + (Vector(itemStack), Vector.empty) ) _ <- Sync[F] .delay { @@ -48,13 +48,13 @@ class PlayerPickupItemListener[F[_]: ConcurrentEffect, G[_]: ContextCoercion[*[_ item.remove() if (SeichiAssist.DEBUG) { player.sendMessage(RED.toString + "pick:" + itemStack.toString) - player.sendMessage(RED.toString + "pickDurability:" + itemStack.getDurability) } } - .whenA(isSucceedTryIntoMineStack) + .whenA(intoSucceedItemStacksAndFailedItemStacks._2.nonEmpty) } yield () program.toIO.unsafeRunAsyncAndForget() + case _ => () } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/MineStackRepository.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/MineStackRepository.scala index 1dd9e97231..a6b48d13f9 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/MineStackRepository.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/MineStackRepository.scala @@ -37,4 +37,14 @@ trait MineStackRepository[F[_], Player, ItemStack] { */ def tryIntoMineStack(player: Player, itemStack: ItemStack, amount: Int): F[Boolean] + /** + * [[Player]]のMineStackリポジトリに[[ItemStack]]を格納することを試みます。 + * @return `itemStacks`の要素である[[ItemStack]]をMineStackに格納し + * 格納できなかったアイテムをTupleの第1要素、格納できたアイテムを第2要素として返す作用 + */ + def tryIntoMineStack( + player: Player, + itemStacks: Vector[ItemStack] + ): F[(Vector[ItemStack], Vector[ItemStack])] + } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/MineStackObject.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/MineStackObject.scala index f742352f38..7a95afeb3a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/MineStackObject.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/MineStackObject.scala @@ -54,15 +54,14 @@ object MineStackObject { category: MineStackObjectCategory, mineStackObjectName: String, japaneseName: String, - material: Material, - durability: Short + material: Material )( implicit minecraftMaterial: MinecraftMaterial[Material, ItemStack] ): MineStackObject[ItemStack] = { MineStackObject( mineStackObjectName, Some(japaneseName), - minecraftMaterial.toItemStack(material, durability), + minecraftMaterial.toItemStack(material), hasNameLore = false, category ) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/MineStackObjectList.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/MineStackObjectList.scala index bd30603044..5a1ce7590a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/MineStackObjectList.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/MineStackObjectList.scala @@ -11,12 +11,12 @@ trait MineStackObjectList[F[_], ItemStack, Player] { /** * @param itemStack 記名することのできるアイテムは、既に記名されていることを想定している - * @return [[ItemStack]]から[[MineStackObject]]を取得しようとする作用 + * @return `itemStack`の各要素に紐づいた[[MineStackObject]]を返す作用 */ - def findBySignedItemStack( - itemStack: ItemStack, + def findBySignedItemStacks( + itemStack: Vector[ItemStack], player: Player - ): F[Option[MineStackObject[ItemStack]]] + ): F[Vector[(ItemStack, Option[MineStackObject[ItemStack]])]] protected implicit val F: Functor[F] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/MineStackObjectWithColorVariants.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/MineStackObjectWithKindVariants.scala similarity index 52% rename from src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/MineStackObjectWithColorVariants.scala rename to src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/MineStackObjectWithKindVariants.scala index 35db22e653..3be993045a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/MineStackObjectWithColorVariants.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/MineStackObjectWithKindVariants.scala @@ -1,10 +1,10 @@ package com.github.unchama.seichiassist.subsystems.minestack.domain.minestackobject -case class MineStackObjectWithColorVariants[ItemStack]( +case class MineStackObjectWithKindVariants[ItemStack]( representative: MineStackObject[ItemStack], - coloredVariants: List[MineStackObject[ItemStack]] + kindVariants: List[MineStackObject[ItemStack]] ) { - require(coloredVariants.forall(_.category == representative.category)) + require(kindVariants.forall(_.category == representative.category)) def category: MineStackObjectCategory = representative.category } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/package.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/package.scala index 77b55a1aca..d1649c73ba 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/package.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/domain/minestackobject/package.scala @@ -3,6 +3,6 @@ package com.github.unchama.seichiassist.subsystems.minestack.domain package object minestackobject { type MineStackObjectGroup[ItemStack] = - Either[MineStackObject[ItemStack], MineStackObjectWithColorVariants[ItemStack]] + Either[MineStackObject[ItemStack], MineStackObjectWithKindVariants[ItemStack]] } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/infrastructure/JdbcMineStackObjectPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/infrastructure/JdbcMineStackObjectPersistence.scala index e57a68055a..be50653998 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/infrastructure/JdbcMineStackObjectPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/infrastructure/JdbcMineStackObjectPersistence.scala @@ -29,7 +29,6 @@ class JdbcMineStackObjectPersistence[F[_]: Sync, ItemStack, Player]( } } .toList() - .apply() .filterNot(_.isEmpty) .map(_.get) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/infrastructure/JdbcPlayerSettingPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/infrastructure/JdbcPlayerSettingPersistence.scala index 15ed250a38..2699cf3dd3 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/infrastructure/JdbcPlayerSettingPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/minestack/infrastructure/JdbcPlayerSettingPersistence.scala @@ -13,24 +13,19 @@ class JdbcPlayerSettingPersistence[F[_]: Sync] extends PlayerSettingPersistence[ sql"SELECT minestackflag FROM playerdata WHERE uuid = ${uuid.toString}" .map(_.boolean("minestackflag")) .single() - .apply() .getOrElse(false) } } override def turnOnAutoMineStack(uuid: UUID): F[Unit] = Sync[F].delay { DB.localTx { implicit session => - sql"UPDATE playerdata SET minestackflag = true WHERE uuid = ${uuid.toString}" - .execute() - .apply() + sql"UPDATE playerdata SET minestackflag = true WHERE uuid = ${uuid.toString}".execute() } } override def turnOffAutoMineStack(uuid: UUID): F[Unit] = Sync[F].delay { DB.localTx { implicit session => - sql"UPDATE playerdata SET minestackflag = false WHERE uuid = ${uuid.toString}" - .execute() - .apply() + sql"UPDATE playerdata SET minestackflag = false WHERE uuid = ${uuid.toString}".execute() } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/openirontrapdoor/bukkit/listeners/PlayerClickIronTrapDoor.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/openirontrapdoor/bukkit/listeners/PlayerClickIronTrapDoor.scala index 1935fce2be..e4208cb298 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/openirontrapdoor/bukkit/listeners/PlayerClickIronTrapDoor.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/openirontrapdoor/bukkit/listeners/PlayerClickIronTrapDoor.scala @@ -2,11 +2,12 @@ package com.github.unchama.seichiassist.subsystems.openirontrapdoor.bukkit.liste import com.github.unchama.util.external.WorldGuardWrapper.isRegionMember import org.bukkit.Material +import org.bukkit.block.data.BlockData import org.bukkit.event.block.Action import org.bukkit.event.player.PlayerInteractEvent import org.bukkit.event.{EventHandler, Listener} import org.bukkit.inventory.EquipmentSlot -import org.bukkit.material.{MaterialData, Openable} +import org.bukkit.material.Openable object PlayerClickIronTrapDoor extends Listener { @EventHandler @@ -21,10 +22,15 @@ object PlayerClickIronTrapDoor extends Listener { ) return // TODO: 手に何も持っていない場合は機能するが、ブロックなどを持っている場合は機能しない(手に持っているものが設置できるもののときや弓矢は反応する) - val blockState = clickedBlock.getState - val materialData = blockState.getData.asInstanceOf[Openable] - materialData.setOpen(!materialData.isOpen) - blockState.setData(materialData.asInstanceOf[MaterialData]) - blockState.update() + val blockData = clickedBlock.getBlockData + blockData match { + case openable: Openable => + openable.setOpen(!openable.isOpen) + + val blockState = clickedBlock.getState + blockState.setBlockData(openable.asInstanceOf[BlockData]) + blockState.update() + case _ => + } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/PlayerHeadSkinAPI.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/PlayerHeadSkinAPI.scala new file mode 100644 index 0000000000..2f853565d2 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/PlayerHeadSkinAPI.scala @@ -0,0 +1,11 @@ +package com.github.unchama.seichiassist.subsystems.playerheadskin + +import com.github.unchama.seichiassist.subsystems.playerheadskin.domain.HeadSkinUrl + +import java.util.UUID + +trait PlayerHeadSkinAPI[F[_], Player] { + + def playerHeadSkinUrlByUUID(player: UUID): F[Option[HeadSkinUrl]] + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/System.scala new file mode 100644 index 0000000000..0ede8a41ab --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/System.scala @@ -0,0 +1,40 @@ +package com.github.unchama.seichiassist.subsystems.playerheadskin + +import cats.effect.Sync +import com.github.unchama.seichiassist.meta.subsystem.Subsystem +import com.github.unchama.seichiassist.subsystems.playerheadskin.domain.{ + PlayerHeadSkinUrlFetcher, + PlayerHeadUrlRepository +} +import com.github.unchama.seichiassist.subsystems.playerheadskin.infrastructure.{ + JdbcPlayerHeadSkinPersistence, + PlayerHeadSkinUrlFetcherByMojangAPI +} +import org.bukkit.entity.Player + +import java.util.UUID + +trait System[F[_], Player] extends Subsystem[F] { + val api: PlayerHeadSkinAPI[F, Player] +} + +object System { + + def wired[F[_]: Sync]: System[F, Player] = { + implicit val playerHeadSkinUrlFetcher: PlayerHeadSkinUrlFetcher[F] = + new PlayerHeadSkinUrlFetcherByMojangAPI[F] + val repository = new PlayerHeadUrlRepository[F] + val persistence = new JdbcPlayerHeadSkinPersistence[F] + + new System[F, Player] { + import cats.implicits._ + + override val api: PlayerHeadSkinAPI[F, Player] = (player: UUID) => + for { + playerName <- persistence.fetchLastSeenPlayerName(player) + url <- playerName.flatTraverse(repository.readUrl) + } yield url + } + } + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/domain/HeadSkinUrl.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/domain/HeadSkinUrl.scala new file mode 100644 index 0000000000..96257cce7f --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/domain/HeadSkinUrl.scala @@ -0,0 +1,3 @@ +package com.github.unchama.seichiassist.subsystems.playerheadskin.domain + +case class HeadSkinUrl(playerName: String, url: String) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/domain/PlayerHeadSkinPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/domain/PlayerHeadSkinPersistence.scala new file mode 100644 index 0000000000..8b4f0aad8c --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/domain/PlayerHeadSkinPersistence.scala @@ -0,0 +1,12 @@ +package com.github.unchama.seichiassist.subsystems.playerheadskin.domain + +import java.util.UUID + +trait PlayerHeadSkinPersistence[F[_]] { + + /** + * @return `player`に一致するUUIDの、最後に確認されたプレイヤー名を取得する作用 + */ + def fetchLastSeenPlayerName(player: UUID): F[Option[String]] + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/domain/PlayerHeadSkinUrlFetcher.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/domain/PlayerHeadSkinUrlFetcher.scala new file mode 100644 index 0000000000..94dbfe2311 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/domain/PlayerHeadSkinUrlFetcher.scala @@ -0,0 +1,10 @@ +package com.github.unchama.seichiassist.subsystems.playerheadskin.domain + +trait PlayerHeadSkinUrlFetcher[F[_]] { + + /** + * @return `playerName`の頭のスキンのURLを取ってくる作用 + */ + def fetchHeadSkinUrl(playerName: String): F[Option[HeadSkinUrl]] + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/domain/PlayerHeadUrlRepository.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/domain/PlayerHeadUrlRepository.scala new file mode 100644 index 0000000000..ea48c0b622 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/domain/PlayerHeadUrlRepository.scala @@ -0,0 +1,24 @@ +package com.github.unchama.seichiassist.subsystems.playerheadskin.domain + +import cats.effect.Sync +import cats.effect.concurrent.Ref +import com.github.unchama.generic.ApplicativeExtra + +class PlayerHeadUrlRepository[F[_]: Sync](implicit fetcher: PlayerHeadSkinUrlFetcher[F]) { + + private val skinUrls: Ref[F, Vector[HeadSkinUrl]] = Ref.unsafe(Vector.empty) + + import cats.implicits._ + + def readUrl(targetPlayer: String): F[Option[HeadSkinUrl]] = for { + urls <- skinUrls.get + targetPlayersHeadSkinUrl <- ApplicativeExtra.whenAOrElse( + !urls.exists(_.playerName == targetPlayer) + )(fetcher.fetchHeadSkinUrl(targetPlayer), None) + _ <- skinUrls + .update(_ :+ targetPlayersHeadSkinUrl.get) + .whenA(targetPlayersHeadSkinUrl.isDefined) + resultUrls <- skinUrls.get + } yield resultUrls.find(_.playerName == targetPlayer) + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/infrastructure/JdbcPlayerHeadSkinPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/infrastructure/JdbcPlayerHeadSkinPersistence.scala new file mode 100644 index 0000000000..6f27c7d293 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/infrastructure/JdbcPlayerHeadSkinPersistence.scala @@ -0,0 +1,20 @@ +package com.github.unchama.seichiassist.subsystems.playerheadskin.infrastructure + +import cats.effect.Sync +import com.github.unchama.seichiassist.subsystems.playerheadskin.domain.PlayerHeadSkinPersistence +import scalikejdbc._ + +import java.util.UUID + +class JdbcPlayerHeadSkinPersistence[F[_]: Sync] extends PlayerHeadSkinPersistence[F] { + + override def fetchLastSeenPlayerName(player: UUID): F[Option[String]] = Sync[F].delay { + DB.readOnly { implicit session => + sql"SELECT name FROM playerdata WHERE uuid = ${player.toString}" + .map(_.string("name")) + .first + .apply() + } + } + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/infrastructure/PlayerHeadSkinUrlFetcherByMojangAPI.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/infrastructure/PlayerHeadSkinUrlFetcherByMojangAPI.scala new file mode 100644 index 0000000000..a0db85fb7c --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/playerheadskin/infrastructure/PlayerHeadSkinUrlFetcherByMojangAPI.scala @@ -0,0 +1,79 @@ +package com.github.unchama.seichiassist.subsystems.playerheadskin.infrastructure + +import cats.effect.Sync +import com.github.unchama.seichiassist.subsystems.playerheadskin.domain.{ + HeadSkinUrl, + PlayerHeadSkinUrlFetcher +} +import org.apache.commons.codec.binary.Base64 +import org.jline.utils.InputStreamReader + +import java.io.BufferedReader +import java.net.{HttpURLConnection, URL} +import java.nio.charset.StandardCharsets + +class PlayerHeadSkinUrlFetcherByMojangAPI[F[_]: Sync] extends PlayerHeadSkinUrlFetcher[F] { + + import cats.implicits._ + + private def getHttpRequest(url: String): F[Option[String]] = for { + url <- Sync[F].pure(new URL(url)) + connection <- Sync[F].delay(url.openConnection().asInstanceOf[HttpURLConnection]) + _ <- Sync[F].delay(connection.connect()) + responseCode <- Sync[F].delay(connection.getResponseCode) + response <- Sync[F].delay { + Option.when(responseCode == HttpURLConnection.HTTP_OK) { + val inputStream = connection.getInputStream + val inputStreamReader = new InputStreamReader(inputStream, "UTF-8") + val bufferedReader = new BufferedReader(inputStreamReader) + + val result = + Iterator.continually(bufferedReader.readLine()).takeWhile(_ != null).mkString + + bufferedReader.close() + inputStreamReader.close() + inputStream.close() + + result + } + } + } yield response + + import io.circe.parser._ + + private def textureUrl(name: String): F[Option[String]] = { + for { + profileOpt <- getHttpRequest(s"https://api.mojang.com/users/profiles/minecraft/$name") + id = for { + profile <- profileOpt + id <- parse(profile).toOption.flatMap(_.hcursor.get[String]("id").toOption) + } yield id + playerDataOpt <- getHttpRequest( + s"https://sessionserver.mojang.com/session/minecraft/profile/$id" + ) + url = for { + playerData <- playerDataOpt + base64TextureProperties <- parse(playerData) + .toOption + .flatMap( + _.hcursor + .downField("properties") + .values + .flatMap(_.head.hcursor.get[String]("value").toOption) + ) + url <- parse( + new String(Base64.decodeBase64(base64TextureProperties), StandardCharsets.UTF_8) + ).toOption + .flatMap( + _.hcursor.downField("textures").downField("SKIN").get[String]("url").toOption + ) + } yield url + + } yield url + } + + override def fetchHeadSkinUrl(playerName: String): F[Option[HeadSkinUrl]] = for { + textureUrl <- textureUrl(playerName) + } yield textureUrl.map(url => HeadSkinUrl(playerName, url)) + +} 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 f63133a04e..841ea82975 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 @@ -1,8 +1,7 @@ package com.github.unchama.seichiassist.subsystems.present -import cats.effect.{ConcurrentEffect, IO} +import cats.effect.ConcurrentEffect import com.github.unchama.concurrent.NonServerThreadContextShift -import com.github.unchama.generic.effect.unsafe.EffectEnvironment import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.domain.actions.UuidToLastSeenName import com.github.unchama.seichiassist.meta.subsystem.Subsystem @@ -11,10 +10,10 @@ import com.github.unchama.seichiassist.subsystems.present.infrastructure.JdbcBac import org.bukkit.command.TabExecutor object System { - def wired[ConcurrentContext[_]: ConcurrentEffect: NonServerThreadContextShift]( - implicit environment: EffectEnvironment, - uuidToLastSeenName: UuidToLastSeenName[ConcurrentContext], - ioOnMainThread: OnMinecraftServerThread[IO] + def wired[ConcurrentContext[ + _ + ]: ConcurrentEffect: NonServerThreadContextShift: OnMinecraftServerThread]( + implicit uuidToLastSeenName: UuidToLastSeenName[ConcurrentContext] ): 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 ace10b8278..04695cbee7 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 @@ -1,8 +1,8 @@ package com.github.unchama.seichiassist.subsystems.present.bukkit.command -import cats.Monad +import cats.data.Kleisli import cats.effect.implicits._ -import cats.effect.{ConcurrentEffect, IO} +import cats.effect.{ConcurrentEffect, Sync} import cats.implicits._ import com.github.unchama.concurrent.NonServerThreadContextShift import com.github.unchama.contextualexecutor.ContextualExecutor @@ -18,8 +18,8 @@ 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._ import com.github.unchama.seichiassist.util.InventoryOperations -import com.github.unchama.targetedeffect.commandsender.MessageEffect -import com.github.unchama.targetedeffect.{SequentialEffect, TargetedEffect} +import com.github.unchama.targetedeffect.{SequentialEffect, TargetedEffectF} +import com.github.unchama.targetedeffect.commandsender.{MessageEffect, MessageEffectF} import eu.timepit.refined.api.Refined import eu.timepit.refined.auto._ import eu.timepit.refined.numeric.Positive @@ -28,7 +28,6 @@ import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack import org.bukkit.{ChatColor, Material} import shapeless.HNil -import shapeless.syntax.std.tuple._ /** * `/present` コマンドを定義する。 @@ -38,8 +37,10 @@ import shapeless.syntax.std.tuple._ * 別に表記がない限り、この実装は以下の条件を満たす: * - 存在しないプレゼントIDの指定は必ずエラーになる。 * - 操作が成功しなかったときは適切なメッセージを表示する。 + * + * TODO: 型パラメータが関数のあちこちに散りばめられているが、classの型引数に集約できそう */ -class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { +class PresentCommand { private val presentIdParser = Parsers.integer(MessageEffect("presentコマンドに与えるプレゼントIDは整数である必要があります。")) @@ -54,7 +55,8 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { MessageEffect("presentコマンドで対象を指定する際のモードは、playerまたはallを指定してください。") ) - private val noPermissionMessage = MessageEffect("You don't have the permission.") + private def noPermissionMessage[F[_]: Sync] = + MessageEffectF[F]("You don't have the permission.") private object SubCommands { object State { @@ -166,47 +168,45 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { * * 出力: 受け取った場合は、その旨表示する。失敗した場合は、適切なエラーメッセージを表示する。 */ - def executor[F[_]: ConcurrentEffect: NonServerThreadContextShift]( - implicit persistence: PresentPersistence[F, ItemStack] - ): ContextualExecutor = + def executor[ + F[_]: ConcurrentEffect: NonServerThreadContextShift: OnMinecraftServerThread + ](implicit persistence: PresentPersistence[F, ItemStack]): ContextualExecutor = playerCommandBuilder .thenParse(presentIdParser) .ifArgumentsMissing(help) - .buildWithExecutionF { context => + .buildWithExecutionCSEffect { context => val player = context.sender.getUniqueId val presentId = context.args.parsed.head - val eff: F[TargetedEffect[Player]] = for { - _ <- NonServerThreadContextShift[F].shift - states <- persistence.fetchState(player) - claimState = states.getOrElse(presentId, PresentClaimingState.Unavailable) - effect <- claimState match { + (for { + _ <- Kleisli.liftF(NonServerThreadContextShift[F].shift) + states <- Kleisli.liftF(persistence.fetchState(player)) + } yield { + val claimState = states.getOrElse(presentId, PresentClaimingState.Unavailable) + + claimState match { case PresentClaimingState.Claimed => - Monad[F].pure(MessageEffect(s"ID: ${presentId}のプレゼントはすでに受け取っています。")) + MessageEffectF[F](s"ID: ${presentId}のプレゼントはすでに受け取っています。") case PresentClaimingState.NotClaimed => - for { - _ <- persistence.markAsClaimed(presentId, player) - item <- persistence.lookup(presentId) - } yield { - // 注釈: この明示的な型変数の指定は必要 - // see: https://discord.com/channels/237758724121427969/565935041574731807/823495317499805776 - item.fold[TargetedEffect[Player]]( - MessageEffect(s"ID: ${presentId}のプレゼントは存在しません。IDをお確かめください。") - ) { item => - SequentialEffect( - InventoryOperations.grantItemStacksEffect[IO](item), - MessageEffect(s"ID: ${presentId}のプレゼントを付与しました。") - ) + Kleisli + .liftF(for { + _ <- persistence.markAsClaimed(presentId, player) + item <- persistence.lookup(presentId) + } yield item) + .flatMap { item => + item.fold[TargetedEffectF[F, Player]]( + MessageEffectF[F](s"ID: ${presentId}のプレゼントは存在しません。IDをお確かめください。") + ) { item => + SequentialEffect( + InventoryOperations.grantItemStacksEffect[F](item), + MessageEffectF[F](s"ID: ${presentId}のプレゼントを付与しました。") + ) + } } - } case PresentClaimingState.Unavailable => - Monad[F].pure( - MessageEffect(s"ID: ${presentId}のプレゼントは存在しないか、あるいは配布対象ではありません。") - ) + MessageEffectF[F](s"ID: ${presentId}のプレゼントは存在しないか、あるいは配布対象ではありません。") } - } yield effect - - eff + }).flatten } } @@ -228,25 +228,23 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { def executor[F[_]: ConcurrentEffect: NonServerThreadContextShift]( implicit persistence: PresentPersistence[F, ItemStack] ): ContextualExecutor = - playerCommandBuilder.buildWithExecutionF { context => + playerCommandBuilder.buildWithExecutionCSEffect { context => val player = context.sender if (player.hasPermission("seichiassist.present.define")) { val mainHandItem = player.getInventory.getItemInMainHand - if (mainHandItem.getType eq Material.AIR) { + if (mainHandItem.getType == Material.AIR) { // おそらくこれは意図した動作ではないのでエラーメッセージを表示する - ConcurrentEffect[F].pure( - MessageEffect("メインハンドに何も持っていません。プレゼントを定義するためには、メインハンドに対象アイテムを持ってください。") - ) + MessageEffectF[F]("メインハンドに何も持っていません。プレゼントを定義するためには、メインハンドに対象アイテムを持ってください。") } else { - for { - _ <- NonServerThreadContextShift[F].shift - presentID <- persistence.define(mainHandItem) + (for { + _ <- Kleisli.liftF(NonServerThreadContextShift[F].shift) + presentID <- Kleisli.liftF(persistence.define(mainHandItem)) } yield { - MessageEffect(s"メインハンドに持ったアイテムをプレゼントとして定義しました。IDは${presentID}です。") - } + MessageEffectF(s"メインハンドに持ったアイテムをプレゼントとして定義しました。IDは${presentID}です。") + }).flatten } } else { - ConcurrentEffect[F].pure(noPermissionMessage) + noPermissionMessage } } } @@ -270,21 +268,21 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { .beginConfiguration .thenParse(presentIdParser) .ifArgumentsMissing(help) - .buildWithExecutionF { context => + .buildWithExecutionCSEffect { context => { val presentId = context.args.parsed.head if (!context.sender.hasPermission("seichiassist.present.delete")) { - ConcurrentEffect[F].pure(noPermissionMessage) + noPermissionMessage } else { - for { - _ <- NonServerThreadContextShift[F].shift - result <- persistence.delete(presentId) + (for { + _ <- Kleisli.liftF(NonServerThreadContextShift[F].shift) + result <- Kleisli.liftF(persistence.delete(presentId)) } yield result match { case DeleteResult.Done => - MessageEffect(s"IDが${presentId}のプレゼントの消去は正常に行われました。") + MessageEffectF(s"IDが${presentId}のプレゼントの消去は正常に行われました。") case DeleteResult.NotFound => - MessageEffect(s"IDが${presentId}のプレゼントは存在しませんでした。") - } + MessageEffectF(s"IDが${presentId}のプレゼントは存在しませんでした。") + }).flatten } } } @@ -325,16 +323,16 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { .thenParse(presentIdParser) .thenParse(presentScopeModeParser) .ifArgumentsMissing(help) - .buildWithExecutionF { context => + .buildWithExecutionCSEffect { context => if (context.sender.hasPermission("seichiassist.present.grant")) { import shapeless.:: val presentId :: mode :: HNil = context.args.parsed // Parserを通した段階でargs[0]は "player" | "all" になっているのでこれでOK val isGlobal = mode == "all" - for { - _ <- NonServerThreadContextShift[F].shift + (for { + _ <- Kleisli.liftF(NonServerThreadContextShift[F].shift) // TODO: 以下の処理は多分共通化できるがうまい方法が思いつかない - globalUUID2Name <- globalPlayerAccessor.entries + globalUUID2Name <- Kleisli.liftF(globalPlayerAccessor.entries) // 可変長引数には対応していないので`yetToBeParsed`を使う restArg = context .args @@ -348,19 +346,19 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { else globalUUID2Name.filter { case (_, name) => restArg.contains(name) }.keys errorIfNobody = Option.when(target.isEmpty) { - MessageEffect("対象のプレイヤーが存在しません!") + MessageEffectF("対象のプレイヤーが存在しません!") } - grantError <- persistence.grant(presentId, target.toSet) + grantError <- Kleisli.liftF(persistence.grant(presentId, target.toSet)) } yield errorIfNobody.getOrElse( grantError .map { case GrantRejectReason.NoSuchPresentID => - MessageEffect("指定されたプレゼントIDは存在しません!") + MessageEffectF("指定されたプレゼントIDは存在しません!") } - .getOrElse(MessageEffect(s"プレゼント(id: $presentId)を受け取れるプレイヤーを追加しました。")) - ) + .getOrElse(MessageEffectF(s"プレゼント(id: $presentId)を受け取れるプレイヤーを追加しました。")) + )).flatten } else { - ConcurrentEffect[F].pure(noPermissionMessage) + noPermissionMessage } } } @@ -399,15 +397,15 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { .thenParse(presentIdParser) .thenParse(presentScopeModeParser) .ifArgumentsMissing(help) - .buildWithExecutionF { context => + .buildWithExecutionCSEffect { context => if (context.sender.hasPermission("seichiassist.present.revoke")) { import shapeless.:: val args = context.args val presentId :: presentScope :: HNil = args.parsed val isGlobal = presentScope == "all" - for { - _ <- NonServerThreadContextShift[F].shift - globalUUID2Name <- globalPlayerAccessor.entries + (for { + _ <- Kleisli.liftF(NonServerThreadContextShift[F].shift) + globalUUID2Name <- Kleisli.liftF(globalPlayerAccessor.entries) // 可変長引数には対応していないので`yetToBeParsed`を使う restArg = args // プレイヤー名は /[A-Za-z0-9_]{,16}/であるため空白が誤って解釈されることはない @@ -420,22 +418,23 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { else globalUUID2Name.filter { case (_, name) => restArg.contains(name) }.keys errorIfNobody = - if (target.isEmpty) Some(MessageEffect("対象のプレイヤーが存在しません!")) else None - warning <- persistence.revoke(presentId, target.toSet) + if (target.isEmpty) Some(MessageEffectF("対象のプレイヤーが存在しません!")) else None + warning <- Kleisli.liftF(persistence.revoke(presentId, target.toSet)) } yield { errorIfNobody.getOrElse { warning .map { - case RevokeWarning.NoSuchPresentID => MessageEffect("そのようなプレゼントIDはありません!") - case RevokeWarning.NoPlayers => MessageEffect("対象となるプレイヤーが存在しません!") + case RevokeWarning.NoSuchPresentID => + MessageEffectF("そのようなプレゼントIDはありません!") + case RevokeWarning.NoPlayers => MessageEffectF("対象となるプレイヤーが存在しません!") } .getOrElse { - MessageEffect(s"プレゼント(id: $presentId)を受け取れるプレイヤーを削除しました。") + MessageEffectF(s"プレゼント(id: $presentId)を受け取れるプレイヤーを削除しました。") } } - } + }).flatten } else { - ConcurrentEffect[F].pure(noPermissionMessage) + noPermissionMessage } } } @@ -473,7 +472,7 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { } } - def executor[F[_]: ConcurrentEffect: NonServerThreadContextShift]( + def executor[F[_]: ConcurrentEffect: NonServerThreadContextShift: OnMinecraftServerThread]( implicit persistence: PresentPersistence[F, ItemStack], globalPlayerAccessor: UuidToLastSeenName[F] ): TabExecutor = BranchedExecutor( 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 228b6814be..aaa2f9d5f0 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 @@ -20,7 +20,6 @@ class GlobalPlayerAccessor[F[_]: Sync] extends UuidToLastSeenName[F] { sql"""SELECT name, uuid from seichiassist.playerdata""" .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/JdbcBackedPresentPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala index 197c85ee08..79e3feffb5 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 @@ -22,8 +22,7 @@ class JdbcBackedPresentPersistence[F[_]: Sync] extends PresentPersistence[F, Ite DB.localTx { implicit session => // プレゼントのIDはauto_incrementなので明示的に指定しなくて良い sql"""INSERT INTO present (itemstack) VALUES ($stackAsBlob)""" - .updateAndReturnGeneratedKey - .apply() + .updateAndReturnGeneratedKey() } } @@ -36,10 +35,10 @@ class JdbcBackedPresentPersistence[F[_]: Sync] extends PresentPersistence[F, Ite 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() val deletedRows = - sql"""DELETE FROM present WHERE present_id = $presentId""".update().apply() + sql"""DELETE FROM present WHERE present_id = $presentId""".update() if (deletedRows == 1) DeleteResult.Done else DeleteResult.NotFound } @@ -50,22 +49,21 @@ class JdbcBackedPresentPersistence[F[_]: Sync] extends PresentPersistence[F, Ite 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() }.contains(presentID) } } yield { if (exists) { Sync[F].delay { - import scala.collection.Seq.iterableFactory val initialValues = players.map { uuid => Seq(presentID, uuid.toString, false) }.toSeq - DB.localTx { implicit session => + DB.localTx { _ => // upsert - これによってfilterなしで整合性違反を起こすことはなくなる sql""" INSERT INTO present_state VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE present_id=present_id, uuid=uuid - """.batch(initialValues: _*).apply() + """.batch(initialValues: _*) } // 型推論 @@ -93,7 +91,6 @@ class JdbcBackedPresentPersistence[F[_]: Sync] extends PresentPersistence[F, Ite // https://discord.com/channels/237758724121427969/565935041574731807/824107651985834004 sql"""DELETE FROM present_state WHERE present_id = $presentID AND uuid IN ($scopeAsSQL)""" .update() - .apply() } Option.when(deleteCount == 0) { RevokeWarning.NoSuchPresentID } @@ -105,7 +102,6 @@ class JdbcBackedPresentPersistence[F[_]: Sync] extends PresentPersistence[F, Ite DB.localTx { implicit session => sql"""UPDATE present_state SET claimed = TRUE WHERE uuid = ${player.toString} AND present_id = $presentId""" .update() - .apply() } } @@ -114,7 +110,6 @@ class JdbcBackedPresentPersistence[F[_]: Sync] extends PresentPersistence[F, Ite sql"""SELECT present_id, itemstack FROM present""" .map { rs => (rs.long("present_id"), unwrapItemStack(rs)) } .list() - .apply() .toMap } } @@ -141,7 +136,7 @@ class JdbcBackedPresentPersistence[F[_]: Sync] extends PresentPersistence[F, Ite |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() } Right( @@ -167,7 +162,6 @@ class JdbcBackedPresentPersistence[F[_]: Sync] extends PresentPersistence[F, Ite sql"""SELECT present_id, claimed FROM present_state WHERE uuid = ${player.toString}""" .map(wrapResultForState) .list() - .apply() } MapExtra.fillOnBaseSet( @@ -183,7 +177,6 @@ class JdbcBackedPresentPersistence[F[_]: Sync] extends PresentPersistence[F, Ite sql"""SELECT itemstack FROM present WHERE present_id = $presentID""" .map(unwrapItemStack) .first() - .apply() } } @@ -197,7 +190,6 @@ class JdbcBackedPresentPersistence[F[_]: Sync] extends PresentPersistence[F, Ite sql"""SELECT present_id FROM present ORDER BY present_id LIMIT ${perPage.value} OFFSET $offset""" .map { _.long("present_id") } .toList() - .apply() .toSet } } @@ -218,7 +210,7 @@ class JdbcBackedPresentPersistence[F[_]: Sync] extends PresentPersistence[F, Ite 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().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 425c46cfdf..30651b6c60 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 @@ -12,6 +12,8 @@ import com.github.unchama.seichiassist.subsystems.ranking.domain.values.{LoginTi import com.github.unchama.seichiassist.subsystems.ranking.infrastructure._ import io.chrisdavenport.log4cats.ErrorLogger +import scala.concurrent.duration.DurationInt + object System { import cats.implicits._ @@ -19,16 +21,20 @@ object System { def wired[F[_]: Timer: Concurrent: ErrorLogger, H[_]]: F[AssortedRankingApi[F]] = for { seichiRanking <- GenericRefreshingRankingCache.withPersistence( - new JdbcSeichiRankingRecordPersistence[F] + new JdbcSeichiRankingRecordPersistence[F], + 30.seconds ) buildRanking <- GenericRefreshingRankingCache.withPersistence( - new JdbcBuildRankingRecordPersistence[F] + new JdbcBuildRankingRecordPersistence[F], + 30.seconds ) loginRanking <- GenericRefreshingRankingCache.withPersistence( - new JdbcLoginRankingRecordPersistence[F] + new JdbcLoginRankingRecordPersistence[F], + 1.hours ) voteRanking <- GenericRefreshingRankingCache.withPersistence( - new JdbcVoteRankingRecordPersistence[F] + new JdbcVoteRankingRecordPersistence[F], + 1.hours ) } yield { new AssortedRankingApi[F] { 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 c1649c7ec3..4ddac9ae9a 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 @@ -11,12 +11,13 @@ import com.github.unchama.generic.effect.stream.StreamExtra import com.github.unchama.seichiassist.subsystems.ranking.domain._ import io.chrisdavenport.log4cats.ErrorLogger -import scala.concurrent.duration.DurationInt +import scala.concurrent.duration.FiniteDuration object GenericRefreshingRankingCache { def withPersistence[F[_]: Concurrent: Timer: ErrorLogger, R: Order: Monoid]( - persistence: RankingRecordPersistence[F, R] + persistence: RankingRecordPersistence[F, R], + duration: FiniteDuration ): F[ReadOnlyRef[F, Ranking[R]]] = for { initialRankingRecords <- persistence.getAllRankingRecords @@ -26,7 +27,7 @@ object GenericRefreshingRankingCache { .compileToRestartingStream[F, Unit]("[GenericRefreshingRankingCache]") { fs2 .Stream - .awakeEvery[F](30.seconds) + .awakeEvery[F](duration) .evalMap(_ => persistence.getAllRankingRecords) .evalTap(newRecords => rankingRef.set(new Ranking(newRecords))) } 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 4efa97c7da..08ea3b1b2c 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,5 +1,7 @@ package com.github.unchama.seichiassist.subsystems.ranking.domain -case class RankingRecord[V](playerName: String, value: V) +import java.util.UUID + +case class RankingRecord[V](playerName: String, uuid: UUID, value: V) case class RankingRecordWithPosition[V](record: RankingRecord[V], positionInRanking: Int) 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 710c99798a..fcb6de1c99 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 @@ -9,20 +9,22 @@ import com.github.unchama.seichiassist.subsystems.ranking.domain.{ } import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} +import java.util.UUID + class JdbcBuildRankingRecordPersistence[F[_]: Sync] extends RankingRecordPersistence[F, BuildAmountData] { override def getAllRankingRecords: F[Vector[RankingRecord[BuildAmountData]]] = Sync[F].delay { DB.readOnly { implicit session => - sql"SELECT name,build_count from playerdata" + sql"SELECT name, uuid, build_count from playerdata" .map { rs => RankingRecord( rs.string("name"), + UUID.fromString(rs.string("uuid")), BuildAmountData(BuildExpAmount(BigDecimal(rs.string("build_count")))) ) } .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 73182ccd80..4322202d79 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 @@ -8,14 +8,21 @@ import com.github.unchama.seichiassist.subsystems.ranking.domain.{ } import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} +import java.util.UUID + 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.long("playtick"))) } + sql"SELECT name, uuid, playtick from playerdata" + .map { rs => + RankingRecord( + rs.string("name"), + UUID.fromString(rs.string("uuid")), + LoginTime(rs.long("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 ea8c6ef7cc..108d40f74d 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 @@ -9,23 +9,25 @@ import com.github.unchama.seichiassist.subsystems.ranking.domain.{ } import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} +import java.util.UUID + 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" + sql"select name, uuid, totalbreaknum from playerdata" .map { rs => RankingRecord( rs.string("name"), + UUID.fromString(rs.string("uuid")), 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 2a06e54cc6..9a9a5726cc 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 @@ -8,14 +8,21 @@ import com.github.unchama.seichiassist.subsystems.ranking.domain.{ } import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} +import java.util.UUID + class JdbcVoteRankingRecordPersistence[F[_]: Sync] extends RankingRecordPersistence[F, VoteCount] { override def getAllRankingRecords: F[Vector[RankingRecord[VoteCount]]] = Sync[F].delay { DB.readOnly { implicit session => - sql"SELECT playerdata.name,vote_number FROM vote INNER JOIN playerdata ON vote.uuid = playerdata.uuid" - .map { rs => RankingRecord(rs.string("name"), VoteCount(rs.int("vote_number"))) } + sql"SELECT playerdata.name, playerdata.uuid, vote_number FROM vote INNER JOIN playerdata ON vote.uuid = playerdata.uuid" + .map { rs => + RankingRecord( + rs.string("name"), + UUID.fromString(rs.string("uuid")), + VoteCount(rs.int("vote_number")) + ) + } .list() - .apply() .toVector } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/rescueplayer/bukkit/listeners/RescuePlayerListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/rescueplayer/bukkit/listeners/RescuePlayerListener.scala index 0dd495654e..0efebbcceb 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/rescueplayer/bukkit/listeners/RescuePlayerListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/rescueplayer/bukkit/listeners/RescuePlayerListener.scala @@ -7,7 +7,7 @@ import org.bukkit.event.{EventHandler, Listener} class RescuePlayerListener extends Listener { @EventHandler def onPlayerFallenToVoid(event: PlayerMoveEvent): Unit = { - if (event.getTo.getBlockY < 0.0) { + if (event.getTo.getBlockY < -64.0) { val player = event.getPlayer val worldSpawnLocation = player.getWorld.getSpawnLocation 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 b4ee0b2c57..a5d793ec42 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 @@ -7,6 +7,7 @@ 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.mana.ManaWriteApi +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.seasonalevents.anniversary.AnniversaryListener import com.github.unchama.seichiassist.subsystems.seasonalevents.api.SeasonalEventsAPI import com.github.unchama.seichiassist.subsystems.seasonalevents.christmas.ChristmasItemListener @@ -43,7 +44,8 @@ object System { implicit manaWriteApi: ManaWriteApi[G, Player], effectEnvironment: EffectEnvironment, ioOnMainThread: OnMinecraftServerThread[IO], - gtToSiinaAPI: GtToSiinaAPI[ItemStack] + gtToSiinaAPI: GtToSiinaAPI[ItemStack], + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ): System[H] = { implicit val repository: LastQuitPersistenceRepository[F, UUID] = 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 259899bcf2..4170c1ed3e 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 @@ -1,13 +1,16 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.anniversary +import cats.effect.IO import com.github.unchama.itemstackbuilder.{SkullItemStackBuilder, SkullOwnerTextureValue} +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.seasonalevents.anniversary.Anniversary.ANNIVERSARY_COUNT import com.github.unchama.seichiassist.util.EnchantNameToJapanese -import de.tr7zw.itemnbtapi.NBTItem +import de.tr7zw.nbtapi.NBTItem import org.bukkit.Bukkit import org.bukkit.ChatColor._ import org.bukkit.Material._ import org.bukkit.enchantments.Enchantment +import org.bukkit.entity.Player import org.bukkit.inventory.meta.BookMeta.Generation import org.bukkit.inventory.meta.{BookMeta, ItemMeta, SkullMeta} import org.bukkit.inventory.{ItemFlag, ItemStack} @@ -22,10 +25,11 @@ object AnniversaryItemData { private val mineChan = SkullOwnerTextureValue( "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDhmNTQ0OGI0ZDg4ZTQwYjE0YzgyOGM2ZjFiNTliMzg1NDVkZGE5MzNlNzNkZmYzZjY5NWU2ZmI0Mjc4MSJ9fX0=" ) - val mineHead: ItemStack = new SkullItemStackBuilder(mineChan) - .title("まいんちゃん") - .lore("", s"${YELLOW}ギガンティック☆整地鯖${ANNIVERSARY_COUNT}周年記念だよ!") - .build() + def mineHead(implicit playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player]): ItemStack = + new SkullItemStackBuilder(mineChan) + .title("まいんちゃん") + .lore("", s"${YELLOW}ギガンティック☆整地鯖${ANNIVERSARY_COUNT}周年記念だよ!") + .build() // endregion @@ -36,13 +40,13 @@ object AnniversaryItemData { .map(lore => s"$RESET$GRAY$lore") .asJava - val itemMeta = Bukkit.getItemFactory.getItemMeta(SAPLING).tap { meta => + val itemMeta = Bukkit.getItemFactory.getItemMeta(OAK_SAPLING).tap { meta => import meta._ setDisplayName(s"$GOLD${BOLD}「気になる木」の苗") setLore(loreList) } - val itemStack = new ItemStack(SAPLING, 1) + val itemStack = new ItemStack(OAK_SAPLING, 1) itemStack.setItemMeta(itemMeta) new NBTItem(itemStack) @@ -64,25 +68,29 @@ object AnniversaryItemData { COAL_BLOCK, DIAMOND_BLOCK, DRAGON_EGG, - FENCE, + OAK_FENCE, + JUNGLE_FENCE, + ACACIA_FENCE, + BIRCH_FENCE, + DARK_OAK_FENCE, FLOWER_POT, GLOWSTONE, GOLD_BLOCK, GRASS, ICE, IRON_BLOCK, - MELON_BLOCK, + MELON, NETHER_BRICK, QUARTZ_BLOCK, SAND, SPONGE, - WORKBENCH + CRAFTING_TABLE ) val strangeSaplingSiinaRate = 0.0008 def isStrangeSapling(item: ItemStack): Boolean = - item != null && item.getType == SAPLING && { + item != null && item.getType == OAK_SAPLING && { new NBTItem(item).getByte(NBTTagConstants.typeIdTag) == 1 } @@ -135,14 +143,15 @@ object AnniversaryItemData { val loreList = { val enchDescription = enchantments.map { - case (ench, lvl) => s"$GRAY${EnchantNameToJapanese.getEnchantName(ench.getName, lvl)}" + case (ench, lvl) => + s"$GRAY${EnchantNameToJapanese.getEnchantName(ench.getKey.getKey, lvl)}" }.toList val lore = List("", "特殊なエンチャントが付与されています").map(lore => s"$YELLOW$lore") enchDescription ::: lore }.map(lore => s"$RESET$lore").asJava - val itemMeta = Bukkit.getItemFactory.getItemMeta(DIAMOND_SPADE).tap { meta => + val itemMeta = Bukkit.getItemFactory.getItemMeta(DIAMOND_SHOVEL).tap { meta => import meta._ setDisplayName(s"$GOLD${BOLD}SCARLET") setLore(loreList) @@ -150,7 +159,7 @@ object AnniversaryItemData { enchantments.foreach { case (ench, lvl) => addEnchant(ench, lvl, true) } } - val itemStack = new ItemStack(DIAMOND_SPADE, 1) + val itemStack = new ItemStack(DIAMOND_SHOVEL, 1) itemStack.setItemMeta(itemMeta) new NBTItem(itemStack) @@ -162,7 +171,7 @@ object AnniversaryItemData { } def isAnniversaryShovel(item: ItemStack): Boolean = - item != null && item.getType == DIAMOND_SPADE && { + item != null && item.getType == DIAMOND_SHOVEL && { new NBTItem(item).getByte(NBTTagConstants.typeIdTag) == 3 } 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 ff9311350f..2af8dde12c 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,6 +4,7 @@ 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.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.seasonalevents.anniversary.Anniversary.{ ANNIVERSARY_COUNT, blogArticleUrl, @@ -21,7 +22,7 @@ import com.github.unchama.targetedeffect.player.FocusedSoundEffect import com.github.unchama.targetedeffect.{SequentialEffect, UnfocusedEffect} import org.bukkit.ChatColor._ import org.bukkit.block.{Block, Chest} -import org.bukkit.entity.LivingEntity +import org.bukkit.entity.{LivingEntity, Player} import org.bukkit.event.block.{Action, BlockBreakEvent, BlockPlaceEvent} import org.bukkit.event.entity.PlayerDeathEvent import org.bukkit.event.player.{PlayerInteractEvent, PlayerJoinEvent} @@ -35,7 +36,8 @@ import scala.util.Random class AnniversaryListener( implicit effectEnvironment: EffectEnvironment, ioOnMainThread: OnMinecraftServerThread[IO], - gtToSiinaAPI: GtToSiinaAPI[ItemStack] + gtToSiinaAPI: GtToSiinaAPI[ItemStack], + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) extends Listener { @EventHandler @@ -91,7 +93,7 @@ class AnniversaryListener( // Y座標を下に動かして(木の上方から)オークの木の頂点を探し、そのブロックを置き換える (10 to 0 by -1) .map(placedBlock.getRelative(0, _, 0)) - .find(block => block.getType == Material.LOG || block.getType == Material.LEAVES) + .find(block => block.getType == Material.OAK_LOG || block.getType == Material.OAK_LEAVES) .foreach(replaceBlockOnTreeTop(_, event.getPlayer.getName)) } @@ -109,7 +111,6 @@ class AnniversaryListener( val offHandItem = player.getInventory.getItemInOffHand if (offHandItem == null) return - offHandItem.setDurability(0) removeItemfromPlayerInventory(player.getInventory, item, 1) } 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 44b8541612..75a1564a03 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,7 +1,7 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.christmas import com.github.unchama.seichiassist.subsystems.seasonalevents.christmas.Christmas.EVENT_YEAR -import de.tr7zw.itemnbtapi.NBTItem +import de.tr7zw.nbtapi.NBTItem import org.bukkit.ChatColor._ import org.bukkit.Color.fromRGB import org.bukkit.enchantments.Enchantment @@ -197,7 +197,7 @@ object ChristmasItemData { s"$RED${UNDERLINE}スポーン地点にいる村人に欲しい物を詰めてもらおう!" ).map(str => s"$RESET$str").asJava - val itemMeta = Bukkit.getItemFactory.getItemMeta(Material.INK_SACK).tap { meta => + Bukkit.getItemFactory.getItemMeta(Material.INK_SAC).tap { meta => import meta._ setDisplayName(s"${AQUA}靴下(${EVENT_YEAR}年)") setLore(loreList) @@ -206,11 +206,7 @@ object ChristmasItemData { } // 赤の染料 - val itemStack = new ItemStack(Material.INK_SACK, 1).tap { itemStack => - import itemStack._ - setDurability(1.toShort) - setItemMeta(itemMeta) - } + val itemStack = new ItemStack(Material.RED_DYE, 1) itemStack } 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 3a9efe1632..a2b12ad3bb 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 @@ -13,7 +13,7 @@ import com.github.unchama.seichiassist.util.InventoryOperations.{ isPlayerInventoryFull, removeItemfromPlayerInventory } -import de.tr7zw.itemnbtapi.NBTItem +import de.tr7zw.nbtapi.NBTItem import org.bukkit.ChatColor._ import org.bukkit.entity.EntityType._ import org.bukkit.entity.{EntityType, LivingEntity, Player} @@ -57,7 +57,7 @@ class ChristmasItemListener[F[_], G[_]: SyncEffect](instance: JavaPlugin)( val rand = new Random().nextDouble() val potionEffectType = if (rand > 0.5) PotionEffectType.LUCK else PotionEffectType.UNLUCK - player.addPotionEffect(new PotionEffect(potionEffectType, 20 * 30, 0), true) + player.addPotionEffect(new PotionEffect(potionEffectType, 20 * 30, 0)) removeItemfromPlayerInventory(player.getInventory, item, 1) @@ -74,7 +74,7 @@ class ChristmasItemListener[F[_], G[_]: SyncEffect](instance: JavaPlugin)( val rand = new Random().nextDouble() val potionEffectType = if (rand > 0.5) PotionEffectType.SPEED else PotionEffectType.SLOW - event.getPlayer.addPotionEffect(new PotionEffect(potionEffectType, 20 * 30, 0), true) + event.getPlayer.addPotionEffect(new PotionEffect(potionEffectType, 20 * 30, 0)) } @EventHandler @@ -117,7 +117,7 @@ class ChristmasItemListener[F[_], G[_]: SyncEffect](instance: JavaPlugin)( GUARDIAN, HUSK, MAGMA_CUBE, - PIG_ZOMBIE, + ZOMBIFIED_PIGLIN, SHULKER, SILVERFISH, SKELETON, @@ -186,7 +186,7 @@ class ChristmasItemListener[F[_], G[_]: SyncEffect](instance: JavaPlugin)( addItem(player, christmasSock) player.sendMessage(s"$AQUA「靴下」を見つけたよ!") } - player.playSound(player.getLocation, Sound.BLOCK_NOTE_HARP, 3.0f, 1.0f) + player.playSound(player.getLocation, Sound.BLOCK_NOTE_BLOCK_HARP, 3.0f, 1.0f) } } 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 837f2538c0..9186ba4773 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 @@ -4,6 +4,7 @@ import cats.effect.IO import com.github.unchama.contextualexecutor.builder.Parsers import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.commands.contextual.builder.BuilderTemplates.playerCommandBuilder +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.seasonalevents.anniversary.AnniversaryItemData._ import com.github.unchama.seichiassist.subsystems.seasonalevents.christmas.ChristmasItemData._ import com.github.unchama.seichiassist.subsystems.seasonalevents.halloween.HalloweenItemData._ @@ -14,7 +15,10 @@ import com.github.unchama.targetedeffect.TargetedEffect._ import org.bukkit.command.TabExecutor import org.bukkit.entity.Player -class EventCommand(implicit ioOnMainThread: OnMinecraftServerThread[IO]) { +class EventCommand( + implicit ioOnMainThread: OnMinecraftServerThread[IO], + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] +) { import com.github.unchama.targetedeffect._ 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 11ad0aeaf1..62e86be13d 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 @@ -1,7 +1,7 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.halloween import com.github.unchama.seichiassist.util.EnchantNameToJapanese -import de.tr7zw.itemnbtapi.NBTItem +import de.tr7zw.nbtapi.NBTItem import org.bukkit.ChatColor._ import org.bukkit.Color.fromRGB import org.bukkit.enchantments.Enchantment @@ -82,7 +82,7 @@ object HalloweenItemData { val year = Calendar.getInstance().get(Calendar.YEAR) val enchDescription = enchantments.map { case (ench, lvl) => - s"$RESET$GRAY${EnchantNameToJapanese.getEnchantName(ench.getName, lvl)}" + s"$RESET$GRAY${EnchantNameToJapanese.getEnchantName(ench.getKey.toString, lvl)}" }.toList val lore = List( "", 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 00c6368525..3ae83e2665 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 @@ -9,7 +9,7 @@ import com.github.unchama.seichiassist.subsystems.seasonalevents.halloween.Hallo isHalloweenHoe, isHalloweenPotion } -import com.github.unchama.util.external.WorldGuardWrapper.isRegionMember +import com.github.unchama.util.external.WorldGuardWrapper import org.bukkit.ChatColor.{DARK_GREEN, LIGHT_PURPLE, UNDERLINE} import org.bukkit.Material import org.bukkit.block.Block @@ -40,7 +40,7 @@ object HalloweenItemListener extends Listener { // 10分 event .getPlayer - .addPotionEffect(new PotionEffect(PotionEffectType.SATURATION, 20 * 60 * 10, 0), true) + .addPotionEffect(new PotionEffect(PotionEffectType.SATURATION, 20 * 60 * 10, 0)) } } @@ -62,17 +62,19 @@ object HalloweenItemListener extends Listener { val player = event.getPlayer // まず、Playerが自分でクリックしたブロックについて判定する if (!canBeReplacedWithSoil(player, clickedBlock)) return - clickedBlock.setType(Material.SOIL) + clickedBlock.setType(Material.FARMLAND) // 次にクリックされたブロックから半径4ブロック以内のブロックについて判定する for (relX <- -4 to 4; relZ <- -4 to 4) { val block = clickedBlock.getRelative(relX, 0, relZ) - if (block != null && canBeReplacedWithSoil(player, block)) block.setType(Material.SOIL) + if (block != null && canBeReplacedWithSoil(player, block)) + block.setType(Material.FARMLAND) } } private def canBeReplacedWithSoil(player: Player, block: Block) = { - (block.getType == Material.DIRT || block.getType == Material.GRASS) && isRegionMember( + (block.getType == Material.FARMLAND || block.getType == Material.GRASS_BLOCK || block + .getType() == Material.DIRT) && WorldGuardWrapper.isRegionMember( player, block.getLocation ) 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 6b69e28bed..44d0985db8 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 @@ -13,10 +13,9 @@ class JdbcLastQuitPersistenceRepository[F[_]](implicit SyncContext: Sync[F]) SyncContext.delay { DB.localTx { implicit session => sql"select lastquit from playerdata where uuid = {uuid}" - .bindByName(Symbol("uuid") -> key.toString) + .bindByName("uuid" -> key.toString) .map(_.timestampOpt("lastquit").map(_.toLocalDateTime)) .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 80785d3ea2..68e20ef15f 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,6 +3,7 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.limitedlogin import cats.effect.IO import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.subsystems.gachaprize.bukkit.factories.BukkitGachaSkullData +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.seasonalevents.limitedlogin.LimitedLoginEvent.{ START_DATE, isInEvent @@ -22,8 +23,10 @@ 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], + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] +) extends Listener { @EventHandler def onPlayerJoin(event: PlayerJoinEvent): Unit = { if (!isInEvent) return 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 92ff0f8111..09802d3fd9 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,14 +1,17 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.newyear +import cats.effect.IO import com.github.unchama.itemstackbuilder.{SkullItemStackBuilder, SkullOwnerTextureValue} +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.seasonalevents.newyear.NewYear.{ END_DATE, EVENT_YEAR, NEW_YEAR_EVE } -import de.tr7zw.itemnbtapi.NBTItem +import de.tr7zw.nbtapi.NBTItem import org.bukkit.ChatColor._ import org.bukkit.enchantments.Enchantment +import org.bukkit.entity.Player import org.bukkit.inventory.{ItemFlag, ItemStack} import org.bukkit.{Bukkit, Material} @@ -43,7 +46,7 @@ object NewYearItemData { .tap { item => import item._ setByte(NBTTagConstants.typeIdTag, 1.toByte) - setObject(NBTTagConstants.expiryDateTag, END_DATE) + setLong(NBTTagConstants.expiryDateTag, END_DATE.toEpochDay) } .pipe(_.getItem) } @@ -76,15 +79,15 @@ object NewYearItemData { itemStack } - // https://minecraft-heads.com/custom-heads/food-drinks/413-bowl-of-noodles private val soba = SkullOwnerTextureValue( "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMjY4MzRiNWIyNTQyNmRlNjM1MzhlYzgyY2E4ZmJlY2ZjYmIzZTY4MmQ4MDYzNjQzZDJlNjdhNzYyMWJkIn19fQ==" ) - val sobaHead: ItemStack = new SkullItemStackBuilder(soba) - .title(s"年越し蕎麦(${NEW_YEAR_EVE.from.getYear}年)") - .lore(List("", s"${YELLOW}大晦日記念アイテムだよ!")) - .build() + def sobaHead(implicit playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player]): ItemStack = + new SkullItemStackBuilder(soba) + .title(s"年越し蕎麦(${NEW_YEAR_EVE.from.getYear}年)") + .lore(List("", s"${YELLOW}大晦日記念アイテムだよ!")) + .build() object NBTTagConstants { val typeIdTag = "newYearItemTypeId" 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 a09d4ac3a8..012a02cbcf 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 @@ -7,6 +7,7 @@ import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.ManagedWorld._ import com.github.unchama.seichiassist.MaterialSets import com.github.unchama.seichiassist.subsystems.mana.ManaWriteApi +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI 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._ @@ -20,7 +21,7 @@ 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 -import de.tr7zw.itemnbtapi.NBTItem +import de.tr7zw.nbtapi.NBTItem import org.bukkit.ChatColor._ import org.bukkit.Sound import org.bukkit.entity.Player @@ -35,7 +36,8 @@ class NewYearListener[F[_]: ConcurrentEffect: NonServerThreadContextShift, G[_]: implicit effectEnvironment: EffectEnvironment, repository: LastQuitPersistenceRepository[F, UUID], manaApi: ManaWriteApi[G, Player], - ioOnMainThread: OnMinecraftServerThread[IO] + ioOnMainThread: OnMinecraftServerThread[IO], + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) extends Listener { import cats.implicits._ @@ -91,7 +93,7 @@ class NewYearListener[F[_]: ConcurrentEffect: NonServerThreadContextShift, G[_]: val player = event.getPlayer val today = LocalDate.now() val expiryDate = - new NBTItem(item).getObject(NBTTagConstants.expiryDateTag, classOf[LocalDate]) + LocalDate.ofEpochDay(new NBTItem(item).getLong(NBTTagConstants.expiryDateTag)) if (today.isBefore(expiryDate) || today.isEqual(expiryDate)) { // マナを10%回復する manaApi.manaAmount(player).restoreFraction(0.1).runSync[SyncIO].unsafeRunSync() @@ -117,7 +119,7 @@ class NewYearListener[F[_]: ConcurrentEffect: NonServerThreadContextShift, G[_]: addItem(player, newYearBag) player.sendMessage(s"$AQUA「お年玉袋」を見つけたよ!") } - player.playSound(player.getLocation, Sound.BLOCK_NOTE_HARP, 3.0f, 1.0f) + player.playSound(player.getLocation, Sound.BLOCK_NOTE_BLOCK_HARP, 3.0f, 1.0f) } } } 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 d5a72532db..b0e0e3db04 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 @@ -1,7 +1,7 @@ package com.github.unchama.seichiassist.subsystems.seasonalevents.seizonsiki import com.github.unchama.seichiassist.subsystems.seasonalevents.seizonsiki.Seizonsiki.END_DATE -import de.tr7zw.itemnbtapi.NBTItem +import de.tr7zw.nbtapi.NBTItem import org.bukkit.ChatColor._ import org.bukkit.inventory.ItemStack import org.bukkit.{Bukkit, Material} @@ -36,7 +36,7 @@ object SeizonsikiItemData { .tap { item => import item._ setByte(NBTTagConstants.typeIdTag, 1.toByte) - setObject(NBTTagConstants.expiryDateTag, END_DATE) + setLong(NBTTagConstants.expiryDateTag, END_DATE.toEpochDay) } .pipe(_.getItem) } 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 e23d9fa1c2..40211b4de8 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,9 +6,9 @@ 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.SendMessageEffect.sendMessageToEveryoneIgnoringPreference +import com.github.unchama.seichiassist.util.SendMessageEffect.sendMessageToEveryoneIgnoringPreferenceIO import com.github.unchama.seichiassist.util.EntityDeathCause.isEntityKilledByThornsEnchant -import de.tr7zw.itemnbtapi.NBTItem +import de.tr7zw.nbtapi.NBTItem import org.bukkit.ChatColor.{DARK_GREEN, LIGHT_PURPLE, UNDERLINE} import org.bukkit.Sound import org.bukkit.entity.{EntityType, Player} @@ -56,7 +56,7 @@ class SeizonsikiListener[F[_], G[_]: SyncEffect](implicit manaApi: ManaWriteApi[ val player = event.getPlayer val today = LocalDate.now() - val exp = new NBTItem(item).getObject(NBTTagConstants.expiryDateTag, classOf[LocalDate]) + val exp = LocalDate.ofEpochDay(new NBTItem(item).getLong(NBTTagConstants.expiryDateTag)) if (today.isBefore(exp)) { // マナを10%回復する manaApi.manaAmount(player).restoreFraction(0.1).runSync[SyncIO].unsafeRunSync() @@ -67,7 +67,8 @@ class SeizonsikiListener[F[_], G[_]: SyncEffect](implicit manaApi: ManaWriteApi[ player.setHealth(0) val messages = deathMessages(player.getName) - sendMessageToEveryoneIgnoringPreference(messages(new Random().nextInt(messages.size))) + sendMessageToEveryoneIgnoringPreferenceIO(messages(new Random().nextInt(messages.size))) + .unsafeRunAsyncAndForget() } } } 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 80a4411381..f1b2d369a2 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 @@ -5,13 +5,13 @@ import com.github.unchama.seichiassist.subsystems.seasonalevents.valentine.Valen EVENT_DURATION, EVENT_YEAR } -import de.tr7zw.itemnbtapi.NBTItem +import de.tr7zw.nbtapi.NBTItem import org.bukkit.ChatColor._ import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.SkullMeta import org.bukkit.{Bukkit, Material} -import java.time.LocalDateTime +import java.time.{LocalDateTime, ZoneOffset} import java.util.UUID import scala.jdk.CollectionConverters._ import scala.util.chaining._ @@ -43,9 +43,12 @@ object ValentineItemData { */ def isUsableCookie(item: ItemStack): Boolean = { val now = LocalDateTime.now() - val exp = Option( - new NBTItem(item).getObject(NBTTagConstants.expiryDateTimeTag, classOf[LocalDateTime]) - ).getOrElse(return false) + val exp = LocalDateTime.ofEpochSecond( + Option(new NBTItem(item).getLong(NBTTagConstants.expiryDateTimeTag)) + .getOrElse(return false), + 0, + ZoneOffset.of("+9") + ) now.isBefore(exp) || now.isEqual(exp) } @@ -74,7 +77,10 @@ object ValentineItemData { .tap { item => import item._ setByte(NBTTagConstants.typeIdTag, droppedCookieTypeId.toByte) - setObject(NBTTagConstants.expiryDateTimeTag, EVENT_DURATION.to) + setLong( + NBTTagConstants.expiryDateTimeTag, + EVENT_DURATION.to.toEpochSecond(ZoneOffset.of("+9")) + ) } .pipe(_.getItem) } @@ -113,8 +119,11 @@ object ValentineItemData { .tap { item => import item._ setByte(NBTTagConstants.typeIdTag, giftedCookieTypeId.toByte) - setObject(NBTTagConstants.expiryDateTimeTag, EVENT_DURATION.to) - setObject(NBTTagConstants.producerUuidTag, playerUuid) + setLong( + NBTTagConstants.expiryDateTimeTag, + EVENT_DURATION.to.toEpochSecond(ZoneOffset.of("+9")) + ) + setUUID(NBTTagConstants.producerUuidTag, playerUuid) setString(NBTTagConstants.producerNameTag, playerName) } .pipe(_.getItem) @@ -128,7 +137,7 @@ object ValentineItemData { * @return UUIDが設定されていれば[[Some]]、なければ[[None]] */ def ownerOf(item: ItemStack): Option[UUID] = - Option(new NBTItem(item).getObject(NBTTagConstants.producerUuidTag, classOf[UUID])) + Option(new NBTItem(item).getOrNull[UUID](NBTTagConstants.producerUuidTag, classOf[UUID])) def deathMessages(playerName: String, cookieProducerName: String): Seq[String] = Seq( s"${playerName}は${cookieProducerName}のチョコチップクッキーを食べた!猟奇的な味だった。", 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 689ed38b76..3c2050bfca 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 @@ -10,11 +10,11 @@ import com.github.unchama.seichiassist.subsystems.seasonalevents.valentine.Valen import com.github.unchama.seichiassist.subsystems.seasonalevents.valentine.ValentineCookieEffectsHandler._ import com.github.unchama.seichiassist.subsystems.seasonalevents.valentine.ValentineItemData._ import com.github.unchama.seichiassist.util.InventoryOperations.grantItemStacksEffect -import com.github.unchama.seichiassist.util.SendMessageEffect.sendMessageToEveryoneIgnoringPreference +import com.github.unchama.seichiassist.util.SendMessageEffect.sendMessageToEveryoneIgnoringPreferenceIO import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.player.FocusedSoundEffect import com.github.unchama.targetedeffect.{SequentialEffect, TargetedEffect} -import de.tr7zw.itemnbtapi.NBTItem +import de.tr7zw.nbtapi.NBTItem import org.bukkit.ChatColor._ import org.bukkit.Sound import org.bukkit.attribute.Attribute @@ -56,7 +56,7 @@ class ValentineListener[F[_]: ConcurrentEffect: NonServerThreadContextShift]( val damager = event.getDamager if (damager == null || damager.getType != EntityType.CREEPER) return - val excludedMonsters = Set(EntityType.WITCH, EntityType.PIG_ZOMBIE) + val excludedMonsters = Set(EntityType.WITCH, EntityType.ZOMBIFIED_PIGLIN) event.getEntity match { case damaged: Monster if !excludedMonsters.contains(damaged.getType) => val entityMaxHealth = damaged.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue @@ -139,7 +139,8 @@ class ValentineListener[F[_]: ConcurrentEffect: NonServerThreadContextShift]( player.getName, new NBTItem(item).getString(NBTTagConstants.producerNameTag) ) - sendMessageToEveryoneIgnoringPreference(messages(new Random().nextInt(messages.size))) + sendMessageToEveryoneIgnoringPreferenceIO(messages(new Random().nextInt(messages.size))) + .unsafeRunAsyncAndForget() } 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 abbaca504c..aab6684b24 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 @@ -1,7 +1,6 @@ package com.github.unchama.seichiassist.subsystems.seichilevelupgift import cats.effect.Async -import com.github.unchama.generic.ContextCoercion import com.github.unchama.generic.effect.stream.StreamExtra import com.github.unchama.minecraft.actions.{OnMinecraftServerThread, SendMinecraftMessage} import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountReadAPI @@ -17,9 +16,7 @@ import org.bukkit.inventory.ItemStack object System { - def backGroundProcess[F[_]: OnMinecraftServerThread: ErrorLogger: Async, G[ - _ - ]: ContextCoercion[*[_], F]]( + def backGroundProcess[F[_]: OnMinecraftServerThread: ErrorLogger: Async, G[_]]( implicit breakCountReadApi: BreakCountReadAPI[F, G, Player], send: SendMinecraftMessage[F, Player], gachaPrizeAPI: GachaPrizeAPI[F, ItemStack, Player], diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/bukkit/BukkitGrantLevelUpGift.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/bukkit/BukkitGrantLevelUpGift.scala index b5437bdd97..473fa15998 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/bukkit/BukkitGrantLevelUpGift.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/seichilevelupgift/bukkit/BukkitGrantLevelUpGift.scala @@ -1,8 +1,6 @@ package com.github.unchama.seichiassist.subsystems.seichilevelupgift.bukkit import cats.data.Kleisli -import cats.effect.Sync -import com.github.unchama.generic.ContextCoercion import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.data.ItemData import com.github.unchama.seichiassist.subsystems.gacha.GachaDrawAPI @@ -17,9 +15,7 @@ import com.github.unchama.seichiassist.util.InventoryOperations.grantItemStacksE import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack -class BukkitGrantLevelUpGift[F[_]: Sync: OnMinecraftServerThread, G[_]: ContextCoercion[*[ - _ -], F]]( +class BukkitGrantLevelUpGift[F[_]: OnMinecraftServerThread, G[_]]( implicit gachaPointApi: GachaPointApi[F, G, Player], gachaPrizeAPI: GachaPrizeAPI[F, ItemStack, Player], gachaDrawAPI: GachaDrawAPI[F, Player] @@ -43,7 +39,7 @@ class BukkitGrantLevelUpGift[F[_]: Sync: OnMinecraftServerThread, G[_]: ContextC object BukkitGrantLevelUpGift { - implicit def apply[F[_]: Sync: OnMinecraftServerThread, G[_]: ContextCoercion[*[_], F]]( + implicit def apply[F[_]: OnMinecraftServerThread, G[_]]( implicit gachaPrizeAPI: GachaPrizeAPI[F, ItemStack, Player], gachaPointApi: GachaPointApi[F, G, Player], gachaDrawAPI: GachaDrawAPI[F, Player] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/sharedinventory/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/sharedinventory/System.scala index f9758db42b..9c8ca2dbab 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/sharedinventory/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/sharedinventory/System.scala @@ -1,6 +1,7 @@ package com.github.unchama.seichiassist.subsystems.sharedinventory import cats.effect.{Concurrent, ConcurrentEffect, Timer} +import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.meta.subsystem.Subsystem import com.github.unchama.seichiassist.subsystems.sharedinventory.bukkit.command.ShareInventoryCommand import com.github.unchama.seichiassist.subsystems.sharedinventory.domain.bukkit.InventoryContents @@ -23,7 +24,8 @@ object System { import cats.implicits._ - def wired[F[_]: ConcurrentEffect: Timer, G[_]: Concurrent]: G[System[F]] = { + def wired[F[_]: ConcurrentEffect: Timer: OnMinecraftServerThread, G[_]: Concurrent] + : G[System[F]] = { implicit val persistence: SharedInventoryPersistence[F] = new JdbcSharedInventoryPersistence[F] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/sharedinventory/bukkit/command/ShareInventoryCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/sharedinventory/bukkit/command/ShareInventoryCommand.scala index 93347afcdd..7cf7009c98 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/sharedinventory/bukkit/command/ShareInventoryCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/sharedinventory/bukkit/command/ShareInventoryCommand.scala @@ -1,106 +1,104 @@ package com.github.unchama.seichiassist.subsystems.sharedinventory.bukkit.command -import cats.effect.ConcurrentEffect.ops.toAllConcurrentEffectOps -import cats.effect.{ConcurrentEffect, IO, Sync} +import cats.data.Kleisli +import cats.effect.{ConcurrentEffect, Sync} +import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.commands.contextual.builder.BuilderTemplates.playerCommandBuilder -import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.onMainThread import com.github.unchama.seichiassist.subsystems.sharedinventory.SharedInventoryAPI import com.github.unchama.seichiassist.subsystems.sharedinventory.domain.SharedFlag import com.github.unchama.seichiassist.subsystems.sharedinventory.domain.bukkit.InventoryContents import com.github.unchama.seichiassist.util.InventoryOperations -import com.github.unchama.targetedeffect.commandsender.MessageEffect -import com.github.unchama.targetedeffect.player.CommandEffect -import com.github.unchama.targetedeffect.{SequentialEffect, TargetedEffect} +import com.github.unchama.targetedeffect.commandsender.MessageEffectF +import com.github.unchama.targetedeffect.player.CommandEffectF +import com.github.unchama.targetedeffect.{SequentialEffect, TargetedEffectF} import org.bukkit.ChatColor._ import org.bukkit.command.TabExecutor import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack import org.bukkit.{Bukkit, Material} -class ShareInventoryCommand[F[_]: ConcurrentEffect]( +class ShareInventoryCommand[F[_]: ConcurrentEffect: OnMinecraftServerThread]( implicit sharedInventoryAPI: SharedInventoryAPI[F, Player] ) { import cats.implicits._ val executor: TabExecutor = playerCommandBuilder - .buildWithExecutionF { context => + .buildWithExecutionCSEffect { context => val sender = context.sender - for { - // TODO: this `toIO` is not needed - sharedFlag <- sharedInventoryAPI.sharedFlag(sender).toIO - eff <- - if (sharedFlag == SharedFlag.Sharing) { - withdrawFromSharedInventory(sender) - } else { - depositToSharedInventory(sender) - } - } yield eff + + Kleisli.liftF(sharedInventoryAPI.sharedFlag(sender)).flatMap { sharedFlag => + if (sharedFlag == SharedFlag.Sharing) { + withdrawFromSharedInventory(sender) + } else { + depositToSharedInventory(sender) + } + } } .asNonBlockingTabExecutor() - private def withdrawFromSharedInventory(player: Player): IO[TargetedEffect[Player]] = { + private def withdrawFromSharedInventory(player: Player): TargetedEffectF[F, Player] = { val uuid = player.getUniqueId - val eff = for { - oldSharedFlag <- sharedInventoryAPI.sharedFlag(player) - loadedInventory <- sharedInventoryAPI.load(uuid) - _ <- sharedInventoryAPI.clear(uuid) - newSharedFlag <- sharedInventoryAPI.sharedFlag(player) - _ <- Sync[F] - .delay { - val playerInventory = player.getInventory - val inventoryContents = loadedInventory.get.inventoryContents - // 手持ちのアイテムをドロップする - playerInventory - .getContents - .filterNot(itemStack => itemStack == null || itemStack.getType == Material.AIR) - .foreach(itemStack => dropIfNotEmpty(Some(itemStack), player)) - // 取り出したアイテムをセットする - playerInventory.setContents(inventoryContents.toArray) - Bukkit.getLogger.info(s"${player.getName}がアイテム取り出しを実施(DB)書き換え成功") - } - .whenA(oldSharedFlag != newSharedFlag && loadedInventory.nonEmpty) + (for { + oldSharedFlag <- Kleisli.liftF(sharedInventoryAPI.sharedFlag(player)) + loadedInventory <- Kleisli.liftF(sharedInventoryAPI.load(uuid)) + _ <- Kleisli.liftF(sharedInventoryAPI.clear(uuid)) + newSharedFlag <- Kleisli.liftF(sharedInventoryAPI.sharedFlag(player)) + _ <- Kleisli.liftF( + Sync[F] + .delay { + val playerInventory = player.getInventory + val inventoryContents = loadedInventory.get.inventoryContents + // 手持ちのアイテムをドロップする + playerInventory + .getContents + .filterNot(itemStack => itemStack == null || itemStack.getType == Material.AIR) + .foreach(itemStack => dropIfNotEmpty(Some(itemStack), player)) + // 取り出したアイテムをセットする + playerInventory.setContents(inventoryContents.toArray) + Bukkit.getLogger.info(s"${player.getName}がアイテム取り出しを実施(DB)書き換え成功") + } + .whenA(oldSharedFlag != newSharedFlag && loadedInventory.nonEmpty) + ) } yield { if (oldSharedFlag != newSharedFlag && loadedInventory.nonEmpty) - MessageEffect(s"${GREEN}アイテムを取得しました。手持ちにあったアイテムはドロップしました。") + MessageEffectF(s"${GREEN}アイテムを取得しました。手持ちにあったアイテムはドロップしました。") else if (oldSharedFlag == newSharedFlag) - MessageEffect(s"$RESET$RED${BOLD}もう少し待ってからアイテム取り出しを行ってください。") + MessageEffectF(s"$RESET$RED${BOLD}もう少し待ってからアイテム取り出しを行ってください。") else - MessageEffect(s"$RESET$RED${BOLD}収納アイテムが存在しません。") - } - - eff.toIO + MessageEffectF(s"$RESET$RED${BOLD}収納アイテムが存在しません。") + }).flatten } - private def depositToSharedInventory(player: Player): IO[TargetedEffect[Player]] = { + private def depositToSharedInventory(player: Player): TargetedEffectF[F, Player] = { val uuid = player.getUniqueId val playerInventory = player.getInventory val inventoryContents = playerInventory.getContents.toList if (inventoryContents.forall(_ == null)) - return IO.pure(MessageEffect(s"$RESET$RED${BOLD}収納アイテムが存在しません。")) + return MessageEffectF(s"$RESET$RED${BOLD}収納アイテムが存在しません。") - val eff = for { - oldSharedFlag <- sharedInventoryAPI.sharedFlag(player) - _ <- sharedInventoryAPI.save(uuid, InventoryContents(inventoryContents)) - newSharedFlag <- sharedInventoryAPI.sharedFlag(player) - _ <- Sync[F] - .delay { - playerInventory.clear() - Bukkit.getLogger.info(s"${player.getName}がアイテム収納を実施(SQL送信成功)") - } - .whenA(oldSharedFlag != newSharedFlag) + (for { + oldSharedFlag <- Kleisli.liftF(sharedInventoryAPI.sharedFlag(player)) + _ <- Kleisli.liftF(sharedInventoryAPI.save(uuid, InventoryContents(inventoryContents))) + newSharedFlag <- Kleisli.liftF(sharedInventoryAPI.sharedFlag(player)) + _ <- Kleisli.liftF( + Sync[F] + .delay { + playerInventory.clear() + Bukkit.getLogger.info(s"${player.getName}がアイテム収納を実施(SQL送信成功)") + } + .whenA(oldSharedFlag != newSharedFlag) + ) } yield { if (oldSharedFlag == newSharedFlag) - MessageEffect(s"$RESET$RED${BOLD}もう少し待ってからアイテムを収納してください。") + MessageEffectF(s"$RESET$RED${BOLD}もう少し待ってからアイテムを収納してください。") else SequentialEffect( - CommandEffect("stick"), - MessageEffect(s"${GREEN}アイテムを収納しました。10秒以上あとに、手持ちを空にして取り出してください。") + CommandEffectF("stick"), + MessageEffectF(s"${GREEN}アイテムを収納しました。10秒以上あとに、手持ちを空にして取り出してください。") ) - } - - eff.toIO + }).flatten } private def dropIfNotEmpty(itemStackOption: Option[ItemStack], to: Player): Unit = diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/sharedinventory/infrastracture/JdbcSharedInventoryPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/sharedinventory/infrastracture/JdbcSharedInventoryPersistence.scala index cb918de45a..5571d88a93 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/sharedinventory/infrastracture/JdbcSharedInventoryPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/sharedinventory/infrastracture/JdbcSharedInventoryPersistence.scala @@ -17,9 +17,7 @@ class JdbcSharedInventoryPersistence[F[_]: Sync] extends SharedInventoryPersiste */ override def clear(targetUuid: UUID): F[Unit] = Sync[F].delay { DB.localTx { implicit session => - sql"UPDATE playerdata SET shareinv = NULL WHERE uuid = ${targetUuid.toString}" - .execute() - .apply() + sql"UPDATE playerdata SET shareinv = NULL WHERE uuid = ${targetUuid.toString}".execute() } } @@ -32,7 +30,6 @@ class JdbcSharedInventoryPersistence[F[_]: Sync] extends SharedInventoryPersiste sql"SELECT shareinv FROM playerdata WHERE uuid = ${targetUuid.toString}" .map(rs => rs.string("shareinv")) .single() - .apply() serializedInventoryOpt.map(serializedInventory => InventoryContents.ofNonEmpty( @@ -56,7 +53,6 @@ class JdbcSharedInventoryPersistence[F[_]: Sync] extends SharedInventoryPersiste ItemListSerialization.serializeToBase64(inventoryContents.inventoryContents.asJava) sql"UPDATE playerdata SET shareinv = $serializedInventory WHERE uuid = ${targetUuid.toString}" .execute() - .apply() } } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gachatrade/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gachatrade/System.scala index 5c636362f7..df917093cc 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gachatrade/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gachatrade/System.scala @@ -1,6 +1,6 @@ package com.github.unchama.seichiassist.subsystems.tradesystems.subsystems.gachatrade -import cats.effect.ConcurrentEffect +import cats.effect.{ConcurrentEffect, IO} import com.github.unchama.seichiassist.meta.subsystem.Subsystem import com.github.unchama.seichiassist.subsystems.gachapoint.GachaPointApi import com.github.unchama.seichiassist.subsystems.gachaprize.GachaPrizeAPI @@ -8,6 +8,7 @@ import com.github.unchama.seichiassist.subsystems.gachaprize.domain.{ CanBeSignedAsGachaPrize, GachaPrizeTableEntry } +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.tradesystems.subsystems.gachatrade.bukkit.listeners.GachaTradeListener import com.github.unchama.seichiassist.subsystems.tradesystems.subsystems.gachatrade.bukkit.traderules.BukkitTrade import com.github.unchama.seichiassist.subsystems.tradesystems.subsystems.gachatrade.domain.{ @@ -22,7 +23,8 @@ object System { def wired[F[_]: ConcurrentEffect, G[_]]( implicit gachaPrizeAPI: GachaPrizeAPI[F, ItemStack, Player], - gachaPointApi: GachaPointApi[F, G, Player] + gachaPointApi: GachaPointApi[F, G, Player], + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ): Subsystem[F] = { implicit val canBeSignedAsGachaPrize: CanBeSignedAsGachaPrize[ItemStack] = gachaPrizeAPI.canBeSignedAsGachaPrize diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gachatrade/bukkit/listeners/GachaTradeListener.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gachatrade/bukkit/listeners/GachaTradeListener.scala index dc31b743fe..4cb3e1dfce 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gachatrade/bukkit/listeners/GachaTradeListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gachatrade/bukkit/listeners/GachaTradeListener.scala @@ -39,7 +39,7 @@ class GachaTradeListener[F[_]: ConcurrentEffect, G[_]](rule: GachaTradeRule[Item // インベントリサイズが6列でない時終了 if (inventory.row != 6) return - if (inventory.getTitle != s"$LIGHT_PURPLE${BOLD}交換したい景品を入れてください") return + if (event.getView.getTitle != s"$LIGHT_PURPLE${BOLD}交換したい景品を入れてください") return // 交換後の情報 val tradedInformation = diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gachatrade/bukkit/traderules/BukkitTrade.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gachatrade/bukkit/traderules/BukkitTrade.scala index 9bfae53479..a56d2389c9 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gachatrade/bukkit/traderules/BukkitTrade.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gachatrade/bukkit/traderules/BukkitTrade.scala @@ -1,6 +1,6 @@ package com.github.unchama.seichiassist.subsystems.tradesystems.subsystems.gachatrade.bukkit.traderules -import com.github.unchama.generic.ListExtra +import cats.effect.IO import com.github.unchama.seichiassist.subsystems.gachaprize.bukkit.factories.BukkitGachaSkullData import com.github.unchama.seichiassist.subsystems.gachaprize.domain.GachaRarity.GachaRarity import com.github.unchama.seichiassist.subsystems.gachaprize.domain.GachaRarity.GachaRarity._ @@ -8,11 +8,13 @@ import com.github.unchama.seichiassist.subsystems.gachaprize.domain.{ CanBeSignedAsGachaPrize, GachaPrizeTableEntry } +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.tradesystems.domain.{ TradeResult, TradeRule, TradeSuccessResult } +import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack sealed trait BigOrRegular @@ -26,7 +28,8 @@ object BigOrRegular { } class BukkitTrade(owner: String, gachaPrizeTable: Vector[GachaPrizeTableEntry[ItemStack]])( - implicit canBeSignedAsGachaPrize: CanBeSignedAsGachaPrize[ItemStack] + implicit canBeSignedAsGachaPrize: CanBeSignedAsGachaPrize[ItemStack], + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) extends TradeRule[ItemStack, (BigOrRegular, Int)] { /** @@ -44,7 +47,7 @@ class BukkitTrade(owner: String, gachaPrizeTable: Vector[GachaPrizeTableEntry[It } val (nonTradable, tradable) = - ListExtra.partitionWith(contents) { itemStack => + contents.partitionMap { itemStack => if (bigList.exists(_.isSimilar(itemStack))) Right(BigOrRegular.Big -> itemStack.getAmount) else if (regularList.exists(_.isSimilar(itemStack))) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gttosiina/bukkit/BukkitStaticTradeItemFactory.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gttosiina/bukkit/BukkitStaticTradeItemFactory.scala index 58b97594fb..aaa05f58b2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gttosiina/bukkit/BukkitStaticTradeItemFactory.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gttosiina/bukkit/BukkitStaticTradeItemFactory.scala @@ -11,9 +11,8 @@ import scala.util.chaining.scalaUtilChainingOps object BukkitStaticTradeItemFactory extends StaticTradeItemFactory[ItemStack] { override val getMaxRingo: String => ItemStack = (name: String) => - new ItemStack(Material.GOLDEN_APPLE, 1).tap { itemStack => + new ItemStack(Material.ENCHANTED_GOLDEN_APPLE, 1).tap { itemStack => import itemStack._ - setDurability(1.toShort) val meta = getItemMeta meta.setDisplayName(s"$YELLOW$BOLD${ITALIC}椎名林檎") meta.setLore( diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gttosiina/bukkit/listeners/GtToSiinaringo.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gttosiina/bukkit/listeners/GtToSiinaringo.scala index e09ac172a9..1b1b1c65e4 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gttosiina/bukkit/listeners/GtToSiinaringo.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/tradesystems/subsystems/gttosiina/bukkit/listeners/GtToSiinaringo.scala @@ -36,7 +36,7 @@ class GtToSiinaringo[F[_]: ConcurrentEffect]( // インベントリサイズが4列でない時終了 if (inventory.row != 4) return - if (inventory.getTitle != s"$GOLD${BOLD}椎名林檎と交換したい景品を入れてネ") return + if (event.getView.getTitle != s"$GOLD${BOLD}椎名林檎と交換したい景品を入れてネ") return // 交換後の情報 val tradedInformation = new BukkitTrade(name, gachaPrizeAPI.allGachaPrizeList.toIO.unsafeRunSync()) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/System.scala index 6ddec4ac5c..a12e0ca6a1 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/System.scala @@ -1,10 +1,11 @@ package com.github.unchama.seichiassist.subsystems.vote import cats.data.Kleisli -import cats.effect.{ConcurrentEffect, SyncEffect} +import cats.effect.{ConcurrentEffect, IO, SyncEffect} import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.meta.subsystem.Subsystem import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountAPI +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.vote.application.actions.ReceiveVoteBenefits import com.github.unchama.seichiassist.subsystems.vote.bukkit.actions.BukkitReceiveVoteBenefits import com.github.unchama.seichiassist.subsystems.vote.bukkit.command.VoteCommand @@ -24,7 +25,8 @@ trait System[F[_], Player] extends Subsystem[F] { object System { def wired[F[_]: ConcurrentEffect: OnMinecraftServerThread, G[_]: SyncEffect]( - implicit breakCountAPI: BreakCountAPI[F, G, Player] + implicit breakCountAPI: BreakCountAPI[F, G, Player], + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ): System[F, Player] = { implicit val _votePersistence: VotePersistence[F] = new JdbcVotePersistence[F] val _receiveVoteBenefits: ReceiveVoteBenefits[F, Player] = diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/bukkit/actions/BukkitReceiveVoteBenefits.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/bukkit/actions/BukkitReceiveVoteBenefits.scala index 5ce674f990..2a9c04d100 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/bukkit/actions/BukkitReceiveVoteBenefits.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/bukkit/actions/BukkitReceiveVoteBenefits.scala @@ -1,11 +1,12 @@ package com.github.unchama.seichiassist.subsystems.vote.bukkit.actions -import cats.effect.{Sync, SyncEffect} +import cats.effect.{IO, Sync, SyncEffect} import com.github.unchama.generic.ContextCoercion import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.data.ItemData import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountAPI import com.github.unchama.seichiassist.subsystems.gachaprize.bukkit.factories.BukkitGachaSkullData +import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.seichiassist.subsystems.vote.application.actions.ReceiveVoteBenefits import com.github.unchama.seichiassist.subsystems.vote.domain.{ EffectPoint, @@ -19,7 +20,8 @@ class BukkitReceiveVoteBenefits[F[_]: OnMinecraftServerThread: Sync, G[ _ ]: SyncEffect: ContextCoercion[*[_], F]]( implicit votePersistence: VotePersistence[F], - breakCountAPI: BreakCountAPI[F, G, Player] + breakCountAPI: BreakCountAPI[F, G, Player], + playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) extends ReceiveVoteBenefits[F, Player] { import cats.implicits._ diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/infrastructure/JdbcVotePersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/infrastructure/JdbcVotePersistence.scala index 622fb63773..4ee45b8eb2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/infrastructure/JdbcVotePersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/infrastructure/JdbcVotePersistence.scala @@ -16,7 +16,7 @@ class JdbcVotePersistence[F[_]: Sync] extends VotePersistence[F] { sql"""INSERT IGNORE INTO vote | (uuid, vote_number, chain_vote_number, effect_point, given_effect_point, last_vote) | VALUES - | (${uuid.toString}, 0, 0, 0, 0, NULL)""".stripMargin.execute().apply() + | (${uuid.toString}, 0, 0, 0, 0, NULL)""".stripMargin.execute() } } @@ -24,7 +24,6 @@ class JdbcVotePersistence[F[_]: Sync] extends VotePersistence[F] { DB.localTx { implicit session => sql"UPDATE vote SET vote_number = vote_number + 1 WHERE uuid = (SELECT uuid FROM playerdata WHERE uuid = ${uuid.toString})" .execute() - .apply() } } @@ -33,7 +32,6 @@ class JdbcVotePersistence[F[_]: Sync] extends VotePersistence[F] { val votePoint = sql"SELECT vote_number FROM vote WHERE uuid = ${uuid.toString}" .map(_.int("vote_number")) .single() - .apply() .get VoteCount(votePoint) } @@ -54,7 +52,6 @@ class JdbcVotePersistence[F[_]: Sync] extends VotePersistence[F] { | WHERE uuid = (SELECT uuid FROM playerdata WHERE uuid = ${uuid.toString})""" .stripMargin .execute() - .apply() } } @@ -65,7 +62,6 @@ class JdbcVotePersistence[F[_]: Sync] extends VotePersistence[F] { sql"SELECT chain_vote_number FROM vote WHERE uuid = ${uuid.toString}" .map(_.int("chain_vote_number")) .single() - .apply() .get ChainVoteDayNumber(chainVoteDays) } @@ -76,7 +72,6 @@ class JdbcVotePersistence[F[_]: Sync] extends VotePersistence[F] { DB.localTx { implicit session => sql"UPDATE vote SET effect_point = effect_point + ${effectPoint.value} WHERE uuid = ${uuid.toString}" .execute() - .apply() } } @@ -85,7 +80,6 @@ class JdbcVotePersistence[F[_]: Sync] extends VotePersistence[F] { DB.localTx { implicit session => sql"UPDATE vote SET effect_point = effect_point - ${effectPoint.value} WHERE uuid = ${uuid.toString}" .execute() - .apply() } } @@ -94,7 +88,6 @@ class JdbcVotePersistence[F[_]: Sync] extends VotePersistence[F] { val effectPoints = sql"SELECT effect_point FROM vote WHERE uuid = ${uuid.toString}" .map(_.int("effect_point")) .single() - .apply() .get EffectPoint(effectPoints) } @@ -104,7 +97,6 @@ class JdbcVotePersistence[F[_]: Sync] extends VotePersistence[F] { DB.localTx { implicit session => sql"UPDATE vote SET given_effect_point = given_effect_point + ${benefit.value} WHERE uuid = ${uuid.toString}" .execute() - .apply() } } @@ -113,7 +105,6 @@ class JdbcVotePersistence[F[_]: Sync] extends VotePersistence[F] { val benefits = sql"SELECT given_effect_point FROM vote WHERE uuid = ${uuid.toString}" .map(_.int("given_effect_point")) .single() - .apply() .get ReceivedVoteCount(benefits) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/subsystems/fairy/infrastructure/JdbcFairyPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/subsystems/fairy/infrastructure/JdbcFairyPersistence.scala index f7f39eb8f9..e7a5f91d2e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/subsystems/fairy/infrastructure/JdbcFairyPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/subsystems/fairy/infrastructure/JdbcFairyPersistence.scala @@ -14,13 +14,12 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { sql"SELECT COUNT(*) as c FROM vote_fairy where uuid = ${player.toString}" .map(_.int("c")) .single() - .apply() .getOrElse(0) } if (playerDataCount == 0) { DB.localTx { implicit session => - sql"INSERT INTO vote_fairy (uuid) VALUES (${player.toString})".execute().apply() + sql"INSERT INTO vote_fairy (uuid) VALUES (${player.toString})".execute() } } } @@ -33,7 +32,6 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { DB.localTx { implicit session => sql"UPDATE vote_fairy SET apple_open_state = ${openState.serializedValue} WHERE uuid = ${player.toString}" .execute() - .apply() } } @@ -43,7 +41,6 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { sql"SELECT apple_open_state FROM vote_fairy WHERE uuid = ${player.toString}" .map(_.int("apple_open_state")) .single() - .apply() .get } FairyAppleConsumeStrategy.values.find(_.serializedValue == serializedValue).get @@ -54,7 +51,6 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { DB.localTx { implicit session => sql"UPDATE vote_fairy SET fairy_summon_cost = ${fairySummonCost.value} WHERE uuid = ${player.toString}" .execute() - .apply() } } @@ -64,7 +60,6 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { sql"SELECT fairy_summon_cost FROM vote_fairy WHERE uuid = ${player.toString}" .map(_.int("fairy_summon_cost")) .single() - .apply() .get FairySummonCost(fairySummonCost) } @@ -77,7 +72,6 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { | SET is_fairy_using = $isFairyUsing WHERE uuid = ${player.toString}""" .stripMargin .execute() - .apply() } } @@ -86,7 +80,6 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { sql"SELECT is_fairy_using FROM vote_fairy WHERE uuid = ${player.toString}" .map(_.boolean("is_fairy_using")) .single() - .apply() }.get } @@ -97,7 +90,6 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { DB.localTx { implicit session => sql"UPDATE vote_fairy SET fairy_recovery_mana_value = ${fairyRecoveryMana.recoveryMana} WHERE uuid = ${player.toString}" .execute() - .apply() } } @@ -107,7 +99,6 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { sql"SELECT fairy_recovery_mana_value FROM vote_fairy WHERE uuid = ${player.toString}" .map(_.int("fairy_recovery_mana_value")) .single() - .apply() .get FairyRecoveryMana(recoveryMana) } @@ -118,7 +109,6 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { DB.localTx { implicit session => sql"UPDATE vote_fairy SET fairy_end_time = ${fairyEndTime.endTime} WHERE uuid = ${player.toString}" .execute() - .apply() } } @@ -127,7 +117,6 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { val dateOpt = sql"SELECT fairy_end_time FROM vote_fairy WHERE uuid = ${player.toString}" .map(_.localDateTime("fairy_end_time")) .single() - .apply() dateOpt.map(FairyEndTime) } } @@ -140,7 +129,6 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { DB.localTx { implicit session => sql"UPDATE vote_fairy SET given_apple_amount = given_apple_amount + ${appleAmount.amount} WHERE uuid = ${player.toString}" .execute() - .apply() } } @@ -151,7 +139,6 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { sql"SELECT given_apple_amount FROM vote_fairy WHERE uuid = ${player.toString}" .map(_.int("given_apple_amount")) .single() - .apply() appleAmountOpt.map(AppleAmount) } } @@ -176,7 +163,6 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { ) ) .toList() - .apply() .find(_._1 == player.toString) .map(_._2) } @@ -200,7 +186,6 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { AppleConsumeAmountRank(name, rank, AppleAmount(givenAppleAmount)) } .toList() - .apply() .toVector } } @@ -210,7 +195,6 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { val amount = sql"SELECT SUM(given_apple_amount) AS allAppleAmount FROM vote_fairy;" .map(_.int("allAppleAmount")) .single() - .apply() .get AppleAmount(amount) } @@ -221,7 +205,6 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { DB.localTx { implicit session => sql"UPDATE vote_fairy SET is_play_fairy_speech_sound = $playOnSpeech WHERE uuid = ${player.toString}" .execute() - .apply() } } @@ -231,7 +214,6 @@ class JdbcFairyPersistence[F[_]: Sync] extends FairyPersistence[F] { sql"SELECT is_play_fairy_speech_sound FROM vote_fairy WHERE uuid=${player.toString}" .map(_.boolean("is_play_fairy_speech_sound")) .single() - .apply() .get } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/subsystems/fairyspeech/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/subsystems/fairyspeech/System.scala index df672e185d..004a5794ce 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/subsystems/fairyspeech/System.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/subsystems/fairyspeech/System.scala @@ -43,7 +43,7 @@ object System { override def togglePlaySoundOnSpeech: Kleisli[F, Player, Unit] = Kleisli { player => for { fairyPlaySound <- playSoundOnSpeech(player.getUniqueId) - _ <- persistence.setPlaySoundOnSpeech(player.getUniqueId, fairyPlaySound) + _ <- persistence.setPlaySoundOnSpeech(player.getUniqueId, !fairyPlaySound) } yield () } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/subsystems/fairyspeech/bukkit/BukkitFairySpeechGateway.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/subsystems/fairyspeech/bukkit/BukkitFairySpeechGateway.scala index 5cee695de1..9f6b05cb6c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/subsystems/fairyspeech/bukkit/BukkitFairySpeechGateway.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/subsystems/fairyspeech/bukkit/BukkitFairySpeechGateway.scala @@ -24,11 +24,11 @@ class BukkitFairySpeechGateway[F[_]: Sync: Timer](player: Player) import cats.implicits._ override def playSpeechSound: F[Unit] = for { - _ <- FocusedSoundEffectF(Sound.BLOCK_NOTE_PLING, 2.0f, 1.0f).run(player) + _ <- FocusedSoundEffectF(Sound.BLOCK_NOTE_BLOCK_PLING, 2.0f, 1.0f).run(player) _ <- Timer[F].sleep(100.millis) - _ <- FocusedSoundEffectF(Sound.BLOCK_NOTE_PLING, 2.0f, 1.5f).run(player) + _ <- FocusedSoundEffectF(Sound.BLOCK_NOTE_BLOCK_PLING, 2.0f, 1.5f).run(player) _ <- Timer[F].sleep(100.millis) - _ <- FocusedSoundEffectF(Sound.BLOCK_NOTE_PLING, 2.0f, 2.0f).run(player) + _ <- FocusedSoundEffectF(Sound.BLOCK_NOTE_BLOCK_PLING, 2.0f, 2.0f).run(player) } yield {} } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/subsystems/fairyspeech/infrastructure/JdbcFairySpeechPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/subsystems/fairyspeech/infrastructure/JdbcFairySpeechPersistence.scala index d73acc49c7..c7e6a82ca2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/subsystems/fairyspeech/infrastructure/JdbcFairySpeechPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/vote/subsystems/fairyspeech/infrastructure/JdbcFairySpeechPersistence.scala @@ -13,7 +13,6 @@ class JdbcFairySpeechPersistence[F[_]: Sync] extends FairySpeechPersistence[F] { DB.localTx { implicit session => sql"UPDATE vote_fairy SET is_play_fairy_speech_sound = $playOnSpeech WHERE uuid = ${player.toString}" .execute() - .apply() } } @@ -22,7 +21,6 @@ class JdbcFairySpeechPersistence[F[_]: Sync] extends FairySpeechPersistence[F] { sql"SELECT is_play_fairy_speech_sound FROM vote_fairy WHERE uuid=${player.toString}" .map(_.boolean("is_play_fairy_speech_sound")) .single() - .apply() .get } } 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 37ee8ffdb4..fc74f93b88 100644 --- a/src/main/scala/com/github/unchama/seichiassist/task/GiganticBerserkTask.scala +++ b/src/main/scala/com/github/unchama/seichiassist/task/GiganticBerserkTask.scala @@ -1,7 +1,6 @@ package com.github.unchama.seichiassist.task import cats.effect.{ConcurrentEffect, IO, SyncIO} -import com.github.unchama.concurrent.NonServerThreadContextShift import com.github.unchama.seichiassist.data.player.PlayerData import com.github.unchama.seichiassist.subsystems.discordnotification.DiscordNotificationAPI import com.github.unchama.seichiassist.subsystems.mana.ManaApi @@ -15,9 +14,7 @@ import org.bukkit.entity.Player import scala.util.Random class GiganticBerserkTask { - def PlayerKillEnemy[F[ - _ - ]: ConcurrentEffect: NonServerThreadContextShift: DiscordNotificationAPI]( + def PlayerKillEnemy[F[_]: ConcurrentEffect: DiscordNotificationAPI]( p: Player )(implicit manaApi: ManaApi[IO, SyncIO, Player]): Unit = { val player = p @@ -35,7 +32,7 @@ class GiganticBerserkTask { // 確率でマナを回復させる val d = Math.random - if (d < playerdata.giganticBerserk.manaRegenerationProbability) { + if (d < playerdata.giganticBerserk.manaRegenerationProbability()) { if (playerdata.giganticBerserk.reachedLimit()) { manaApi.manaAmount(p).restoreCompletely.unsafeRunSync() player.sendMessage( @@ -92,9 +89,8 @@ class GiganticBerserkTask { val program = List( DiscordNotificationAPI[F].sendPlainText(messageWithoutColor).toIO, IO { - SendSoundEffect.sendEverySound(Sound.ENTITY_ENDERDRAGON_DEATH, 1, 1.2f) - SendMessageEffect.sendMessageToEveryoneIgnoringPreference(messageWithColor) - } + SendSoundEffect.sendEverySound(Sound.ENTITY_ENDER_DRAGON_DEATH, 1, 1.2f) + } >> SendMessageEffect.sendMessageToEveryoneIgnoringPreferenceIO(messageWithColor) ).sequence program.unsafeRunAsyncAndForget() 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 98f9160091..361712165e 100644 --- a/src/main/scala/com/github/unchama/seichiassist/task/PlayerDataSaveTask.scala +++ b/src/main/scala/com/github/unchama/seichiassist/task/PlayerDataSaveTask.scala @@ -166,19 +166,20 @@ object PlayerDataSaveTask { .delay { println(s"$RED${playerdata.name}のプレイヤーデータ保存失敗") } - .as(Right(ActionStatus.Fail)) - } else + .as(Right(())) + } else { commitUpdate.flatMap { result => if (result == ActionStatus.Ok) { Sync[F] .delay { println(s"$GREEN${player.getName}のプレイヤーデータ保存完了") } - .as(Right(ActionStatus.Ok)) + .as(Right(())) } else { Monad[F].pure(Left(remaining - 1)) } } + } } } } 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 957646237c..ab5653c732 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 @@ -27,8 +27,10 @@ object PlayerDataBackupRoutine { import scala.jdk.CollectionConverters._ for { + _ <- SendMessageEffect.sendMessageToEveryoneIgnoringPreferenceIO( + s"${AQUA}プレイヤーデータセーブ中…" + ) _ <- IO { - SendMessageEffect.sendMessageToEveryoneIgnoringPreference(s"${AQUA}プレイヤーデータセーブ中…") Bukkit.getLogger.info(s"${AQUA}プレイヤーデータセーブ中…") } players <- IO { @@ -38,8 +40,10 @@ object PlayerDataBackupRoutine { PlayerDataSaveTask .savePlayerData[IO](player, SeichiAssist.playermap(player.getUniqueId)) } + _ <- SendMessageEffect.sendMessageToEveryoneIgnoringPreferenceIO( + s"${AQUA}プレイヤーデータセーブ完了" + ) _ <- IO { - SendMessageEffect.sendMessageToEveryoneIgnoringPreference(s"${AQUA}プレイヤーデータセーブ完了") Bukkit.getLogger.info(s"${AQUA}プレイヤーデータセーブ完了") } } yield () 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 b7ed284b9f..bbaf2b5823 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 @@ -5,6 +5,7 @@ import com.github.unchama.concurrent.{RepeatingRoutine, RepeatingTaskContext} import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.SeichiAssist import com.github.unchama.seichiassist.achievement.SeichiAchievement +import com.github.unchama.seichiassist.achievement.hierarchy.AchievementGroup import org.bukkit.Bukkit import scala.concurrent.duration.FiniteDuration @@ -48,13 +49,22 @@ object PlayerDataRecalculationRoutine { .toList .sequence .map(_.flatMap { - case (achievementId, true) => Some(achievementId) - case _ => None + case (achievementId, true) => + val displayGroupName = + AchievementGroup.getGroupNameByEntryId(achievementId).getOrElse("未実装") + Some((achievementId, displayGroupName)) + case _ => None }) .flatMap(unlockTargets => IO { - playerData.TitleFlags.addAll(unlockTargets) - unlockTargets.map("実績No" + _ + "が解除されました!おめでとうございます!").foreach(player.sendMessage) + val achievementIds = unlockTargets.map(_._1) + playerData.TitleFlags.addAll(achievementIds) + unlockTargets.foreach { + case (achievementId, displayGroupName) => + player.sendMessage( + s"[${displayGroupName}]実績No${achievementId}が解除されました!おめでとうございます!" + ) + } } ) .unsafeRunSync() 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 4ad89f1da6..29edc3a824 100644 --- a/src/main/scala/com/github/unchama/seichiassist/util/BreakUtil.scala +++ b/src/main/scala/com/github/unchama/seichiassist/util/BreakUtil.scala @@ -3,7 +3,6 @@ package com.github.unchama.seichiassist.util import cats.Monad import cats.effect.{IO, SyncIO} import com.github.unchama.generic.ApplicativeExtra.whenAOrElse -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 @@ -19,18 +18,16 @@ import com.github.unchama.seichiassist.subsystems.breakcount.domain.level.Seichi import com.github.unchama.seichiassist.subsystems.breakskilltargetconfig.domain.BreakSkillTargetConfigKey import com.github.unchama.targetedeffect.player.ActionBarMessageEffect import com.github.unchama.util.bukkit.ItemStackUtil -import com.github.unchama.util.external.ExternalPlugins +import com.github.unchama.util.external.{ExternalPlugins, WorldGuardWrapper} import org.bukkit.ChatColor._ -import org.bukkit.World.Environment import org.bukkit._ -import org.bukkit.block.Block +import org.bukkit.block.{Block, Container} import org.bukkit.enchantments.Enchantment import org.bukkit.entity.{Entity, EntityType, Player} -import org.bukkit.inventory.ItemStack -import org.bukkit.material.Dye import java.util.Random import java.util.stream.IntStream +import scala.jdk.CollectionConverters._ object BreakUtil { @@ -66,7 +63,7 @@ object BreakUtil { val playerData = SeichiAssist.playermap(player.getUniqueId) // 壊されるブロックがワールドガード範囲だった場合処理を終了 - if (!ExternalPlugins.getWorldGuard.canBuild(player, checkTarget.getLocation)) { + if (!WorldGuardWrapper.canBuild(player, checkTarget.getLocation)) { if (playerData.settings.shouldDisplayWorldGuardLogs) { player.sendMessage(s"${RED}ワールドガードで保護されています。") } @@ -102,9 +99,8 @@ object BreakUtil { } val isBlockProtectedSlab = - checkTarget.getType == Material.STEP && - checkTarget.getY == halfBlockLayerYCoordinate && - checkTarget.getData == 0.toByte + checkTarget.getType == Material.STONE_SLAB && + checkTarget.getY == halfBlockLayerYCoordinate if (isBlockProtectedSlab) return false } @@ -125,26 +121,26 @@ object BreakUtil { } def isProtectedChest(player: Player, checkTarget: Block): Boolean = { - checkTarget.getType match { - case Material.CHEST | Material.TRAPPED_CHEST => - if ( - !SeichiAssist - .instance - .breakSkillTargetConfigSystem - .api - .breakSkillTargetConfig(player, BreakSkillTargetConfigKey.Chest) - .unsafeRunSync() - ) { - ActionBarMessageEffect(s"${RED}スキルでのチェスト破壊は無効化されています").run(player).unsafeRunSync() - true - } else if (!player.getWorld.isSeichi) { - ActionBarMessageEffect(s"${RED}スキルでのチェスト破壊は整地ワールドでのみ有効です").run(player).unsafeRunSync() - true - } else { - false - } - case _ => false - } + if ( + checkTarget.getType == Material.CHEST || checkTarget.getType == Material.TRAPPED_CHEST + ) { + if ( + !SeichiAssist + .instance + .breakSkillTargetConfigSystem + .api + .breakSkillTargetConfig(player, BreakSkillTargetConfigKey.Chest) + .unsafeRunSync() + ) { + ActionBarMessageEffect(s"${RED}スキルでのチェスト破壊は無効化されています").run(player).unsafeRunSync() + true + } else if (!player.getWorld.isSeichi) { + ActionBarMessageEffect(s"${RED}スキルでのチェスト破壊は整地ワールドでのみ有効です").run(player).unsafeRunSync() + true + } else { + false + } + } else false } /** @@ -157,9 +153,8 @@ object BreakUtil { val materialType = targetBlock.getType val isNotQuartzBlockAndQuartzStairs = materialType != Material.QUARTZ_BLOCK && materialType != Material.QUARTZ_STAIRS - // NOTE: targetBlock#getDataが7は下つきハーフブロック、15は上つきハーフブロック val isNotQuartzSlab = - materialType != Material.STEP || (targetBlock.getData != 7.toByte && targetBlock.getData != 15.toByte) + materialType != Material.QUARTZ_SLAB val isNotMadeFromQuartz = isNotQuartzBlockAndQuartzStairs && isNotQuartzSlab if (isNotMadeFromQuartz) { return true @@ -185,214 +180,6 @@ object BreakUtil { world.shouldMuteCoreProtect } - // ブロックを破壊する処理、ドロップも含む、統計増加も含む - 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) - ) - - sealed trait BlockBreakResult - - object BlockBreakResult { - - case class ItemDrop(itemStack: ItemStack) extends BlockBreakResult - - case class SpawnSilverFish(location: Location) extends BlockBreakResult - - } - - /** - * ブロックをツールで破壊した時のドロップを計算する - * - * Bukkit/Spigotが提供するBlock.getDropsは信頼できる値を返さない。 本来はNMSのメソッドを呼ぶのが確実らしいが、一時的な実装として使用している。 参考: - * https://www.spigotmc.org/threads/getdrops-on-crops-not-functioning-as-expected.167751/#post-1779788 - */ - 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 _ => - } - - val rand = Math.random() - val bonus = Math.max(1, rand * (fortuneLevel + 2)).toInt - - 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 { - case Material.GLOWING_REDSTONE_ORE => - new ItemStack(Material.REDSTONE_ORE) - case Material.LOG | Material.LOG_2 | Material.LEAVES | Material.LEAVES_2 => - new ItemStack(blockMaterial, 1, b_tree.toShort) - case Material.MONSTER_EGGS => - new ItemStack(Material.STONE) - case Material.WOOD_STEP | Material.STEP | Material.STONE_SLAB2 | - Material.PURPUR_SLAB if (blockDataLeast4Bits & 8) != 0 => - // 上付きハーフブロックのmissing texture化を防ぐ - new ItemStack(blockMaterial, 1, (blockDataLeast4Bits & 7).toShort) - case Material.QUARTZ_BLOCK if (blockData >= 2 && blockData <= 4) => - // 柱状クォーツブロックのmissing texture化を防ぐ - new ItemStack(blockMaterial, 1, 2.toShort) - case _ => - new ItemStack(blockMaterial, 1, blockDataLeast4Bits.toShort) - } - } - } - } else if (fortuneLevel > 0 && MaterialSets.fortuneMaterials.contains(blockMaterial)) { - // 幸運の処理 - Some { - BlockBreakResult.ItemDrop { - blockMaterial match { - case Material.COAL_ORE => - new ItemStack(Material.COAL, bonus) - case Material.DIAMOND_ORE => - new ItemStack(Material.DIAMOND, bonus) - case Material.EMERALD_ORE => - new ItemStack(Material.EMERALD, bonus) - case Material.QUARTZ_ORE => - new ItemStack(Material.QUARTZ, bonus) - // レッドストーン鉱石, グロウストーン, スイカブロック, シーランタン, ラピスラズリ鉱石は、 - // ドロップアイテムの個数を求める計算が通常の鉱石の扱いと異なるため、特別な処理が必要である。 - case Material.REDSTONE_ORE | Material.GLOWING_REDSTONE_ORE => - val withBonus = (rand * (fortuneLevel + 2) + 4).toInt - new ItemStack(Material.REDSTONE, withBonus) - case Material.LAPIS_ORE => - val dye = new Dye() - dye.setColor(DyeColor.BLUE) - // 幸運エンチャントなしで掘った時のアイテムが得られる個数(4~9)に、幸運ボーナスを掛ける - val withBonus = (rand * 6 + 4).toInt * bonus - dye.toItemStack(withBonus) - // グロウストーンは幸運エンチャントがついていると高確率でより多くのダストをドロップする - // しかし、最大でも4個までしかドロップしない - case Material.GLOWSTONE => - val withBonus = (rand * (fortuneLevel + 3) + 2).toInt - val amount = if (withBonus > 4) 4 else withBonus - new ItemStack(Material.GLOWSTONE_DUST, amount) - // 同様に、メロンブロックは幸運エンチャントがついている場合、9個までしかドロップしない - case Material.MELON_BLOCK => - val withBonus = (rand * (fortuneLevel + 5) + 3).toInt - val amount = if (withBonus > 9) 9 else withBonus - new ItemStack(Material.MELON, amount) - case Material.SEA_LANTERN => - val withBonus = (rand * (fortuneLevel + 2) + 2).toInt - val amount = if (withBonus > 5) 5 else withBonus - new ItemStack(Material.PRISMARINE_CRYSTALS, amount) - case _ => - // unreachable - new ItemStack(blockMaterial, bonus) - } - } - } - } else { - // シルク幸運なしの処理 - blockMaterial match { - case Material.COAL_ORE => - Some(BlockBreakResult.ItemDrop(new ItemStack(Material.COAL))) - case Material.DIAMOND_ORE => - Some(BlockBreakResult.ItemDrop(new ItemStack(Material.DIAMOND))) - case Material.LAPIS_ORE => - val dye = new Dye() - dye.setColor(DyeColor.BLUE) - Some(BlockBreakResult.ItemDrop(dye.toItemStack((rand * 6 + 4).toInt))) - case Material.EMERALD_ORE => - Some(BlockBreakResult.ItemDrop(new ItemStack(Material.EMERALD))) - case Material.REDSTONE_ORE | Material.GLOWING_REDSTONE_ORE => - Some( - BlockBreakResult.ItemDrop(new ItemStack(Material.REDSTONE, ((rand * 2) + 4).toInt)) - ) - case Material.QUARTZ_ORE => - Some(BlockBreakResult.ItemDrop(new ItemStack(Material.QUARTZ))) - // グロウストーンは、2から4個のグロウストーンダストをドロップする - case Material.GLOWSTONE => - Some( - BlockBreakResult - .ItemDrop(new ItemStack(Material.GLOWSTONE_DUST, (rand * 3 + 2).toInt)) - ) - // スイカブロックは、3から7個のスイカをドロップする - case Material.MELON_BLOCK => - Some(BlockBreakResult.ItemDrop(new ItemStack(Material.MELON, (rand * 5 + 3).toInt))) - // シーランタンは、2から3個のプリズマリンクリスタルをドロップする - case Material.SEA_LANTERN => - Some( - BlockBreakResult - .ItemDrop(new ItemStack(Material.PRISMARINE_CRYSTALS, (rand * 2 + 2).toInt)) - ) - case Material.STONE => - Some { - BlockBreakResult.ItemDrop { - if (blockData.toInt == 0x00) { - // 焼き石の処理 - new ItemStack(Material.COBBLESTONE) - } else { - // 他の石の処理 - new ItemStack(blockMaterial, 1, blockDataLeast4Bits.toShort) - } - } - } - case Material.GRASS => - Some(BlockBreakResult.ItemDrop(new ItemStack(Material.DIRT))) - case Material.GRAVEL => - val p = fortuneLevel match { - case 1 => 0.14 - case 2 => 0.25 - case 3 => 1.00 - case _ => 0.1 - } - val dropMaterial = if (p > rand) Material.FLINT else Material.GRAVEL - - Some(BlockBreakResult.ItemDrop(new ItemStack(dropMaterial, bonus))) - case Material.LEAVES | Material.LEAVES_2 => - None - case Material.CLAY => - Some(BlockBreakResult.ItemDrop(new ItemStack(Material.CLAY_BALL, 4))) - case Material.MONSTER_EGGS => - Some(BlockBreakResult.SpawnSilverFish(blockLocation)) - case Material.LOG | Material.LOG_2 => - Some(BlockBreakResult.ItemDrop(new ItemStack(blockMaterial, 1, b_tree.toShort))) - case Material.WOOD_STEP | Material.STEP | Material.STONE_SLAB2 | Material.PURPUR_SLAB - if (blockDataLeast4Bits & 8) != 0 => - // 上付きハーフブロックをそのままドロップするとmissing textureとして描画されるため、下付きの扱いとする - Some( - BlockBreakResult - .ItemDrop(new ItemStack(blockMaterial, 1, (blockDataLeast4Bits & 7).toShort)) - ) - case Material.QUARTZ_BLOCK if (blockData >= 2 && blockData <= 4) => - // 柱状クォーツブロックのmissing texture化を防ぐ (柱状クォーツのData valueは2, 3, 4のいずれか) - Some(BlockBreakResult.ItemDrop(new ItemStack(blockMaterial, 1, 2.toShort))) - case Material.BOOKSHELF => - // 本棚を破壊すると、本が3つドロップする - Some(BlockBreakResult.ItemDrop(new ItemStack(Material.BOOK, 3))) - case _ => - Some( - BlockBreakResult - .ItemDrop(new ItemStack(blockMaterial, 1, blockDataLeast4Bits.toShort)) - ) - } - } - } - /** * TODO: これはビジネスロジックである。breakcountシステムによって管理されるべき。 * @@ -417,10 +204,10 @@ object BreakUtil { def totalBreakCount(materials: Seq[Material]): Long = materials .filter(MaterialSets.materialsToCountBlockBreak.contains) - .map { + .map { m => // 氷塊とマグマブロックの整地量を2倍 - case Material.PACKED_ICE | Material.MAGMA => 2L - case _ => 1L + if (m == Material.PACKED_ICE || m == Material.MAGMA_BLOCK) 2L + else 1L } .sum @@ -446,34 +233,38 @@ object BreakUtil { targetBlocksInformation <- PluginExecutionContexts .onMainThread .runAction(SyncIO { - val seq: Seq[(Location, Material, Byte)] = targetBlocks + val seq: Seq[(Location, Block)] = targetBlocks .toSeq - .filter { block => - block.getType match { - case Material.AIR => - if (SeichiAssist.DEBUG) - Bukkit.getLogger.warning(s"AIRの破壊が${block.getLocation.toString}にて試行されました。") - false - case _ => true - } - } - .map(block => (block.getLocation.clone(), block.getType, block.getData)) - - // ブロックをすべて[[toMaterial]]に変える - targetBlocks.foreach(_.setType(toMaterial)) + .filterNot(_.getType == Material.AIR) + .map(block => (block.getLocation.clone(), block)) seq }) + notContainerBlocks <- PluginExecutionContexts + .onMainThread + .runAction(SyncIO { + targetBlocksInformation.filterNot(_._2.getState.isInstanceOf[Container]) + }) + breakResults = { - val plainBreakResult = targetBlocksInformation.flatMap(dropItemOnTool(miningTool)) + val plainBreakResult = + notContainerBlocks.map { + case (location, block) => + val clonedTool = miningTool.clone() + clonedTool.setType(Material.NETHERITE_PICKAXE) + (location, block.getDrops(clonedTool, player).asScala) + } val drops = plainBreakResult.mapFilter { - case BlockBreakResult.ItemDrop(itemStack) => Some(itemStack) - case BlockBreakResult.SpawnSilverFish(_) => None - } + case (_, drops) if drops.nonEmpty => Some(drops) + case _ => None + }.flatten val silverFishLocations = plainBreakResult.mapFilter { - case BlockBreakResult.ItemDrop(_) => None - case BlockBreakResult.SpawnSilverFish(location) => Some(location) + case (location, _) + if location.getBlock.getType == Material.INFESTED_STONE && !miningTool + .containsEnchantment(Enchantment.SILK_TOUCH) => + Some(location) + case _ => None } // 纏めなければ、FAWEの干渉を受け勝手に消される危険性などがある @@ -502,18 +293,30 @@ object BreakUtil { ).map(Option.unless(_)(itemStack)) } + // NOTE: SpigotのBlockはLocationを保存しているため、Blockを置き換える前にMaterialとして + // 保存しておかないとすべてMaterial.AIRとして取得されてしまう + breakMaterials = targetBlocksInformation.map { case (_, block) => block.getType } + + _ <- PluginExecutionContexts + .onMainThread + .runAction(SyncIO { + // ブロックをすべて[[toMaterial]]に変える + targetBlocks.filter(_.getState.isInstanceOf[Container]).foreach(_.breakNaturally()) + targetBlocks.foreach(_.setType(toMaterial)) + }) + _ <- IO { // 壊した時の音を再生する if (shouldPlayBreakSound) { targetBlocksInformation.foreach { - case (location, material, _) => - dropLocation.getWorld.playEffect(location, Effect.STEP_SOUND, material) + case (location, block) => + dropLocation.getWorld.playEffect(location, Effect.STEP_SOUND, block) } } } // プレイヤーの統計を増やす - totalCount = totalBreakCount(targetBlocksInformation.map { case (_, m, _) => m }) + totalCount = totalBreakCount(breakMaterials) blockCountWeight <- blockCountWeight[IO](player.getWorld) expIncrease = SeichiExpAmount.ofNonNegative(totalCount * blockCountWeight) @@ -531,6 +334,7 @@ object BreakUtil { // アイテムドロップは非同期スレッドで行ってはならない itemsToBeDropped .flatten + .filterNot(_.getType == Material.AIR) .foreach(dropLocation.getWorld.dropItemNaturally(dropLocation, _)) breakResults._2.foreach { location => location.getWorld.spawnEntity(location, EntityType.SILVERFISH) @@ -569,11 +373,9 @@ object BreakUtil { * ref: [バージョン1.12.x時の最新記事アーカイブ](https://minecraft.fandom.com/wiki/Solid_block?oldid=1132868) */ private def isAffectedByGravity(material: Material): Boolean = { - material match { - case Material.BEDROCK => false - case m if MaterialSets.fluidMaterials.contains(m) || m.isSolid => true - case _ => false - } + if (material == Material.BEDROCK) false + else if (MaterialSets.fluidMaterials.contains(material)) true + else material.isSolid } /** @@ -670,7 +472,7 @@ object BreakUtil { /** * 最大ループ数 */ - val maxY = if (player.getWorld.getEnvironment == Environment.NETHER) 121 else 255 + val maxY = player.getWorld.getMaxHeight val maxOffsetY = maxY - blockRelativeHeight // NOTE: `1 until 0`など、`x > y`が満たされる`x until y`はイテレーションが行われない diff --git a/src/main/scala/com/github/unchama/seichiassist/util/EnemyEntity.scala b/src/main/scala/com/github/unchama/seichiassist/util/EnemyEntity.scala index 10de3f680f..bb72bc2cc3 100644 --- a/src/main/scala/com/github/unchama/seichiassist/util/EnemyEntity.scala +++ b/src/main/scala/com/github/unchama/seichiassist/util/EnemyEntity.scala @@ -5,15 +5,20 @@ import org.bukkit.entity.EntityType.{ BLAZE, CAVE_SPIDER, CREEPER, + DROWNED, ELDER_GUARDIAN, ENDERMAN, ENDERMITE, EVOKER, GHAST, GUARDIAN, + HOGLIN, HUSK, MAGMA_CUBE, - PIG_ZOMBIE, + PIGLIN, + PIGLIN_BRUTE, + PILLAGER, + RAVAGER, SHULKER, SILVERFISH, SKELETON, @@ -24,8 +29,10 @@ import org.bukkit.entity.EntityType.{ VINDICATOR, WITCH, WITHER_SKELETON, + ZOGLIN, ZOMBIE, - ZOMBIE_VILLAGER + ZOMBIE_VILLAGER, + ZOMBIFIED_PIGLIN } object EnemyEntity { @@ -41,7 +48,7 @@ object EnemyEntity { GUARDIAN, HUSK, MAGMA_CUBE, - PIG_ZOMBIE, + ZOMBIFIED_PIGLIN, SHULKER, SILVERFISH, SKELETON, @@ -53,6 +60,13 @@ object EnemyEntity { WITCH, WITHER_SKELETON, ZOMBIE, - ZOMBIE_VILLAGER + ZOMBIE_VILLAGER, + PIGLIN, + HOGLIN, + DROWNED, + PIGLIN_BRUTE, + PILLAGER, + RAVAGER, + ZOGLIN ).contains(entityType) } diff --git a/src/main/scala/com/github/unchama/seichiassist/util/ItemInformation.scala b/src/main/scala/com/github/unchama/seichiassist/util/ItemInformation.scala index afdfeab6f9..015b1a0510 100644 --- a/src/main/scala/com/github/unchama/seichiassist/util/ItemInformation.scala +++ b/src/main/scala/com/github/unchama/seichiassist/util/ItemInformation.scala @@ -1,8 +1,10 @@ package com.github.unchama.seichiassist.util +import com.github.unchama.itemstackbuilder.SkullOwnerUuid +import com.github.unchama.seichiassist.SkullOwners import org.bukkit.ChatColor.GREEN -import org.bukkit.block.{Block, Skull} -import org.bukkit.{Material, SkullType} +import org.bukkit.block.Block +import org.bukkit.Material import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.SkullMeta @@ -11,61 +13,44 @@ import java.util.stream.IntStream object ItemInformation { import scala.jdk.CollectionConverters._ - import scala.util.chaining._ def isGachaTicket(itemStack: ItemStack): Boolean = { val containsRightClickMessage: String => Boolean = _.contains(s"${GREEN}右クリックで使えます") - if (itemStack.getType != Material.SKULL_ITEM) return false + if (itemStack.getType != Material.PLAYER_HEAD) return false val skullMeta = itemStack.getItemMeta.asInstanceOf[SkullMeta] - if (!(skullMeta.hasOwner && skullMeta.getOwner == "unchama")) return false + /* + Note: skullMeta.getOwner == "unchama"という条件は、後方互換性を保つためのコードである。 + 1.12.2のバージョンでskullMeta.getOwnerで頭のオーナーを取得できていたが、 + 1.18.2ではsetOwner、getOwnerともに使用できない。 + そのため、1.18.2からはPlayerProfileにUUIDを書き込み、UUIDを利用した判定を行うことになった。 + + 1.18.2の環境で、1.12.2から持ってきたガチャ券(1.12.2の環境でItemStack化されたもの)からgetOwnerすることが + できなければ該当のコードを削除して良い。 + */ + if ( + !(skullMeta.hasOwner && (SkullOwnerUuid( + skullMeta.getOwningPlayer.getPlayerProfile.getUniqueId + ) == SkullOwners.unchama || skullMeta.getOwner == "unchama")) + ) return false skullMeta.hasLore && skullMeta.getLore.asScala.exists(containsRightClickMessage) } def isMineHeadItem(itemstack: ItemStack): Boolean = { - itemstack.getType == Material.CARROT_STICK && + itemstack.getType == Material.CARROT_ON_A_STICK && loreIndexOf(itemstack.getItemMeta.getLore.asScala.toList, "頭を狩り取る形をしている...") >= 0 } def getSkullDataFromBlock(block: Block): Option[ItemStack] = { - if (block.getType != Material.SKULL) return None - - val skull = block.getState.asInstanceOf[Skull] - val itemStack = new ItemStack(Material.SKULL_ITEM) + if (block.getType != Material.PLAYER_HEAD) return None - // 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.SKELETON => SkullType.SKELETON.ordinal.toShort - 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にして返す - Some(block.getDrops.asScala.head.tap(_.setDurability(SkullType.PLAYER.ordinal.toShort))) + Some(block.getDrops.asScala.head) } - /** - * 指定された`String`が指定された[[ItemStack]]のloreに含まれているかどうか - * - * @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 - else loreIndexOf(itemStack.getItemMeta.getLore.asScala.toList, sentence) >= 0 - /** * loreを捜査して、要素の中に`find`が含まれているかを調べる。 * diff --git a/src/main/scala/com/github/unchama/seichiassist/util/ItemMetaFactory.java b/src/main/scala/com/github/unchama/seichiassist/util/ItemMetaFactory.java index 6df78bcc77..56811061fa 100644 --- a/src/main/scala/com/github/unchama/seichiassist/util/ItemMetaFactory.java +++ b/src/main/scala/com/github/unchama/seichiassist/util/ItemMetaFactory.java @@ -9,5 +9,5 @@ @SuppressWarnings("UtilityClassCanBeEnum") public final class ItemMetaFactory { private static final ItemFactory FACTORY = Bukkit.getItemFactory(); - public static final ValueHolder SKULL = new ValueHolder<>((SkullMeta) FACTORY.getItemMeta(Material.SKULL_ITEM), SkullMeta::clone); + public static final ValueHolder SKULL = new ValueHolder<>((SkullMeta) FACTORY.getItemMeta(Material.PLAYER_HEAD), SkullMeta::clone); } 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 7b14dcc0a7..a448f5da32 100644 --- a/src/main/scala/com/github/unchama/seichiassist/util/PlayerSendable.scala +++ b/src/main/scala/com/github/unchama/seichiassist/util/PlayerSendable.scala @@ -21,7 +21,7 @@ object PlayerSendable { implicit def forStringArray[F[_]: OnMinecraftServerThread] : PlayerSendable[Array[String], F] = { (player, content) => OnMinecraftServerThread[F].runAction(SyncIO { - player.sendMessage(content) + player.sendMessage(content.mkString("\n")) }) } diff --git a/src/main/scala/com/github/unchama/seichiassist/util/SendMessageEffect.scala b/src/main/scala/com/github/unchama/seichiassist/util/SendMessageEffect.scala index 23887c61e2..534cc1c6e5 100644 --- a/src/main/scala/com/github/unchama/seichiassist/util/SendMessageEffect.scala +++ b/src/main/scala/com/github/unchama/seichiassist/util/SendMessageEffect.scala @@ -1,25 +1,15 @@ package com.github.unchama.seichiassist.util import cats.Monad -import cats.effect.IO +import cats.effect.{IO, LiftIO} import com.github.unchama.minecraft.actions.GetConnectedPlayers import com.github.unchama.minecraft.bukkit.actions.GetConnectedBukkitPlayers import com.github.unchama.seichiassist.SeichiAssist import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts.onMainThread -import org.bukkit.Bukkit import org.bukkit.entity.Player object SendMessageEffect { - import scala.jdk.CollectionConverters._ - - @deprecated("It's side-effectful") - def sendMessageToEveryoneIgnoringPreference[T]( - content: T - )(implicit send: PlayerSendable[T, IO]): Unit = { - sendMessageToEveryoneIgnoringPreferenceIO(content).unsafeRunAsyncAndForget() - } - def sendMessageToEveryoneIgnoringPreferenceIO[T: PlayerSendable[*, IO]]( content: T ): IO[Unit] = { @@ -38,23 +28,23 @@ object SendMessageEffect { } yield () } - @deprecated("It's side-effectful") - def sendMessageToEveryone[T](content: T)(implicit ev: PlayerSendable[T, IO]): Unit = { + def sendMessageToEveryone[T, F[_]: Monad: LiftIO: GetConnectedPlayers[*[_], Player]]( + content: T + )(implicit ev: PlayerSendable[T, F]): F[Unit] = { import cats.implicits._ - Bukkit - .getOnlinePlayers - .asScala - .toList - .traverse { player => + for { + players <- GetConnectedPlayers[F, Player].now + _ <- players.traverse { player => for { playerSettings <- SeichiAssist .playermap(player.getUniqueId) .settings .getBroadcastMutingSettings - _ <- IO { if (!playerSettings.shouldMuteMessages) ev.send(player, content) } + .to[F] + _ <- ev.send(player, content).unlessA(playerSettings.shouldMuteMessages) } yield () } - .unsafeRunSync() + } yield () } } 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 424880affd..32667be2be 100644 --- a/src/main/scala/com/github/unchama/targetedeffect/commandsender/MessageEffect.scala +++ b/src/main/scala/com/github/unchama/targetedeffect/commandsender/MessageEffect.scala @@ -26,6 +26,6 @@ object MessageEffectF { TargetedEffect.delay(_.sendMessage(string)) def apply[F[_]: Sync](stringList: List[String]): Kleisli[F, CommandSender, Unit] = - TargetedEffect.delay(_.sendMessage(stringList.toArray)) + TargetedEffect.delay(_.sendMessage(stringList.mkString("\n"))) } 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 1da9b76645..fe4e75fd1d 100644 --- a/src/main/scala/com/github/unchama/targetedeffect/player/CommandEffect.scala +++ b/src/main/scala/com/github/unchama/targetedeffect/player/CommandEffect.scala @@ -18,3 +18,12 @@ object CommandEffect { }) } } + +object CommandEffectF { + def apply[F[_]: OnMinecraftServerThread](string: String): Kleisli[F, Player, Unit] = + Kleisli { player => + OnMinecraftServerThread[F].runAction(SyncIO { + player.chat(s"/$string") + }) + } +} 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 564eaaab65..6709c76cdd 100644 --- a/src/main/scala/com/github/unchama/targetedeffect/player/PlayerEffects.scala +++ b/src/main/scala/com/github/unchama/targetedeffect/player/PlayerEffects.scala @@ -9,7 +9,17 @@ import org.bukkit.entity.Player import org.bukkit.inventory.Inventory object PlayerEffects { - val closeInventoryEffect: TargetedEffect[Player] = TargetedEffect.delay(_.closeInventory()) + + def closeInventoryEffect( + implicit onMainThread: OnMinecraftServerThread[IO] + ): TargetedEffect[Player] = { + Kleisli { player => + // インベントリを閉じる操作はサーバースレッドでなければならない(Spigot 1.18.2) + onMainThread.runAction(SyncIO { + player.closeInventory() + }) + } + } def openInventoryEffect( inventory: => Inventory 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 d75eec2460..f0007d3c21 100644 --- a/src/main/scala/com/github/unchama/util/bukkit/ItemStackUtil.scala +++ b/src/main/scala/com/github/unchama/util/bukkit/ItemStackUtil.scala @@ -8,7 +8,7 @@ object ItemStackUtil { * `stacks` に含まれるアイテムスタックをできるだけマージしたような新たな `Seq` を返す */ def amalgamate(stacks: Seq[ItemStack]): Seq[ItemStack] = { - val originals = stacks.map(_.clone()) + val originals = stacks.filterNot(_ == null).map(_.clone()) val result = scala.collection.mutable.ArrayBuffer.empty[ItemStack] 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 985501bf8e..8ceb0b3b11 100644 --- a/src/main/scala/com/github/unchama/util/bukkit/WorldUtil.scala +++ b/src/main/scala/com/github/unchama/util/bukkit/WorldUtil.scala @@ -6,10 +6,12 @@ import org.bukkit.World.Environment 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.THE_END => s"$base/DIM1" + case Environment.CUSTOM => s"$base/COSTOM" } } } 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 cfae476980..eda19fff99 100644 --- a/src/main/scala/com/github/unchama/util/external/CoreProtectWrapper.scala +++ b/src/main/scala/com/github/unchama/util/external/CoreProtectWrapper.scala @@ -7,14 +7,11 @@ import org.bukkit.entity.Player class CoreProtectWrapper(val backbone: CoreProtectAPI) { def queueBlockRemoval(who: Player, where: Block): Boolean = { - // where.getDataが非推奨になっていますが、現在これ以外の方法でByteデータを取得する方法がないようです。 - backbone.logRemoval(who.getName, where.getLocation, where.getType, where.getData) + backbone.logRemoval(who.getName, where.getLocation, where.getType, where.getBlockData) } - /* - // For >= 1.13 - def queueBlockRemoval(who: Player, where: Location, data: BlockData): Boolean = { - return backbone.logRemoval(who.getName, where, where.getBlock.getType, data) + def isNotEditedBlock(where: Block): Boolean = { + backbone.blockLookup(where, Int.MaxValue).isEmpty } - */ + } diff --git a/src/main/scala/com/github/unchama/util/external/WorldEditWrapper.scala b/src/main/scala/com/github/unchama/util/external/WorldEditWrapper.scala index 15812be9e3..8b40674e36 100644 --- a/src/main/scala/com/github/unchama/util/external/WorldEditWrapper.scala +++ b/src/main/scala/com/github/unchama/util/external/WorldEditWrapper.scala @@ -1,7 +1,7 @@ package com.github.unchama.util.external import com.sk89q.worldedit.bukkit.WorldEditPlugin -import com.sk89q.worldedit.bukkit.selections.Selection +import com.sk89q.worldedit.math.BlockVector3 import org.bukkit.entity.Player object WorldEditWrapper { @@ -14,6 +14,7 @@ object WorldEditWrapper { /** * @return `player`が選択している範囲 */ - def getSelection(player: Player): Option[Selection] = Option(plugin.getSelection(player)) + def getSelection(player: Player): BlockVector3 = + plugin.getSession(player).getPlacementPosition(plugin.wrapPlayer(player)) } diff --git a/src/main/scala/com/github/unchama/util/external/WorldGuardWrapper.scala b/src/main/scala/com/github/unchama/util/external/WorldGuardWrapper.scala index e4e407a0b7..3ffee61781 100644 --- a/src/main/scala/com/github/unchama/util/external/WorldGuardWrapper.scala +++ b/src/main/scala/com/github/unchama/util/external/WorldGuardWrapper.scala @@ -1,12 +1,11 @@ package com.github.unchama.util.external -import com.sk89q.worldedit.BlockVector -import com.sk89q.worldguard.LocalPlayer -import com.sk89q.worldguard.bukkit.commands.AsyncCommandHelper -import com.sk89q.worldguard.bukkit.commands.task.RegionAdder +import com.sk89q.worldedit.bukkit.BukkitAdapter +import com.sk89q.worldguard.bukkit.WorldGuardPlugin +import com.sk89q.worldguard.protection.flags.Flags import com.sk89q.worldguard.protection.managers.RegionManager -import com.sk89q.worldguard.protection.regions.{ProtectedCuboidRegion, ProtectedRegion} -import com.sk89q.worldguard.protection.util.DomainInputResolver +import com.sk89q.worldguard.protection.regions.ProtectedRegion +import com.sk89q.worldguard.{LocalPlayer, WorldGuard} import org.bukkit.entity.Player import org.bukkit.{Location, World} @@ -19,141 +18,89 @@ import scala.jdk.CollectionConverters._ */ object WorldGuardWrapper { - /** - * WorldGuardのインスタンス - */ - private val plugin = ExternalPlugins.getWorldGuard + private val worldGuard = WorldGuard.getInstance() /** * [[LocalPlayer]]を返す */ - private def wrapPlayer(player: Player): LocalPlayer = plugin.wrapPlayer(player) - - /** - * [[RegionManager]]を返す - */ - def getRegionManager(world: World): Option[RegionManager] = Option( - // The expression is nullable: WorldConfiguration#useRegions is false => null - plugin.getRegionManager(world) - ) - - /** - * [[Player]]が[[World]]の中で持っている保護の数を返す - * - * @return [[Player]]が[[World]]の中で持っている保護の数。[[getRegionManager]]が[[None]]であれば0。 - */ - def getRegionCountOfPlayer(player: Player, world: World): Int = - getRegionManager(world).map(_.getRegionCountOfPlayer(wrapPlayer(player))).getOrElse(0) - - /** - * [[World]]における[[Player]] の最大保護可能数を取得します. - * @return [[Player]]の[[World]] における最大保護可能数 - */ - def getMaxRegionCount(player: Player, world: World): Int = - // TODO: migrate this to OptionalInt - plugin.getGlobalStateManager.get(world).getMaxRegionCount(player) - - /** - * 現在[[Player]]が[[World]]でオーナーになっている保護の数を返す。 - * @param who 誰か - * @param where どのワールドか - * @return オーナーになっている保護の数。どこのオーナーでもない場合は0 - */ - def getNumberOfRegions(who: Player, where: World): Int = - // TODO: migrate this to OptionalInt - plugin.getRegionContainer.get(where).getRegionCountOfPlayer(wrapPlayer(who)) - - /** - * 現在[[Player]]が[[Location]]の座標でOwnerになっている保護があるかどうかを返す。 - * @param player 調べる対象であるPlayer - * @param location どの座標か - * @return Ownerである保護が1つだけあればtrue、ないか保護が2個以上重なっていて判定できなければfalse - */ - def isRegionOwner(player: Player, location: Location): Boolean = - getOneRegion(location).exists(_.isOwner(wrapPlayer(player))) - - /** - * [[Player]]が[[Location]]の座標でMemberになっている保護があるかどうかを返す。 - * NOTE: Ownerでもある場合も含まれる。 - * @param player 調べる対象であるPlayer - * @param location どの座標か - * @return Memberである保護が1つだけあればtrue、ないか保護が2個以上重なっていて判定できなければfalse - */ - def isRegionMember(player: Player, location: Location): Boolean = - getOneRegion(location).exists(_.isMember(wrapPlayer(player))) + private def wrapPlayer(player: Player): LocalPlayer = + WorldGuardPlugin.inst().wrapPlayer(player) + + def getRegionManager(world: World): RegionManager = + worldGuard.getPlatform.getRegionContainer.get(BukkitAdapter.adapt(world)) + + def getRegion(loc: Location): List[ProtectedRegion] = { + val container = + worldGuard.getPlatform.getRegionContainer.get(BukkitAdapter.adapt(loc.getWorld)) + container + .getApplicableRegions(BukkitAdapter.adapt(loc).toVector.toBlockPoint) + .getRegions + .asScala + .toList + } - /** - * [[Location]]の座標にある保護を1つだけ取得する - * @param location どの座標か - * @return [[ProtectedRegion]]。保護が1個もないか、2個以上ある場合は[[None]] - */ - def getOneRegion(location: Location): Option[ProtectedRegion] = { - val regions = getRegions(location) + def getRegions(world: World): List[ProtectedRegion] = { + worldGuard + .getPlatform + .getRegionContainer + .get(BukkitAdapter.adapt(world)) + .getRegions + .values() + .asScala + .toList + } - Option.when(regions.size == 1)(regions.head) + def canBuild(p: Player, loc: Location): Boolean = { + worldGuard + .getPlatform + .getRegionContainer + .createQuery() + .testState(BukkitAdapter.adapt(loc), wrapPlayer(p), Flags.BUILD) } - /** - * [[Location]]の座標にある保護をすべて取得する - * @param location どの座標か - * @return [[ProtectedRegion]]の[[Set]] - */ - def getRegions(location: Location): Set[ProtectedRegion] = - getRegionManager(location.getWorld) - .map(_.getApplicableRegions(location).getRegions.asScala.toSet) - .getOrElse(Set.empty) + def findByRegionName(name: String): Option[RegionManager] = + worldGuard + .getPlatform + .getRegionContainer + .getLoaded + .asScala + .find(_.getRegions.asScala.exists(_._1 == name)) + + def removeByProtectedRegionRegion(world: World, region: ProtectedRegion): Unit = { + worldGuard + .getPlatform + .getRegionContainer + .get(BukkitAdapter.adapt(world)) + .removeRegion(region.getId) + } - /** - * `minimumPoint`と`maximumPoint`の範囲内にある保護の数を取得する - * @param world 調べたいワールド - * @param regionName 保護名 - * @param minimumPoint 範囲の対角の1つ - * @param maximumPoint 範囲の対角の1つ - * @return 指定した範囲の保護の数を返す作用 - */ - def getApplicableRegionCount( - world: World, - regionName: String, - minimumPoint: BlockVector, - maximumPoint: BlockVector - ): Int = { - val region = new ProtectedCuboidRegion(regionName, minimumPoint, maximumPoint) - getRegionManager(world).map(_.getApplicableRegions(region).size).getOrElse(0) + def getMaxRegion(player: Player, world: World): Int = { + worldGuard + .getPlatform + .getGlobalStateManager + .get(BukkitAdapter.adapt(world)) + .getMaxRegionCount(wrapPlayer(player)) } - /** - * @param regionName 保護名 - * @param regionOwner 保護のオーナー - * @param world 保護をしたいワールド - * @param minimumPoint 保護範囲の対角の1つ - * @param maximumPoint 保護範囲の対角の1つ - * @return 保護の作成を試みることができればtrue、できなければfalse - * ただし、trueでも確実に保護が作成できるとは限らない - */ - def tryCreateRegion( - regionName: String, - regionOwner: Player, - world: World, - minimumPoint: BlockVector, - maximumPoint: BlockVector - ): Boolean = { - val region = new ProtectedCuboidRegion(regionName, minimumPoint, maximumPoint) - - getRegionManager(world) match { - case Some(manager) => - val task = new RegionAdder(plugin, manager, region) - task.setLocatorPolicy(DomainInputResolver.UserLocatorPolicy.UUID_ONLY) - task.setOwnersInput(Array(regionOwner.getName)) - val future = plugin.getExecutorService.submit(task) - - AsyncCommandHelper - .wrap(future, plugin, regionOwner) - .formatUsing(regionName) - .registerWithSupervisor("保護申請中") - .thenRespondWith("保護申請完了。保護名: '%s'", "保護作成失敗") - true - case None => false - } + def getNumberOfRegions(player: Player, world: World): Int = + worldGuard + .getPlatform + .getRegionContainer + .get(BukkitAdapter.adapt(world)) + .getRegionCountOfPlayer(wrapPlayer(player)) + + def getWorldMaxRegion(world: World): Int = { + worldGuard + .getPlatform + .getGlobalStateManager + .get(BukkitAdapter.adapt(world)) + .maxRegionCountPerPlayer } + def isRegionMember(player: Player, location: Location): Boolean = + getRegion(location).exists(_.isMember(wrapPlayer(player))) + + def canProtectionWorld(world: World): Boolean = + worldGuard.getPlatform.getGlobalStateManager.get(BukkitAdapter.adapt(world)).useRegions + } 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_18_2/world/WorldChunkSaving.scala similarity index 92% rename from src/main/scala/com/github/unchama/util/nms/v1_12_2/world/WorldChunkSaving.scala rename to src/main/scala/com/github/unchama/util/nms/v1_18_2/world/WorldChunkSaving.scala index 4b048b1366..442d24c239 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_18_2/world/WorldChunkSaving.scala @@ -1,4 +1,4 @@ -package com.github.unchama.util.nms.v1_12_2.world +package com.github.unchama.util.nms.v1_18_2.world import cats.effect.{Concurrent, Sync} @@ -7,12 +7,12 @@ object WorldChunkSaving { import scala.jdk.CollectionConverters._ private object Reflection { - private val nmsPackage_1_12_R1 = "net.minecraft.server.v1_12_R1" - private val craftBukkitPackage_1_12_R1 = "org.bukkit.craftbukkit.v1_12_R1" + private val nmsPackage_1_18_R2 = "net.minecraft.server.v1_18_R1" + private val craftBukkitPackage_1_18_R2 = "org.bukkit.craftbukkit.v1_18_R2" object FileIOThread { private[Reflection] lazy val clazz: Class[_] = - Class.forName(s"$nmsPackage_1_12_R1.FileIOThread") + Class.forName(s"$nmsPackage_1_18_R2.FileIOThread") // public static FileIOThread method() lazy val getInstance: () => AnyRef = { @@ -32,7 +32,7 @@ object WorldChunkSaving { object Entity { private[Reflection] lazy val clazz: Class[_] = - Class.forName(s"$nmsPackage_1_12_R1.Entity") + Class.forName(s"$nmsPackage_1_18_R2.Entity") // public int field lazy val chunkX: AnyRef => Int = { @@ -56,7 +56,7 @@ object WorldChunkSaving { } object Chunk { - private[Reflection] lazy val clazz: Class[_] = Class.forName(s"$nmsPackage_1_12_R1.Chunk") + private[Reflection] lazy val clazz: Class[_] = Class.forName(s"$nmsPackage_1_18_R2.Chunk") // public void method(Entity) lazy val untrackEntity: AnyRef => AnyRef => Unit = { @@ -66,7 +66,7 @@ object WorldChunkSaving { } object World { - private[Reflection] lazy val clazz: Class[_] = Class.forName(s"$nmsPackage_1_12_R1.World") + private[Reflection] lazy val clazz: Class[_] = Class.forName(s"$nmsPackage_1_18_R2.World") // public final List field lazy val entityList: AnyRef => java.util.List[_ <: AnyRef] = { @@ -121,7 +121,7 @@ object WorldChunkSaving { object CraftWorld { private[Reflection] lazy val clazz: Class[_] = - Class.forName(s"$craftBukkitPackage_1_12_R1.CraftWorld") + Class.forName(s"$craftBukkitPackage_1_18_R2.CraftWorld") // public final nms.WorldServer (<: nms.World) // originally diff --git a/src/scalafix/scala/fix/MatchForMaterialToErrorForPerformance.scala b/src/scalafix/scala/fix/MatchForMaterialToErrorForPerformance.scala new file mode 100644 index 0000000000..a1d058274a --- /dev/null +++ b/src/scalafix/scala/fix/MatchForMaterialToErrorForPerformance.scala @@ -0,0 +1,31 @@ +package fix + +import scalafix.v1._ + +import scala.meta._ + +/** + * Lints on string interpolation where its variable part contains `case class` without `toString`. + */ +// noinspection ScalaUnusedSymbol; referred from scalafix implicitly +// NOTE: see AST on https://xuwei-k.github.io/scalameta-ast/ or https://astexplorer.net +class MatchForMaterialToErrorForPerformance extends SemanticRule("MatchForMaterialToErrorForPerformance") { + override def fix(implicit doc: SemanticDocument): Patch = { + doc.tree.collect { + case t @ Term.Match.After_4_4_5(term, _, _) => + term.symbol.info match { + case Some(info) if info.signature.toString == "Material" => + info.signature match { + case ValueSignature(TypeRef(_, symbol, _)) if SymbolMatcher.normalized("org/bukkit/Material").matches(symbol) => + val message = + s""" + |Don't use org.bukkit.Material in scrutinee of match expressions! + |See https://github.com/GiganticMinecraft/SeichiAssist/issues/2226 for more detail.""".stripMargin + Patch.lint(Diagnostic("error", message, t.pos)) + case _ => Patch.empty + } + case _ => Patch.empty + } + }.asPatch + } +} 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 a06b51d455..041c51b15b 100644 --- a/src/test/scala/com/github/unchama/generic/effect/ResourceScopeSpec.scala +++ b/src/test/scala/com/github/unchama/generic/effect/ResourceScopeSpec.scala @@ -138,7 +138,7 @@ class ResourceScopeSpec extends AnyWordSpec with Matchers with MockFactory { useTracked(firstResourceScope, NumberedObject(0), finalizer) { o => // noinspection ZeroIndexToHead runImpureFunction(o) >> - blockerList(0).await >> + blockerList(0).await() >> IO.never }.start _ <- blockerList(1).await() @@ -253,7 +253,7 @@ class ResourceScopeSpec extends AnyWordSpec with Matchers with MockFactory { useTrackedForSome(firstResourceScope, NumberedObject(0), finalizer) { o => // noinspection ZeroIndexToHead runImpureFunction(o) >> - blockerList(0).await >> + blockerList(0).await() >> IO.never }.start _ <- blockerList(1).await() diff --git a/src/test/scala/com/github/unchama/seichiassist/ManagedWorldSpec.scala b/src/test/scala/com/github/unchama/seichiassist/ManagedWorldSpec.scala index 74e5550757..41c080ddf1 100644 --- a/src/test/scala/com/github/unchama/seichiassist/ManagedWorldSpec.scala +++ b/src/test/scala/com/github/unchama/seichiassist/ManagedWorldSpec.scala @@ -6,6 +6,8 @@ import org.scalamock.scalatest.MockFactory import org.scalatest.Inspectors.forAll import org.scalatest.wordspec.AnyWordSpec +import scala.annotation.nowarn + /** * Created by karayuu on 2020/10/07 */ @@ -40,6 +42,7 @@ class ManagedWorldSpec extends AnyWordSpec with MockFactory { "return the appropriate truth-value" in { forAll(blockLineUpSkillEnableMap) { case (worldName, value) => + @nowarn val world = mock[World] (world.getName _).expects().returning(worldName) assert(world.isBlockLineUpSkillEnabled == value) @@ -51,6 +54,7 @@ class ManagedWorldSpec extends AnyWordSpec with MockFactory { "return the appropriate truth-value" in { forAll(inTrackedWorldMap) { case (worldName, value) => + @nowarn val world = mock[World] (world.getName _).expects().returning(worldName) assert(world.shouldTrackBuildBlock == value)