Compare commits
20 Commits
build-32af
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 988c2338fd | |||
| 43630597dd | |||
| b03444d986 | |||
| fe4675d606 | |||
| c94c29cb5f | |||
| e82f89791f | |||
| 91454b90a9 | |||
| 9f597b4672 | |||
| 81d98ac840 | |||
| 437d9b0496 | |||
| 2fce517e18 | |||
| 0d0b4900b0 | |||
| 1713748c11 | |||
| 7db4423fe2 | |||
| 096df79aca | |||
| 562748b79d | |||
| be700eb008 | |||
| b004e05575 | |||
| 5862f0bb1e | |||
| 2657bbe5af |
@@ -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}")"
|
|
||||||
29
.gitea/workflows/dev-verify.yml
Normal file
29
.gitea/workflows/dev-verify.yml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
name: Dev Verify
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
verify:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: 21
|
||||||
|
|
||||||
|
- name: Verify
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
bash ./gradlew --no-daemon compileClientJava
|
||||||
78
.gitea/workflows/main-release.yml
Normal file
78
.gitea/workflows/main-release.yml
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
name: Main Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: 21
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
bash ./gradlew --no-daemon 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="${GITHUB_REF_NAME}"
|
||||||
|
release_name="${GITHUB_REF_NAME}"
|
||||||
|
prerelease=false
|
||||||
|
else
|
||||||
|
release_tag="build-${GITHUB_SHA:0:7}"
|
||||||
|
release_name="build-${GITHUB_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 ${GITHUB_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "${release_payload}" \
|
||||||
|
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/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
|
||||||
|
|
||||||
|
if [[ "${GITHUB_SERVER_URL}" == "https://github.com" ]]; then
|
||||||
|
curl -fsS \
|
||||||
|
-H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
|
-H "Content-Type: application/octet-stream" \
|
||||||
|
--data-binary @"${jar_path}" \
|
||||||
|
"https://uploads.github.com/repos/${GITHUB_REPOSITORY}/releases/${release_id}/assets?name=$(basename "${jar_path}")"
|
||||||
|
else
|
||||||
|
curl -fsS \
|
||||||
|
-H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
|
-H "Content-Type: multipart/form-data" \
|
||||||
|
-F "attachment=@${jar_path}" \
|
||||||
|
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases/${release_id}/assets?name=$(basename "${jar_path}")"
|
||||||
|
fi
|
||||||
29
.github/workflows/dev-verify.yml
vendored
Normal file
29
.github/workflows/dev-verify.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
name: Dev Verify
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
verify:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: 21
|
||||||
|
|
||||||
|
- name: Verify
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
bash ./gradlew --no-daemon compileClientJava
|
||||||
78
.github/workflows/main-release.yml
vendored
Normal file
78
.github/workflows/main-release.yml
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
name: Main Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: 21
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
bash ./gradlew --no-daemon build
|
||||||
|
|
||||||
|
- name: Publish 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="${GITHUB_REF_NAME}"
|
||||||
|
release_name="${GITHUB_REF_NAME}"
|
||||||
|
prerelease=false
|
||||||
|
else
|
||||||
|
release_tag="build-${GITHUB_SHA:0:7}"
|
||||||
|
release_name="build-${GITHUB_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 ${GITHUB_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "${release_payload}" \
|
||||||
|
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/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
|
||||||
|
|
||||||
|
if [[ "${GITHUB_SERVER_URL}" == "https://github.com" ]]; then
|
||||||
|
curl -fsS \
|
||||||
|
-H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
|
-H "Content-Type: application/octet-stream" \
|
||||||
|
--data-binary @"${jar_path}" \
|
||||||
|
"https://uploads.github.com/repos/${GITHUB_REPOSITORY}/releases/${release_id}/assets?name=$(basename "${jar_path}")"
|
||||||
|
else
|
||||||
|
curl -fsS \
|
||||||
|
-H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
|
-H "Content-Type: multipart/form-data" \
|
||||||
|
-F "attachment=@${jar_path}" \
|
||||||
|
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases/${release_id}/assets?name=$(basename "${jar_path}")"
|
||||||
|
fi
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -24,3 +24,8 @@
|
|||||||
hs_err_pid*
|
hs_err_pid*
|
||||||
replay_pid*
|
replay_pid*
|
||||||
|
|
||||||
|
.gradle/*
|
||||||
|
.gradle/
|
||||||
|
build/*
|
||||||
|
build/
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
>
|
>
|
||||||
> 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.
|
This is a Fabric client mod for Minecraft 1.21.1 that defensively patches forced sign-editor translation and keybind leak probes.
|
||||||
|
|
||||||
Bypass's Anti Mod systems on Donut SMP and otehr like it.
|
Bypass's Anti Mod systems on Donut SMP and otehr like it.
|
||||||
|
|||||||
10
build.gradle
10
build.gradle
@@ -5,6 +5,7 @@ plugins {
|
|||||||
|
|
||||||
version = project.mod_version
|
version = project.mod_version
|
||||||
group = project.maven_group
|
group = project.maven_group
|
||||||
|
def modVersion = project.version.toString()
|
||||||
|
|
||||||
base {
|
base {
|
||||||
archivesName = project.archives_base_name
|
archivesName = project.archives_base_name
|
||||||
@@ -15,6 +16,11 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loom {
|
loom {
|
||||||
|
mixin {
|
||||||
|
useLegacyMixinAp = true
|
||||||
|
defaultRefmapName = "client-signleakshield.refmap.json"
|
||||||
|
}
|
||||||
|
|
||||||
splitEnvironmentSourceSets()
|
splitEnvironmentSourceSets()
|
||||||
|
|
||||||
mods {
|
mods {
|
||||||
@@ -33,10 +39,10 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
processResources {
|
processResources {
|
||||||
inputs.property "version", project.version
|
inputs.property "version", modVersion
|
||||||
|
|
||||||
filesMatching("fabric.mod.json") {
|
filesMatching("fabric.mod.json") {
|
||||||
expand "version": inputs.properties.version
|
expand "version": modVersion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
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=true
|
||||||
|
|
||||||
minecraft_version=1.21.1
|
minecraft_version=1.21.1
|
||||||
yarn_mappings=1.21.1+build.3
|
yarn_mappings=1.21.1+build.3
|
||||||
@@ -8,6 +10,6 @@ loader_version=0.18.4
|
|||||||
loom_version=1.9.1
|
loom_version=1.9.1
|
||||||
fabric_version=0.116.8+1.21.1
|
fabric_version=0.116.8+1.21.1
|
||||||
|
|
||||||
mod_version=1.0.0
|
mod_version=1.0.1
|
||||||
maven_group=com.example
|
maven_group=com.example
|
||||||
archives_base_name=sign-leak-shield
|
archives_base_name=sign-leak-shield
|
||||||
@@ -10,6 +10,7 @@ public final class SignLeakShieldClient implements ClientModInitializer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInitializeClient() {
|
public void onInitializeClient() {
|
||||||
LOGGER.info("Sign Leak Shield initialized");
|
SignLeakShieldTraceLog.reset();
|
||||||
|
SignLeakShieldTraceLog.info("Sign Leak Shield initialized");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,24 @@ import net.minecraft.text.TextContent;
|
|||||||
import net.minecraft.text.TranslatableTextContent;
|
import net.minecraft.text.TranslatableTextContent;
|
||||||
|
|
||||||
public final class TextSanitizer {
|
public final class TextSanitizer {
|
||||||
|
private static final String[] BLACKLISTED_KEY_PARTS = {
|
||||||
|
"gey.glazed.",
|
||||||
|
"meteorline",
|
||||||
|
"meteorclient",
|
||||||
|
"metiorclient",
|
||||||
|
"itemscroller",
|
||||||
|
"moremousetweaks",
|
||||||
|
"invmove",
|
||||||
|
"autototem",
|
||||||
|
"smartoffhand",
|
||||||
|
"freecam",
|
||||||
|
"jsmacros",
|
||||||
|
"inventoryprofiles",
|
||||||
|
"tweakeroo",
|
||||||
|
"soundboard",
|
||||||
|
"accurateblockplacement"
|
||||||
|
};
|
||||||
|
|
||||||
private TextSanitizer() {
|
private TextSanitizer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,8 +35,12 @@ public final class TextSanitizer {
|
|||||||
|
|
||||||
TextContent content = text.getContent();
|
TextContent content = text.getContent();
|
||||||
|
|
||||||
if (content instanceof TranslatableTextContent || content instanceof KeybindTextContent) {
|
if (content instanceof TranslatableTextContent translatable) {
|
||||||
return true;
|
return isBlacklistedKey(translatable.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content instanceof KeybindTextContent keybind) {
|
||||||
|
return isBlacklistedKey(keybind.getKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Text sibling : text.getSiblings()) {
|
for (Text sibling : text.getSiblings()) {
|
||||||
@@ -53,7 +75,7 @@ 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());
|
out.append(text.getString());
|
||||||
} else {
|
} else {
|
||||||
out.append(text.getString());
|
out.append(text.getString());
|
||||||
}
|
}
|
||||||
@@ -62,4 +84,19 @@ public final class TextSanitizer {
|
|||||||
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 : BLACKLISTED_KEY_PARTS) {
|
||||||
|
if (lower.contains(part)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
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;
|
||||||
@@ -17,44 +17,86 @@ 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) {
|
||||||
|
ExploitState.ForcedOpenContext forcedOpen = ExploitState.pendingForcedOpen;
|
||||||
|
ExploitState.CapturedSignData captured = ExploitState.SIGNS.get(signPacket.getPos());
|
||||||
|
SignLeakShieldTraceLog.info(
|
||||||
|
"Outgoing sign packet observed: pos=%s front=%s forcedOpen=%s captured=%s packetText=%s",
|
||||||
|
signPacket.getPos(),
|
||||||
|
signPacket.isFront(),
|
||||||
|
forcedOpen == null ? "null" : forcedOpen.pos() + "/front=" + forcedOpen.front() + "/ageMs=" + (System.currentTimeMillis() - forcedOpen.timeMs()),
|
||||||
|
captured == null ? "null" : "suspicious=" + captured.isSuspicious(),
|
||||||
|
Arrays.toString(signPacket.getText())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!(packet instanceof UpdateSignC2SPacket signPacket)) {
|
if (!(packet instanceof UpdateSignC2SPacket signPacket)) {
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExploitState.ForcedOpenContext forcedOpen = ExploitState.pendingForcedOpen;
|
ExploitState.ForcedOpenContext forcedOpen = ExploitState.pendingForcedOpen;
|
||||||
if (forcedOpen == null) {
|
if (forcedOpen == null) {
|
||||||
|
SignLeakShieldTraceLog.info(
|
||||||
|
"Outgoing sign packet not rewritten: no pending forced-open context for pos=%s front=%s",
|
||||||
|
signPacket.getPos(),
|
||||||
|
signPacket.isFront()
|
||||||
|
);
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockPos pos = signPacket.getPos();
|
BlockPos pos = signPacket.getPos();
|
||||||
if (!forcedOpen.pos().equals(pos)) {
|
if (!forcedOpen.pos().equals(pos)) {
|
||||||
|
SignLeakShieldTraceLog.info(
|
||||||
|
"Outgoing sign packet not rewritten: pos mismatch packet=%s forcedOpen=%s",
|
||||||
|
pos,
|
||||||
|
forcedOpen.pos()
|
||||||
|
);
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forcedOpen.front() != signPacket.isFront()) {
|
if (forcedOpen.front() != signPacket.isFront()) {
|
||||||
|
SignLeakShieldTraceLog.info(
|
||||||
|
"Outgoing sign packet not rewritten: front mismatch packet=%s forcedOpen=%s",
|
||||||
|
signPacket.isFront(),
|
||||||
|
forcedOpen.front()
|
||||||
|
);
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
long ageMs = System.currentTimeMillis() - forcedOpen.timeMs();
|
long ageMs = System.currentTimeMillis() - forcedOpen.timeMs();
|
||||||
if (ageMs < 0L || ageMs > 5000L) {
|
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;
|
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()) {
|
||||||
|
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;
|
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 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]);
|
||||||
|
|
||||||
|
SignLeakShieldTraceLog.info(
|
||||||
|
"Blocked forced sign translation event at %s front=%s: got=%s, returned=%s",
|
||||||
pos,
|
pos,
|
||||||
signPacket.isFront(),
|
signPacket.isFront(),
|
||||||
Arrays.toString(signPacket.getText()),
|
Arrays.toString(signPacket.getText()),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.example.signleakshield.mixin;
|
package com.example.signleakshield.mixin;
|
||||||
|
|
||||||
import com.example.signleakshield.ExploitState;
|
import com.example.signleakshield.ExploitState;
|
||||||
|
import com.example.signleakshield.SignLeakShieldTraceLog;
|
||||||
import com.example.signleakshield.SignTextExtractor;
|
import com.example.signleakshield.SignTextExtractor;
|
||||||
import net.minecraft.block.entity.BlockEntityType;
|
import net.minecraft.block.entity.BlockEntityType;
|
||||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||||
@@ -13,7 +14,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
|
|
||||||
@Mixin(ClientPlayNetworkHandler.class)
|
@Mixin(ClientPlayNetworkHandler.class)
|
||||||
public abstract class ClientPlayNetworkHandlerBlockEntityUpdateMixin {
|
public abstract class ClientPlayNetworkHandlerBlockEntityUpdateMixin {
|
||||||
@Inject(method = "onBlockEntityUpdate", at = @At("HEAD"))
|
@Inject(method = "onBlockEntityUpdate(Lnet/minecraft/network/packet/s2c/play/BlockEntityUpdateS2CPacket;)V", at = @At("HEAD"))
|
||||||
private void signleakshield$captureSign(BlockEntityUpdateS2CPacket packet, CallbackInfo ci) {
|
private void signleakshield$captureSign(BlockEntityUpdateS2CPacket packet, CallbackInfo ci) {
|
||||||
if (packet.getNbt() == null) {
|
if (packet.getNbt() == null) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.example.signleakshield.mixin;
|
package com.example.signleakshield.mixin;
|
||||||
|
|
||||||
import com.example.signleakshield.ExploitState;
|
import com.example.signleakshield.ExploitState;
|
||||||
|
import com.example.signleakshield.SignLeakShieldTraceLog;
|
||||||
import com.example.signleakshield.SignTextExtractor;
|
import com.example.signleakshield.SignTextExtractor;
|
||||||
import net.minecraft.block.entity.BlockEntityType;
|
import net.minecraft.block.entity.BlockEntityType;
|
||||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||||
@@ -14,7 +15,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
|
|
||||||
@Mixin(ClientPlayNetworkHandler.class)
|
@Mixin(ClientPlayNetworkHandler.class)
|
||||||
public abstract class ClientPlayNetworkHandlerChunkDataMixin {
|
public abstract class ClientPlayNetworkHandlerChunkDataMixin {
|
||||||
@Inject(method = "onChunkData", at = @At("HEAD"))
|
@Inject(method = "onChunkData(Lnet/minecraft/network/packet/s2c/play/ChunkDataS2CPacket;)V", at = @At("HEAD"))
|
||||||
private void signleakshield$captureChunkData(ChunkDataS2CPacket packet, CallbackInfo ci) {
|
private void signleakshield$captureChunkData(ChunkDataS2CPacket packet, CallbackInfo ci) {
|
||||||
packet.getChunkData().getBlockEntities(packet.getChunkX(), packet.getChunkZ()).accept((localPos, type, nbt) -> {
|
packet.getChunkData().getBlockEntities(packet.getChunkX(), packet.getChunkZ()).accept((localPos, type, nbt) -> {
|
||||||
if (nbt == null) {
|
if (nbt == null) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.example.signleakshield.mixin;
|
package com.example.signleakshield.mixin;
|
||||||
|
|
||||||
import com.example.signleakshield.ExploitState;
|
import com.example.signleakshield.ExploitState;
|
||||||
|
import com.example.signleakshield.SignLeakShieldTraceLog;
|
||||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||||
import net.minecraft.network.packet.s2c.play.SignEditorOpenS2CPacket;
|
import net.minecraft.network.packet.s2c.play.SignEditorOpenS2CPacket;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
@@ -12,6 +13,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
public abstract class ClientPlayNetworkHandlerSignEditorOpenMixin {
|
public abstract class ClientPlayNetworkHandlerSignEditorOpenMixin {
|
||||||
@Inject(method = "onSignEditorOpen", at = @At("HEAD"))
|
@Inject(method = "onSignEditorOpen", at = @At("HEAD"))
|
||||||
private void signleakshield$rememberForcedOpen(SignEditorOpenS2CPacket packet, CallbackInfo ci) {
|
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());
|
ExploitState.rememberForcedOpen(packet.getPos(), packet.isFront());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user