diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 37b45d5e..a8a1637c 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -22,8 +22,6 @@ permissions: actions: read packages: read pull-requests: write - issues: write - jobs: codec: uses: ./.github/workflows/build_and_test.yml @@ -306,161 +304,3 @@ jobs: with: module: main module_id: anvillib - - # ── Roseau summary comment ───────────────────────────────────── - - roseau-summary: - needs: - - codec-roseau - - collision-roseau - - config-roseau - - font-roseau - - integration-roseau - - moveable-entity-block-roseau - - multiblock-roseau - - network-roseau - - recipe-roseau - - registrum-roseau - - rendering-roseau - - space-select-roseau - - sync-roseau - - util-roseau - - wheel-roseau - - main-roseau - if: always() - runs-on: ubuntu-latest - env: - CODEC_BC: ${{ needs.codec-roseau.outputs.has_bc }} - CODEC_N: ${{ needs.codec-roseau.outputs.bc_count }} - COLLISION_BC: ${{ needs.collision-roseau.outputs.has_bc }} - COLLISION_N: ${{ needs.collision-roseau.outputs.bc_count }} - CONFIG_BC: ${{ needs.config-roseau.outputs.has_bc }} - CONFIG_N: ${{ needs.config-roseau.outputs.bc_count }} - FONT_BC: ${{ needs.font-roseau.outputs.has_bc }} - FONT_N: ${{ needs.font-roseau.outputs.bc_count }} - INTEGRATION_BC: ${{ needs.integration-roseau.outputs.has_bc }} - INTEGRATION_N: ${{ needs.integration-roseau.outputs.bc_count }} - MEB_BC: ${{ needs.moveable-entity-block-roseau.outputs.has_bc }} - MEB_N: ${{ needs.moveable-entity-block-roseau.outputs.bc_count }} - MULTIBLOCK_BC: ${{ needs.multiblock-roseau.outputs.has_bc }} - MULTIBLOCK_N: ${{ needs.multiblock-roseau.outputs.bc_count }} - NETWORK_BC: ${{ needs.network-roseau.outputs.has_bc }} - NETWORK_N: ${{ needs.network-roseau.outputs.bc_count }} - RECIPE_BC: ${{ needs.recipe-roseau.outputs.has_bc }} - RECIPE_N: ${{ needs.recipe-roseau.outputs.bc_count }} - REGISTRUM_BC: ${{ needs.registrum-roseau.outputs.has_bc }} - REGISTRUM_N: ${{ needs.registrum-roseau.outputs.bc_count }} - RENDERING_BC: ${{ needs.rendering-roseau.outputs.has_bc }} - RENDERING_N: ${{ needs.rendering-roseau.outputs.bc_count }} - SPACESELECT_BC: ${{ needs.space-select-roseau.outputs.has_bc }} - SPACESELECT_N: ${{ needs.space-select-roseau.outputs.bc_count }} - SYNC_BC: ${{ needs.sync-roseau.outputs.has_bc }} - SYNC_N: ${{ needs.sync-roseau.outputs.bc_count }} - UTIL_BC: ${{ needs.util-roseau.outputs.has_bc }} - UTIL_N: ${{ needs.util-roseau.outputs.bc_count }} - WHEEL_BC: ${{ needs.wheel-roseau.outputs.has_bc }} - WHEEL_N: ${{ needs.wheel-roseau.outputs.bc_count }} - MAIN_BC: ${{ needs.main-roseau.outputs.has_bc }} - MAIN_N: ${{ needs.main-roseau.outputs.bc_count }} - steps: - - name: Download Roseau reports - uses: actions/download-artifact@v4 - with: - pattern: roseau-* - path: roseau-reports - - - name: Generate and post PR comment - run: | - row() { - local name=$1 bc=$2 count=$3 - if [ -z "$bc" ]; then - echo "| ${name} | ⚪ Skipped | — |" - elif [ "$bc" = "true" ]; then - echo "| ${name} | 🔴 BC detected | ${count} |" - else - echo "| ${name} | ✅ Compatible | 0 |" - fi - } - - { - echo "## 🌿 Roseau API Breaking Change Report" - echo "" - echo "| Module | Status | Breaking Changes |" - echo "|--------|--------|-----------------|" - row "codec" "$CODEC_BC" "$CODEC_N" - row "collision" "$COLLISION_BC" "$COLLISION_N" - row "config" "$CONFIG_BC" "$CONFIG_N" - row "font" "$FONT_BC" "$FONT_N" - row "integration" "$INTEGRATION_BC" "$INTEGRATION_N" - row "moveable-entity-block" "$MEB_BC" "$MEB_N" - row "multiblock" "$MULTIBLOCK_BC" "$MULTIBLOCK_N" - row "network" "$NETWORK_BC" "$NETWORK_N" - row "recipe" "$RECIPE_BC" "$RECIPE_N" - row "registrum" "$REGISTRUM_BC" "$REGISTRUM_N" - row "rendering" "$RENDERING_BC" "$RENDERING_N" - row "space-select" "$SPACESELECT_BC" "$SPACESELECT_N" - row "sync" "$SYNC_BC" "$SYNC_N" - row "util" "$UTIL_BC" "$UTIL_N" - row "wheel" "$WHEEL_BC" "$WHEEL_N" - row "main" "$MAIN_BC" "$MAIN_N" - echo "" - } > /tmp/roseau-body.md - - # Append breaking change details for each affected module - append_details() { - local mod=$1 bc=$2 count=$3 - if [ "$bc" != "true" ]; then return; fi - local csv="roseau-reports/roseau-${mod}/report.csv" - if [ ! -f "$csv" ]; then return; fi - { - echo "### 🔴 ${mod} — ${count} breaking change(s)" - echo "" - while IFS=';' read -r type symbol kind nature location newSymbol binaryBreaking sourceBreaking; do - echo "#### \`${location}\`" - echo '```' - echo "${type}" - echo "${symbol}" - echo "${kind}" - [ "$binaryBreaking" = "true" ] && echo "✗ binary-breaking" || echo "✓ binary-compatible" - [ "$sourceBreaking" = "true" ] && echo "✗ source-breaking" || echo "✓ source-compatible" - echo '```' - echo "" - done < <(tail -n +2 "$csv") - } >> /tmp/roseau-body.md - } - - append_details "codec" "$CODEC_BC" "$CODEC_N" - append_details "collision" "$COLLISION_BC" "$COLLISION_N" - append_details "config" "$CONFIG_BC" "$CONFIG_N" - append_details "font" "$FONT_BC" "$FONT_N" - append_details "integration" "$INTEGRATION_BC" "$INTEGRATION_N" - append_details "moveable-entity-block" "$MEB_BC" "$MEB_N" - append_details "multiblock" "$MULTIBLOCK_BC" "$MULTIBLOCK_N" - append_details "network" "$NETWORK_BC" "$NETWORK_N" - append_details "recipe" "$RECIPE_BC" "$RECIPE_N" - append_details "registrum" "$REGISTRUM_BC" "$REGISTRUM_N" - append_details "rendering" "$RENDERING_BC" "$RENDERING_N" - append_details "space-select" "$SPACESELECT_BC" "$SPACESELECT_N" - append_details "sync" "$SYNC_BC" "$SYNC_N" - append_details "util" "$UTIL_BC" "$UTIL_N" - append_details "wheel" "$WHEEL_BC" "$WHEEL_N" - append_details "main" "$MAIN_BC" "$MAIN_N" - - echo "> Full CSVs: see the *Artifacts* section of [this workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})." >> /tmp/roseau-body.md - - - name: Find existing Roseau comment - id: find-comment - uses: peter-evans/find-comment@v3 - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: anvil-craft - body-includes: Roseau API Breaking Change Report - - - name: Create or update PR comment - uses: peter-evans/create-or-update-comment@v4 - with: - token: ${{ secrets.PAT_TOKEN }} - comment-id: ${{ steps.find-comment.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - body-path: /tmp/roseau-body.md - edit-mode: replace diff --git a/.github/workflows/roseau_comment.yml b/.github/workflows/roseau_comment.yml new file mode 100644 index 00000000..bce66794 --- /dev/null +++ b/.github/workflows/roseau_comment.yml @@ -0,0 +1,150 @@ +name: Roseau PR Comment + +on: + workflow_run: + workflows: + - Pull Request Check + types: + - completed + +permissions: + contents: read + actions: read + pull-requests: write + +jobs: + comment: + if: ${{ github.event.workflow_run.conclusion != 'skipped' }} + runs-on: ubuntu-latest + steps: + - name: Download Roseau reports + uses: actions/download-artifact@v4 + with: + pattern: roseau-* + path: roseau-reports + run-id: ${{ github.event.workflow_run.id }} + + - name: Find PR number + id: pr + run: | + HEAD_BRANCH="${{ github.event.workflow_run.head_branch }}" + HEAD_SHA="${{ github.event.workflow_run.head_sha }}" + PR_NUMBER=$(gh pr list \ + --head "$HEAD_BRANCH" \ + --state open \ + --json number \ + --jq '.[0].number') + if [ -z "$PR_NUMBER" ]; then + PR_NUMBER=$(gh pr list \ + --search "$HEAD_SHA" \ + --state open \ + --json number \ + --jq '.[0].number') + fi + if [ -z "$PR_NUMBER" ]; then + echo "::warning::Could not find PR number for branch=$HEAD_BRANCH sha=$HEAD_SHA" + fi + echo "number=$PR_NUMBER" >> "$GITHUB_OUTPUT" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Generate report body + run: | + row() { + local name=$1 dir=$2 + local csv="${dir}/report.csv" + if [ -f "$csv" ] && [ "$(wc -l < "$csv")" -gt 1 ]; then + local count=$(($(wc -l < "$csv") - 1)) + echo "| ${name} | 🔴 BC detected | ${count} |" + elif [ -f "$csv" ]; then + echo "| ${name} | ✅ Compatible | 0 |" + else + echo "| ${name} | ⚪ Skipped | — |" + fi + } + + { + echo "## 🌿 Roseau API Breaking Change Report" + echo "" + echo "| Module | Status | Breaking Changes |" + echo "|--------|--------|-----------------|" + row "codec" "roseau-reports/roseau-codec" + row "collision" "roseau-reports/roseau-collision" + row "config" "roseau-reports/roseau-config" + row "font" "roseau-reports/roseau-font" + row "integration" "roseau-reports/roseau-integration" + row "moveable-entity-block" "roseau-reports/roseau-moveable-entity-block" + row "multiblock" "roseau-reports/roseau-multiblock" + row "network" "roseau-reports/roseau-network" + row "recipe" "roseau-reports/roseau-recipe" + row "registrum" "roseau-reports/roseau-registrum" + row "rendering" "roseau-reports/roseau-rendering" + row "space-select" "roseau-reports/roseau-space-select" + row "sync" "roseau-reports/roseau-sync" + row "util" "roseau-reports/roseau-util" + row "wheel" "roseau-reports/roseau-wheel" + row "main" "roseau-reports/roseau-main" + echo "" + } > /tmp/roseau-body.md + + append_details() { + local name=$1 dir=$2 + local csv="${dir}/report.csv" + if [ ! -f "$csv" ]; then return; fi + local count=$(($(wc -l < "$csv") - 1)) + if [ "$count" -le 0 ]; then return; fi + { + echo "### 🔴 ${name} — ${count} breaking change(s)" + echo "" + while IFS=';' read -r type symbol kind nature location newSymbol binaryBreaking sourceBreaking; do + echo "#### \`${location}\`" + echo '```' + echo "${type}" + echo "${symbol}" + echo "${kind}" + [ "$binaryBreaking" = "true" ] && echo "✗ binary-breaking" || echo "✓ binary-compatible" + [ "$sourceBreaking" = "true" ] && echo "✗ source-breaking" || echo "✓ source-compatible" + echo '```' + echo "" + done < <(tail -n +2 "$csv") + } >> /tmp/roseau-body.md + } + + append_details "codec" "roseau-reports/roseau-codec" + append_details "collision" "roseau-reports/roseau-collision" + append_details "config" "roseau-reports/roseau-config" + append_details "font" "roseau-reports/roseau-font" + append_details "integration" "roseau-reports/roseau-integration" + append_details "moveable-entity-block" "roseau-reports/roseau-moveable-entity-block" + append_details "multiblock" "roseau-reports/roseau-multiblock" + append_details "network" "roseau-reports/roseau-network" + append_details "recipe" "roseau-reports/roseau-recipe" + append_details "registrum" "roseau-reports/roseau-registrum" + append_details "rendering" "roseau-reports/roseau-rendering" + append_details "space-select" "roseau-reports/roseau-space-select" + append_details "sync" "roseau-reports/roseau-sync" + append_details "util" "roseau-reports/roseau-util" + append_details "wheel" "roseau-reports/roseau-wheel" + append_details "main" "roseau-reports/roseau-main" + + echo "> Full CSVs: see the *Artifacts* section of [this workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }})." >> /tmp/roseau-body.md + + - name: Find existing Roseau comment + id: find-comment + if: steps.pr.outputs.number != '' + uses: peter-evans/find-comment@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + issue-number: ${{ steps.pr.outputs.number }} + comment-author: anvil-craft + body-includes: Roseau API Breaking Change Report + + - name: Create or update PR comment + if: steps.pr.outputs.number != '' + uses: peter-evans/create-or-update-comment@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + comment-id: ${{ steps.find-comment.outputs.comment-id }} + issue-number: ${{ steps.pr.outputs.number }} + body-path: /tmp/roseau-body.md + edit-mode: replace diff --git a/module.network/src/main/java/dev/anvilcraft/lib/v2/network/register/PacketData.java b/module.network/src/main/java/dev/anvilcraft/lib/v2/network/register/PacketData.java index 440dd809..b0cfc24b 100644 --- a/module.network/src/main/java/dev/anvilcraft/lib/v2/network/register/PacketData.java +++ b/module.network/src/main/java/dev/anvilcraft/lib/v2/network/register/PacketData.java @@ -13,6 +13,8 @@ import java.lang.reflect.AccessFlag; import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.Set; @Slf4j @@ -35,11 +37,17 @@ static PacketData find(Class pac ) { continue; } - Class declaringClass = field.getType(); - if (declaringClass.isAssignableFrom(CustomPacketPayload.Type.class)) { + Class fieldType = field.getType(); + if (CustomPacketPayload.Type.class.isAssignableFrom(fieldType)) { + if (!isMatchingTypeArgument(field.getGenericType(), 0, packetClass)) { + continue; + } field.setAccessible(true); type = (CustomPacketPayload.Type) field.get(null); - } else if (declaringClass.isAssignableFrom(StreamCodec.class)) { + } else if (StreamCodec.class.isAssignableFrom(fieldType)) { + if (!isMatchingTypeArgument(field.getGenericType(), 1, packetClass)) { + continue; + } field.setAccessible(true); codec = (StreamCodec) field.get(null); } @@ -77,4 +85,27 @@ static PacketData find(Class pac } return new PacketData<>(type, codec, direction, handler); } + + /** + * 检查字段的参数化类型中,指定索引的类型实参是否与期望的类匹配。 + * 如果字段类型不是参数化类型、类型实参不足、或类型不匹配,返回 false。 + */ + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + private static boolean isMatchingTypeArgument( + Type genericType, + int typeArgIndex, + Class expectedClass + ) { + if (!(genericType instanceof ParameterizedType pt)) { + return false; + } + Type[] typeArgs = pt.getActualTypeArguments(); + if (typeArgs.length <= typeArgIndex) { + return false; + } + if (!(typeArgs[typeArgIndex] instanceof Class typeArg)) { + return false; + } + return typeArg.isAssignableFrom(expectedClass); + } }