diff --git a/.github/actions/run-reviewdog/action.yml b/.github/actions/run-reviewdog/action.yml index 6a74e31fe8..13e842093e 100644 --- a/.github/actions/run-reviewdog/action.yml +++ b/.github/actions/run-reviewdog/action.yml @@ -6,6 +6,9 @@ inputs: description: The reviewdog's name. required: true default: reviewdog + github-token: + description: The GitHub Actions's token + required: true runs: using: composite steps: @@ -14,18 +17,17 @@ runs: if !(type "reviewdog" > /dev/null 2>&1); then REVIEWDOG_VERSION="0.14.1" FILE_NAME="reviewdog_${REVIEWDOG_VERSION}_Linux_x86_64.tar.gz" - wget -q https://github.com/reviewdog/reviewdog/releases/download/v${REVIEWDOG_VERSION}/${FILE_NAME} - tar -zxvf ${FILE_NAME} - rm -rf ${FILE_NAME} + curl -LSs https://github.com/reviewdog/reviewdog/releases/download/v${REVIEWDOG_VERSION}/${FILE_NAME} > "${FILE_NAME}" + tar -zxf "${FILE_NAME}" + rm -rf "${FILE_NAME}" chmod +x ./reviewdog - sudo mv reviewdog /usr/local/bin + mv reviewdog /usr/local/bin fi reviewdog -version shell: bash - name: Run reviewdog run: | - export REVIEWDOG_GITHUB_API_TOKEN=${{ secrets.GITHUB_TOKEN }} - TMPFILE=$(mktemp) + TMPFILE="$(mktemp)" git diff > "${TMPFILE}" reviewdog \ -name="${REVIEWDOG_NAME}" \ @@ -37,4 +39,5 @@ runs: -level="warning" <"${TMPFILE}" env: REVIEWDOG_NAME: ${{ inputs.reviewdog-name }} + REVIEWDOG_GITHUB_API_TOKEN: ${{ inputs.github-token }} shell: bash diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml index 1c2159a6ce..84283d0034 100644 --- a/.github/workflows/build_and_deploy.yml +++ b/.github/workflows/build_and_deploy.yml @@ -109,6 +109,7 @@ jobs: if: ${{ (github.event_name == 'pull_request') && failure() }} with: reviewdog-name: scalafmt + github-token: ${{ secrets.GITHUB_TOKEN }} - name: Check lint with Scalafix on push if: ${{ (github.event_name == 'push')}} @@ -121,6 +122,7 @@ jobs: if: ${{ (github.event_name == 'pull_request') && failure() }} with: reviewdog-name: scalafix + github-token: ${{ secrets.GITHUB_TOKEN }} - name: Test and build artifact run: sbt assembly diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..579b344ff6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,223 @@ +# 開発体制 + +## 開発を始めるために必要なもの +- [Intellij IDEA](https://www.jetbrains.com/idea/) などの統合開発環境 +- [AdoptOpenJDK 1.8](https://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot) +- [sbt 1.6](https://www.scala-sbt.org/1.x/docs/Setup.html) +- [Scala 2.13](https://www.scala-lang.org/download/) +- Spigot 1.12.2 +- Docker +- GitHubのアカウント +- Git + +### 準備 +#### Java Development Kit +最初に、Java Development Kit (JDK) 8をインストールする必要があります。 +[AdoptOpenJDK 1.8](https://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot) のインストールを推奨します。 + +#### 統合開発環境 +次に、[Intellij IDEA](https://www.jetbrains.com/idea/)などの統合開発環境を導入します。 + +##### Intellij +* インストールする時、Gitプラグインを有効にします。 +* Scala用の[プラグイン](https://plugins.jetbrains.com/plugin/1347-scala)を導入してください。 + +#### sbt +それが終わった後、[sbtの公式ページ](https://www.scala-sbt.org/1.x/docs/Setup.html) に従ってsbtのインストールをします。 +sbtがコマンドラインで使える状態で`sbt assembly`を実行すると、`target/build`フォルダにjarが出力されます。 + +#### Scala +Scalaはsbtによって自動的にダウンロード及びインストールされます。 + +#### Docker +SpigotサーバーのDockerコンテナを立ち上げるために、Dockerのインストールが必要です。 + +#### Spigot +SpigotサーバーはDockerコンテナによって提供されます。 + +#### GitHubのアカウント +GitHubにアカウントを[登録](https://github.com/join)します。 +詳細な手順は有志の方の[記事](https://qiita.com/mfunaki/items/e01762475967d4e05a1f)をご覧ください。 + +#### 上級者向け:ローカルにJavaとかsbtを入れたくない場合 + +(注: 自分が何をしているかわかっていないのであれば、この部分を飛ばしてください) + +* VSCode + WSLで開発している +* ビルドして立ち上げたいだけ +* ランタイムの導入のコストが高いと考えている + +場合は、以下のシェルスクリプトを使うと便利です。 + +```bash +$ rm -rf target/build # 再ビルドしたいなら既存のターゲットは削除 +$ docker run --rm -it -v `pwd`:/app ghcr.io/giganticminecraft/seichiassist-builder:1a64049 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 +``` + +#### Git +最後に、Gitのインストールも必要です。公式の[ガイド](https://git-scm.com/book/ja/v2/%E4%BD%BF%E3%81%84%E5%A7%8B%E3%82%81%E3%82%8B-Git%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)をご覧ください。 + +### SeichiAssistを自分のGitHubアカウントにコピーする +(注意: この手順は、[gm-gh-repo]にpushできる人であれば飛ばすことができます。よくわからない場合は、この注意書きを無視して進んでください。) + +注意:この手順は、最初に一回だけやる必要があります。二回以上行う必要はありません。 + +変更を加える前に、SeichiAssistを自分の手元に「コピー」する必要があります。 +最初に、[GiganticMinecraftのページ][gm-gh-repo]を開いて、画面右上にある「fork」と書かれた枝分かれしているアイコンがあるボタンを押します。 + +すると「Create a new fork」と書かれた画面に移動します。 +![img.png](img.png) +いくつか入力欄がありますが、何も触らずにCreate forkを押します。 + +また画面が切り替わります。画面左上に書かれた文字が「GiganticMinecraft/SeichiAssist」ではなく、「(あなたのID)/SeichiAssist」になっていることを確認できたら次へ進みます。 + +### SeichiAssistを自分の手元にコピーする +SeichiAssistは、Gitというバージョンを管理するシステムを使っています。そのため、どうにかして自分のパソコンにSeichiAssistを自分の手元にコピーしてくる必要があります。 + +#### IntelliJの場合 +[JetBrainsのヘルプ](https://www.jetbrains.com/help/idea/cloning-repository.html) (英語) をご覧ください。 + + +#### protocol以下のファイルを入手 +(注意: この手順は統合開発環境を使わない場合の手順です。) + +(注意: この手順は最初に一度だけ行う必要があります。) + +protocol以下のファイルは`git clone`では入手することができません。以下のどちらかのコマンドを実行してください: +* `git clone --recursive` +* `git submodule update --init --recursive` + +### issueを見る +一旦[GiganticMinecraftのページ][gm-gh-repo]へ戻って、画面上部の[Issues](https://github.com/GiganticMinecraft/SeichiAssist/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc)と書かれたタブをクリックしてみましょう。 + +すると画面が切り替わり、たくさんのやりたいこと (主に[Redmineで承認されたもの](https://github.com/GiganticMinecraft/SeichiAssist/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22Status%2FIdea%3A+Accepted%E2%9C%85%22+label%3A%22Tracked%3A+Redmine%22)) や、[バグ報告](https://github.com/GiganticMinecraft/SeichiAssist/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Abug)が出てきます。 + +はじめはこの中からできそうなものを探すと良いと思います。初めての場合は、"good first issue"というラベルがつけられた中から探すのがおすすめです。[ +ここ](https://github.com/GiganticMinecraft/SeichiAssist/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)から飛べます。 + +### IntelliJ特有の手順 +IntelliJ IDEAの設定でフォーマットに `scalafmt` を使う +- `Editor` > `Code Style` > `Scala` で + - `Formatter` を `Scalafmt` に変更 + - `Reformat on file save` にチェックを付ける + + +### 変更を加える +1. developというブランチ (セーブデータのスロットのようなもの) を元に、新しいブランチを作ります。 +2. 必要な変更を加えてファイルを保存します +3. コミット (Gitに対するセーブ) します。コミットする時、メッセージを書くように求められます。[コンベンショナルコミット](https://www.conventionalcommits.org/ja/v1.0.0/)という、メッセージの書き方を推奨しています。わからなければ、今は飛ばしても大丈夫です。 + * メッセージの1行目には変更した点の大まかな内容を書くように心がけてみてください。 + * 1行目は45文字以内で書くことを推奨します。 + * コンベンショナルコミット:1行目は「変更の区分」、半角コロン、半角スペースで始める方法を推奨しています。 + * 新しい機能を実装したときは`feat: [大まかな内容]`とします。 + * バグを修正したときは`fix: [大まかな内容]`とします。 + * ドキュメントを触ったときは`docs: [大まかな内容]`とします。 + * GitHub Actionsを触ったときは`ci: [大まかな内容]`とします。 + * リファクタリングしたときは`refactor: [大まかな内容]`とします。 + * テストを書いたときは`test: [大まかな内容]`とします。 + * scalafmtやscalafixを反映したときは`style: [大まかな内容]`とします。 + * パフォーマンスを改善したときは`perf: [大まかな内容]`とします。 + * その他のコード品質に関わらない変更をしたときは、`chore: [大まかな内容]`とします。 + * 発展的な内容:コンベンショナルコミットにおいて複数の種別に該当する場合、引き返して複数のコミットに分割することが推奨されています。 + * メッセージの2行目以降は、より詳しい内容を書くことができます。 + * メッセージは日本語か英語で書くことを推奨します。 +4. 良くなるまで繰り返します + +#### 手元でデバッグ +SeichiAssistは手元でデバッグできる環境を整えています。環境を立ち上げるためには、Dockerが必要です。 + +##### Dockerを立ち上げる + +Linux環境では、`./prepare-docker.sh`、Windowsでは`prepare-docker.bat`を実行することで +デバッグ用のBungeecordとSpigotの環境を構築することができます。 + +サーバーやDB等を停止する場合、 `docker compose down` を実行してください。 + +なお、SeichiAssistがJava 8以外でコンパイルされた場合は、実行時にエラーとなります。必ずJDKのバージョンを揃えるようにしてください。 + +##### データベースの初期化 + +(注: この手順は初めてDockerを立ち上げた場合のみ必要です) + +初回起動後、データベースが作成されますが、ガチャ景品のデータがありません。そのため、次のSQLのダンプをインポートします。 +- [`gachadata.sql`](https://redmine.seichi.click/attachments/download/995/gachadata.sql) + +SQLのダンプをインポートする手順は以下の通りです。 +1. 一旦サーバーを起動させる +2. phpMyAdminを開く +3. トップ画面の上部メニューから「データベース」を開く +4. `seichiassist`と`flyway_managed_schema`にチェックを入れて、「削除」、「OK」 +5. 「データベースを作成する」の下にあるテキストボックスに`seichiassist`と入力し、「作成」 +6. `seichiassist`のデータベースを開き、上部メニューから「インポート」 +7. 「File to import」の「ファイルを選択」から、ダウンロードした`gachadata.sql`を選択 +8. 画面下部の「実行」 +9. サーバーを再起動させる + +##### デバッグ用環境への接続 + +DockerマシンのIPアドレス(Linux等なら`localhost`)を`DOCKER_IP`とします。 + +`docker`により各サービスが起動したら、マルチプレイヤーのメニューで`DOCKER_IP`へと接続することができます。 +また、`DOCKER_IP:8080`へとWebブラウザでアクセスすることで、phpMyAdminを介してデータベースを操作することができます。 + +`/op`などのコマンドを実行するためにSpigotのコンソールにアクセスする必要がある場合、 +`spigota`または`spigotb`へのコンテナ名とともに `docker attach [CONTAINER_NAME]` を実行してください。 +コンテナ名は `docker ps` を実行すると `seichiassist_spigotb_1` のような形式で表示されます。 +コンソールからは CtrlキーとCキーを同時押しすることで出ることができます。サーバーは停止されません。 + +### 反映する +さあ、いよいよ反映の時間がやってきました。 +まずは手元で`sbt scalafixAll`をします。次に`sbt scalafmtAll`をします。 +その後、自分の手元から自分のGitHubアカウントへ内容を反映します。 + +#### Pull Requestの作成 +次に、自分のGitHubアカウントにあるSeichiAssistを開いて、変更を依頼する手続き (Pull Request) の準備画面へ移動します。 +画面が切り替わります。右上のCreate pull requestと書かれたボタンを押してください。 +そうすると、高さが狭い場所と広い場所が表示されます。 +狭い場所には、変更の概要を50文字以内で簡単に書いてください。 (TODO: コンベンショナルコミット) +広い場所には、書くべきだと思ったことを書いてください。詳しすぎるということはありません。 +一通り書き終わったら、長い場所の右下にある「Create pull request」と書かれたボタンを押してください。 + +### コードレビューを待つ +Pull Requestが作成されたら、GitHubのサーバーでコンパイルやファイルのチェックが自動的に始まります。 +また、[確認できる人](https://github.com/orgs/GiganticMinecraft/people) (主に@kory33、@rito528、@KisaragiEffective、@Lucky3028)が方向性が正しいかどうかレビューを行います。それまでは優雅に紅茶を飲んだり踊ったりして待つことができます。 + +SeichiAssistでPull Requestが受け入れられる (マージされる) には、下の条件をすべてクリアする必要があります。 +* コンパイルやファイルのチェックがすべてエラーなく終わる + * コンパイル + * scalafix - エラーになった場合は`sbt scalafixAll`をしてください + * scalafmt - エラーになった場合は`sbt scalafmtAll`をしてください +* 誰かから変更を承認してもらう + +### 承認とサーバーへの反映 +条件がすべてクリアされると、Pull Requestがマージ (変更依頼手続きが完了) されます。 + +#### デバッグサーバーへの反映 +マージされた後、自動的にデバッグサーバーへの反映手続きが始まります。Discordで進捗を確認することができます。 +通常、デバッグサーバーへの反映は数分程度で終わります。 +デバッグ環境へは、以下の手順で接続できます。 +1. Minecraft Java Editionで`play-debug.seichi.click`に接続 +2. Tキーでチャットを開く +3. `/server deb112`と入力して`Enter`を押す + +#### 本番サーバーへの反映 +(注: この部分はGiganticMinecraftのメンバーへ向けた内容です) + +本番サーバーへの反映は通常GitHub ActionsでPull Requestを作成し、それをマージすることで行います。 +1. [GitHub Actionsのタブ](https://github.com/GiganticMinecraft/SeichiAssist/actions/workflows/create_new_release.yml)へ移動します。 +2. 画面右の「Run workflow」を押します。 +3. しばらくすると`master <- develop`のPull Requestが作成されます。 +4. 本番サーバーへ反映したいタイミングでマージします。 +5. マージした後、朝4時の再起動で変更が反映されます。 + +緊急を要する場合は、`hotfix-*`ブランチを作成し、そのブランチから`master`ブランチへ向けてPull Requestを作成してください。 +`develop`ブランチへの直プッシュは、CIによるチェックが事後となってしまうため避けてください。 + +#### 自動リリースの範囲 +自動リリースはSeichiAssistのプログラムの部分のみ行われます。より正確に言うのであれば、jar以外の自動リリースは未対応です(`config.yml`など)。運営チームへ更新を依頼する必要があります。 +この問題点があるため、各サーバーや環境で共通で構わないパラメータは`config.yml`を読まず、コードへの直接実装を推奨します。 + +[gm-gh-repo]: https://github.com/GiganticMinecraft/SeichiAssist diff --git a/README.md b/README.md index 4f54712c80..966d7cde88 100644 --- a/README.md +++ b/README.md @@ -22,112 +22,21 @@ ## 前提プラグイン(整地鯖内製) - RegenWorld [リポジトリ](https://github.com/GiganticMinecraft/RegenWorld) | [jar](https://redmine.seichi.click/attachments/download/890/RegenWorld-1.0.jar) -## ビルド - -最初に、Java Development Kit (JDK) 8をインストールする必要があります。 -[AdoptOpenJDK 1.8](https://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot) のインストールを推奨します。 - -[sbtの公式ページ](https://www.scala-sbt.org/1.x/docs/Setup.html) に従ってsbtのインストールをします。 -sbtがコマンドラインで使える状態で`sbt assembly`を実行すると、`target/build`フォルダにjarが出力されます。 - -### IntelliJ IDEAの画面からビルドする - -IntelliJ IDEAを開発に使用している場合、プロジェクトをsbtプロジェクトとして読み込み、 -sbtタブからSeichiAssist -> SeichiAssist -> sbt tasks -> assemblyを実行すれば`target/build`フォルダにjarが出力されます。 - -## デバッグ用docker環境 - -`docker`及び`sbt`が実行可能であるとします。 -Linux環境では、`./prepare-docker.sh`、Windowsでは`prepare-docker.bat`を実行することで -デバッグ用のBungeecord + Spigot環境を構築することができます。 - -サーバーやDB等を停止する場合、 `docker compose down` を実行してください。 - -なお、SeichiAssistがJava 8以外でコンパイルされた場合は、実行時にエラーとなります。必ずJDKのバージョンを揃えるようにしてください。 - -### デバッグ用環境への接続 - -DockerマシンのIPアドレス(Linux等なら`localhost`)を`DOCKER_IP`とします。 - -`docker`により各サービスが起動したら、`DOCKER_IP`へとMinecraftを接続することができます。 -また、`DOCKER_IP:8080`へとWebブラウザでアクセスすることで、phpMyAdminを介してデータベースを操作することができます。 - -`/op`などのコマンドを実行するためにSpigotのコンソールにアクセスする必要がある場合、 -`spigota`または`spigotb`へのコンテナ名とともに `docker attach [CONTAINER_NAME]` を実行してください。 -コンテナ名は `docker ps` を実行すると `seichiassist_spigotb_1` のような形式で表示されます。 -コンソールからは `Ctrl+C` で抜けることができます(サーバーは停止されません)。 - -## DBの準備 - -### ガチャ景品データの準備 - -初回起動後、DBが作成されますが、ガチャ景品のデータがありません。そのため、次のSQLdumpをインポートします。 -- [gachadata.sql](https://redmine.seichi.click/attachments/download/992/gachadata.sql) - -手順は以下の通りです。 -1. 一旦サーバーを起動させる -2. phpMyAdminを開く -3. トップ画面の上部メニューから「データベース」を開く -4. `seichiassist`と`flyway_managed_schema`にチェックを入れて、「削除」、「OK」 -5. 「データベースを作成する」の下にあるテキストボックスに`seichiassist`と入力し、「作成」 -6. `seichiassist`のデータベースを開き、上部メニューから「インポート」 -7. 「File to import」の「ファイルを選択」から、ダウンロードした`gachadata.sql`を選択 -8. 画面下部の「実行」 -9. サーバーを再起動させる - -### どうしてもローカルにJavaとかsbtを入れたくない人のための救済策 - -VSCode + WSLで開発している場合や、純粋にビルドして立ち上げたいだけの場合はランタイムの導入のコストが高いので、以下の方法を使うと便利です。 - -```bash -$ rm -rf target/build # 再ビルドしたいなら既存のターゲットは削除 -$ docker run --rm -it -v `pwd`:/app ghcr.io/giganticminecraft/seichiassist-builder:1a64049 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 -``` - -## protocolディレクトリ以下のクローン -protocol以下のファイルは`git clone`では入手することができません。以下のどちらかのコマンドを実行してください: -* `git clone --recursive` -* `git submodule update --init --recursive` - -## 開発スタイル -### ドキュメンテーション -publicなメソッドについては、ドキュメンテーションを記載するよう心がけてください。 -その他は各自が必要だと判断した場合のみ記載してください。 - -### Commit Style -1コミットあたりの情報は最小限としてください。 -コミットメッセージは[コンベンショナルコミット](https://www.conventionalcommits.org/ja/v1.0.0/)を採用することを推奨しています。 - -### デプロイ -#### ブランチ -[Git-flow](https://qiita.com/KosukeSone/items/514dd24828b485c69a05) を簡略化したものを使用します。 -新規機能の開発など、変更を加える際は`develop`ブランチから <任意の文字列> ブランチを作り、そこで作業してください。 -開発が終了したら`develop`ブランチにマージします。`develop`ブランチへ直接コミットすることは避けてください (CIによる検査がコミットの事後となってしまうため)。 -`master`ブランチは本番環境に反映されます。 - -#### リリース -- `develop`ブランチが更新されると、そのコードを基に実行用jarがビルドされ、デバッグ環境に配布されます。デバッグ環境はjarの配布を検知すると自動で再起動し、最新のjarを使用して稼働します。 - - デバッグ環境へは、Minecraft Java Editionで`play-debug.seichi.click`に接続し、`T`キーでチャットを開き、`/server deb112`と入力して`Enter`を押すとアクセスできます。 -- `master`ブランチが更新されると、そのコードを基に実行用jarがビルドされ、本番環境に配布されます。本番環境は翌再起動時に自動で最新のjarを取り込んで稼働します。 - - `master`ブランチの更新は必ず `develop` または `hotfix-*` からのPull Requestによって行ってください。 - また、 `develop` からのリリース用 Pull Request は [`create_new_release`](https://github.com/GiganticMinecraft/SeichiAssist/actions/workflows/create_new_release.yml) ワークフローを実行することで作成してください(`build.sbt` の自動更新などが行われます)。 -- jar以外の自動リリースは未対応です(`config.yml`など)。運営チームへ更新を依頼する必要があります。 - - 各サーバーや環境で共通で構わないパラメータは`config.yml`を読まず、コードへの直接実装を推奨します。 - -### フォーマットおよびlintに関して -フォーマットには[scalafmt](https://scalameta.org/scalafmt)、lintには[scalafix](https://scalacenter.github.io/scalafix/)を利用しています。 - -コード品質を最低限保つため、PRが受け入れられるにはscalafmtとscalafixの両方のチェックが通る必要があります。そのため、 - - IntelliJ IDEAの設定でフォーマットに `scalafmt` を使う - - `Editor` > `Code Style` > `Scala` で - - `Formatter` を `Scalafmt` に変更 - - `Reformat on file save` にチェックを付ける - - PRを送る前に `sbt` コンソールで `scalafixAll` と `scalafmtAll` を実行する - -ようにお願いします。 +## コントリビューション +どのように貢献するかは[CONTRIBUTING.md](./CONTRIBUTING.md)へ移動しました。 + +慣れている開発者向け: +1. 自分のGitHubアカウントへ[fork](https://github.com/GiganticMinecraft/SeichiAssist/fork) +2. `git clone https://github.com/${YOUR_GITHUB_ID}/SeichiAssist` +3. `cd SeichiAssist` +4. `git checkout -b ${YOUR_BRANCH_NAME} develop` +5. `git commit -am "feat: 〇〇を実装"` (コンベンショナルコミットを推奨。本文は英語または日本語を推奨) +6. `git push` +7. GiganticMinecraftのレポジトリに向かってPull Requestを作成 +8. CIとコードレビューを待つ +9. マージされる +10. CDが実行される +11. (本番サーバーへのデプロイはGiganticMinecraftのメンバーがPRによって行う) ## 利用条件 - [GPLv3ライセンス](https://github.com/GiganticMinecraft/SeichiAssist/blob/develop/LICENSE) での公開です。ソースコードの使用規約等はGPLv3ライセンスに従います。 diff --git a/build.sbt b/build.sbt index b6108f6653..07e3d931ff 100644 --- a/build.sbt +++ b/build.sbt @@ -8,7 +8,7 @@ import java.io._ ThisBuild / scalaVersion := "2.13.4" // ThisBuild / version はGitHub Actionsによって取得/自動更新される。 // 次の行は ThisBuild / version := "(\d*)" の形式でなければならない。 -ThisBuild / version := "72" +ThisBuild / version := "73" ThisBuild / organization := "click.seichi" ThisBuild / description := "ギガンティック☆整地鯖の独自要素を司るプラグイン" diff --git a/src/main/scala/com/github/unchama/seichiassist/database/manipulators/PlayerDataManipulator.scala b/src/main/scala/com/github/unchama/seichiassist/database/manipulators/PlayerDataManipulator.scala index 3593dec0ed..bce4e4a1cd 100644 --- a/src/main/scala/com/github/unchama/seichiassist/database/manipulators/PlayerDataManipulator.scala +++ b/src/main/scala/com/github/unchama/seichiassist/database/manipulators/PlayerDataManipulator.scala @@ -111,7 +111,7 @@ class PlayerDataManipulator(private val gateway: DatabaseGateway) { val LastLong = LastDate.getTime val dateDiff = (TodayLong - LastLong) / (1000 * 60 * 60 * 24) - val shouldIncrementChainVote = dateDiff <= 4L + val shouldIncrementChainVote = dateDiff <= 2L val newCount = if (shouldIncrementChainVote) { sql"""select chainvote from playerdata where name = $name""" 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 77c0685997..6d04ede6af 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerBlockBreakListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerBlockBreakListener.scala @@ -1,6 +1,7 @@ package com.github.unchama.seichiassist.listener import cats.effect.{Fiber, IO, SyncIO} +import com.github.unchama.generic.ApplicativeExtra.whenAOrElse import com.github.unchama.generic.effect.unsafe.EffectEnvironment import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.ManagedWorld._ @@ -14,6 +15,7 @@ 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.util.effect.BukkitResources @@ -28,7 +30,6 @@ import org.bukkit.event.{EventHandler, EventPriority, Listener} import org.bukkit.inventory.ItemStack import scala.collection.mutable.ArrayBuffer -import scala.jdk.CollectionConverters.CollectionHasAsScala import scala.util.control.Breaks class PlayerBlockBreakListener( @@ -301,6 +302,12 @@ class PlayerBlockBreakListener( SeichiAssist.instance.breakCountSystem.api.incrementSeichiExp.of(player, amount).toIO ) + val tool: BreakTool = MaterialSets + .refineItemStack(player.getInventory.getItemInMainHand, MaterialSets.breakToolMaterials) + .getOrElse( + return + ) + /** * 手彫りで破壊したアイテムを直接MineStackに入れる * 一つのBlockBreakEventから複数の種類のアイテムが出てくることはない。 @@ -308,19 +315,29 @@ class PlayerBlockBreakListener( * 破壊された`b`のみが`BlockBreakEvent`のドロップ対象となるため、 * 中身のドロップがキャンセルされることはない。 */ - event - .getBlock - .getDrops(event.getPlayer.getInventory.getItemInMainHand) - .asScala - .toList - .traverse { droppedItemStack => - mineStackAPI - .mineStackRepository - .tryIntoMineStack(player, droppedItemStack, droppedItemStack.getAmount) - .map(if (_) event.setDropItems(false) else ()) - } - .unsafeRunSync() - .foreach(program => program) + 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.setCancelled(false) + else () + } + program.unsafeRunSync() + case _ => () + } } /** 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 98d79cfc22..a85cf22714 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,19 +1,23 @@ package com.github.unchama.seichiassist.subsystems.donate.bukkit.commands import cats.effect.ConcurrentEffect.ops.toAllConcurrentEffectOps -import cats.effect.{ConcurrentEffect, IO} +import cats.effect.{ConcurrentEffect, IO, Sync} import com.github.unchama.contextualexecutor.ContextualExecutor import com.github.unchama.contextualexecutor.builder.{ContextualExecutorBuilder, Parsers} import com.github.unchama.contextualexecutor.executors.BranchedExecutor import com.github.unchama.seichiassist.subsystems.donate.domain.{ DonatePersistence, DonatePremiumEffectPoint, + Obtained, PlayerName } import com.github.unchama.targetedeffect.commandsender.MessageEffect import org.bukkit.ChatColor._ import org.bukkit.command.TabExecutor +import java.time.LocalDate +import java.time.format.DateTimeFormatter + class DonationCommand[F[_]: ConcurrentEffect]( implicit donatePersistence: DonatePersistence[F] ) { @@ -33,10 +37,30 @@ class DonationCommand[F[_]: ConcurrentEffect]( val args = context.args.parsed val playerName = PlayerName(args.head.toString) val donatePoint = DonatePremiumEffectPoint(args(1).asInstanceOf[Int]) + + 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 eff = for { - _ <- donatePersistence.addDonatePremiumEffectPoint(playerName, donatePoint) + date <- 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) } yield { - MessageEffect(s"$GREEN${playerName.name}に${donatePoint.value}のプレミアムエフェクトポイントを付与しました。") + if (!isMatchedPattern) + MessageEffect(s"${RED}購入日はyyyy-MM-ddの形式で指定してください。") + else + MessageEffect( + s"$GREEN${playerName.name}に${donatePoint.value}のプレミアムエフェクトポイントを付与しました。" + ) } eff.toIO } @@ -49,8 +73,10 @@ class DonationCommand[F[_]: ConcurrentEffect]( IO { MessageEffect( List( - s"$RED/donation record <プレイヤー名> <ポイント数>", - "寄付者用プレミアムエフェクトポイント配布コマンドです(マルチ鯖対応済)" + s"$RED/donation record <プレイヤー名> <ポイント数> <(購入日)>", + "寄付者用プレミアムエフェクトポイント配布コマンドです(マルチ鯖対応済)", + "購入日を指定しなければ今日の日付になります。", + "※購入日はyyyy-MM-ddの形式で指定してください。" ) ) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/donate/domain/DonatePersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/donate/domain/DonatePersistence.scala index 421cd78264..3fcc6f3954 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/donate/domain/DonatePersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/donate/domain/DonatePersistence.scala @@ -11,7 +11,7 @@ trait DonatePersistence[F[_]] { */ def addDonatePremiumEffectPoint( playerName: PlayerName, - donatePremiumEffectPoint: DonatePremiumEffectPoint + obtainedPremiumEffectPoint: Obtained ): F[Unit] /** 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 874d81db3c..01fa757d4e 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 @@ -18,16 +18,15 @@ class JdbcDonatePersistence[F[_]: Sync] extends DonatePersistence[F] { override def addDonatePremiumEffectPoint( playerName: PlayerName, - donatePremiumEffectPoint: DonatePremiumEffectPoint + obtainedPremiumEffectPoint: Obtained ): F[Unit] = Sync[F].delay { DB.localTx { implicit session => sql"""INSERT INTO donate_purchase_history - | (uuid, get_points) + | (uuid, get_points, timestamp) | VALUES - | ((SELECT uuid FROM playerdata WHERE name = ${playerName.name}), ${donatePremiumEffectPoint.value})""" - .stripMargin - .execute() - .apply() + | ((SELECT uuid FROM playerdata WHERE name = ${playerName.name}), + | ${obtainedPremiumEffectPoint.effectPoint.value}, + | ${obtainedPremiumEffectPoint.purchaseDate})""".stripMargin.execute().apply() } } 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 40f3d83156..6eff3c4164 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 @@ -3,6 +3,7 @@ package com.github.unchama.seichiassist.subsystems.gacha.bukkit.actions import cats.Monad import cats.data.Kleisli import cats.effect.Sync +import com.github.unchama.generic.ApplicativeExtra.whenAOrElse import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.subsystems.gacha.application.actions.GrantGachaPrize import com.github.unchama.seichiassist.subsystems.gacha.domain.GrantState @@ -18,16 +19,24 @@ class BukkitGrantGachaPrize[F[_]: Sync: OnMinecraftServerThread]( mineStackAPI: MineStackAPI[F, Player, ItemStack] ) extends GrantGachaPrize[F, ItemStack] { + import cats.implicits._ + override def tryInsertIntoMineStack( prize: GachaPrize[ItemStack] ): Kleisli[F, Player, Boolean] = Kleisli { player => val itemStack = prize.itemStack - mineStackAPI.mineStackRepository.tryIntoMineStack(player, itemStack, itemStack.getAmount) + for { + currentAutoMineStackState <- mineStackAPI.autoMineStack(player) + isSucceedTryIntoMineStack <- whenAOrElse(currentAutoMineStackState)( + mineStackAPI + .mineStackRepository + .tryIntoMineStack(player, itemStack, itemStack.getAmount), + false + ) + } yield isSucceedTryIntoMineStack } - import cats.implicits._ - override def insertIntoPlayerInventoryOrDrop( prize: GachaPrize[ItemStack], ownerName: Option[String] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/idletime/subsystems/awayscreenname/bukkit/BukkitUpdatePlayerScreenName.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/idletime/subsystems/awayscreenname/bukkit/BukkitUpdatePlayerScreenName.scala index 2c5037a486..5c4431ee2d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/idletime/subsystems/awayscreenname/bukkit/BukkitUpdatePlayerScreenName.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/idletime/subsystems/awayscreenname/bukkit/BukkitUpdatePlayerScreenName.scala @@ -1,6 +1,7 @@ package com.github.unchama.seichiassist.subsystems.idletime.subsystems.awayscreenname.bukkit import cats.effect.Sync +import com.github.unchama.seichiassist.SeichiAssist import com.github.unchama.seichiassist.subsystems.idletime.IdleTimeAPI import com.github.unchama.seichiassist.subsystems.idletime.subsystems.awayscreenname.domain.{ NameColorByIdleMinute, @@ -19,13 +20,24 @@ class BukkitUpdatePlayerScreenName[F[_]: Sync]( override def updatePlayerNameColor(player: Player): F[Unit] = for { currentIdleMinute <- idleTimeAPI.currentIdleMinute(player) - } yield { - val currentDisplayName = player.getDisplayName - val currentPlayerListName = player.getPlayerListName - val newPlayerNameColor = nameColorByIdleMinute.getNameColor(currentIdleMinute) + currentDisplayName = player.getDisplayName + currentPlayerListName = player.getPlayerListName + newPlayerNameColor = nameColorByIdleMinute.getNameColor(currentIdleMinute) + _ <- Sync[F].delay { + /* + * 表示名とマナをレベルと同期する + * FIXME: ここの更新は、もともとPlayerDataRecalculationRoutine.scalaで行われていたもので、 + * https://github.com/GiganticMinecraft/SeichiAssist/issues/1878 + * を修正するためにやむを得ずここに記載している。 + * この処理は本来ここにあるべきではなく、プレイヤー名関連の処理をリファクタリングする際に + * 適切な場所へ配置するべきである。 + */ + val playerData = SeichiAssist.playermap(player.getUniqueId) + playerData.synchronizeDisplayNameToLevelState() - player.setDisplayName(newPlayerNameColor + currentDisplayName) - player.setPlayerListName(newPlayerNameColor + currentPlayerListName) - } + player.setDisplayName(s"$newPlayerNameColor$currentDisplayName") + player.setPlayerListName(s"$newPlayerNameColor$currentPlayerListName") + } + } yield () } 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 c67a91cafa..b4c3d07ac9 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 @@ -25,7 +25,6 @@ case class MineStackObject[ItemStack: Cloneable]( name: String )(implicit gachaPrizeAPI: GachaPrizeAPI[F, ItemStack, Player]): F[Option[ItemStack]] = { if (category != MineStackObjectCategory.GACHA_PRIZES) return Sync[F].pure(None) - println("gachaPrize") implicit val canBeSignedAsGachaPrize: CanBeSignedAsGachaPrize[ItemStack] = gachaPrizeAPI.canBeSignedAsGachaPrize @@ -33,7 +32,6 @@ case class MineStackObject[ItemStack: Cloneable]( for { foundGachaPrize <- gachaPrizeAPI.findOfRegularGachaPrizesByNotSignedItemStack(itemStack) } yield { - println(s"foundGachaPrize: ${foundGachaPrize.isDefined}") foundGachaPrize.map { gachaPrize => gachaPrize.materializeWithOwnerSignature(name) } } } 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 e06dbe899e..e8f96aed17 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 @@ -35,13 +35,15 @@ class ValentineListener[F[_]: ConcurrentEffect: NonServerThreadContextShift]( ioOnMainThread: OnMinecraftServerThread[IO] ) extends Listener { + // クリーパーが爆発した場合、確率でアイテムをドロップ @EventHandler def onEntityExplode(event: EntityExplodeEvent): Unit = { - val entity = event.getEntity - if (!isInEvent || entity == null) return + if (!isInEvent) return - if (entity.isInstanceOf[Monster] && entity.isDead) { - randomlyDropItemAt(entity, droppedCookie, itemDropRate) + event.getEntity match { + case monster: Monster if monster.isDead && monster.getType == EntityType.CREEPER => + randomlyDropItemAt(monster, droppedCookie, itemDropRate) + case _ => } } @@ -49,20 +51,20 @@ class ValentineListener[F[_]: ConcurrentEffect: NonServerThreadContextShift]( @EventHandler def onEntityDeath(event: EntityDamageByEntityEvent): Unit = { if (!isInEvent) return + if (event.getCause != DamageCause.ENTITY_EXPLOSION) return val damager = event.getDamager - if (damager == null) return - - if (event.getCause != DamageCause.ENTITY_EXPLOSION || damager.getType != EntityType.CREEPER) - return + if (damager == null || damager.getType != EntityType.CREEPER) return + val excludedMonsters = Set(EntityType.WITCH, EntityType.PIG_ZOMBIE) event.getEntity match { - case monster: Monster => - val entityMaxHealth = monster.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue - // monsterが死んだならば + case damaged: Monster if !excludedMonsters.contains(damaged.getType) => + val entityMaxHealth = damaged.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue + // damagedが死んだならば if (entityMaxHealth <= event.getDamage) { - randomlyDropItemAt(monster, droppedCookie, itemDropRate) + randomlyDropItemAt(damaged, droppedCookie, itemDropRate) } + case _ => } } @@ -70,7 +72,7 @@ class ValentineListener[F[_]: ConcurrentEffect: NonServerThreadContextShift]( def onPlayerJoinEvent(event: PlayerJoinEvent): Unit = { if (isInEvent) { Seq( - s"$LIGHT_PURPLE${END_DATE_TIME}までの期間限定で、イベント『バレンタインイベント${EVENT_YEAR}』を開催しています。", + s"$LIGHT_PURPLE${END_DATE_TIME}までの期間限定で、イベント『バレンタインイベント$EVENT_YEAR』を開催しています。", "詳しくは下記URLのサイトをご覧ください。", s"$DARK_GREEN$UNDERLINE$blogArticleUrl" ).foreach(event.getPlayer.sendMessage(_)) diff --git a/src/main/scala/com/github/unchama/seichiassist/task/PlayerDataLoading.scala b/src/main/scala/com/github/unchama/seichiassist/task/PlayerDataLoading.scala index 80d9ed009a..e2bf95fa3d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/task/PlayerDataLoading.scala +++ b/src/main/scala/com/github/unchama/seichiassist/task/PlayerDataLoading.scala @@ -236,7 +236,7 @@ object PlayerDataLoading { if (dateDiff >= 1L) { val newTotalLoginDay = playerData.loginStatus.totalLoginDay + 1 val newConsecutiveLoginDays = - if (dateDiff <= 4L) + if (dateDiff <= 2L) playerData.loginStatus.consecutiveLoginDays + 1 else 1 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 5378d2fafa..dd5246bad9 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 @@ -38,9 +38,6 @@ object PlayerDataRecalculationRoutine { for (player <- onlinePlayers) { val playerData = SeichiAssist.playermap(player.getUniqueId) - // 表示名とマナをレベルと同期する - playerData.synchronizeDisplayNameToLevelState() - // 総プレイ時間更新 playerData.updatePlayTick() 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 e85323f108..3f62687dfd 100644 --- a/src/main/scala/com/github/unchama/seichiassist/util/BreakUtil.scala +++ b/src/main/scala/com/github/unchama/seichiassist/util/BreakUtil.scala @@ -2,6 +2,7 @@ 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._ @@ -154,9 +155,9 @@ object BreakUtil { massBreakBlock(player, Set(targetBlock), dropLocation, tool, shouldPlayBreakSound) ) - private sealed trait BlockBreakResult + sealed trait BlockBreakResult - private object BlockBreakResult { + object BlockBreakResult { case class ItemDrop(itemStack: ItemStack) extends BlockBreakResult @@ -170,7 +171,7 @@ object BreakUtil { * Bukkit/Spigotが提供するBlock.getDropsは信頼できる値を返さない。 本来はNMSのメソッドを呼ぶのが確実らしいが、一時的な実装として使用している。 参考: * https://www.spigotmc.org/threads/getdrops-on-crops-not-functioning-as-expected.167751/#post-1779788 */ - private def dropItemOnTool( + def dropItemOnTool( tool: BreakTool )(blockInformation: (Location, Material, Byte)): Option[BlockBreakResult] = { val fortuneLevel = tool.getEnchantmentLevel(Enchantment.LOOT_BONUS_BLOCKS) @@ -394,17 +395,25 @@ object BreakUtil { (ItemStackUtil.amalgamate(drops), silverFishLocations) } + currentAutoMineStackState <- SeichiAssist + .instance + .mineStackSystem + .api + .autoMineStack(player) + itemsToBeDropped <- // アイテムのマインスタック自動格納を試みる // 格納できなかったらドロップするアイテムとしてリストに入れる breakResults._1.toList.traverse { itemStack => - SeichiAssist - .instance - .mineStackSystem - .api - .mineStackRepository - .tryIntoMineStack(player, itemStack, itemStack.getAmount) - .map(Option.unless(_)(itemStack)) + whenAOrElse(currentAutoMineStackState)( + SeichiAssist + .instance + .mineStackSystem + .api + .mineStackRepository + .tryIntoMineStack(player, itemStack, itemStack.getAmount), + false + ).map(Option.unless(_)(itemStack)) } _ <- IO { diff --git a/src/main/scala/com/github/unchama/seichiassist/util/ItemListSerialization.java b/src/main/scala/com/github/unchama/seichiassist/util/ItemListSerialization.java index 2925bdb67b..02ebd1b270 100644 --- a/src/main/scala/com/github/unchama/seichiassist/util/ItemListSerialization.java +++ b/src/main/scala/com/github/unchama/seichiassist/util/ItemListSerialization.java @@ -12,10 +12,6 @@ import java.util.ArrayList; import java.util.List; -/** - * @deprecated Please use BukkitItemStackSerializeAndDeserialize - */ -@Deprecated public final class ItemListSerialization { private ItemListSerialization() {