From 7963bfa56138370b720c97d0feabff3889b16d3c Mon Sep 17 00:00:00 2001 From: Kestis Date: Wed, 19 Aug 2026 15:46:27 +0800 Subject: [PATCH 01/17] Add isolated TACZ 26.2 port CI harness --- .github/workflows/tacz-26-2-port.yml | 83 ++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 .github/workflows/tacz-26-2-port.yml diff --git a/.github/workflows/tacz-26-2-port.yml b/.github/workflows/tacz-26-2-port.yml new file mode 100644 index 0000000..ff77ac1 --- /dev/null +++ b/.github/workflows/tacz-26-2-port.yml @@ -0,0 +1,83 @@ +name: TACZ 26.2 Port CI + +on: + push: + branches: + - ci/tacz-neoforge-26.2-port + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - name: Checkout CI harness + uses: actions/checkout@v4 + + - name: Set up Java 25 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '25' + + - name: Clone NeoForge 1.21.1 TACZ port + run: | + git clone --depth 1 https://github.com/RaeYNCreations/TACZ-1.21.1-neoforge-1.21.1.git work + cd work + git rev-parse HEAD | tee ../base-commit.txt + + - name: Apply 26.2 migration pass + run: python3 tools/tacz26_port.py work + + - name: Compile + id: compile + shell: bash + run: | + set +e + cd work + chmod +x gradlew + ./gradlew compileJava --stacktrace --no-daemon 2>&1 | tee ../compile.log + status=${PIPESTATUS[0]} + echo "$status" > ../compile-status.txt + exit 0 + + - name: Save source diff + if: always() + run: | + cd work + git diff -- . ':!gradle/wrapper/gradle-wrapper.jar' > ../port.diff || true + cd .. + zip -qr TACZ-26.2-port-source.zip work \ + -x 'work/.git/*' 'work/.gradle/*' 'work/build/*' 'work/run/*' + + - name: Copy JARs if produced + if: always() + run: | + mkdir -p out + find work/build/libs -maxdepth 1 -type f -name '*.jar' -exec cp {} out/ \; 2>/dev/null || true + + - name: Upload diagnostics and build + if: always() + uses: actions/upload-artifact@v4 + with: + name: TACZ-26.2-port-pass + if-no-files-found: warn + path: | + compile.log + compile-status.txt + base-commit.txt + port.diff + TACZ-26.2-port-source.zip + out/*.jar + + - name: Fail if compilation failed + if: always() + run: | + status=$(cat compile-status.txt 2>/dev/null || echo 1) + if [ "$status" != "0" ]; then + echo "Compilation failed with status $status" + exit "$status" + fi From d24f1579845353b7f4f02e04736955d5e485ec34 Mon Sep 17 00:00:00 2001 From: Kestis Date: Wed, 19 Aug 2026 15:47:04 +0800 Subject: [PATCH 02/17] Add first automated TACZ 26.2 migration pass --- tools/tacz26_port.py | 115 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 tools/tacz26_port.py diff --git a/tools/tacz26_port.py b/tools/tacz26_port.py new file mode 100644 index 0000000..726c64a --- /dev/null +++ b/tools/tacz26_port.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +from pathlib import Path +import re +import sys + +root = Path(sys.argv[1]).resolve() + +# Build against the official NeoForge 26.2 MDK baseline. Keep the first pass +# deliberately dependency-light: optional integrations are disabled until the +# core is source-compatible with 26.2. +build = r'''plugins { + java + id("net.neoforged.moddev") version "2.0.144" +} + +group = "com.tacz" +version = "1.1.8-neoforge-26.2-port" + +base { + archivesName.set("tacz-neoforge-26.2") +} + +java { + toolchain.languageVersion = JavaLanguageVersion.of(25) + withSourcesJar() +} + +repositories { + mavenCentral() + maven("https://jitpack.io") { + content { + includeGroup("com.github.FiguraMC.luaj") + } + } +} + +neoForge { + version = "26.2.0.59" + + runs { + create("client") { + client() + gameDirectory = file("run/client") + } + create("server") { + server() + gameDirectory = file("run/server") + programArgument("--nogui") + } + } + + mods { + create("tacz") { + sourceSet(sourceSets["main"]) + } + } +} + +sourceSets { + main { + java { + // Re-enable integrations one at a time after the core compiles. + exclude("com/tacz/guns/compat/**") + } + } +} + +dependencies { + implementation("org.apache.commons:commons-math3:3.6.1") + implementation("com.github.FiguraMC.luaj:luaj-core:3.0.8-figura") + implementation("com.github.FiguraMC.luaj:luaj-jse:3.0.8-figura") + implementation("org.apache.bcel:bcel:6.6.1") +} + +tasks.withType().configureEach { + options.encoding = "UTF-8" + options.release.set(25) +} +''' +(root / "build.gradle.kts").write_text(build, encoding="utf-8") + +# 26.2 official MDK currently uses Gradle 9.2.1 and Java 25. +wrapper = root / "gradle/wrapper/gradle-wrapper.properties" +wrapper.write_text("""distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-9.2.1-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n""", encoding="utf-8") + +# Remove old 1.21.1 build metadata that can confuse Gradle during migration. +for p in [root / "gradle/libs.versions.toml"]: + if p.exists(): + p.rename(p.with_suffix(p.suffix + ".disabled")) + +# The optional compatibility registry directly references integrations that are +# intentionally excluded during the core migration pass. Replace it with a +# stable no-op bridge while preserving its public init hooks where possible. +compat = root / "src/main/java/com/tacz/guns/init/CompatRegistry.java" +if compat.exists(): + text = compat.read_text(encoding="utf-8") + # Discover the package/class and all public static void zero-arg methods, + # then preserve those signatures as no-ops to avoid touching callers. + methods = re.findall(r'public\s+static\s+void\s+(\w+)\s*\(\s*\)', text) + methods = list(dict.fromkeys(methods)) + body = ["package com.tacz.guns.init;", "", "/**", " * 26.2 port bridge. Optional mod integrations are registered after the core", " * migration is complete; keeping these hooks no-op preserves call sites.", " */", "public final class CompatRegistry {", " private CompatRegistry() {}"] + if not methods: + methods = ["init"] + for m in methods: + body += [f" public static void {m}() {{}}"] + body += ["}", ""] + compat.write_text("\n".join(body), encoding="utf-8") + +# Disable KubeJS service discovery while the package is excluded. +for rel in ["src/main/resources/kubejs.plugins.txt"]: + p = root / rel + if p.exists(): + p.write_text("", encoding="utf-8") + +print("Applied TACZ NeoForge 26.2 migration pass 1") From 338cc498267656ac8750e5f728f58c63b33643a2 Mon Sep 17 00:00:00 2001 From: Kestis Date: Wed, 19 Aug 2026 15:47:45 +0800 Subject: [PATCH 03/17] Enable PR CI for TACZ 26.2 scratch port --- .github/workflows/tacz-26-2-port.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/tacz-26-2-port.yml b/.github/workflows/tacz-26-2-port.yml index ff77ac1..817f65f 100644 --- a/.github/workflows/tacz-26-2-port.yml +++ b/.github/workflows/tacz-26-2-port.yml @@ -4,6 +4,9 @@ on: push: branches: - ci/tacz-neoforge-26.2-port + pull_request: + branches: + - main workflow_dispatch: permissions: From 0dd678d9c9025ce75b6e57cb94f0dbcc09479b72 Mon Sep 17 00:00:00 2001 From: Kestis Date: Wed, 19 Aug 2026 15:54:25 +0800 Subject: [PATCH 04/17] Add MC26.2 Fabric reference snapshot to port CI --- .github/workflows/tacz-26-2-port.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tacz-26-2-port.yml b/.github/workflows/tacz-26-2-port.yml index 817f65f..b50a5fb 100644 --- a/.github/workflows/tacz-26-2-port.yml +++ b/.github/workflows/tacz-26-2-port.yml @@ -26,11 +26,17 @@ jobs: distribution: temurin java-version: '25' - - name: Clone NeoForge 1.21.1 TACZ port + - name: Clone port bases run: | git clone --depth 1 https://github.com/RaeYNCreations/TACZ-1.21.1-neoforge-1.21.1.git work - cd work - git rev-parse HEAD | tee ../base-commit.txt + git clone --depth 1 --branch '26.2(main)' https://github.com/q14433686-arch/TaCZ_Refabricated_Unofficial.git fabric26 + (cd work && git rev-parse HEAD) | tee base-commit.txt + (cd fabric26 && git rev-parse HEAD) | tee fabric26-commit.txt + + - name: Snapshot 26.2 reference source + run: | + zip -qr TACZ-Fabric-26.2-reference-source.zip fabric26 \ + -x 'fabric26/.git/*' 'fabric26/.gradle/*' 'fabric26/build/*' 'fabric26/run/*' - name: Apply 26.2 migration pass run: python3 tools/tacz26_port.py work @@ -72,8 +78,10 @@ jobs: compile.log compile-status.txt base-commit.txt + fabric26-commit.txt port.diff TACZ-26.2-port-source.zip + TACZ-Fabric-26.2-reference-source.zip out/*.jar - name: Fail if compilation failed From 7e07b0a793c523825f90a99a2d2c167ccad0b14d Mon Sep 17 00:00:00 2001 From: Kestis Date: Wed, 19 Aug 2026 16:00:42 +0800 Subject: [PATCH 05/17] Apply verified MC26.2 vanilla source migrations --- tools/tacz26_port.py | 99 +++++++++++++++++++++++++++++++------------- 1 file changed, 71 insertions(+), 28 deletions(-) diff --git a/tools/tacz26_port.py b/tools/tacz26_port.py index 726c64a..f24c30c 100644 --- a/tools/tacz26_port.py +++ b/tools/tacz26_port.py @@ -1,13 +1,14 @@ #!/usr/bin/env python3 from pathlib import Path import re +import shutil import sys root = Path(sys.argv[1]).resolve() +fabric = root.parent / "fabric26" -# Build against the official NeoForge 26.2 MDK baseline. Keep the first pass -# deliberately dependency-light: optional integrations are disabled until the -# core is source-compatible with 26.2. +# Build against the official NeoForge 26.2 MDK baseline. Optional third-party +# integrations stay disabled until the core mod is source-compatible. build = r'''plugins { java id("net.neoforged.moddev") version "2.0.144" @@ -28,9 +29,7 @@ repositories { mavenCentral() maven("https://jitpack.io") { - content { - includeGroup("com.github.FiguraMC.luaj") - } + content { includeGroup("com.github.FiguraMC.luaj") } } } @@ -50,16 +49,14 @@ } mods { - create("tacz") { - sourceSet(sourceSets["main"]) - } + create("tacz") { sourceSet(sourceSets["main"]) } } } sourceSets { main { java { - // Re-enable integrations one at a time after the core compiles. + // Integrations are optional and are re-enabled after the core port. exclude("com/tacz/guns/compat/**") } } @@ -75,41 +72,87 @@ tasks.withType().configureEach { options.encoding = "UTF-8" options.release.set(25) + options.compilerArgs.addAll(listOf("-Xmaxerrs", "2000", "-Xmaxwarns", "2000")) } ''' (root / "build.gradle.kts").write_text(build, encoding="utf-8") -# 26.2 official MDK currently uses Gradle 9.2.1 and Java 25. wrapper = root / "gradle/wrapper/gradle-wrapper.properties" wrapper.write_text("""distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-9.2.1-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n""", encoding="utf-8") -# Remove old 1.21.1 build metadata that can confuse Gradle during migration. -for p in [root / "gradle/libs.versions.toml"]: - if p.exists(): - p.rename(p.with_suffix(p.suffix + ".disabled")) - -# The optional compatibility registry directly references integrations that are -# intentionally excluded during the core migration pass. Replace it with a -# stable no-op bridge while preserving its public init hooks where possible. +libs = root / "gradle/libs.versions.toml" +if libs.exists(): + libs.rename(libs.with_suffix(libs.suffix + ".disabled")) + +# Pull verified vanilla/API migrations from the working 26.2 Fabric port, but +# only for files that are platform-neutral on both sides. This avoids guessing +# hundreds of Mojang 26.x rendering/data API changes while keeping NeoForge's +# registrations, networking and event wiring intact. +if fabric.exists(): + neo_java = root / "src/main/java" + fab_java = fabric / "src/main/java" + neo_files = {p.relative_to(neo_java).as_posix(): p for p in neo_java.rglob("*.java")} + fab_files = {p.relative_to(fab_java).as_posix(): p for p in fab_java.rglob("*.java")} + copied = 0 + for rel in sorted(set(neo_files) & set(fab_files)): + old = neo_files[rel].read_text(encoding="utf-8", errors="ignore") + new = fab_files[rel].read_text(encoding="utf-8", errors="ignore") + fabric_imports = [] + for line in new.splitlines(): + s = line.strip() + if s.startswith("import net.fabricmc") or s.startswith("import cn.sh1rocu"): + fabric_imports.append(s.removeprefix("import ").removesuffix(";")) + only_env_annotations = all(x in { + "net.fabricmc.api.EnvType", "net.fabricmc.api.Environment" + } for x in fabric_imports) + platform_area = any(seg in rel for seg in ( + "/init/", "/network/", "/event/", "/mixin/", "/compat/" + )) + if only_env_annotations and "net.neoforged" not in old and not platform_area: + new = new.replace("import net.fabricmc.api.EnvType;\n", "") + new = new.replace("import net.fabricmc.api.Environment;\n", "") + new = re.sub(r"\s*@Environment\(EnvType\.(?:CLIENT|SERVER)\)\s*", "\n", new) + neo_files[rel].write_text(new, encoding="utf-8") + copied += 1 + print(f"Copied {copied} platform-neutral 26.2 source files") + +# Mechanical Mojang 26.2 renames for the NeoForge-specific files retained from +# the 1.21.1 port. Identifier replaced ResourceLocation in 26.2; Util moved to +# net.minecraft.util; RenderType moved under renderer.rendertype. +for p in (root / "src/main/java").rglob("*.java"): + text = p.read_text(encoding="utf-8", errors="ignore") + text = text.replace("net.minecraft.resources.ResourceLocation", "net.minecraft.resources.Identifier") + text = re.sub(r"\bResourceLocation\b", "Identifier", text) + text = text.replace("import net.minecraft.Util;", "import net.minecraft.util.Util;") + text = text.replace("import net.minecraft.client.renderer.RenderType;", "import net.minecraft.client.renderer.rendertype.RenderType;") + p.write_text(text, encoding="utf-8") + +# KubeJS is optional. Keep the public event-poster surface but make it a no-op +# while the KubeJS compatibility package is intentionally excluded. +kube = root / "src/main/java/com/tacz/guns/api/event/common/KubeJSGunEventPoster.java" +if kube.exists(): + kube.write_text('''package com.tacz.guns.api.event.common;\n\nimport net.neoforged.bus.api.Event;\n\npublic interface KubeJSGunEventPoster {\n default void postEventToKubeJS(E event) {}\n default void postClientEventToKubeJS(E event) {}\n default void postServerEventToKubeJS(E event) {}\n}\n''', encoding="utf-8") + +# Optional integration registry bridge. Preserve the zero-argument hooks used by +# core setup without linking excluded compatibility implementations. compat = root / "src/main/java/com/tacz/guns/init/CompatRegistry.java" if compat.exists(): - text = compat.read_text(encoding="utf-8") - # Discover the package/class and all public static void zero-arg methods, - # then preserve those signatures as no-ops to avoid touching callers. - methods = re.findall(r'public\s+static\s+void\s+(\w+)\s*\(\s*\)', text) - methods = list(dict.fromkeys(methods)) - body = ["package com.tacz.guns.init;", "", "/**", " * 26.2 port bridge. Optional mod integrations are registered after the core", " * migration is complete; keeping these hooks no-op preserves call sites.", " */", "public final class CompatRegistry {", " private CompatRegistry() {}"] + text = compat.read_text(encoding="utf-8", errors="ignore") + methods = list(dict.fromkeys(re.findall(r'public\s+static\s+void\s+(\w+)\s*\(\s*\)', text))) if not methods: methods = ["init"] + body = [ + "package com.tacz.guns.init;", "", + "public final class CompatRegistry {", " private CompatRegistry() {}" + ] for m in methods: - body += [f" public static void {m}() {{}}"] + body.append(f" public static void {m}() {{}}") body += ["}", ""] compat.write_text("\n".join(body), encoding="utf-8") -# Disable KubeJS service discovery while the package is excluded. for rel in ["src/main/resources/kubejs.plugins.txt"]: p = root / rel if p.exists(): p.write_text("", encoding="utf-8") -print("Applied TACZ NeoForge 26.2 migration pass 1") +print("Applied TACZ NeoForge 26.2 migration pass 2") From 8f106940d8270a69709bab2bd9ba741dce416767 Mon Sep 17 00:00:00 2001 From: Kestis Date: Wed, 19 Aug 2026 16:10:57 +0800 Subject: [PATCH 06/17] Broaden verified 26.2 source transplant and isolate optional integrations --- tools/tacz26_port.py | 231 +++++++++++++++++++++++-------------------- 1 file changed, 123 insertions(+), 108 deletions(-) diff --git a/tools/tacz26_port.py b/tools/tacz26_port.py index f24c30c..5f5b158 100644 --- a/tools/tacz26_port.py +++ b/tools/tacz26_port.py @@ -6,9 +6,8 @@ root = Path(sys.argv[1]).resolve() fabric = root.parent / "fabric26" +java_root = root / "src/main/java" -# Build against the official NeoForge 26.2 MDK baseline. Optional third-party -# integrations stay disabled until the core mod is source-compatible. build = r'''plugins { java id("net.neoforged.moddev") version "2.0.144" @@ -17,9 +16,7 @@ group = "com.tacz" version = "1.1.8-neoforge-26.2-port" -base { - archivesName.set("tacz-neoforge-26.2") -} +base { archivesName.set("tacz-neoforge-26.2") } java { toolchain.languageVersion = JavaLanguageVersion.of(25) @@ -28,38 +25,16 @@ repositories { mavenCentral() - maven("https://jitpack.io") { - content { includeGroup("com.github.FiguraMC.luaj") } - } + maven("https://jitpack.io") { content { includeGroup("com.github.FiguraMC.luaj") } } } neoForge { version = "26.2.0.59" - runs { - create("client") { - client() - gameDirectory = file("run/client") - } - create("server") { - server() - gameDirectory = file("run/server") - programArgument("--nogui") - } - } - - mods { - create("tacz") { sourceSet(sourceSets["main"]) } - } -} - -sourceSets { - main { - java { - // Integrations are optional and are re-enabled after the core port. - exclude("com/tacz/guns/compat/**") - } + create("client") { client(); gameDirectory = file("run/client") } + create("server") { server(); gameDirectory = file("run/server"); programArgument("--nogui") } } + mods { create("tacz") { sourceSet(sourceSets["main"]) } } } dependencies { @@ -72,87 +47,127 @@ tasks.withType().configureEach { options.encoding = "UTF-8" options.release.set(25) - options.compilerArgs.addAll(listOf("-Xmaxerrs", "2000", "-Xmaxwarns", "2000")) + options.compilerArgs.addAll(listOf("-Xmaxerrs", "3000", "-Xmaxwarns", "3000")) } ''' (root / "build.gradle.kts").write_text(build, encoding="utf-8") -wrapper = root / "gradle/wrapper/gradle-wrapper.properties" -wrapper.write_text("""distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-9.2.1-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n""", encoding="utf-8") - +(root / "gradle/wrapper/gradle-wrapper.properties").write_text( + "distributionBase=GRADLE_USER_HOME\n" + "distributionPath=wrapper/dists\n" + "distributionUrl=https\\://services.gradle.org/distributions/gradle-9.2.1-bin.zip\n" + "networkTimeout=10000\nvalidateDistributionUrl=true\n" + "zipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n", encoding="utf-8") libs = root / "gradle/libs.versions.toml" -if libs.exists(): - libs.rename(libs.with_suffix(libs.suffix + ".disabled")) - -# Pull verified vanilla/API migrations from the working 26.2 Fabric port, but -# only for files that are platform-neutral on both sides. This avoids guessing -# hundreds of Mojang 26.x rendering/data API changes while keeping NeoForge's -# registrations, networking and event wiring intact. +if libs.exists(): libs.rename(libs.with_suffix(libs.suffix + ".disabled")) + +def strip_env(text: str) -> str: + text = text.replace("import net.fabricmc.api.EnvType;\n", "") + text = text.replace("import net.fabricmc.api.Environment;\n", "") + text = re.sub(r"\s*@Environment\(EnvType\.(?:CLIENT|SERVER)\)\s*", "\n", text) + return text + +def imports_with_prefix(text: str, prefixes): + out=[] + for line in text.splitlines(): + s=line.strip() + if s.startswith("import "): + imp=s.removeprefix("import ").removesuffix(";") + if imp.startswith(prefixes): out.append(imp) + return out + +# Use the already-working 26.2 Fabric port as the source of truth for Mojang's +# 26.2 API changes, while retaining NeoForge-specific event/registry/network files. if fabric.exists(): - neo_java = root / "src/main/java" - fab_java = fabric / "src/main/java" - neo_files = {p.relative_to(neo_java).as_posix(): p for p in neo_java.rglob("*.java")} - fab_files = {p.relative_to(fab_java).as_posix(): p for p in fab_java.rglob("*.java")} - copied = 0 - for rel in sorted(set(neo_files) & set(fab_files)): - old = neo_files[rel].read_text(encoding="utf-8", errors="ignore") - new = fab_files[rel].read_text(encoding="utf-8", errors="ignore") - fabric_imports = [] - for line in new.splitlines(): - s = line.strip() - if s.startswith("import net.fabricmc") or s.startswith("import cn.sh1rocu"): - fabric_imports.append(s.removeprefix("import ").removesuffix(";")) - only_env_annotations = all(x in { - "net.fabricmc.api.EnvType", "net.fabricmc.api.Environment" - } for x in fabric_imports) - platform_area = any(seg in rel for seg in ( - "/init/", "/network/", "/event/", "/mixin/", "/compat/" - )) - if only_env_annotations and "net.neoforged" not in old and not platform_area: - new = new.replace("import net.fabricmc.api.EnvType;\n", "") - new = new.replace("import net.fabricmc.api.Environment;\n", "") - new = re.sub(r"\s*@Environment\(EnvType\.(?:CLIENT|SERVER)\)\s*", "\n", new) - neo_files[rel].write_text(new, encoding="utf-8") - copied += 1 - print(f"Copied {copied} platform-neutral 26.2 source files") - -# Mechanical Mojang 26.2 renames for the NeoForge-specific files retained from -# the 1.21.1 port. Identifier replaced ResourceLocation in 26.2; Util moved to -# net.minecraft.util; RenderType moved under renderer.rendertype. -for p in (root / "src/main/java").rglob("*.java"): - text = p.read_text(encoding="utf-8", errors="ignore") - text = text.replace("net.minecraft.resources.ResourceLocation", "net.minecraft.resources.Identifier") - text = re.sub(r"\bResourceLocation\b", "Identifier", text) - text = text.replace("import net.minecraft.Util;", "import net.minecraft.util.Util;") - text = text.replace("import net.minecraft.client.renderer.RenderType;", "import net.minecraft.client.renderer.rendertype.RenderType;") - p.write_text(text, encoding="utf-8") - -# KubeJS is optional. Keep the public event-poster surface but make it a no-op -# while the KubeJS compatibility package is intentionally excluded. -kube = root / "src/main/java/com/tacz/guns/api/event/common/KubeJSGunEventPoster.java" -if kube.exists(): - kube.write_text('''package com.tacz.guns.api.event.common;\n\nimport net.neoforged.bus.api.Event;\n\npublic interface KubeJSGunEventPoster {\n default void postEventToKubeJS(E event) {}\n default void postClientEventToKubeJS(E event) {}\n default void postServerEventToKubeJS(E event) {}\n}\n''', encoding="utf-8") - -# Optional integration registry bridge. Preserve the zero-argument hooks used by -# core setup without linking excluded compatibility implementations. -compat = root / "src/main/java/com/tacz/guns/init/CompatRegistry.java" + fab_root = fabric / "src/main/java" + neo = {p.relative_to(java_root).as_posix(): p for p in java_root.rglob("*.java")} + fab = {p.relative_to(fab_root).as_posix(): p for p in fab_root.rglob("*.java")} + copied_common=0 + for rel in sorted(set(neo) & set(fab)): + old=neo[rel].read_text(encoding="utf-8", errors="ignore") + new=fab[rel].read_text(encoding="utf-8", errors="ignore") + fi=imports_with_prefix(new,("net.fabricmc","cn.sh1rocu")) + ni=imports_with_prefix(old,("net.neoforged",)) + fabric_is_neutral=all(x in {"net.fabricmc.api.EnvType","net.fabricmc.api.Environment"} for x in fi) + neo_is_neutral=all(x in {"net.neoforged.api.distmarker.Dist","net.neoforged.api.distmarker.OnlyIn"} for x in ni) + if fabric_is_neutral and neo_is_neutral: + neo[rel].write_text(strip_env(new), encoding="utf-8") + copied_common += 1 + + # New 26.2 helper/render/data classes which do not exist in the 1.21.1 + # NeoForge tree. Only loader-neutral com.tacz classes are admitted. + copied_new=0 + allowed_prefixes=("java.","javax.","net.minecraft.","net.fabricmc.api.","com.tacz.", + "org.jetbrains.","org.joml.","com.mojang.","cn.sh1rocu.","com.google.", + "org.apache.","org.slf4j.","org.luaj.","org.lwjgl.","io.netty.","it.unimi.dsi.","static ") + for rel in sorted(set(fab)-set(neo)): + if not rel.startswith("com/tacz/guns/") or "/compat/" in rel or "/mixin/" in rel: + continue + text=fab[rel].read_text(encoding="utf-8", errors="ignore") + fi=imports_with_prefix(text,("net.fabricmc","cn.sh1rocu")) + if not all(x in {"net.fabricmc.api.EnvType","net.fabricmc.api.Environment"} for x in fi): + continue + bad=False + for line in text.splitlines(): + s=line.strip() + if not s.startswith("import "): continue + imp=s.removeprefix("import ").removesuffix(";") + if not imp.startswith(allowed_prefixes): + bad=True; break + if bad: continue + dst=java_root/rel + dst.parent.mkdir(parents=True,exist_ok=True) + dst.write_text(strip_env(text),encoding="utf-8") + copied_new += 1 + print(f"Copied {copied_common} common + {copied_new} new loader-neutral 26.2 Java files") + +# Optional integrations are not allowed to hold the core port hostage. Remove +# their old 1.21.1 implementations and optional mixins, then provide small 26.2 +# facades. Iris remains a real reflection-based compatibility bridge. +compat_dir=java_root/"com/tacz/guns/compat" +if compat_dir.exists(): shutil.rmtree(compat_dir) +for rel in [ + "com/tacz/guns/mixin/client/ar", "com/tacz/guns/mixin/client/iris", + "com/tacz/guns/mixin/carryon", "com/tacz/guns/mixin/compat" +]: + p=java_root/rel + if p.exists(): shutil.rmtree(p) + +def put(rel, text): + p=java_root/rel; p.parent.mkdir(parents=True,exist_ok=True); p.write_text(text,encoding="utf-8") + +put("com/tacz/guns/compat/ar/ARCompat.java", '''package com.tacz.guns.compat.ar;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport com.mojang.blaze3d.vertex.VertexConsumer;\npublic final class ARCompat { public static boolean LOADED=false; public static void init(){} public static boolean shouldAccelerate(){return false;} public static boolean isAccelerated(VertexConsumer v){return false;} public static void setRenderingLevel(){} public static void resetRenderingLevel(){} public static void setRenderLayer(int l){} public static void setRenderBeforeFunction(Runnable r){} public static void setRenderAfterFunction(Runnable r){} public static void resetRenderLayer(){} public static void resetRenderBeforeFunction(){} public static void resetRenderAfterFunction(){} public static void disableAcceleration(){} public static void resetAcceleration(){} public static void renderLaser(VertexConsumer v,float z,float w,boolean f,PoseStack p,int c){} }\n''') +put("com/tacz/guns/compat/controllable/ControllableCompat.java", '''package com.tacz.guns.compat.controllable;\nimport com.tacz.guns.api.item.gun.FireMode; import net.minecraft.world.item.ItemStack;\npublic final class ControllableCompat { public static void init(){} public static void onGunShoot(ItemStack s, FireMode m){} }\n''') +put("com/tacz/guns/compat/cloth/MenuIntegration.java", '''package com.tacz.guns.compat.cloth;\nimport net.minecraft.client.gui.screens.Screen;\npublic final class MenuIntegration { public static Screen getConfigScreen(Screen parent){ return null; } }\n''') +put("com/tacz/guns/compat/firstperson/FirstPersonAnimationCompat.java", '''package com.tacz.guns.compat.firstperson;\nimport net.minecraft.client.player.LocalPlayer; import net.minecraft.world.item.ItemStack;\npublic final class FirstPersonAnimationCompat { public static void init(){} public static ItemStack getMainRenderStack(LocalPlayer p){return p.getMainHandItem();} public static boolean isTaczViewmodel(ItemStack s){return true;} public static void beginDirectArmRender(){} public static void endDirectArmRender(){} }\n''') +put("com/tacz/guns/compat/immediatelyfast/ImmediatelyFastCompat.java", '''package com.tacz.guns.compat.immediatelyfast;\nimport net.minecraft.world.item.ItemStack; public final class ImmediatelyFastCompat { public static void init(){} public static void renderHotbarItem(ItemStack s,boolean pre){} public static boolean isInstalled(){return false;} }\n''') +put("com/tacz/guns/compat/shouldersurfing/ShoulderSurfingCompat.java", '''package com.tacz.guns.compat.shouldersurfing; public final class ShoulderSurfingCompat { public static void init(){} public static boolean showCrosshair(){return false;} public static boolean isInstalled(){return false;} }\n''') +put("com/tacz/guns/compat/zoomify/ZoomifyCompat.java", '''package com.tacz.guns.compat.zoomify; public final class ZoomifyCompat { public static void init(){} public static double getFov(double f,float t){return f;} }\n''') +put("com/tacz/guns/compat/playeranimator/PlayerAnimatorCompat.java", '''package com.tacz.guns.compat.playeranimator;\nimport com.tacz.guns.client.resource.GunDisplayInstance; import net.minecraft.world.entity.LivingEntity;\npublic final class PlayerAnimatorCompat { public static void init(){} public static boolean isInstalled(){return false;} public static boolean hasPlayerAnimator3rd(LivingEntity e,GunDisplayInstance d){return false;} public static void stopAllAnimation(LivingEntity e){} public static void stopAllAnimation(LivingEntity e,int f){} public static void playAnimation(LivingEntity e,GunDisplayInstance d,float l){} public static void registerReloadListener(Object o){} }\n''') +put("com/tacz/guns/compat/carryon/CarryOnReflection.java", '''package com.tacz.guns.compat.carryon;\nimport net.minecraft.core.BlockPos; import net.minecraft.core.HolderLookup; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState;\npublic final class CarryOnReflection { public static BlockState getCarriedBlock(Player p){return null;} public static BlockEntity getCarriedBlockEntity(Player p,BlockPos b,HolderLookup.Provider h){return null;} }\n''') + +put("com/tacz/guns/compat/iris/IrisCompat.java", '''package com.tacz.guns.compat.iris;\nimport com.mojang.blaze3d.pipeline.RenderPipeline; import net.minecraft.client.renderer.SubmitNodeCollector; import net.neoforged.fml.ModList;\npublic final class IrisCompat {\n private static boolean iris(){ try{return ModList.get().isLoaded("iris");}catch(Throwable t){return false;} }\n public static void initCompat(){} public static boolean isRenderShadow(){ if(!iris())return false; try{Class c=Class.forName("net.irisshaders.iris.shadows.ShadowRenderingState"); return (Boolean)c.getMethod("areShadowsCurrentlyBeingRendered").invoke(null);}catch(Throwable t){return false;} }\n public static boolean isUsingRenderPack(){ if(!iris())return false; try{Class c=Class.forName("net.irisshaders.iris.api.v0.IrisApi"); Object i=c.getMethod("getInstance").invoke(null); return (Boolean)c.getMethod("isShaderPackInUse").invoke(i);}catch(Throwable t){return false;} }\n public static boolean assignScopePipelineToHand(RenderPipeline p,String n){ if(!iris())return false; try{Class a=Class.forName("net.irisshaders.iris.api.v0.IrisApi"); Object i=a.getMethod("getInstance").invoke(null); Class pr=Class.forName("net.irisshaders.iris.api.v0.IrisProgram"); Object hand=Enum.valueOf((Class)pr.asSubclass(Enum.class),"HAND"); a.getMethod("assignPipeline",RenderPipeline.class,pr).invoke(i,p,hand); return true;}catch(Throwable t){return false;} }\n public static void assignCommonEntityPipelinesToHandIfNeeded(){} public static boolean shouldDisableScopeMaskUnderShaderPack(){return false;}\n public static boolean isHandRendererActive(){ if(!isUsingRenderPack())return false; try{Class c=Class.forName("net.irisshaders.iris.pathways.HandRenderer"); Object i=c.getField("INSTANCE").get(null); return (Boolean)c.getMethod("isActive").invoke(i);}catch(Throwable t){return false;} }\n public static boolean endBatch(Object o){return false;} public static boolean endBatch(SubmitNodeCollector c){return false;}\n}\n''') + +# Mojang 26.2 renames for retained NeoForge-specific code. +for p in java_root.rglob("*.java"): + text=p.read_text(encoding="utf-8",errors="ignore") + text=text.replace("net.minecraft.resources.ResourceLocation","net.minecraft.resources.Identifier") + text=re.sub(r"\bResourceLocation\b","Identifier",text) + text=text.replace("import net.minecraft.Util;","import net.minecraft.util.Util;") + text=text.replace("import net.minecraft.client.renderer.RenderType;","import net.minecraft.client.renderer.rendertype.RenderType;") + text=text.replace("import net.minecraft.client.renderer.LightTexture;","import net.minecraft.client.renderer.Lightmap;") + text=re.sub(r"\bLightTexture\b","Lightmap",text) + p.write_text(text,encoding="utf-8") + +# KubeJS optional bridge. +kube=java_root/"com/tacz/guns/api/event/common/KubeJSGunEventPoster.java" +if kube.exists(): kube.write_text('''package com.tacz.guns.api.event.common;\nimport net.neoforged.bus.api.Event; public interface KubeJSGunEventPoster{ default void postEventToKubeJS(E e){} default void postClientEventToKubeJS(E e){} default void postServerEventToKubeJS(E e){} }\n''',encoding="utf-8") + +# Keep only stable public constants/hooks from the old integration registry. +compat=java_root/"com/tacz/guns/init/CompatRegistry.java" if compat.exists(): - text = compat.read_text(encoding="utf-8", errors="ignore") - methods = list(dict.fromkeys(re.findall(r'public\s+static\s+void\s+(\w+)\s*\(\s*\)', text))) - if not methods: - methods = ["init"] - body = [ - "package com.tacz.guns.init;", "", - "public final class CompatRegistry {", " private CompatRegistry() {}" - ] - for m in methods: - body.append(f" public static void {m}() {{}}") - body += ["}", ""] - compat.write_text("\n".join(body), encoding="utf-8") - -for rel in ["src/main/resources/kubejs.plugins.txt"]: - p = root / rel - if p.exists(): - p.write_text("", encoding="utf-8") - -print("Applied TACZ NeoForge 26.2 migration pass 2") + compat.write_text('''package com.tacz.guns.init; public final class CompatRegistry { public static final String IRIS="iris"; private CompatRegistry(){} public static void init(){} public static void initClient(){} }\n''',encoding="utf-8") + +p=root/"src/main/resources/kubejs.plugins.txt" +if p.exists(): p.write_text("",encoding="utf-8") +print("Applied TACZ NeoForge 26.2 migration pass 3") From 0bbd35475d36d72ba586c112782648d4b4102830 Mon Sep 17 00:00:00 2001 From: Kestis Date: Wed, 19 Aug 2026 16:18:31 +0800 Subject: [PATCH 07/17] Speed up TACZ 26.2 compatibility CI iterations --- .github/workflows/tacz-26-2-port.yml | 60 +++++++--------------------- 1 file changed, 15 insertions(+), 45 deletions(-) diff --git a/.github/workflows/tacz-26-2-port.yml b/.github/workflows/tacz-26-2-port.yml index b50a5fb..56efc42 100644 --- a/.github/workflows/tacz-26-2-port.yml +++ b/.github/workflows/tacz-26-2-port.yml @@ -2,11 +2,9 @@ name: TACZ 26.2 Port CI on: push: - branches: - - ci/tacz-neoforge-26.2-port + branches: [ci/tacz-neoforge-26.2-port] pull_request: - branches: - - main + branches: [main] workflow_dispatch: permissions: @@ -17,58 +15,37 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 steps: - - name: Checkout CI harness - uses: actions/checkout@v4 - - - name: Set up Java 25 - uses: actions/setup-java@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 with: distribution: temurin java-version: '25' - - name: Clone port bases run: | git clone --depth 1 https://github.com/RaeYNCreations/TACZ-1.21.1-neoforge-1.21.1.git work git clone --depth 1 --branch '26.2(main)' https://github.com/q14433686-arch/TaCZ_Refabricated_Unofficial.git fabric26 - (cd work && git rev-parse HEAD) | tee base-commit.txt - (cd fabric26 && git rev-parse HEAD) | tee fabric26-commit.txt - - - name: Snapshot 26.2 reference source - run: | - zip -qr TACZ-Fabric-26.2-reference-source.zip fabric26 \ - -x 'fabric26/.git/*' 'fabric26/.gradle/*' 'fabric26/build/*' 'fabric26/run/*' - - - name: Apply 26.2 migration pass + (cd work && git rev-parse HEAD) > base-commit.txt + (cd fabric26 && git rev-parse HEAD) > fabric26-commit.txt + - name: Apply migration run: python3 tools/tacz26_port.py work - - - name: Compile - id: compile + - name: Compile or build shell: bash run: | set +e cd work chmod +x gradlew - ./gradlew compileJava --stacktrace --no-daemon 2>&1 | tee ../compile.log + task=compileJava + if [ "${TACZ_FULL_BUILD:-0}" = "1" ]; then task=build; fi + ./gradlew "$task" --stacktrace --no-daemon 2>&1 | tee ../compile.log status=${PIPESTATUS[0]} echo "$status" > ../compile-status.txt exit 0 - - - name: Save source diff - if: always() - run: | - cd work - git diff -- . ':!gradle/wrapper/gradle-wrapper.jar' > ../port.diff || true - cd .. - zip -qr TACZ-26.2-port-source.zip work \ - -x 'work/.git/*' 'work/.gradle/*' 'work/build/*' 'work/run/*' - - - name: Copy JARs if produced + - name: Collect outputs if: always() run: | mkdir -p out find work/build/libs -maxdepth 1 -type f -name '*.jar' -exec cp {} out/ \; 2>/dev/null || true - - - name: Upload diagnostics and build + - name: Upload diagnostics if: always() uses: actions/upload-artifact@v4 with: @@ -79,16 +56,9 @@ jobs: compile-status.txt base-commit.txt fabric26-commit.txt - port.diff - TACZ-26.2-port-source.zip - TACZ-Fabric-26.2-reference-source.zip out/*.jar - - - name: Fail if compilation failed + - name: Enforce result if: always() run: | status=$(cat compile-status.txt 2>/dev/null || echo 1) - if [ "$status" != "0" ]; then - echo "Compilation failed with status $status" - exit "$status" - fi + test "$status" = "0" From f14dad8cfc20c8cda83ec687f431e68b0308654b Mon Sep 17 00:00:00 2001 From: Kestis Date: Wed, 19 Aug 2026 16:24:13 +0800 Subject: [PATCH 08/17] Add targeted TaCZ 26.2 NeoForge migration pass 4 --- tools/tacz26_pass4.py | 157 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 tools/tacz26_pass4.py diff --git a/tools/tacz26_pass4.py b/tools/tacz26_pass4.py new file mode 100644 index 0000000..2dcf666 --- /dev/null +++ b/tools/tacz26_pass4.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +from pathlib import Path +import json +import re +import sys + +root = Path(sys.argv[1]).resolve() +fabric = root.parent / "fabric26" +java = root / "src/main/java" +res = root / "src/main/resources" +fabjava = fabric / "src/main/java" + + +def copy_fabric(rel: str) -> Path: + src = fabjava / rel + dst = java / rel + if not src.exists(): + raise FileNotFoundError(src) + dst.parent.mkdir(parents=True, exist_ok=True) + text = src.read_text(encoding="utf-8", errors="ignore") + text = text.replace("import net.fabricmc.api.EnvType;\n", "") + text = text.replace("import net.fabricmc.api.Environment;\n", "") + text = re.sub(r"\s*@Environment\(EnvType\.(?:CLIENT|SERVER)\)\s*", "\n", text) + dst.write_text(text, encoding="utf-8") + return dst + + +def replace_file(path: Path, pairs): + text = path.read_text(encoding="utf-8", errors="ignore") + for a, b in pairs: + text = text.replace(a, b) + path.write_text(text, encoding="utf-8") + + +# --------------------------------------------------------------------------- +# 26.2 GUI migration. The 26.2 Fabric source already contains the vanilla GUI +# rewrite. Only the transport and Screen accessor are loader-specific. +# --------------------------------------------------------------------------- +for rel in [ + "com/tacz/guns/client/gui/GunSmithTableScreen.java", + "com/tacz/guns/client/gui/GunRefitScreen.java", +]: + p = copy_fabric(rel) + t = p.read_text(encoding="utf-8") + t = t.replace("import cn.sh1rocu.tacz.mixin.accessor.ScreenAccessor;", + "import com.tacz.guns.mixin.client.ScreenAccessor;") + t = t.replace("import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;", + "import net.neoforged.neoforge.network.PacketDistributor;") + t = t.replace("ClientPlayNetworking.send(", "PacketDistributor.sendToServer(") + p.write_text(t, encoding="utf-8") + +# Local equivalent of the Fabric accessor, registered in the existing TACZ +# Mixin config so no Fabric helper package is needed at runtime. +accessor = java / "com/tacz/guns/mixin/client/ScreenAccessor.java" +accessor.parent.mkdir(parents=True, exist_ok=True) +accessor.write_text('''package com.tacz.guns.mixin.client;\n\nimport net.minecraft.client.gui.components.Renderable;\nimport net.minecraft.client.gui.screens.Screen;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.gen.Accessor;\nimport java.util.List;\n\n@Mixin(Screen.class)\npublic interface ScreenAccessor {\n @Accessor("renderables")\n List tacz$getRenderables();\n}\n''', encoding="utf-8") + +mixins = res / "tacz.mixins.json" +if mixins.exists(): + data = json.loads(mixins.read_text(encoding="utf-8")) + data["compatibilityLevel"] = "JAVA_25" + client = data.setdefault("client", []) + if "client.ScreenAccessor" not in client: + client.append("client.ScreenAccessor") + mixins.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") + +# --------------------------------------------------------------------------- +# Client configs added by the 26.2 port. NeoForge uses ModConfigSpec directly. +# --------------------------------------------------------------------------- +for rel in [ + "com/tacz/guns/config/client/SoundConfig.java", + "com/tacz/guns/config/client/ResourceConfig.java", +]: + p = copy_fabric(rel) + replace_file(p, [ + ("net.minecraftforge.common.ForgeConfigSpec", "net.neoforged.neoforge.common.ModConfigSpec"), + ("ForgeConfigSpec", "ModConfigSpec"), + ]) + +client_config = java / "com/tacz/guns/config/ClientConfig.java" +if client_config.exists(): + text = client_config.read_text(encoding="utf-8") + if "ResourceConfig.init(builder);" not in text: + text = text.replace("RenderConfig.init(builder);", + "RenderConfig.init(builder);\n ResourceConfig.init(builder);\n SoundConfig.init(builder);") + if "import com.tacz.guns.config.client.*;" not in text: + text = text.replace("import com.tacz.guns.config.client.KeyConfig;\nimport com.tacz.guns.config.client.RenderConfig;\nimport com.tacz.guns.config.client.ZoomConfig;", + "import com.tacz.guns.config.client.*;") + client_config.write_text(text, encoding="utf-8") + +sound_mgr = java / "com/tacz/guns/client/sound/SoundPlayManager.java" +if sound_mgr.exists(): + text = sound_mgr.read_text(encoding="utf-8") + text = re.sub(r"ModSounds\.GUN(?!\.get\(\))", "ModSounds.GUN.get()", text) + sound_mgr.write_text(text, encoding="utf-8") + +# --------------------------------------------------------------------------- +# Minecart target: take Mojang 26.2 behavior from the working Fabric port, then +# restore NeoForge registrations and event bus semantics. +# --------------------------------------------------------------------------- +target = copy_fabric("com/tacz/guns/entity/TargetMinecart.java") +text = target.read_text(encoding="utf-8") +text = text.replace("import cn.sh1rocu.tacz.api.LogicalSide;", "import net.neoforged.fml.LogicalSide;\nimport net.neoforged.neoforge.common.NeoForge;") +text = text.replace("import cn.sh1rocu.tacz.api.extension.IMinecart;\n", "") +text = text.replace("implements ITargetEntity, IMinecart", "implements ITargetEntity") +text = text.replace("ModSounds.TARGET_HIT,", "ModSounds.TARGET_HIT.get(),") +text = text.replace("new ItemStack(ModItems.TARGET_MINECART)", "new ItemStack(ModItems.TARGET_MINECART.get())") +text = text.replace("return ModItems.TARGET_MINECART;", "return ModItems.TARGET_MINECART.get();") +text = text.replace("return ModBlocks.TARGET.defaultBlockState();", "return ModBlocks.TARGET.get().defaultBlockState();") +text = text.replace("EntityHurtByGunEvent.Post event = new EntityHurtByGunEvent.Post(projectile, this, player, projectile.getGunId(), projectile.getGunDisplayId(), damage, Pair.of(source, source), isHeadshot, headshotMultiplier, LogicalSide.SERVER);\n EntityHurtByGunEvent.POST.invoker().post(event);", + "EntityHurtByGunEvent.Post event = new EntityHurtByGunEvent.Post(projectile, this, player, projectile.getGunId(), projectile.getGunDisplayId(), damage, Pair.of(source, source), isHeadshot, headshotMultiplier, LogicalSide.SERVER);\n NeoForge.EVENT_BUS.post(event);") +# Fabric's IMinecart mixin supplies this extension point. NeoForge does not need +# the shim for compilation; remove the method rather than pretending it overrides +# a vanilla API. +text = re.sub(r"\n\s*@Override\n\s*public boolean tacz\$canBeRidden\(\) \{\n\s*return false;\n\s*\}\n", "\n", text) +target.write_text(text, encoding="utf-8") + +# --------------------------------------------------------------------------- +# Reconstruct 1.21's interpolatable walk distance on 26.2. This is a tiny, +# loader-neutral interface + NeoForge mixin, copied from the proven Fabric port. +# --------------------------------------------------------------------------- +copy_fabric("cn/sh1rocu/tacz/api/extension/IMoveDistTracker.java") +ctx = copy_fabric("com/tacz/guns/client/animation/statemachine/GunAnimationStateContext.java") +move_mixin = java / "com/tacz/guns/mixin/common/EntityMoveDistMixin.java" +move_mixin.parent.mkdir(parents=True, exist_ok=True) +move_mixin.write_text('''package com.tacz.guns.mixin.common;\n\nimport cn.sh1rocu.tacz.api.extension.IMoveDistTracker;\nimport net.minecraft.world.entity.Entity;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.Unique;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfo;\n\n@Mixin(Entity.class)\npublic abstract class EntityMoveDistMixin implements IMoveDistTracker {\n @Unique private float tacz$moveDistO;\n @Unique private boolean tacz$moveDistInit;\n @Unique @Override public float tacz$getMoveDistO() {\n return this.tacz$moveDistInit ? this.tacz$moveDistO : ((Entity)(Object)this).moveDist;\n }\n @Inject(method = "tick", at = @At("HEAD"))\n private void tacz$captureMoveDistO(CallbackInfo ci) {\n this.tacz$moveDistO = ((Entity)(Object)this).moveDist;\n this.tacz$moveDistInit = true;\n }\n}\n''', encoding="utf-8") +if mixins.exists(): + data = json.loads(mixins.read_text(encoding="utf-8")) + common = data.setdefault("mixins", []) + if "common.EntityMoveDistMixin" not in common: + common.append("common.EntityMoveDistMixin") + mixins.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") + +# --------------------------------------------------------------------------- +# 26.2 item rendering no longer uses the old BEWLR/MultiBufferSource path. Use +# the tested 26.2 ItemModel/SpecialModelRenderer bridge from the working port. +# The registry helper itself is loader-neutral despite its historical package. +# --------------------------------------------------------------------------- +for rel in [ + "cn/sh1rocu/tacz/compat/fabric/BuiltinItemRendererRegistry.java", + "com/tacz/guns/client/renderer/item/TaczDynamicItemModel.java", + "com/tacz/guns/client/renderer/item/AttachmentItemRenderer.java", + "com/tacz/guns/client/renderer/item/AmmoItemRenderer.java", + "com/tacz/guns/client/renderer/item/GunSmithTableItemRenderer.java", +]: + copy_fabric(rel) + +# PlayerAnimator is optional in the core build, but ClientSetupEvent passes a +# method reference. Preserve the exact Consumer target type so javac can infer it. +pa = java / "com/tacz/guns/compat/playeranimator/PlayerAnimatorCompat.java" +if pa.exists(): + text = pa.read_text(encoding="utf-8") + text = text.replace("public static void registerReloadListener(Object o){}", + "public static void registerReloadListener(java.util.function.Consumer register){}") + pa.write_text(text, encoding="utf-8") + +print("Applied TACZ NeoForge 26.2 targeted migration pass 4") From 2b53aa2369a9ec811f6e11d0ccbb0fdf26d9c543 Mon Sep 17 00:00:00 2001 From: Kestis Date: Wed, 19 Aug 2026 16:24:32 +0800 Subject: [PATCH 09/17] Run targeted TaCZ 26.2 migration pass 4 --- .github/workflows/tacz-26-2-port.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tacz-26-2-port.yml b/.github/workflows/tacz-26-2-port.yml index 56efc42..021c476 100644 --- a/.github/workflows/tacz-26-2-port.yml +++ b/.github/workflows/tacz-26-2-port.yml @@ -27,7 +27,9 @@ jobs: (cd work && git rev-parse HEAD) > base-commit.txt (cd fabric26 && git rev-parse HEAD) > fabric26-commit.txt - name: Apply migration - run: python3 tools/tacz26_port.py work + run: | + python3 tools/tacz26_port.py work + python3 tools/tacz26_pass4.py work - name: Compile or build shell: bash run: | From 99cc9e14e9341fcbf66d7cdb4c1565a5db33803a Mon Sep 17 00:00:00 2001 From: Kestis Date: Wed, 19 Aug 2026 16:29:57 +0800 Subject: [PATCH 10/17] Add broad loader-neutral 26.2 source refresh and core item/entity port --- tools/tacz26_pass5.py | 120 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 tools/tacz26_pass5.py diff --git a/tools/tacz26_pass5.py b/tools/tacz26_pass5.py new file mode 100644 index 0000000..fe45f78 --- /dev/null +++ b/tools/tacz26_pass5.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +from pathlib import Path +import re +import sys + +root = Path(sys.argv[1]).resolve() +fabric = root.parent / "fabric26" +java = root / "src/main/java" +fabjava = fabric / "src/main/java" + + +def clean_env(text: str) -> str: + text = text.replace("import net.fabricmc.api.EnvType;\n", "") + text = text.replace("import net.fabricmc.api.Environment;\n", "") + return re.sub(r"\s*@Environment\(EnvType\.(?:CLIENT|SERVER)\)\s*", "\n", text) + + +def copy_rel(rel: str) -> Path: + src=fabjava/rel; dst=java/rel + dst.parent.mkdir(parents=True, exist_ok=True) + dst.write_text(clean_env(src.read_text(encoding='utf-8',errors='ignore')),encoding='utf-8') + return dst + +# Pass 3 was intentionally conservative and retained a 1.21.1 file whenever the +# NeoForge version itself used NeoForge APIs. For 26.2 that leaves many files on +# obsolete Mojang rendering/world APIs. If the 26.2 reference version is truly +# loader-neutral, it is safe to prefer it regardless of what the old file used. +common_neo={p.relative_to(java).as_posix():p for p in java.rglob('*.java')} +common_fab={p.relative_to(fabjava).as_posix():p for p in fabjava.rglob('*.java')} +copied=0 +for rel in sorted(set(common_neo)&set(common_fab)): + text=common_fab[rel].read_text(encoding='utf-8',errors='ignore') + bad=False + for line in text.splitlines(): + s=line.strip() + if not s.startswith('import '): continue + imp=s.removeprefix('import ').removesuffix(';') + if imp.startswith(('net.fabricmc.fabric.api','net.fabricmc.loader.api','cn.sh1rocu.')): + bad=True; break + if not bad: + common_neo[rel].write_text(clean_env(text),encoding='utf-8') + copied+=1 +print(f'Pass5: refreshed {copied} loader-neutral files from the MC26.2 source') + +# 26.2 inventory abstraction used by the working port is local, small and has no +# Fabric runtime dependency. Bring it over so gun ammo inventory logic no longer +# depends on the removed 1.21 NeoForge capability surface. +for src in (fabjava/'cn/sh1rocu/tacz/util/itemhandler').rglob('*.java'): + rel=src.relative_to(fabjava).as_posix() + copy_rel(rel) +copy_rel('cn/sh1rocu/tacz/api/extension/IItem.java') + +# Dynamic-renderer item implementations. Their only loader-looking imports are +# the local registry and IItem bridge already provided by pass4/pass5. +for rel in [ + 'com/tacz/guns/api/item/gun/AbstractGunItem.java', + 'com/tacz/guns/item/AttachmentItem.java', + 'com/tacz/guns/item/AmmoItem.java', + 'com/tacz/guns/item/GunSmithTableItem.java', +]: + copy_rel(rel) + +# --------------------------------------------------------------------------- +# Kinetic bullet: use the validated Mojang 26.2 implementation, retaining +# NeoForge's native complex-spawn packet and event bus. NeoForge still supplies +# Entity#getPersistentData(), so the temporary Fabric entity-data shim is not +# needed here. +# --------------------------------------------------------------------------- +p=copy_rel('com/tacz/guns/entity/EntityKineticBullet.java') +t=p.read_text(encoding='utf-8') +t=t.replace('import cn.sh1rocu.tacz.api.LogicalSide;', 'import net.neoforged.fml.LogicalSide;') +t=t.replace('import cn.sh1rocu.tacz.api.extension.IEntityAdditionalSpawnData;\n', 'import net.neoforged.neoforge.entity.IEntityWithComplexSpawn;\n') +t=t.replace('import cn.sh1rocu.tacz.api.extension.IEntityPersistentData;\n', 'import net.neoforged.neoforge.common.NeoForge;\n') +t=t.replace('import net.minecraft.network.FriendlyByteBuf;', 'import net.minecraft.network.RegistryFriendlyByteBuf;') +t=t.replace('import net.minecraft.network.protocol.Packet;\n', '') +t=t.replace('import net.minecraft.network.protocol.game.ClientGamePacketListener;\n', '') +t=t.replace('import net.minecraft.server.level.ServerEntity;\n', '') +t=t.replace('implements IEntityAdditionalSpawnData', 'implements IEntityWithComplexSpawn') +# NeoForge's spawn extension generates the advanced spawn payload itself. +t=re.sub(r'\n\s*@Override\n\s*public @NotNull Packet getAddEntityPacket\(ServerEntity entity\) \{\n\s*return IEntityAdditionalSpawnData\.getEntitySpawningPacket\(this\);\n\s*\}\n', '\n', t) +t=t.replace('writeSpawnData(FriendlyByteBuf buffer)', 'writeSpawnData(RegistryFriendlyByteBuf buffer)') +t=t.replace('readSpawnData(FriendlyByteBuf additionalData)', 'readSpawnData(RegistryFriendlyByteBuf additionalData)') +t=t.replace('((IEntityPersistentData) this).tacz$getPersistentData()', 'this.getPersistentData()') +# Restore the cancellable NeoForge event semantics from the native port. +t=t.replace('EntityHurtByGunEvent.PRE.invoker().post(preEvent);', 'if (NeoForge.EVENT_BUS.post(preEvent).isCanceled()) { return; }') +t=t.replace('EntityKillByGunEvent.CALLBACK.invoker().post(killByGunEvent);', 'NeoForge.EVENT_BUS.post(killByGunEvent);') +t=t.replace('EntityHurtByGunEvent.POST.invoker().post(hurtByGunEvent);', 'NeoForge.EVENT_BUS.post(hurtByGunEvent);') +t=t.replace('AmmoHitBlockEvent.CALLBACK.invoker().post(ammoHitBlockEvent);', 'if (NeoForge.EVENT_BUS.post(ammoHitBlockEvent).isCanceled()) { return; }') +p.write_text(t,encoding='utf-8') + +# --------------------------------------------------------------------------- +# 26.2 NeoForge DeferredRegister factories now inject registry-aware properties. +# Use registerBlock/registerItem for classes whose constructors take Properties. +# --------------------------------------------------------------------------- +blocks=java/'com/tacz/guns/init/ModBlocks.java' +if blocks.exists(): + t=blocks.read_text(encoding='utf-8') + for name,ctor in [ + ('gun_smith_table','GunSmithTableBlockB'),('workbench_a','GunSmithTableBlockA'), + ('workbench_b','GunSmithTableBlockB'),('workbench_c','GunSmithTableBlockC'), + ('target','TargetBlock'),('statue','StatueBlock')]: + t=t.replace(f'BLOCKS.register("{name}", {ctor}::new)', f'BLOCKS.registerBlock("{name}", {ctor}::new)') + blocks.write_text(t,encoding='utf-8') + +items=java/'com/tacz/guns/init/ModItems.java' +if items.exists(): + t=items.read_text(encoding='utf-8') + t=t.replace('ITEMS.register("modern_kinetic_gun", ModernKineticGunItem::new)', 'ITEMS.registerItem("modern_kinetic_gun", ModernKineticGunItem::new)') + t=t.replace('ITEMS.register("target_minecart", TargetMinecartItem::new)', 'ITEMS.registerItem("target_minecart", TargetMinecartItem::new)') + t=t.replace('ITEMS.register("gun_smith_table", () -> new DefaultTableItem(ModBlocks.GUN_SMITH_TABLE.get()))', 'ITEMS.registerItem("gun_smith_table", props -> new DefaultTableItem(ModBlocks.GUN_SMITH_TABLE.get(), props))') + items.write_text(t,encoding='utf-8') + +# 26.2 EventBusSubscriber no longer selects a separate MOD bus in the annotation. +for p in java.rglob('*.java'): + t=p.read_text(encoding='utf-8',errors='ignore') + t=t.replace('@EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD, ', '@EventBusSubscriber(') + t=t.replace('@EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD)', '@EventBusSubscriber') + p.write_text(t,encoding='utf-8') + +print('Applied TACZ NeoForge 26.2 migration pass 5') From d0225cb9db4cc22fd349eaa873edb6c9cdcf6529 Mon Sep 17 00:00:00 2001 From: Kestis Date: Wed, 19 Aug 2026 16:30:14 +0800 Subject: [PATCH 11/17] Run TaCZ 26.2 migration pass 5 --- .github/workflows/tacz-26-2-port.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tacz-26-2-port.yml b/.github/workflows/tacz-26-2-port.yml index 021c476..7d83ff9 100644 --- a/.github/workflows/tacz-26-2-port.yml +++ b/.github/workflows/tacz-26-2-port.yml @@ -30,6 +30,7 @@ jobs: run: | python3 tools/tacz26_port.py work python3 tools/tacz26_pass4.py work + python3 tools/tacz26_pass5.py work - name: Compile or build shell: bash run: | From 523c805052ca391a6e637f3cb770daa5aba092fd Mon Sep 17 00:00:00 2001 From: Kestis Date: Wed, 19 Aug 2026 16:31:12 +0800 Subject: [PATCH 12/17] Cancel obsolete TaCZ port CI runs automatically --- .github/workflows/tacz-26-2-port.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/tacz-26-2-port.yml b/.github/workflows/tacz-26-2-port.yml index 7d83ff9..d0e179a 100644 --- a/.github/workflows/tacz-26-2-port.yml +++ b/.github/workflows/tacz-26-2-port.yml @@ -7,6 +7,10 @@ on: branches: [main] workflow_dispatch: +concurrency: + group: tacz26-${{ github.ref }} + cancel-in-progress: true + permissions: contents: read From 7994b7be109f9ab790911552aafca0eca82ada5b Mon Sep 17 00:00:00 2001 From: Kestis Date: Wed, 19 Aug 2026 16:40:43 +0800 Subject: [PATCH 13/17] Add TaCZ 26.2 NeoForge pass 6: restore loader boundaries and port input/HUD --- tools/tacz26_pass6.py | 155 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 tools/tacz26_pass6.py diff --git a/tools/tacz26_pass6.py b/tools/tacz26_pass6.py new file mode 100644 index 0000000..ae60f44 --- /dev/null +++ b/tools/tacz26_pass6.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +from pathlib import Path +import json +import re +import shutil +import subprocess +import sys + +root = Path(sys.argv[1]).resolve() +fabric = root.parent / "fabric26" +java = root / "src/main/java" +fabjava = fabric / "src/main/java" +res = root / "src/main/resources" + + +def git_restore(rel: str): + subprocess.run(["git", "-C", str(root), "checkout", "HEAD", "--", rel], check=True) + + +def clean_env(text: str) -> str: + text = text.replace("import net.fabricmc.api.EnvType;\n", "") + text = text.replace("import net.fabricmc.api.Environment;\n", "") + return re.sub(r"\s*@Environment\(EnvType\.(?:CLIENT|SERVER)\)\s*", "\n", text) + + +def copy_fabric(rel: str) -> Path: + src = fabjava / rel + dst = java / rel + dst.parent.mkdir(parents=True, exist_ok=True) + dst.write_text(clean_env(src.read_text(encoding="utf-8", errors="ignore")), encoding="utf-8") + return dst + + +def modernize(text: str) -> str: + text = text.replace("net.minecraft.resources.ResourceLocation", "net.minecraft.resources.Identifier") + text = re.sub(r"\bResourceLocation\b", "Identifier", text) + text = text.replace("import net.minecraft.Util;", "import net.minecraft.util.Util;") + text = text.replace("import net.minecraft.client.renderer.RenderType;", "import net.minecraft.client.renderer.rendertype.RenderType;") + text = text.replace("import net.minecraft.client.renderer.LightTexture;", "import net.minecraft.client.renderer.Lightmap;") + text = re.sub(r"\bLightTexture\b", "Lightmap", text) + text = re.sub(r"@EventBusSubscriber\(([^)]*?)\s*,?\s*bus\s*=\s*EventBusSubscriber\.Bus\.MOD\s*,?\s*([^)]*?)\)", lambda m: "@EventBusSubscriber(" + ", ".join(x.strip(" ,") for x in [m.group(1), m.group(2)] if x.strip(" ,")) + ")", text) + text = text.replace("@EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD)", "@EventBusSubscriber") + return text + +# Pass5 deliberately tried a broad 26.2 source transplant. A number of loader +# boundary files have identical-looking Java but completely different lifecycle +# semantics. Reset those boundaries to the known NeoForge port, then migrate +# them explicitly below. +for rel in [ + "src/main/java/com/tacz/guns/init", + "src/main/java/com/tacz/guns/client/init", + "src/main/java/com/tacz/guns/network", + "src/main/java/com/tacz/guns/config", + "src/main/java/com/tacz/guns/api/item/gun/AbstractGunItem.java", + "src/main/java/com/tacz/guns/item/AttachmentItem.java", + "src/main/java/com/tacz/guns/item/AmmoItem.java", + "src/main/java/com/tacz/guns/item/GunSmithTableItem.java", +]: + git_restore(rel) + +# Remove the experimental Fabric-side inventory abstraction brought in by pass5. +# The 1.21 NeoForge gun/item core already compiled much further without it. +for rel in [ + "cn/sh1rocu/tacz/util/itemhandler", + "cn/sh1rocu/tacz/api/extension/IItem.java", +]: + p = java / rel + if p.is_dir(): shutil.rmtree(p) + elif p.exists(): p.unlink() + +# Modernize the restored loader-boundary files for Mojang/NeoForge 26.2 names. +for rel in ["com/tacz/guns/init", "com/tacz/guns/client/init", "com/tacz/guns/network", "com/tacz/guns/config"]: + for p in (java / rel).rglob("*.java"): + p.write_text(modernize(p.read_text(encoding="utf-8", errors="ignore")), encoding="utf-8") + +# --------------------------------------------------------------------------- +# Native NeoForge deferred registration. In 26.2 Block/Item constructors receive +# registry-aware Properties, so use the specialized registerBlock/registerItem +# factories instead of handing those constructors an Identifier. +# --------------------------------------------------------------------------- +blocks = java / "com/tacz/guns/init/ModBlocks.java" +t = blocks.read_text(encoding="utf-8") +for name, ctor in [ + ("gun_smith_table", "GunSmithTableBlockB"), ("workbench_a", "GunSmithTableBlockA"), + ("workbench_b", "GunSmithTableBlockB"), ("workbench_c", "GunSmithTableBlockC"), + ("target", "TargetBlock"), ("statue", "StatueBlock") +]: + t = t.replace(f'BLOCKS.register("{name}", {ctor}::new)', f'BLOCKS.registerBlock("{name}", {ctor}::new)') +blocks.write_text(t, encoding="utf-8") + +items = java / "com/tacz/guns/init/ModItems.java" +t = items.read_text(encoding="utf-8") +t = t.replace('ITEMS.register("modern_kinetic_gun", ModernKineticGunItem::new)', 'ITEMS.registerItem("modern_kinetic_gun", ModernKineticGunItem::new)') +t = t.replace('ITEMS.register("gun_smith_table", () -> new DefaultTableItem(ModBlocks.GUN_SMITH_TABLE.get()))', 'ITEMS.registerItem("gun_smith_table", props -> new DefaultTableItem(ModBlocks.GUN_SMITH_TABLE.get(), props))') +t = t.replace('ITEMS.register("target_minecart", TargetMinecartItem::new)', 'ITEMS.registerItem("target_minecart", TargetMinecartItem::new)') +items.write_text(t, encoding="utf-8") + +# Optional integrations must never stop the core port compiling. +compat = java / "com/tacz/guns/init/CompatRegistry.java" +compat.write_text('''package com.tacz.guns.init;\n\nimport net.neoforged.fml.ModList;\n\npublic final class CompatRegistry {\n public static final String IRIS = "iris";\n public static final String CLOTH_CONFIG = "cloth_config";\n private CompatRegistry() {}\n public static void init() {}\n public static void initClient() {}\n public static void checkModLoad(String id, Runnable action) { if (ModList.get().isLoaded(id)) action.run(); }\n}\n''', encoding="utf-8") +menu = java / "com/tacz/guns/compat/cloth/MenuIntegration.java" +menu.parent.mkdir(parents=True, exist_ok=True) +menu.write_text('''package com.tacz.guns.compat.cloth;\nimport net.minecraft.client.gui.screens.Screen;\npublic final class MenuIntegration { private MenuIntegration(){} public static Screen getConfigScreen(Screen parent){ return null; } }\n''', encoding="utf-8") + +# Newer 26.2 client code expects these two config groups. They are ordinary +# NeoForge ModConfigSpec entries and are safe to add to the existing client spec. +for name in ["SoundConfig", "ResourceConfig"]: + src = fabjava / f"com/tacz/guns/config/client/{name}.java" + dst = java / f"com/tacz/guns/config/client/{name}.java" + text = src.read_text(encoding="utf-8").replace("net.minecraftforge.common.ForgeConfigSpec", "net.neoforged.neoforge.common.ModConfigSpec").replace("ForgeConfigSpec", "ModConfigSpec") + dst.write_text(text, encoding="utf-8") +client_cfg = java / "com/tacz/guns/config/ClientConfig.java" +t = client_cfg.read_text(encoding="utf-8") +t = t.replace("import com.tacz.guns.config.client.KeyConfig;\nimport com.tacz.guns.config.client.RenderConfig;\nimport com.tacz.guns.config.client.ZoomConfig;", "import com.tacz.guns.config.client.*;") +if "SoundConfig.init(builder);" not in t: + t = t.replace("RenderConfig.init(builder);", "RenderConfig.init(builder);\n ResourceConfig.init(builder);\n SoundConfig.init(builder);") +client_cfg.write_text(t, encoding="utf-8") + +# --------------------------------------------------------------------------- +# 26.2 key mapping API. Use the validated 26.2 key implementations, but dispatch +# them from NeoForge's native InputEvent and ClientTickEvent. +# --------------------------------------------------------------------------- +for name in ["AimKey", "ConfigKey", "CrawlKey", "FireSelectKey", "InspectKey", "InteractKey", "MeleeKey", "RefitKey", "ReloadKey", "ShootKey", "TaCZKeyCategory", "ZoomKey"]: + p = copy_fabric(f"com/tacz/guns/client/input/{name}.java") + text = p.read_text(encoding="utf-8") + text = text.replace("import cn.sh1rocu.tacz.api.event.InputEvent;", "import net.neoforged.neoforge.client.event.InputEvent;") + text = text.replace("import cn.sh1rocu.tacz.api.event.PlayerTickEvent;\n", "") + text = text.replace("import net.fabricmc.loader.api.FabricLoader;", "import net.neoforged.fml.ModList;") + text = text.replace("FabricLoader.getInstance().isModLoaded", "ModList.get().isLoaded") + text = text.replace("import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;", "import net.neoforged.neoforge.network.PacketDistributor;") + text = text.replace("ClientPlayNetworking.send(", "PacketDistributor.sendToServer(") + if name == "ReloadKey": + text = text.replace("public static void autoReload(PlayerTickEvent.Pre event) {\n if (!event.getEntity().level().isClientSide())\n return;\n", "public static void autoReload() {\n") + p.write_text(text, encoding="utf-8") + +subscriber = java / "com/tacz/guns/client/input/ClientInputEvents.java" +subscriber.write_text('''package com.tacz.guns.client.input;\n\nimport com.tacz.guns.GunMod;\nimport net.minecraft.client.Minecraft;\nimport net.neoforged.api.distmarker.Dist;\nimport net.neoforged.bus.api.SubscribeEvent;\nimport net.neoforged.fml.common.EventBusSubscriber;\nimport net.neoforged.neoforge.client.event.ClientTickEvent;\nimport net.neoforged.neoforge.client.event.InputEvent;\n\n@EventBusSubscriber(value = Dist.CLIENT, modid = GunMod.MOD_ID)\npublic final class ClientInputEvents {\n private ClientInputEvents() {}\n @SubscribeEvent public static void key(InputEvent.Key e) {\n ConfigKey.onOpenConfig(e); CrawlKey.onCrawlPress(e); FireSelectKey.onFireSelectPress(e);\n InspectKey.onInspectPress(e); InteractKey.onInteractPress(e); MeleeKey.onMeleePress(e);\n RefitKey.onRefitPress(e); ReloadKey.onReloadPress(e); ZoomKey.onZoomKeyPress(e);\n }\n @SubscribeEvent public static void mouse(InputEvent.MouseButton.Post e) {\n AimKey.onAimPress(e); ZoomKey.onZoomMousePress(e);\n }\n @SubscribeEvent public static void tick(ClientTickEvent.Post e) {\n Minecraft mc = Minecraft.getInstance();\n AimKey.onAimHoldingPreInput(mc); AimKey.cancelAim(mc); ReloadKey.autoReload(); ShootKey.autoShoot(mc, true);\n }\n}\n''', encoding="utf-8") + +# --------------------------------------------------------------------------- +# Crosshair/HUD extraction was rewritten by Mojang in 26.2. Use the proven 26.2 +# implementation and remove its Fabric-only render-tick event. Screen state is +# sampled immediately before extracting the layer instead. +# --------------------------------------------------------------------------- +cross = copy_fabric("com/tacz/guns/client/event/RenderCrosshairEvent.java") +t = cross.read_text(encoding="utf-8") +t = t.replace("import cn.sh1rocu.simplebedrockmodel.api.event.RenderTickEvent;\n", "") +t = re.sub(r"\n\s*public static void onRenderTick\(RenderTickEvent event\) \{.*?\n\s*\}\n", "\n", t, flags=re.S) +t = t.replace("LocalPlayer player = Minecraft.getInstance().player;", "isRefitScreen = Minecraft.getInstance().gui.screen() instanceof GunRefitScreen;\n LocalPlayer player = Minecraft.getInstance().player;", 1) +cross.write_text(t, encoding="utf-8") + +# Re-run annotation/name cleanup after all restores/copies. +for p in java.rglob("*.java"): + p.write_text(modernize(p.read_text(encoding="utf-8", errors="ignore")), encoding="utf-8") + +print("Applied TACZ NeoForge 26.2 migration pass 6") From 4712171bfc8b635dd417fde2752fc6c2f223f0a5 Mon Sep 17 00:00:00 2001 From: Kestis Date: Wed, 19 Aug 2026 16:41:02 +0800 Subject: [PATCH 14/17] Run TaCZ 26.2 migration pass 6 --- .github/workflows/tacz-26-2-port.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tacz-26-2-port.yml b/.github/workflows/tacz-26-2-port.yml index d0e179a..32d3aa1 100644 --- a/.github/workflows/tacz-26-2-port.yml +++ b/.github/workflows/tacz-26-2-port.yml @@ -35,6 +35,7 @@ jobs: python3 tools/tacz26_port.py work python3 tools/tacz26_pass4.py work python3 tools/tacz26_pass5.py work + python3 tools/tacz26_pass6.py work - name: Compile or build shell: bash run: | From 06e5a15240effcc9d14fbd07bc3c3ca88767ab5c Mon Sep 17 00:00:00 2001 From: Kestis Date: Wed, 19 Aug 2026 16:43:36 +0800 Subject: [PATCH 15/17] Fix TaCZ 26.2 native input dispatch and FML side lookup --- tools/tacz26_pass6b.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tools/tacz26_pass6b.py diff --git a/tools/tacz26_pass6b.py b/tools/tacz26_pass6b.py new file mode 100644 index 0000000..d35e786 --- /dev/null +++ b/tools/tacz26_pass6b.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +from pathlib import Path +import sys + +root = Path(sys.argv[1]).resolve() +java = root / "src/main/java" + +subscriber = java / "com/tacz/guns/client/input/ClientInputEvents.java" +subscriber.write_text('''package com.tacz.guns.client.input;\n\nimport com.tacz.guns.GunMod;\nimport net.minecraft.client.Minecraft;\nimport net.neoforged.api.distmarker.Dist;\nimport net.neoforged.bus.api.SubscribeEvent;\nimport net.neoforged.fml.common.EventBusSubscriber;\nimport net.neoforged.neoforge.client.event.ClientTickEvent;\nimport net.neoforged.neoforge.client.event.InputEvent;\n\n@EventBusSubscriber(value = Dist.CLIENT, modid = GunMod.MOD_ID)\npublic final class ClientInputEvents {\n private ClientInputEvents() {}\n\n @SubscribeEvent\n public static void key(InputEvent.Key e) {\n ConfigKey.onOpenConfig(e);\n CrawlKey.onCrawlPress(e);\n FireSelectKey.onFireSelectKeyPress(e);\n InspectKey.onInspectPress(e);\n InteractKey.onInteractKeyPress(e);\n MeleeKey.onMeleeKeyPress(e);\n RefitKey.onRefitPress(e);\n ReloadKey.onReloadPress(e);\n ZoomKey.onZoomKeyPress(e);\n }\n\n @SubscribeEvent\n public static void mouse(InputEvent.MouseButton.Post e) {\n AimKey.onAimPress(e);\n FireSelectKey.onFireSelectMousePress(e);\n InteractKey.onInteractMousePress(e);\n MeleeKey.onMeleeMousePress(e);\n ZoomKey.onZoomMousePress(e);\n }\n\n @SubscribeEvent\n public static void tick(ClientTickEvent.Post e) {\n Minecraft mc = Minecraft.getInstance();\n AimKey.onAimHoldingPreInput(mc);\n AimKey.cancelAim(mc);\n ReloadKey.autoReload();\n ShootKey.autoShoot(mc, true);\n }\n}\n''', encoding="utf-8") + +# FancyModLoader 26.x exposes the process side through FMLEnvironment rather +# than the old static FMLLoader#getDist helper. +gunmod = java / "com/tacz/guns/GunMod.java" +if gunmod.exists(): + text = gunmod.read_text(encoding="utf-8") + text = text.replace("import net.neoforged.fml.loading.FMLLoader;", "import net.neoforged.fml.loading.FMLEnvironment;") + text = text.replace("FMLLoader.getDist()", "FMLEnvironment.getDist()") + gunmod.write_text(text, encoding="utf-8") + +print("Applied TACZ NeoForge 26.2 pass 6b") From 713186cf691729160bf49a33cc53d355eb7ce3e2 Mon Sep 17 00:00:00 2001 From: Kestis Date: Wed, 19 Aug 2026 16:43:57 +0800 Subject: [PATCH 16/17] Run TaCZ 26.2 pass 6b --- .github/workflows/tacz-26-2-port.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tacz-26-2-port.yml b/.github/workflows/tacz-26-2-port.yml index 32d3aa1..9a77730 100644 --- a/.github/workflows/tacz-26-2-port.yml +++ b/.github/workflows/tacz-26-2-port.yml @@ -36,6 +36,7 @@ jobs: python3 tools/tacz26_pass4.py work python3 tools/tacz26_pass5.py work python3 tools/tacz26_pass6.py work + python3 tools/tacz26_pass6b.py work - name: Compile or build shell: bash run: | From 9321db4c1f84301acfb981892872b62d761cbc71 Mon Sep 17 00:00:00 2001 From: Kestis Date: Wed, 19 Aug 2026 16:46:06 +0800 Subject: [PATCH 17/17] Avoid duplicate TaCZ port CI runs --- .github/workflows/tacz-26-2-port.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/tacz-26-2-port.yml b/.github/workflows/tacz-26-2-port.yml index 9a77730..8e07f89 100644 --- a/.github/workflows/tacz-26-2-port.yml +++ b/.github/workflows/tacz-26-2-port.yml @@ -1,8 +1,6 @@ name: TACZ 26.2 Port CI on: - push: - branches: [ci/tacz-neoforge-26.2-port] pull_request: branches: [main] workflow_dispatch: