43 Commits

Author SHA1 Message Date
5a9572336a Update plugin fabric-loom to v1.17.3
Some checks failed
Build and Release / build (0.114.1+1.21.3, 1.21.3, 1.21.3+build.2) (pull_request) Failing after 29s
Build and Release / build (0.106.1+1.21.2, 1.21.2, 1.21.2+build.1) (pull_request) Failing after 36s
Build and Release / build (0.116.10+1.21.1, 1.21.1, 1.21.1+build.3) (pull_request) Failing after 20s
Build and Release / build (0.119.4+1.21.4, 1.21.4, 1.21.4+build.8) (pull_request) Failing after 21s
Build and Release / build (0.128.2+1.21.5, 1.21.5, 1.21.5+build.1) (pull_request) Failing after 22s
Build and Release / build (0.128.2+1.21.6, 1.21.6, 1.21.6+build.1) (pull_request) Failing after 24s
Build and Release / build (0.129.0+1.21.7, 1.21.7, 1.21.7+build.8) (pull_request) Failing after 19s
Build and Release / build (0.134.1+1.21.9, 1.21.9, 1.21.9+build.1) (pull_request) Failing after 20s
Build and Release / build (0.136.1+1.21.8, 1.21.8, 1.21.8+build.1) (pull_request) Failing after 18s
Build and Release / build (0.138.4+1.21.10, 1.21.10, 1.21.10+build.3) (pull_request) Failing after 25s
Build and Release / build (0.141.3+1.21.11, 1.21.11, 1.21.11+build.4) (pull_request) Failing after 20s
Build and Release / publish (pull_request) Has been skipped
2026-06-08 08:09:19 +00:00
45fc0db2e5 Merge pull request 'Update plugin fabric-loom to v1.16.3' (#5) from renovate/loom_version into main
Some checks failed
Build and Release / build (0.106.1+1.21.2, 1.21.2, 1.21.2+build.1) (push) Failing after 55s
Build and Release / build (0.116.10+1.21.1, 1.21.1, 1.21.1+build.3) (push) Failing after 21s
Build and Release / build (0.114.1+1.21.3, 1.21.3, 1.21.3+build.2) (push) Failing after 1m40s
Build and Release / build (0.119.4+1.21.4, 1.21.4, 1.21.4+build.8) (push) Failing after 20s
Build and Release / build (0.128.2+1.21.6, 1.21.6, 1.21.6+build.1) (push) Failing after 21s
Build and Release / build (0.128.2+1.21.5, 1.21.5, 1.21.5+build.1) (push) Failing after 23s
Build and Release / build (0.129.0+1.21.7, 1.21.7, 1.21.7+build.8) (push) Failing after 23s
Build and Release / build (0.134.1+1.21.9, 1.21.9, 1.21.9+build.1) (push) Failing after 23s
Build and Release / build (0.136.1+1.21.8, 1.21.8, 1.21.8+build.1) (push) Failing after 22s
Build and Release / build (0.138.4+1.21.10, 1.21.10, 1.21.10+build.3) (push) Failing after 23s
Build and Release / build (0.141.3+1.21.11, 1.21.11, 1.21.11+build.4) (push) Failing after 21s
Build and Release / publish (push) Has been skipped
Reviewed-on: #5
2026-06-02 20:47:22 +00:00
a17a7d2d6b Update plugin fabric-loom to v1.16.3
Some checks failed
Build and Release / build (0.106.1+1.21.2, 1.21.2, 1.21.2+build.1) (pull_request) Failing after 1m3s
Build and Release / build (0.114.1+1.21.3, 1.21.3, 1.21.3+build.2) (pull_request) Failing after 1m7s
Build and Release / build (0.116.10+1.21.1, 1.21.1, 1.21.1+build.3) (pull_request) Failing after 26s
Build and Release / build (0.119.4+1.21.4, 1.21.4, 1.21.4+build.8) (pull_request) Failing after 24s
Build and Release / build (0.128.2+1.21.5, 1.21.5, 1.21.5+build.1) (pull_request) Failing after 20s
Build and Release / build (0.128.2+1.21.6, 1.21.6, 1.21.6+build.1) (pull_request) Failing after 24s
Build and Release / build (0.129.0+1.21.7, 1.21.7, 1.21.7+build.8) (pull_request) Failing after 19s
Build and Release / build (0.134.1+1.21.9, 1.21.9, 1.21.9+build.1) (pull_request) Failing after 22s
Build and Release / build (0.136.1+1.21.8, 1.21.8, 1.21.8+build.1) (pull_request) Failing after 21s
Build and Release / build (0.138.4+1.21.10, 1.21.10, 1.21.10+build.3) (pull_request) Failing after 24s
Build and Release / build (0.141.3+1.21.11, 1.21.11, 1.21.11+build.4) (pull_request) Failing after 21s
Build and Release / publish (pull_request) Has been skipped
2026-05-31 18:14:20 +00:00
b08a749282 Merge pull request 'Update plugin fabric-loom to v1.16.2' (#3) from renovate/loom_version into main
Some checks failed
Build and Release / build (0.114.1+1.21.3, 1.21.3, 1.21.3+build.2) (push) Failing after 51s
Build and Release / build (0.106.1+1.21.2, 1.21.2, 1.21.2+build.1) (push) Failing after 1m2s
Build and Release / build (0.116.10+1.21.1, 1.21.1, 1.21.1+build.3) (push) Failing after 20s
Build and Release / build (0.119.4+1.21.4, 1.21.4, 1.21.4+build.8) (push) Failing after 22s
Build and Release / build (0.128.2+1.21.5, 1.21.5, 1.21.5+build.1) (push) Failing after 18s
Build and Release / build (0.128.2+1.21.6, 1.21.6, 1.21.6+build.1) (push) Failing after 20s
Build and Release / build (0.129.0+1.21.7, 1.21.7, 1.21.7+build.8) (push) Failing after 18s
Build and Release / build (0.134.1+1.21.9, 1.21.9, 1.21.9+build.1) (push) Failing after 21s
Build and Release / build (0.136.1+1.21.8, 1.21.8, 1.21.8+build.1) (push) Failing after 17s
Build and Release / build (0.141.3+1.21.11, 1.21.11, 1.21.11+build.4) (push) Failing after 18s
Build and Release / build (0.138.4+1.21.10, 1.21.10, 1.21.10+build.3) (push) Failing after 22s
Build and Release / publish (push) Has been skipped
Reviewed-on: #3
2026-05-18 01:28:34 +00:00
7d7d07d040 Update plugin fabric-loom to v1.16.2
Some checks failed
Build and Release / build (0.114.1+1.21.3, 1.21.3, 1.21.3+build.2) (pull_request) Failing after 4m15s
Build and Release / build (0.106.1+1.21.2, 1.21.2, 1.21.2+build.1) (pull_request) Failing after 4m15s
Build and Release / build (0.116.10+1.21.1, 1.21.1, 1.21.1+build.3) (pull_request) Failing after 3m52s
Build and Release / build (0.119.4+1.21.4, 1.21.4, 1.21.4+build.8) (pull_request) Failing after 3m57s
Build and Release / build (0.128.2+1.21.5, 1.21.5, 1.21.5+build.1) (pull_request) Failing after 2m19s
Build and Release / build (0.128.2+1.21.6, 1.21.6, 1.21.6+build.1) (pull_request) Failing after 2m21s
Build and Release / build (0.134.1+1.21.9, 1.21.9, 1.21.9+build.1) (pull_request) Failing after 11s
Build and Release / build (0.129.0+1.21.7, 1.21.7, 1.21.7+build.8) (pull_request) Failing after 2m17s
Build and Release / build (0.136.1+1.21.8, 1.21.8, 1.21.8+build.1) (pull_request) Failing after 2m25s
Build and Release / build (0.138.4+1.21.10, 1.21.10, 1.21.10+build.3) (pull_request) Failing after 1m37s
Build and Release / build (0.141.3+1.21.11, 1.21.11, 1.21.11+build.4) (pull_request) Failing after 1m33s
Build and Release / publish (pull_request) Has been skipped
2026-05-14 12:03:21 +00:00
344cc6882b Merge pull request 'Update gradle Docker tag to v8.14.5' (#2) from renovate/gradle-8.x into main
Some checks failed
Build and Release / build (0.106.1+1.21.2, 1.21.2, 1.21.2+build.1) (push) Failing after 16s
Build and Release / build (0.116.10+1.21.1, 1.21.1, 1.21.1+build.3) (push) Failing after 1m20s
Build and Release / build (0.114.1+1.21.3, 1.21.3, 1.21.3+build.2) (push) Failing after 1m36s
Build and Release / build (0.128.2+1.21.5, 1.21.5, 1.21.5+build.1) (push) Failing after 2m19s
Build and Release / build (0.119.4+1.21.4, 1.21.4, 1.21.4+build.8) (push) Failing after 3m42s
Build and Release / build (0.128.2+1.21.6, 1.21.6, 1.21.6+build.1) (push) Failing after 2m18s
Build and Release / build (0.129.0+1.21.7, 1.21.7, 1.21.7+build.8) (push) Failing after 2m9s
Build and Release / build (0.134.1+1.21.9, 1.21.9, 1.21.9+build.1) (push) Failing after 1m27s
Build and Release / build (0.138.4+1.21.10, 1.21.10, 1.21.10+build.3) (push) Failing after 1m4s
Build and Release / build (0.136.1+1.21.8, 1.21.8, 1.21.8+build.1) (push) Failing after 2m6s
Build and Release / build (0.141.3+1.21.11, 1.21.11, 1.21.11+build.4) (push) Failing after 1m1s
Build and Release / publish (push) Has been skipped
Reviewed-on: #2
2026-05-10 03:29:25 +00:00
b919c8e792 Update gradle Docker tag to v8.14.5
Some checks failed
Build and Release / build (0.106.1+1.21.2, 1.21.2, 1.21.2+build.1) (pull_request) Failing after 5m55s
Build and Release / build (0.114.1+1.21.3, 1.21.3, 1.21.3+build.2) (pull_request) Failing after 6m1s
Build and Release / build (0.116.10+1.21.1, 1.21.1, 1.21.1+build.3) (pull_request) Failing after 3m22s
Build and Release / build (0.119.4+1.21.4, 1.21.4, 1.21.4+build.8) (pull_request) Failing after 3m32s
Build and Release / build (0.128.2+1.21.5, 1.21.5, 1.21.5+build.1) (pull_request) Failing after 2m20s
Build and Release / build (0.128.2+1.21.6, 1.21.6, 1.21.6+build.1) (pull_request) Failing after 2m16s
Build and Release / build (0.134.1+1.21.9, 1.21.9, 1.21.9+build.1) (pull_request) Failing after 1m38s
Build and Release / build (0.129.0+1.21.7, 1.21.7, 1.21.7+build.8) (pull_request) Failing after 2m18s
Build and Release / build (0.138.4+1.21.10, 1.21.10, 1.21.10+build.3) (pull_request) Failing after 1m24s
Build and Release / build (0.136.1+1.21.8, 1.21.8, 1.21.8+build.1) (pull_request) Failing after 1m52s
Build and Release / build (0.141.3+1.21.11, 1.21.11, 1.21.11+build.4) (pull_request) Failing after 1m15s
Build and Release / publish (pull_request) Has been skipped
2026-05-09 00:02:44 +00:00
9cd722d585 fixed CI
Some checks failed
Build and Release / build (0.106.1+1.21.2, 1.21.2, 1.21.2+build.1) (push) Failing after 5m12s
Build and Release / build (0.114.1+1.21.3, 1.21.3, 1.21.3+build.2) (push) Failing after 6m31s
Build and Release / build (0.134.1+1.21.9, 1.21.9, 1.21.9+build.1) (push) Failing after 1m59s
Build and Release / build (0.141.3+1.21.11, 1.21.11, 1.21.11+build.4) (push) Failing after 10m33s
Build and Release / build (0.138.4+1.21.10, 1.21.10, 1.21.10+build.3) (push) Failing after 10m38s
Build and Release / build (0.136.1+1.21.8, 1.21.8, 1.21.8+build.1) (push) Failing after 10m43s
Build and Release / build (0.129.0+1.21.7, 1.21.7, 1.21.7+build.8) (push) Failing after 11m39s
Build and Release / build (0.128.2+1.21.6, 1.21.6, 1.21.6+build.1) (push) Failing after 11m45s
Build and Release / build (0.128.2+1.21.5, 1.21.5, 1.21.5+build.1) (push) Failing after 11m49s
Build and Release / build (0.119.4+1.21.4, 1.21.4, 1.21.4+build.8) (push) Failing after 11m55s
Build and Release / build (0.116.10+1.21.1, 1.21.1, 1.21.1+build.3) (push) Failing after 11m59s
Build and Release / publish (push) Has been cancelled
2026-04-07 19:03:55 -05:00
96448073e8 ci test
Some checks failed
Build and Release / build (push) Failing after 14m24s
2026-04-07 18:45:45 -05:00
b18700285b fixed CI AGAIN
Some checks are pending
Build and Release / build (push) Waiting to run
2026-04-07 18:38:13 -05:00
edf1bbc1e4 Merge pull request 'Configure Renovate' (#1) from renovate/configure into main
Some checks failed
Build and Release / build (push) Failing after 3m36s
Reviewed-on: #1
2026-04-07 23:32:53 +00:00
54dfddec33 fixed ci
Some checks failed
Build and Release / build (push) Has been cancelled
2026-04-07 18:32:13 -05:00
4c8f3c23a4 Add renovate.json
Some checks failed
Build and Release / build (pull_request) Failing after 3m36s
2026-04-07 23:20:34 +00:00
12bbaf157d Log sign rewrite inputs and outputs
Some checks failed
Build and Release / build (push) Failing after 2m58s
2026-04-06 22:18:02 -05:00
8eb03d3e83 Harden sign keybind handling
Some checks failed
Build and Release / build (push) Failing after 3m43s
2026-04-06 18:42:06 -05:00
945af4f565 add more CI mc versions
Some checks failed
Build and Release / build (push) Failing after 3m8s
2026-04-06 18:11:50 -05:00
915c2b5c4f Merge remote-tracking branch 'origin/main'
All checks were successful
Build and Release / build (push) Successful in 3m46s
2026-04-06 18:02:13 -05:00
5a0d956221 made all builds on main be a release 2026-04-06 18:02:07 -05:00
7acfc35e01 Fixed typo
Some checks failed
Build and Release / build (push) Has been cancelled
2026-04-06 23:00:04 +00:00
96792a0091 CI
All checks were successful
Build and Release / build (push) Successful in 4m5s
2026-04-06 17:49:20 -05:00
08bd2aa272 working on CI
Some checks failed
Main Release / build (push) Failing after 16s
2026-04-06 17:47:04 -05:00
4e70c0e57e Merge branch 'dev'
Some checks failed
Main Release / build (push) Failing after 21s
2026-04-06 17:42:18 -05:00
988c2338fd Make workflows work on GitHub and Gitea
Some checks failed
Dev Verify / verify (push) Failing after 26s
2026-04-06 17:38:21 -05:00
43630597dd bump version 2026-04-06 17:36:21 -05:00
b03444d986 Reset trace log on client init 2026-04-06 17:36:07 -05:00
fe4675d606 Narrow sign exploit blacklist 2026-04-06 17:32:56 -05:00
c94c29cb5f Delete mixin trace logging code 2026-04-06 17:28:57 -05:00
e82f89791f Blacklist mod translation keys 2026-04-06 17:25:32 -05:00
91454b90a9 Allow vanilla translation text 2026-04-06 17:23:01 -05:00
9f597b4672 Preserve fallback sign text in rewrite 2026-04-06 17:19:17 -05:00
81d98ac840 Add file tracing for sign hooks 2026-04-06 17:11:17 -05:00
dbee665fd5 Merge branch 'dev'
Some checks failed
Main Release / build (push) Failing after 3m21s
2026-04-06 16:58:21 -05:00
437d9b0496 Undid all of the stupid optimizations in CI
Some checks failed
Dev Verify / verify (push) Failing after 3m27s
2026-04-06 16:55:58 -05:00
2fce517e18 optimized dev workflow
Some checks failed
Dev Verify / verify (push) Has been cancelled
2026-04-06 16:50:39 -05:00
0d0b4900b0 optimze some CI
Some checks failed
Main Release / build (push) Failing after 4m32s
2026-04-06 16:49:52 -05:00
1713748c11 Fix client refmap loading
Some checks failed
Main Release / build (push) Has been cancelled
2026-04-06 16:46:33 -05:00
7db4423fe2 Split release and verify workflows 2026-04-06 16:43:56 -05:00
096df79aca updated gitignore 2026-04-06 16:40:16 -05:00
562748b79d Fix mixin remapping for client hooks
All checks were successful
Build and Release / build (push) Successful in 3m48s
2026-04-06 16:39:33 -05:00
be700eb008 Fix outgoing packet mixin target
All checks were successful
Build and Release / build (push) Successful in 3m49s
2026-04-06 16:23:52 -05:00
b004e05575 Fix client network mixin targets
All checks were successful
Build and Release / build (push) Successful in 3m37s
2026-04-06 16:11:26 -05:00
5862f0bb1e Fix logging mixin and speed up builds
Some checks failed
Build and Release / build (push) Has been cancelled
2026-04-06 16:10:39 -05:00
2657bbe5af Fix chunk data mixin target
All checks were successful
Build and Release / build (push) Successful in 3m35s
2026-04-06 16:06:52 -05:00
18 changed files with 751 additions and 306 deletions

View File

@@ -1,81 +0,0 @@
name: Build and Release
on:
push:
branches:
- main
tags:
- "v*"
pull_request:
workflow_dispatch:
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
container:
image: gradle:8.11.1-jdk21
env:
GITEA_TOKEN: ${{ github.token }}
GITEA_API_URL: ${{ github.server_url }}/api/v1
GITEA_REPO: ${{ github.repository }}
GITEA_REPO_URL: ${{ github.server_url }}/${{ github.repository }}.git
GITEA_REF_NAME: ${{ github.ref_name }}
GITEA_SHA: ${{ github.sha }}
steps:
- name: Prepare source
shell: bash
run: |
set -euo pipefail
if [ ! -f build.gradle ]; then
git -c http.extraHeader="Authorization: token ${GITEA_TOKEN}" \
clone --depth 1 "${GITEA_REPO_URL}" .
fi
- name: Build
shell: bash
run: |
set -euo pipefail
gradle --no-daemon clean build
- name: Publish Gitea release
if: github.event_name != 'pull_request'
shell: bash
run: |
set -euo pipefail
jar_path="$(ls -1 build/libs/*.jar | head -n 1)"
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
release_tag="${GITEA_REF_NAME}"
release_name="${GITEA_REF_NAME}"
prerelease=false
else
release_tag="build-${GITEA_SHA:0:7}"
release_name="build-${GITEA_SHA:0:7}"
prerelease=true
fi
release_payload="$(printf '{"tag_name":"%s","name":"%s","body":"Automated build for %s.","draft":false,"prerelease":%s}' \
"${release_tag}" "${release_name}" "${release_name}" "${prerelease}")"
release_json="$(curl -fsS \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "${release_payload}" \
"${GITEA_API_URL}/repos/${GITEA_REPO}/releases")"
release_id="$(printf '%s' "${release_json}" | sed -n 's/.*"id":[[:space:]]*\([0-9][0-9]*\).*/\1/p' | head -n 1)"
if [ -z "${release_id}" ]; then
echo "Failed to parse release ID from response:"
printf '%s\n' "${release_json}"
exit 1
fi
curl -fsS \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: multipart/form-data" \
-F "attachment=@${jar_path}" \
"${GITEA_API_URL}/repos/${GITEA_REPO}/releases/${release_id}/assets?name=$(basename "${jar_path}")"

View File

@@ -0,0 +1,37 @@
name: Dev Verify
on:
push:
branches:
- dev
pull_request:
branches:
- dev
workflow_dispatch:
jobs:
verify:
runs-on: ubuntu-latest
container:
image: gradle:8.14.5-jdk21
env:
GITEA_TOKEN: ${{ github.token }}
GITEA_API_URL: ${{ github.server_url }}/api/v1
GITEA_REPO: ${{ github.repository }}
GITEA_REPO_URL: ${{ github.server_url }}/${{ github.repository }}.git
steps:
- name: Prepare source
shell: bash
run: |
set -euo pipefail
if [ ! -f build.gradle ]; then
git -c http.extraHeader="Authorization: token ${GITEA_TOKEN}" \
clone --depth 1 "${GITEA_REPO_URL}" .
fi
- name: Verify
shell: bash
run: |
set -euo pipefail
gradle --no-daemon compileClientJava

View File

@@ -0,0 +1,177 @@
name: Build and Release
on:
push:
branches:
- main
tags:
- "v*"
pull_request:
workflow_dispatch:
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
container:
image: gradle:8.14.5-jdk21
strategy:
fail-fast: false
matrix:
include:
- minecraft_version: "1.21.1"
yarn_mappings: "1.21.1+build.3"
fabric_version: "0.116.10+1.21.1"
- minecraft_version: "1.21.2"
yarn_mappings: "1.21.2+build.1"
fabric_version: "0.106.1+1.21.2"
- minecraft_version: "1.21.3"
yarn_mappings: "1.21.3+build.2"
fabric_version: "0.114.1+1.21.3"
- minecraft_version: "1.21.4"
yarn_mappings: "1.21.4+build.8"
fabric_version: "0.119.4+1.21.4"
- minecraft_version: "1.21.5"
yarn_mappings: "1.21.5+build.1"
fabric_version: "0.128.2+1.21.5"
- minecraft_version: "1.21.6"
yarn_mappings: "1.21.6+build.1"
fabric_version: "0.128.2+1.21.6"
- minecraft_version: "1.21.7"
yarn_mappings: "1.21.7+build.8"
fabric_version: "0.129.0+1.21.7"
- minecraft_version: "1.21.8"
yarn_mappings: "1.21.8+build.1"
fabric_version: "0.136.1+1.21.8"
- minecraft_version: "1.21.9"
yarn_mappings: "1.21.9+build.1"
fabric_version: "0.134.1+1.21.9"
- minecraft_version: "1.21.10"
yarn_mappings: "1.21.10+build.3"
fabric_version: "0.138.4+1.21.10"
- minecraft_version: "1.21.11"
yarn_mappings: "1.21.11+build.4"
fabric_version: "0.141.3+1.21.11"
env:
GITEA_TOKEN: ${{ github.token }}
GITEA_API_URL: ${{ github.server_url }}/api/v1
GITEA_REPO_URL: ${{ github.server_url }}/${{ github.repository }}.git
steps:
- name: Prepare source
shell: bash
run: |
set -euo pipefail
if [ ! -f build.gradle ]; then
git -c http.extraHeader="Authorization: token ${GITEA_TOKEN}" \
clone --depth 1 "${GITEA_REPO_URL}" .
fi
- name: Build
shell: bash
run: |
set -euo pipefail
echo "Building Minecraft ${{ matrix.minecraft_version }}"
gradle --no-daemon clean build \
-Pminecraft_version="${{ matrix.minecraft_version }}" \
-Pyarn_mappings="${{ matrix.yarn_mappings }}" \
-Pfabric_version="${{ matrix.fabric_version }}"
mkdir -p release-artifacts
jar_path=""
shopt -s nullglob
for candidate in build/libs/*.jar; do
case "$(basename "${candidate}")" in
*-sources.jar|*-dev.jar)
continue
;;
esac
jar_path="${candidate}"
break
done
if [ -z "${jar_path}" ]; then
echo "No build jar found for Minecraft ${{ matrix.minecraft_version }}"
exit 1
fi
cp "${jar_path}" "release-artifacts/sign-leak-shield-${{ matrix.minecraft_version }}.jar"
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: sign-leak-shield-${{ matrix.minecraft_version }}
path: release-artifacts/sign-leak-shield-${{ matrix.minecraft_version }}.jar
if-no-files-found: error
retention-days: 7
publish:
needs: build
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
container:
image: gradle:8.14.5-jdk21
env:
GITEA_TOKEN: ${{ github.token }}
GITEA_API_URL: ${{ github.server_url }}/api/v1
GITEA_REPO: ${{ github.repository }}
GITEA_REF_NAME: ${{ github.ref_name }}
GITEA_SHA: ${{ github.sha }}
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
path: versioned-jars
pattern: sign-leak-shield-*
merge-multiple: true
- name: Publish Gitea release
shell: bash
run: |
set -euo pipefail
shopt -s nullglob
jar_paths=(versioned-jars/*.jar)
if [ "${#jar_paths[@]}" -eq 0 ]; then
echo "No build artifacts were downloaded"
exit 1
fi
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
release_tag="${GITEA_REF_NAME}"
release_name="${GITEA_REF_NAME}"
prerelease=false
else
release_tag="build-${GITEA_SHA:0:7}"
release_name="build-${GITEA_SHA:0:7}"
prerelease=false
fi
release_payload="$(printf '{"tag_name":"%s","name":"%s","body":"Automated parallel build for Minecraft 1.21.1 through 1.21.11.","draft":false,"prerelease":%s}' \
"${release_tag}" "${release_name}" "${prerelease}")"
release_json="$(curl -fsS \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "${release_payload}" \
"${GITEA_API_URL}/repos/${GITEA_REPO}/releases")"
release_id="$(printf '%s' "${release_json}" | sed -n 's/.*"id":[[:space:]]*\([0-9][0-9]*\).*/\1/p' | head -n 1)"
if [ -z "${release_id}" ]; then
echo "Failed to parse release ID from response:"
printf '%s\n' "${release_json}"
exit 1
fi
for jar_path in "${jar_paths[@]}"; do
curl -fsS \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: multipart/form-data" \
-F "attachment=@${jar_path}" \
"${GITEA_API_URL}/repos/${GITEA_REPO}/releases/${release_id}/assets?name=$(basename "${jar_path}")"
done

57
.gitignore vendored
View File

@@ -1,26 +1,31 @@
# ---> Java # ---> Java
# Compiled class file # Compiled class file
*.class *.class
# Log file # Log file
*.log *.log
# BlueJ files # BlueJ files
*.ctxt *.ctxt
# Mobile Tools for Java (J2ME) # Mobile Tools for Java (J2ME)
.mtj.tmp/ .mtj.tmp/
# Package Files # # Package Files #
*.jar *.jar
*.war *.war
*.nar *.nar
*.ear *.ear
*.zip *.zip
*.tar.gz *.tar.gz
*.rar *.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid* hs_err_pid*
replay_pid* replay_pid*
.gradle/*
.gradle/
build/*
build/

View File

@@ -4,9 +4,13 @@
> >
> This mod was built with the help of AI. > This mod was built with the help of AI.
This is a Fabric client mod for Minecraft 1.21.1 that defensively patches forced sign-editor translation and keybind leak probes. ![Main Release](https://dock-it.dev/OnniSystems/SignShield/actions/workflows/main-release.yml/badge.svg?branch=main)
Bypass's Anti Mod systems on Donut SMP and otehr like it. ![Dev Verify](https://dock-it.dev/OnniSystems/SignShield/actions/workflows/dev-verify.yml/badge.svg?branch=dev)
This is a Fabric client mod for Minecraft 1.21.11 that defensively patches forced sign-editor translation and keybind leak probes.
Bypass's Anti Mod systems on Donut SMP and others like it.
Build: Build:
@@ -17,5 +21,5 @@ Build:
Notes: Notes:
- The project targets Fabric for Minecraft 1.21.1. - The project targets Fabric for Minecraft 1.21.11.
- The implementation is scoped to forced sign editor traffic and only rewrites matching outgoing sign update packets. - The implementation is scoped to forced sign editor traffic and only rewrites matching outgoing sign update packets.

View File

@@ -5,6 +5,8 @@ plugins {
version = project.mod_version version = project.mod_version
group = project.maven_group group = project.maven_group
def modVersion = project.version.toString()
def minecraftVersion = project.minecraft_version.toString()
base { base {
archivesName = project.archives_base_name archivesName = project.archives_base_name
@@ -15,6 +17,11 @@ repositories {
} }
loom { loom {
mixin {
useLegacyMixinAp = true
defaultRefmapName = "client-signleakshield.refmap.json"
}
splitEnvironmentSourceSets() splitEnvironmentSourceSets()
mods { mods {
@@ -33,10 +40,14 @@ dependencies {
} }
processResources { processResources {
inputs.property "version", project.version inputs.property "version", modVersion
inputs.property "minecraft_version", minecraftVersion
filesMatching("fabric.mod.json") { filesMatching("fabric.mod.json") {
expand "version": inputs.properties.version expand([
"version": modVersion,
"minecraft_version": minecraftVersion
])
} }
} }

View File

@@ -1,13 +1,15 @@
org.gradle.jvmargs=-Xmx2G org.gradle.jvmargs=-Xmx2G -Dfile.encoding=UTF-8
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.caching=true org.gradle.caching=true
org.gradle.daemon=true
org.gradle.configuration-cache=false
minecraft_version=1.21.1 minecraft_version=1.21.11
yarn_mappings=1.21.1+build.3 yarn_mappings=1.21.11+build.4
loader_version=0.18.4 loader_version=0.18.4
loom_version=1.9.1 loom_version=1.17.3
fabric_version=0.116.8+1.21.1 fabric_version=0.141.3+1.21.11
mod_version=1.0.0 mod_version=1.0.2
maven_group=com.example maven_group=com.example
archives_base_name=sign-leak-shield archives_base_name=sign-leak-shield

3
renovate.json Normal file
View File

@@ -0,0 +1,3 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
}

View File

@@ -1,15 +1,16 @@
package com.example.signleakshield; package com.example.signleakshield;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public final class SignLeakShieldClient implements ClientModInitializer { public final class SignLeakShieldClient implements ClientModInitializer {
public static final String MOD_ID = "signleakshield"; public static final String MOD_ID = "signleakshield";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
LOGGER.info("Sign Leak Shield initialized"); SignLeakShieldTraceLog.reset();
} SignLeakShieldTraceLog.info("Sign Leak Shield initialized");
} }
}

View File

@@ -0,0 +1,58 @@
package com.example.signleakshield;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
public final class SignLeakShieldTraceLog {
private static final Path LOG_PATH = Paths.get("logs", "signleakshield-trace.log");
private SignLeakShieldTraceLog() {
}
public static synchronized void reset() {
try {
Files.deleteIfExists(LOG_PATH);
} catch (IOException ignored) {
}
}
public static void info(String message) {
SignLeakShieldClient.LOGGER.info(message);
append(message);
}
public static void info(String format, Object... args) {
info(String.format(format, args));
}
private static synchronized void append(String message) {
try {
Path parent = LOG_PATH.getParent();
if (parent != null) {
Files.createDirectories(parent);
}
String line = String.format(
"%s %s%n",
DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(OffsetDateTime.now()),
message
);
Files.writeString(
LOG_PATH,
line,
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.APPEND,
StandardOpenOption.WRITE
);
} catch (IOException ignored) {
}
}
}

View File

@@ -1,50 +1,86 @@
package com.example.signleakshield; package com.example.signleakshield;
import net.minecraft.text.KeybindTextContent; import net.minecraft.text.KeybindTextContent;
import net.minecraft.text.PlainTextContent; import net.minecraft.text.PlainTextContent;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.text.TextContent; import net.minecraft.text.TextContent;
import net.minecraft.text.TranslatableTextContent; import net.minecraft.text.TranslatableTextContent;
public final class TextSanitizer { public final class TextSanitizer {
private TextSanitizer() { private static final String[] SUSPICIOUS_KEY_PARTS = {
} "gey.glazed.",
"key.meteor-client.",
"meteor-client",
"meteorline",
"meteorclient",
"metiorclient",
"meteordevelopment.meteorclient",
"itemscroller",
"moremousetweaks",
"invmove",
"autototem",
"smartoffhand",
"freecam",
"jsmacros",
"inventoryprofiles",
"tweakeroo",
"soundboard",
"accurateblockplacement"
};
private TextSanitizer() {
}
public static boolean isSuspicious(Text text) { public static boolean isSuspicious(Text text) {
if (text == null) { if (text == null) {
return false; return false;
} }
TextContent content = text.getContent(); TextContent content = text.getContent();
if (content instanceof TranslatableTextContent || content instanceof KeybindTextContent) { if (content instanceof TranslatableTextContent translatable) {
return true; if (isBlacklistedKey(translatable.getKey())) {
}
for (Text sibling : text.getSiblings()) {
if (isSuspicious(sibling)) {
return true; return true;
} }
for (Object arg : translatable.getArgs()) {
if (isSuspiciousArgument(arg)) {
return true;
}
}
return false;
} }
return false; if (content instanceof KeybindTextContent keybind) {
} String key = keybind.getKey();
return key != null && !VanillaKeybinds.isVanillaKey(key);
public static String sanitize(Text text) {
StringBuilder out = new StringBuilder();
append(text, out);
return out.toString();
}
private static void append(Text text, StringBuilder out) {
if (text == null) {
return;
} }
TextContent content = text.getContent(); for (Text sibling : text.getSiblings()) {
if (isSuspicious(sibling)) {
if (content instanceof PlainTextContent plain) { return true;
out.append(plain.string()); }
}
return false;
}
public static String sanitize(Text text) {
StringBuilder out = new StringBuilder();
append(text, out);
return out.toString();
}
private static void append(Text text, StringBuilder out) {
if (text == null) {
return;
}
TextContent content = text.getContent();
if (content instanceof PlainTextContent plain) {
out.append(plain.string());
} else if (content instanceof TranslatableTextContent translatable) { } else if (content instanceof TranslatableTextContent translatable) {
String fallback = translatable.getFallback(); String fallback = translatable.getFallback();
if (fallback != null && !fallback.isEmpty()) { if (fallback != null && !fallback.isEmpty()) {
@@ -53,13 +89,61 @@ public final class TextSanitizer {
out.append(translatable.getKey()); out.append(translatable.getKey());
} }
} else if (content instanceof KeybindTextContent keybind) { } else if (content instanceof KeybindTextContent keybind) {
out.append(keybind.getKey()); String key = keybind.getKey();
out.append(key != null ? key : "");
} else { } else {
out.append(text.getString()); out.append(text.getString());
} }
for (Text sibling : text.getSiblings()) { for (Text sibling : text.getSiblings()) {
append(sibling, out); append(sibling, out);
}
}
private static boolean isBlacklistedKey(String key) {
if (key == null || key.isEmpty()) {
return false;
} }
String lower = key.toLowerCase();
for (String part : SUSPICIOUS_KEY_PARTS) {
if (lower.contains(part)) {
return true;
}
}
return false;
}
private static boolean isSuspiciousArgument(Object arg) {
if (arg == null) {
return false;
}
if (arg instanceof Text text) {
return isSuspicious(text);
}
if (arg instanceof KeybindTextContent keybind) {
return !VanillaKeybinds.isVanillaKey(keybind.getKey());
}
if (arg instanceof TranslatableTextContent translatable) {
if (isBlacklistedKey(translatable.getKey())) {
return true;
}
for (Object nested : translatable.getArgs()) {
if (isSuspiciousArgument(nested)) {
return true;
}
}
}
if (arg instanceof CharSequence sequence) {
return isBlacklistedKey(sequence.toString());
}
return false;
} }
} }

View File

@@ -0,0 +1,81 @@
package com.example.signleakshield;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.GameOptions;
import net.minecraft.client.option.KeyBinding;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public final class VanillaKeybinds {
private static volatile Set<String> vanillaKeys;
private VanillaKeybinds() {
}
public static boolean isVanillaKey(String key) {
if (key == null || key.isEmpty()) {
return false;
}
Set<String> keys = vanillaKeys;
if (keys == null) {
keys = loadVanillaKeys();
if (keys != null) {
vanillaKeys = keys;
}
}
return keys != null && keys.contains(key);
}
private static Set<String> loadVanillaKeys() {
MinecraftClient client = MinecraftClient.getInstance();
if (client == null || client.options == null) {
return null;
}
Set<String> keys = new HashSet<>();
for (Field field : GameOptions.class.getDeclaredFields()) {
Class<?> type = field.getType();
if (!KeyBinding.class.isAssignableFrom(type) && !KeyBinding[].class.isAssignableFrom(type)) {
continue;
}
try {
field.setAccessible(true);
Object value = field.get(client.options);
collectKeyBindings(keys, value);
} catch (IllegalAccessException | RuntimeException ignored) {
}
}
return Collections.unmodifiableSet(keys);
}
private static void collectKeyBindings(Set<String> keys, Object value) {
if (value instanceof KeyBinding keyBinding) {
String translationKey = keyBinding.getTranslationKey();
if (translationKey != null && !translationKey.isEmpty()) {
keys.add(translationKey);
}
return;
}
if (value instanceof KeyBinding[] keyBindings) {
for (KeyBinding keyBinding : keyBindings) {
if (keyBinding == null) {
continue;
}
String translationKey = keyBinding.getTranslationKey();
if (translationKey != null && !translationKey.isEmpty()) {
keys.add(translationKey);
}
}
}
}
}

View File

@@ -1,67 +1,121 @@
package com.example.signleakshield.mixin; package com.example.signleakshield.mixin;
import com.example.signleakshield.ExploitState; import com.example.signleakshield.ExploitState;
import com.example.signleakshield.SignLeakShieldClient; import com.example.signleakshield.SignLeakShieldTraceLog;
import com.example.signleakshield.TextSanitizer; import com.example.signleakshield.TextSanitizer;
import net.minecraft.network.ClientConnection; import net.minecraft.network.ClientConnection;
import net.minecraft.network.PacketCallbacks; import net.minecraft.network.PacketCallbacks;
import net.minecraft.network.packet.Packet; import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.c2s.play.UpdateSignC2SPacket; import net.minecraft.network.packet.c2s.play.UpdateSignC2SPacket;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.ModifyVariable;
import java.util.Arrays; import java.util.Arrays;
@Mixin(ClientConnection.class) @Mixin(ClientConnection.class)
public abstract class ClientConnectionMixin { public abstract class ClientConnectionMixin {
@ModifyVariable(method = "send(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/PacketCallbacks;Z)V", at = @At("HEAD"), argsOnly = true) @ModifyVariable(method = "send(Lnet/minecraft/network/packet/Packet;)V", at = @At("HEAD"), argsOnly = true)
private Packet<?> signleakshield$rewriteOutgoing(Packet<?> packet) { private Packet<?> signleakshield$rewriteOutgoing(Packet<?> packet) {
if (!(packet instanceof UpdateSignC2SPacket signPacket)) { if (packet instanceof UpdateSignC2SPacket signPacket) {
return packet; ExploitState.ForcedOpenContext forcedOpen = ExploitState.pendingForcedOpen;
} ExploitState.CapturedSignData captured = ExploitState.SIGNS.get(signPacket.getPos());
SignLeakShieldTraceLog.info(
ExploitState.ForcedOpenContext forcedOpen = ExploitState.pendingForcedOpen; "Outgoing sign packet observed: pos=%s front=%s forcedOpen=%s captured=%s packetText=%s",
if (forcedOpen == null) { signPacket.getPos(),
return packet; signPacket.isFront(),
} forcedOpen == null ? "null" : forcedOpen.pos() + "/front=" + forcedOpen.front() + "/ageMs=" + (System.currentTimeMillis() - forcedOpen.timeMs()),
captured == null ? "null" : "suspicious=" + captured.isSuspicious(),
BlockPos pos = signPacket.getPos(); Arrays.toString(signPacket.getText())
if (!forcedOpen.pos().equals(pos)) { );
return packet; }
}
if (!(packet instanceof UpdateSignC2SPacket signPacket)) {
if (forcedOpen.front() != signPacket.isFront()) { return packet;
return packet; }
}
ExploitState.ForcedOpenContext forcedOpen = ExploitState.pendingForcedOpen;
long ageMs = System.currentTimeMillis() - forcedOpen.timeMs(); if (forcedOpen == null) {
if (ageMs < 0L || ageMs > 5000L) { SignLeakShieldTraceLog.info(
return packet; "Outgoing sign packet not rewritten: no pending forced-open context for pos=%s front=%s",
} signPacket.getPos(),
signPacket.isFront()
);
return packet;
}
BlockPos pos = signPacket.getPos();
if (!forcedOpen.pos().equals(pos)) {
SignLeakShieldTraceLog.info(
"Outgoing sign packet not rewritten: pos mismatch packet=%s forcedOpen=%s",
pos,
forcedOpen.pos()
);
return packet;
}
if (forcedOpen.front() != signPacket.isFront()) {
SignLeakShieldTraceLog.info(
"Outgoing sign packet not rewritten: front mismatch packet=%s forcedOpen=%s",
signPacket.isFront(),
forcedOpen.front()
);
return packet;
}
long ageMs = System.currentTimeMillis() - forcedOpen.timeMs();
if (ageMs < 0L || ageMs > 5000L) {
SignLeakShieldTraceLog.info(
"Outgoing sign packet not rewritten: forced-open age out of range pos=%s front=%s ageMs=%s",
pos,
signPacket.isFront(),
ageMs
);
return packet;
}
ExploitState.CapturedSignData captured = ExploitState.SIGNS.get(pos); ExploitState.CapturedSignData captured = ExploitState.SIGNS.get(pos);
if (captured == null || !captured.isSuspicious()) { if (captured == null || !captured.isSuspicious()) {
return packet; SignLeakShieldTraceLog.info(
"Outgoing sign packet allowed: captured sign missing or not blacklisted pos=%s front=%s capturedPresent=%s",
pos,
signPacket.isFront(),
captured != null
);
return packet;
} }
Text[] lines = signPacket.isFront() ? captured.getFront() : captured.getBack(); Text[] textLines;
String line1 = TextSanitizer.sanitize(lines[0]); textLines = signPacket.isFront() ? captured.getFront() : captured.getBack();
String line2 = TextSanitizer.sanitize(lines[1]); String[] originalResponse = signPacket.getText();
String line3 = TextSanitizer.sanitize(lines[2]);
String line4 = TextSanitizer.sanitize(lines[3]);
SignLeakShieldClient.LOGGER.info( String line1 = TextSanitizer.sanitize(textLines[0]);
"Blocked forced sign translation event at {} front={}: got={}, returned={}", String line2 = TextSanitizer.sanitize(textLines[1]);
String line3 = TextSanitizer.sanitize(textLines[2]);
String line4 = TextSanitizer.sanitize(textLines[3]);
String[] returnedResponse = new String[] { line1, line2, line3, line4 };
SignLeakShieldTraceLog.info(
"Blocked forced sign translation event at %s front=%s: receivedSign=%s, unmodifiedResponse=%s, returned=%s",
pos, pos,
signPacket.isFront(), signPacket.isFront(),
Arrays.toString(signPacket.getText()), describeTextLines(textLines),
Arrays.toString(new String[] { line1, line2, line3, line4 }) Arrays.toString(originalResponse),
Arrays.toString(returnedResponse)
); );
ExploitState.clearForcedOpen(); ExploitState.clearForcedOpen();
return new UpdateSignC2SPacket(pos, signPacket.isFront(), line1, line2, line3, line4); return new UpdateSignC2SPacket(pos, signPacket.isFront(), line1, line2, line3, line4);
} }
private static String describeTextLines(Text[] lines) {
String[] values = new String[lines.length];
for (int i = 0; i < lines.length; i++) {
values[i] = lines[i] != null ? lines[i].getString() : "";
}
return Arrays.toString(values);
}
} }

View File

@@ -1,29 +1,30 @@
package com.example.signleakshield.mixin; package com.example.signleakshield.mixin;
import com.example.signleakshield.ExploitState; import com.example.signleakshield.ExploitState;
import com.example.signleakshield.SignTextExtractor; import com.example.signleakshield.SignLeakShieldTraceLog;
import net.minecraft.block.entity.BlockEntityType; import com.example.signleakshield.SignTextExtractor;
import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket; import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.util.math.BlockPos; import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket;
import org.spongepowered.asm.mixin.Mixin; import net.minecraft.util.math.BlockPos;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ClientPlayNetworkHandler.class)
public abstract class ClientPlayNetworkHandlerBlockEntityUpdateMixin { @Mixin(ClientPlayNetworkHandler.class)
@Inject(method = "onBlockEntityUpdate", at = @At("HEAD")) public abstract class ClientPlayNetworkHandlerBlockEntityUpdateMixin {
private void signleakshield$captureSign(BlockEntityUpdateS2CPacket packet, CallbackInfo ci) { @Inject(method = "onBlockEntityUpdate(Lnet/minecraft/network/packet/s2c/play/BlockEntityUpdateS2CPacket;)V", at = @At("HEAD"))
if (packet.getNbt() == null) { private void signleakshield$captureSign(BlockEntityUpdateS2CPacket packet, CallbackInfo ci) {
return; if (packet.getNbt() == null) {
} return;
}
if (packet.getBlockEntityType() != BlockEntityType.SIGN && packet.getBlockEntityType() != BlockEntityType.HANGING_SIGN) {
return; if (packet.getBlockEntityType() != BlockEntityType.SIGN && packet.getBlockEntityType() != BlockEntityType.HANGING_SIGN) {
} return;
}
BlockPos pos = packet.getPos();
ExploitState.SIGNS.put(pos.toImmutable(), SignTextExtractor.fromNbt(packet.getNbt())); BlockPos pos = packet.getPos();
} ExploitState.SIGNS.put(pos.toImmutable(), SignTextExtractor.fromNbt(packet.getNbt()));
} }
}

View File

@@ -1,32 +1,33 @@
package com.example.signleakshield.mixin; package com.example.signleakshield.mixin;
import com.example.signleakshield.ExploitState; import com.example.signleakshield.ExploitState;
import com.example.signleakshield.SignTextExtractor; import com.example.signleakshield.SignLeakShieldTraceLog;
import net.minecraft.block.entity.BlockEntityType; import com.example.signleakshield.SignTextExtractor;
import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.network.packet.s2c.play.ChunkDataS2CPacket; import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.nbt.NbtCompound; import net.minecraft.network.packet.s2c.play.ChunkDataS2CPacket;
import net.minecraft.util.math.BlockPos; import net.minecraft.nbt.NbtCompound;
import org.spongepowered.asm.mixin.Mixin; import net.minecraft.util.math.BlockPos;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ClientPlayNetworkHandler.class)
public abstract class ClientPlayNetworkHandlerChunkDataMixin { @Mixin(ClientPlayNetworkHandler.class)
@Inject(method = "onChunkData", at = @At("HEAD")) public abstract class ClientPlayNetworkHandlerChunkDataMixin {
private void signleakshield$captureChunkData(ChunkDataS2CPacket packet, CallbackInfo ci) { @Inject(method = "onChunkData(Lnet/minecraft/network/packet/s2c/play/ChunkDataS2CPacket;)V", at = @At("HEAD"))
packet.getChunkData().getBlockEntities(packet.getChunkX(), packet.getChunkZ()).accept((localPos, type, nbt) -> { private void signleakshield$captureChunkData(ChunkDataS2CPacket packet, CallbackInfo ci) {
if (nbt == null) { packet.getChunkData().getBlockEntities(packet.getChunkX(), packet.getChunkZ()).accept((localPos, type, nbt) -> {
return; if (nbt == null) {
} return;
}
if (type != BlockEntityType.SIGN && type != BlockEntityType.HANGING_SIGN) {
return; if (type != BlockEntityType.SIGN && type != BlockEntityType.HANGING_SIGN) {
} return;
}
BlockPos pos = localPos.toImmutable();
ExploitState.SIGNS.put(pos, SignTextExtractor.fromNbt((NbtCompound) nbt)); BlockPos pos = localPos.toImmutable();
}); ExploitState.SIGNS.put(pos, SignTextExtractor.fromNbt((NbtCompound) nbt));
} });
} }
}

View File

@@ -1,17 +1,23 @@
package com.example.signleakshield.mixin; package com.example.signleakshield.mixin;
import com.example.signleakshield.ExploitState; import com.example.signleakshield.ExploitState;
import net.minecraft.client.network.ClientPlayNetworkHandler; import com.example.signleakshield.SignLeakShieldTraceLog;
import net.minecraft.network.packet.s2c.play.SignEditorOpenS2CPacket; import net.minecraft.client.network.ClientPlayNetworkHandler;
import org.spongepowered.asm.mixin.Mixin; import net.minecraft.network.packet.s2c.play.SignEditorOpenS2CPacket;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ClientPlayNetworkHandler.class)
public abstract class ClientPlayNetworkHandlerSignEditorOpenMixin { @Mixin(ClientPlayNetworkHandler.class)
@Inject(method = "onSignEditorOpen", at = @At("HEAD")) public abstract class ClientPlayNetworkHandlerSignEditorOpenMixin {
private void signleakshield$rememberForcedOpen(SignEditorOpenS2CPacket packet, CallbackInfo ci) { @Inject(method = "onSignEditorOpen", at = @At("HEAD"))
ExploitState.rememberForcedOpen(packet.getPos(), packet.isFront()); private void signleakshield$rememberForcedOpen(SignEditorOpenS2CPacket packet, CallbackInfo ci) {
} SignLeakShieldTraceLog.info(
} "Sign editor open received: pos=%s front=%s",
packet.getPos(),
packet.isFront()
);
ExploitState.rememberForcedOpen(packet.getPos(), packet.isFront());
}
}

View File

@@ -23,8 +23,8 @@
], ],
"depends": { "depends": {
"fabricloader": ">=0.18.4", "fabricloader": ">=0.18.4",
"minecraft": "1.21.1", "minecraft": "${minecraft_version}",
"java": ">=21", "java": ">=21",
"fabric-api": "*" "fabric-api": "*"
} }
} }

View File

@@ -1,6 +1,7 @@
{ {
"required": true, "required": true,
"package": "com.example.signleakshield.mixin", "package": "com.example.signleakshield.mixin",
"refmap": "client-signleakshield.refmap.json",
"compatibilityLevel": "JAVA_21", "compatibilityLevel": "JAVA_21",
"client": [ "client": [
"ClientConnectionMixin", "ClientConnectionMixin",