Build offline OpenPrestige release bundles

Rename the injector and package it with an embedded payload for Windows, Linux, and macOS x64/ARM64. Add a core-only ImGui native build, multi-platform CI and release packaging, and shared per-user storage with legacy config migration.
This commit is contained in:
bbldrizzy committed 2026-11-03 18:45:13 +00:00
1 parent 5152e6e0a6
commit 2d6b2a3cd1
37 files changed
+1284 -451

No files matched your search

+182
View File
@@ -0,0 +1,182 @@
"""Build the core-only JNI library matching imgui-java 1.92.7.1.
Requires Python 3, a JDK, CMake, and a C++17 compiler. Select a cross compiler
with --toolchain or a Visual Studio target with --architecture ARM64.
"""
import argparse
import os
import re
from pathlib import Path
import shutil
import subprocess
import tempfile
import urllib.request
import zipfile
ROOT = Path(__file__).resolve().parents[1]
BINDING_COMMIT = "b164a88602af3e28483efbb4d85aea372010c7c5"
IMGUI_COMMIT = "b1bcb12a624af7509894c8e77dd47416997777fa"
NATIVE_FILES = (
"_common.h", "_internal.h", "imconfig.h",
"jni_jvm", "jni_common", "jni_binding_struct", "jni_callbacks", "jni_assertion", "jni_internal",
)
def write_if_changed(path, data):
if not path.is_file() or path.read_bytes() != data:
path.write_bytes(data)
def download(url, destination):
if not destination.is_file():
destination.parent.mkdir(parents=True, exist_ok=True)
with tempfile.NamedTemporaryFile(dir=destination.parent, suffix=".download", delete=False) as output:
temporary = Path(output.name)
try:
with urllib.request.urlopen(url, timeout=60) as response:
shutil.copyfileobj(response, output)
except BaseException:
output.close()
temporary.unlink(missing_ok=True)
raise
try:
if not destination.is_file():
try:
temporary.replace(destination)
except PermissionError:
if not destination.is_file():
raise
finally:
temporary.unlink(missing_ok=True)
return destination
def archive(repository, commit, cache):
path = download(f"https://codeload.github.com/{repository}/zip/{commit}",
cache / "downloads" / f"{repository.split('/')[-1]}-{commit}.zip")
return zipfile.ZipFile(path)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--build-dir", type=Path, default=ROOT / "build/imgui-core")
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--toolchain", type=Path)
parser.add_argument("--architecture", choices=("x64", "ARM64"))
parser.add_argument("--cache-dir", type=Path)
parser.add_argument("--zig-target", choices=(
"x86_64-windows-gnu", "aarch64-windows-gnu",
"x86_64-linux-gnu.2.17", "aarch64-linux-gnu.2.17",
"x86_64-macos.11.0", "aarch64-macos.11.0",
))
args = parser.parse_args()
# cmd.exe cannot use extended-length (\\?\) working directory paths.
build = Path(os.path.abspath(args.build_dir))
cache = Path(os.path.abspath(args.cache_dir)) if args.cache_dir else build
output = Path(os.path.abspath(args.output))
generated = build / "jni"
sources = build / "sources"
for directory in (build, output, generated, sources):
directory.mkdir(parents=True, exist_ok=True)
dependencies = []
for group, artifact, version in (
("com.badlogicgames.gdx", "gdx-jnigen", "2.5.2"),
("com.badlogicgames.gdx", "gdx-jnigen-loader", "2.5.2"),
("com.github.javaparser", "javaparser-core", "3.14.14"),
("io.github.spair", "imgui-java-binding", "1.92.7.1"),
):
name = f"{artifact}-{version}.jar"
dependencies.append(download(
f"https://repo.maven.apache.org/maven2/{group.replace('.', '/')}/{artifact}/{version}/{name}",
cache / "deps" / name,
))
binding = dependencies[-1]
with zipfile.ZipFile(dependencies[0]) as generator:
header_prefix = "com/badlogic/gdx/jnigen/resources/headers/"
for entry in generator.infolist():
if entry.filename.startswith(header_prefix) and not entry.is_dir():
destination = generated / "jni-headers" / entry.filename[len(header_prefix):]
destination.parent.mkdir(parents=True, exist_ok=True)
write_if_changed(destination, generator.read(entry))
with archive("SpaiR/imgui-java", BINDING_COMMIT, cache) as upstream:
prefix = upstream.namelist()[0]
java_prefix = prefix + "imgui-binding/src/generated/java/"
native_prefix = prefix + "imgui-binding/src/main/native/"
for entry in upstream.infolist():
if entry.filename.startswith(java_prefix) and entry.filename.endswith(".java"):
relative = entry.filename[len(java_prefix):]
if relative.startswith(("imgui/extension/", "imgui/lwjgl3/")):
continue
target = sources / relative
target.parent.mkdir(parents=True, exist_ok=True)
write_if_changed(target, upstream.read(entry))
if entry.filename.startswith(native_prefix):
name = entry.filename[len(native_prefix):]
if name in NATIVE_FILES or name.rsplit(".", 1)[0] in NATIVE_FILES:
write_if_changed(generated / name, upstream.read(entry))
write_if_changed(output / "LICENSE-imgui-java.txt", upstream.read(prefix + "LICENSE"))
with archive("ocornut/imgui", IMGUI_COMMIT, cache) as upstream:
prefix = upstream.namelist()[0]
for entry in upstream.infolist():
name = entry.filename[len(prefix):]
if "/" not in name and name.endswith((".h", ".cpp")) and name != "imconfig.h":
write_if_changed(generated / name, upstream.read(entry))
write_if_changed(output / "LICENSE-imgui.txt", upstream.read(prefix + "LICENSE.txt"))
java_home = os.environ.get("JAVA_HOME")
java = str(Path(java_home) / "bin/java") if java_home else shutil.which("java")
if not java:
raise RuntimeError("Set JAVA_HOME to a JDK.")
env = os.environ.copy()
if java_home:
env["PATH"] = str(Path(java_home) / "bin") + os.pathsep + env["PATH"]
# jnigen constructs its javac command as a string. Relative paths avoid its
# whitespace bug without depending on Windows short filename support.
write_if_changed(build / "binding.jar", binding.read_bytes())
subprocess.run([
java, "-cp", os.pathsep.join(map(str, dependencies)),
str(ROOT / "scripts/imgui-core/GenerateCore.java"),
"sources", "binding.jar", "jni",
], check=True, env=env, cwd=build)
# Upstream's GNU builds accept variable-length arrays. Use standard C++
# storage in generated JNI code so the identical methods also build in MSVC.
declaration = re.compile(r"\b(ImVec2|const char\*|jlong)\s+(\w+)\[(\w+)\];")
for source in generated.glob("imgui_*.cpp"):
text = source.read_text(encoding="utf-8")
text, count = declaration.subn(
lambda match: f"std::vector<{match[1]}> {match[2]}Storage({match[3]});\n"
f" auto* {match[2]} = {match[2]}Storage.data();",
text,
)
if count:
source.write_text("#include <vector>\n" + text, encoding="utf-8")
configuration = [
"cmake", "-S", str(ROOT / "scripts/imgui-core"), "-B", str(build / "cmake"),
f"-DCORE_JNI_DIR={generated.as_posix()}", f"-DCORE_OUTPUT_DIR={output.as_posix()}", "-DCMAKE_BUILD_TYPE=Release",
]
if args.zig_target:
if args.toolchain or args.architecture:
parser.error("--zig-target cannot be combined with --toolchain or --architecture")
import ziglang
executable = Path(ziglang.__file__).parent / ("zig.exe" if os.name == "nt" else "zig")
family = "Windows" if "windows" in args.zig_target else "Darwin" if "macos" in args.zig_target else "Linux"
toolchain = build / "zig-toolchain.cmake"
toolchain.write_text(
f'set(CMAKE_SYSTEM_NAME {family})\n'
f'set(CMAKE_SYSTEM_PROCESSOR {args.zig_target.split("-")[0]})\n'
f'set(CMAKE_CXX_COMPILER "{executable.as_posix()}")\n'
f'set(CMAKE_CXX_COMPILER_ARG1 "c++ -target {args.zig_target} -Wno-nullability-completeness")\n',
encoding="utf-8",
)
configuration.extend(("-G", "Ninja", f"-DCMAKE_TOOLCHAIN_FILE={toolchain.as_posix()}"))
if args.toolchain:
configuration.append(f"-DCMAKE_TOOLCHAIN_FILE={args.toolchain.resolve().as_posix()}")
if args.architecture:
configuration.extend(("-A", args.architecture))
subprocess.run(configuration, check=True, env=env)
subprocess.run(["cmake", "--build", str(build / "cmake"), "--config", "Release", "--parallel", "4"],
check=True, env=env)
if __name__ == "__main__":
main()
+127
View File
@@ -0,0 +1,127 @@
"""Fetch and build every core ImGui native required by build-release.bat."""
import argparse
import importlib.util
import importlib.metadata
import os
from pathlib import Path
import shutil
import struct
import subprocess
import sys
import sysconfig
ROOT = Path(__file__).resolve().parents[1]
TARGETS = (
("windows-x86_64", "x86_64-windows-gnu", "imgui-java64.dll"),
("windows-aarch64", "aarch64-windows-gnu", "imgui-java64.dll"),
("linux-x86_64", "x86_64-linux-gnu.2.17", "libimgui-java64.so"),
("linux-aarch64", "aarch64-linux-gnu.2.17", "libimgui-java64.so"),
("macos-x86_64", "x86_64-macos.11.0", "libimgui-java64.dylib"),
("macos-aarch64", "aarch64-macos.11.0", "libimgui-java64.dylib"),
)
def ensure_tools():
scripts = Path(sys.executable).parent / "Scripts"
user_scheme = "nt_user" if os.name == "nt" else "posix_user"
user_scripts = Path(sysconfig.get_path("scripts", scheme=user_scheme))
os.environ["PATH"] = os.pathsep.join((str(scripts), str(user_scripts), os.environ["PATH"]))
missing = [tool for tool in ("cmake", "ninja") if not shutil.which(tool)]
if importlib.util.find_spec("ziglang") is None or importlib.metadata.version("ziglang") != "0.16.0":
missing.append("ziglang==0.16.0")
if missing:
subprocess.run([sys.executable, "-m", "pip", "install", "--user", *missing], check=True)
for tool in ("cmake", "ninja"):
if not shutil.which(tool):
raise RuntimeError(f"{tool} is unavailable after installation. Add the Python Scripts directory to PATH.")
def universal(slices, destination):
images = [path.read_bytes() for path in slices]
records = []
offset = 16384
for image in images:
magic, cpu, subtype = struct.unpack_from("<III", image)
if magic != 0xFEEDFACF or cpu not in (0x01000007, 0x0100000C):
raise ValueError("The macOS build produced an unexpected Mach-O architecture.")
records.append((cpu, subtype, offset, len(image), 14))
offset = (offset + len(image) + 16383) & ~16383
if len({record[0] for record in records}) != 2:
raise ValueError("The universal library needs one Intel slice and one ARM64 slice.")
temporary = destination.with_suffix(".dylib.tmp")
with temporary.open("wb") as output:
output.write(struct.pack(">II", 0xCAFEBABE, len(images)))
for record in records:
output.write(struct.pack(">IIIII", *record))
for record, image in zip(records, images):
output.seek(record[2])
output.write(image)
temporary.replace(destination)
def validate_native(path, platform):
with path.open("rb") as library:
header = library.read(64)
if platform.startswith("windows-") and header[:2] == b"MZ":
library.seek(struct.unpack_from("<I", header, 60)[0])
signature, machine = struct.unpack("<4sH", library.read(6))
expected = 0xAA64 if platform.endswith("aarch64") else 0x8664
if signature == b"PE\0\0" and machine == expected:
return
elif platform.startswith("linux-") and header[:6] == b"\x7fELF\x02\x01":
expected = 183 if platform.endswith("aarch64") else 62
if struct.unpack_from("<H", header, 18)[0] == expected:
return
elif platform.startswith("macos-") and header[:4] == b"\xcf\xfa\xed\xfe":
expected = 0x0100000C if platform.endswith("aarch64") else 0x01000007
if struct.unpack_from("<I", header, 4)[0] == expected:
return
raise ValueError(f"The {platform} build produced a library with an unexpected architecture.")
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--platform", choices=[item[0] for item in TARGETS],
help="Build one slice for troubleshooting. Default builds the full release set.")
args = parser.parse_args()
ensure_tools()
cache = ROOT / "build/imgui-core-cache"
for platform, target, filename in TARGETS:
if args.platform and args.platform != platform:
continue
print(f"Building small ImGui JNI for {platform}", flush=True)
output = ROOT / "build/core-natives" / platform
if platform.startswith("macos-"):
output = ROOT / "build/imgui-core-slices" / platform
work = ROOT / "build/imgui-core-release" / platform
work.mkdir(parents=True, exist_ok=True)
log_path = work / "build.log"
command = [
sys.executable, str(ROOT / "scripts/build_imgui_core.py"),
"--build-dir", str(work),
"--cache-dir", str(cache), "--output", str(output), "--zig-target", target,
]
with log_path.open("w", encoding="utf-8") as log:
result = subprocess.run(command, stdout=log, stderr=subprocess.STDOUT)
if result.returncode:
print("\n".join(log_path.read_text(encoding="utf-8", errors="replace").splitlines()[-40:]), flush=True)
raise RuntimeError(f"The {platform} build failed. Full log: {log_path}")
if not (output / filename).is_file():
raise RuntimeError(f"The {platform} build did not produce {filename}.")
validate_native(output / filename, platform)
print(f"Built {output / filename} ({(output / filename).stat().st_size:,} bytes)", flush=True)
if not args.platform:
destination = ROOT / "build/core-natives/macos-universal"
destination.mkdir(parents=True, exist_ok=True)
slices = ROOT / "build/imgui-core-slices"
universal([
slices / "macos-x86_64/libimgui-java64.dylib",
slices / "macos-aarch64/libimgui-java64.dylib",
], destination / "libimgui-java64.dylib")
for license_file in (slices / "macos-x86_64").glob("LICENSE-*.txt"):
shutil.copyfile(license_file, destination / license_file.name)
print("All five release ImGui native libraries are ready.", flush=True)
if __name__ == "__main__":
main()
+58
View File
@@ -0,0 +1,58 @@
cmake_minimum_required(VERSION 3.20)
project(openprestige_imgui LANGUAGES CXX)
file(GLOB GENERATED CONFIGURE_DEPENDS "${CORE_JNI_DIR}/imgui_*.cpp")
add_library(imgui-java64 SHARED
${GENERATED}
"${CORE_JNI_DIR}/imgui.cpp"
"${CORE_JNI_DIR}/imgui_draw.cpp"
"${CORE_JNI_DIR}/imgui_tables.cpp"
"${CORE_JNI_DIR}/imgui_widgets.cpp"
"${CORE_JNI_DIR}/imgui_demo.cpp"
"${CORE_JNI_DIR}/jni_jvm.cpp"
"${CORE_JNI_DIR}/jni_common.cpp"
"${CORE_JNI_DIR}/jni_binding_struct.cpp"
"${CORE_JNI_DIR}/jni_callbacks.cpp"
"${CORE_JNI_DIR}/jni_assertion.cpp"
"${CORE_JNI_DIR}/jni_internal.cpp"
)
target_compile_features(imgui-java64 PRIVATE cxx_std_17)
if(WIN32)
set(JNI_OS win32)
elseif(APPLE)
set(JNI_OS mac)
else()
set(JNI_OS linux)
endif()
target_include_directories(imgui-java64 PRIVATE
"${CORE_JNI_DIR}"
"${CORE_JNI_DIR}/jni-headers"
"${CORE_JNI_DIR}/jni-headers/${JNI_OS}"
)
target_compile_definitions(imgui-java64 PRIVATE IMGUI_DISABLE_DEMO_WINDOWS)
set_target_properties(imgui-java64 PROPERTIES
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN YES
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
LIBRARY_OUTPUT_DIRECTORY "${CORE_OUTPUT_DIR}"
RUNTIME_OUTPUT_DIRECTORY "${CORE_OUTPUT_DIR}"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CORE_OUTPUT_DIR}"
)
if(MSVC)
target_compile_options(imgui-java64 PRIVATE /O1 /Gy /GL /utf-8)
target_link_options(imgui-java64 PRIVATE /LTCG /OPT:REF /OPT:ICF)
else()
target_compile_options(imgui-java64 PRIVATE -Os -ffunction-sections -fdata-sections)
if(APPLE)
target_link_options(imgui-java64 PRIVATE -Wl,-dead_strip)
else()
target_link_options(imgui-java64 PRIVATE -Wl,--gc-sections -s)
endif()
if(MINGW)
target_link_options(imgui-java64 PRIVATE -static-libgcc -static-libstdc++)
endif()
endif()
if(WIN32)
set_target_properties(imgui-java64 PROPERTIES PREFIX "")
target_link_libraries(imgui-java64 PRIVATE imm32 user32)
endif()
+8
View File
@@ -0,0 +1,8 @@
import com.badlogic.gdx.jnigen.NativeCodeGenerator;
public final class GenerateCore {
public static void main(String[] args) throws Exception {
new NativeCodeGenerator().generate(args[0], args[1], args[2], null,
new String[] {"**/extension/**", "**/lwjgl3/**"});
}
}
+49
View File
@@ -0,0 +1,49 @@
# Core ImGui JNI build
OpenPrestige uses core ImGui with its Java OpenGL backend. This build keeps all core and internal
JNI methods from imgui-java 1.92.7.1, and removes the native extension implementations for ImPlot,
ImNodes, ImGuizmo, imgui-node-editor, text editor, file dialog, knobs, and memory editor. Demo
windows are disabled. Core callbacks, font rendering, tables, docking, and draw lists remain available.
The source revisions and generator dependencies are pinned in `../build_imgui_core.py`.
The build consumes upstream generated Java without modifying it. A portability step replaces
GNU variable-length arrays in the generated C++ with standard C++ vectors for MSVC.
## Windows x64
Set `JAVA_HOME` to JDK 21 and install Visual Studio C++ tools and CMake. From the repository root run
```powershell
python scripts/build_imgui_core.py --output build/core-natives/windows-x86_64 --architecture x64
```
The output contains `imgui-java64.dll` and the upstream licenses. Gradle picks up
`build/core-natives`, embeds it under `prestige-natives`, and omits the corresponding upstream
native. The Java extension wrappers are omitted from the shaded payload.
Use a distinct `--build-dir` for each target. ARM64 cross builds use the toolchain files in
`toolchains`, which require LLVM-MinGW on Windows targets and an aarch64 Linux cross compiler on
Linux targets. The macOS toolchain produces a universal library. CI builds all five variants.
## Full release build on Windows
`build-release.bat` calls `python scripts/build_release_imgui.py` before Gradle builds the JAR.
The helper fetches the pinned sources and generator dependencies, installs CMake, Ninja and Zig
when needed, and builds Windows and Linux x64/ARM64 plus both macOS slices. It combines the macOS
slices into a universal library. Downloads are cached in `build/imgui-core-cache`, and compilation
is incremental under `build/imgui-core-release`.
The complete release still requires JDK 21, Rust, and the MSVC x64/ARM64 build tools used by the
Rust Windows targets. Python's package installer needs network access on the first run.
To diagnose one cross build, use `python scripts/build_release_imgui.py --platform windows-aarch64`.
Running the helper without arguments builds the entire ImGui release set.
## JNI verification
```powershell
java -Dimgui.library.path=build/core-natives/windows-x86_64 -cp build/imgui-core/deps/imgui-java-binding-1.92.7.1.jar scripts/imgui-core/SmokeTest.java src/main/resources/prestige_ui/fonts/DMSans-Regular.ttf
```
This initializes JNI, builds the real UI font atlas, and generates geometry from widgets and draw
lists. A Minecraft smoke test should additionally exercise the ClickGUI and OpenGL backend.
+59
View File
@@ -0,0 +1,59 @@
import imgui.ImDrawList;
import imgui.ImFontConfig;
import imgui.ImGui;
import imgui.ImGuiIO;
import imgui.type.ImInt;
import imgui.type.ImString;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
/** Exercises core JNI entry points, real UI fonts, input widgets and draw data without a game. */
public final class SmokeTest {
public static void main(String[] args) throws Exception {
ImGui.createContext();
try {
ImGuiIO io = ImGui.getIO();
io.setIniFilename(null);
io.setDisplaySize(1000, 600);
io.setDeltaTime(1.0f / 60);
ImFontConfig config = new ImFontConfig();
try {
config.setPixelSnapH(true);
io.getFonts().addFontFromMemoryTTF(Files.readAllBytes(Path.of(args[0])), 18, config);
} finally {
config.destroy();
}
if (!io.getFonts().build()) throw new AssertionError("Font atlas did not build");
ImInt width = new ImInt();
ImInt height = new ImInt();
ByteBuffer pixels = io.getFonts().getTexDataAsRGBA32(width, height);
if (pixels == null || width.get() <= 0 || height.get() <= 0) {
throw new AssertionError("Font atlas is empty");
}
ImString input = new ImString("OpenPrestige", 64);
float[] value = {0.5f};
int vertices = 0;
for (int frame = 0; frame < 3; frame++) {
ImGui.newFrame();
ImGui.setNextWindowSize(500, 350);
ImGui.begin("Core JNI smoke test");
ImGui.text("OpenPrestige core-only ImGui");
ImGui.button("Inject");
ImGui.sliderFloat("Scale", value, 0, 1);
ImGui.inputText("Config name", input);
ImDrawList draw = ImGui.getWindowDrawList();
draw.addCircleFilled(100, 100, 20, 0xff80a0ff);
draw.addText(120, 100, 0xffffffff, "Draw list");
ImGui.end();
ImGui.render();
vertices = ImGui.getDrawData().getTotalVtxCount();
}
if (vertices <= 0) throw new AssertionError("No UI geometry was generated");
System.out.println("Core JNI passed. Font atlas " + width.get() + "x" + height.get()
+ ", vertices " + vertices);
} finally {
ImGui.destroyContext();
}
}
}
@@ -0,0 +1,3 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
@@ -0,0 +1,2 @@
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "" FORCE)
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "" FORCE)
@@ -0,0 +1,4 @@
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_CXX_COMPILER aarch64-w64-mingw32-clang++)
set(CMAKE_RC_COMPILER aarch64-w64-mingw32-windres)
@@ -0,0 +1,4 @@
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-clang++)
set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
+28 -80
View File
@@ -1,9 +1,7 @@
"""Packs one platform's release zip, or writes SHA256SUMS.txt for dist/release. Called by release.bat.
"""Package openprestige releases.
package_release.py zip <out_dir> <version> <platform> <injector> <native_lib> <payload_jar>
package_release.py offline <out_dir> <version> <platform> <injector> <native_lib> <payload_jar>
package_release.py checksums <out_dir>
Zips are written with unix permissions so PrestigeInjector stays executable after unzip on Linux/macOS.
"""
import hashlib
import os
@@ -11,69 +9,7 @@ import sys
import time
import zipfile
README = {
"windows": """Prestige {version} - Windows (x86_64)
Contents
PrestigeInjector.exe injector
prestige_native.dll loader library (keep it next to the injector)
{jar} the same client as a Fabric mod (optional, see below)
Inject
1. Start Minecraft 1.21.11 with Fabric Loader 0.19+ and Fabric API.
2. Run PrestigeInjector.exe and pick the game from the list.
If SmartScreen blocks the exe: More info -> Run anyway.
""",
"linux": """Prestige {version} - Linux (x86_64)
Works on any glibc distro from 2012 on (glibc 2.17+): Ubuntu, Debian, Fedora, Arch, Mint, openSUSE, ...
Not for musl distros (Alpine) or ARM machines.
Contents
PrestigeInjector injector
libprestige_native.so loader library (keep it next to the injector)
{jar} the same client as a Fabric mod (optional, see below)
Inject
1. Start Minecraft 1.21.11 with Fabric Loader 0.19+ and Fabric API.
2. In a terminal in this folder: ./PrestigeInjector
Run it as the same user as the game. The game must not use -XX:+DisableAttachMechanism.
""",
"macos": """Prestige {version} - macOS (universal: Intel + Apple Silicon)
Contents
PrestigeInjector injector
libprestige_native.dylib loader library (keep it next to the injector)
{jar} the same client as a Fabric mod (optional, see below)
Inject
1. Start Minecraft 1.21.11 with Fabric Loader 0.19+ and Fabric API.
2. In Terminal, in this folder, clear the download quarantine once:
xattr -dr com.apple.quarantine .
3. Run: ./PrestigeInjector
Run it as the same user as the game. The game must not use -XX:+DisableAttachMechanism.
""",
}
MOD_NOTE = """
Install as a mod instead
Put {jar} into the mods/ folder of a Fabric 1.21.11 instance; no injector needed.
Do not use both at once.
"""
TESTED = "Tested on Prism Launcher with Minecraft 1.21.11 + Fabric.\n"
FOOTER = """
Credits
Cracked by Skid Solutions
technix sourcing / Frida scripts
stutheguyy fixing
bynbang / nitrogenoliquido ZKM transformers
dario AI models
Discord: https://discord.gg/x2FJyGrDNg
"""
FOOTER = '\nCredits\n Cracked by Skid Solutions\n technix sourcing / Frida scripts\n stutheguyy fixing\n bynbang / nitrogenoliquido ZKM transformers\n dario AI models\n\nDiscord: https://discord.gg/x2FJyGrDNg\n'
def add(zf, arcname, data, mode):
@@ -85,19 +21,31 @@ def add(zf, arcname, data, mode):
def make_zip(out_dir, version, platform, injector, lib, jar):
family = platform.split("-")[0]
name = f"prestige-{version}-{platform}"
jar_name = os.path.basename(jar)
title, body = README[family].format(version=version, jar=jar_name).split("\n", 1)
readme = title + "\n" + TESTED + body + MOD_NOTE.format(jar=jar_name) + FOOTER
if family == "windows":
readme = readme.replace("\n", "\r\n")
if platform in ("windows-aarch64", "linux-aarch64"):
native = "imgui-java64.dll" if platform.startswith("windows") else "libimgui-java64.so"
with zipfile.ZipFile(jar) as payload:
entry = f"prestige-natives/{platform}/{native}"
if entry not in payload.namelist():
raise ValueError(f"ARM64 payload is missing {entry}. Build the core ImGui natives first.")
name = f"openprestige-{version}-{platform}"
out = os.path.join(out_dir, name + ".zip")
with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED, compresslevel=9) as zf:
for path, mode in ((injector, 0o755), (lib, 0o644), (jar, 0o644)):
with open(path, "rb") as f:
add(zf, f"{name}/{os.path.basename(path)}", f.read(), mode)
add(zf, f"{name}/README.txt", readme.encode(), 0o644)
os.makedirs(out_dir, exist_ok=True)
with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED, compresslevel=9) as bundle:
executable = "OpenPrestigeInjector" + (".exe" if platform.startswith("windows-") else "")
for path, filename, permissions in ((injector, executable, 0o755), (lib, os.path.basename(lib), 0o644)):
with open(path, "rb") as file:
add(bundle, f"{name}/{filename}", file.read(), permissions)
readme = (f"OpenPrestige {version} - {platform}\n\n"
f"Extract this complete folder and run {executable}.\n"
"Original CLI: Up/Down selects a Minecraft JVM, Enter injects, Esc quits.\n"
f"Keep {os.path.basename(lib)} beside the executable.\n"
"The native library embeds its payload. No external JAR or runtime download is required.\n"
"The launcher has no downloader or update checks.\n\n"
"Requires a matching 64-bit Minecraft 1.21.11 Fabric JVM.\n"
"Linux/macOS: run from a terminal as the same user as the game; attach must be enabled.\n"
"The Fabric mod JAR is available separately. Use either injection or the mod.\n"
+ FOOTER)
add(bundle, f"{name}/README.txt", readme.encode(), 0o644)
print(f" wrote {out} ({os.path.getsize(out) // 1024} KiB)")
@@ -114,7 +62,7 @@ def checksums(out_dir):
if __name__ == "__main__":
if len(sys.argv) == 8 and sys.argv[1] == "zip":
if len(sys.argv) == 8 and sys.argv[1] == "offline":
make_zip(*sys.argv[2:])
elif len(sys.argv) == 3 and sys.argv[1] == "checksums":
checksums(sys.argv[2])
+48
View File
@@ -0,0 +1,48 @@
"""Check this repository's release contents and architecture requirements."""
import contextlib
import hashlib
import io
from pathlib import Path
import tempfile
import unittest
import zipfile
import package_release
class ReleasePackaging(unittest.TestCase):
def test_contents_and_checksums(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
injector = root / "injector.exe"
library = root / "prestige_native.dll"
jar = root / "prestige-1.2.3.jar"
injector.write_bytes(b"launcher")
library.write_bytes(b"library")
with zipfile.ZipFile(jar, "w") as payload:
payload.writestr("example.class", b"payload")
out = root / "release"
with contextlib.redirect_stdout(io.StringIO()):
package_release.make_zip(str(out), "1.2.3", "windows-x86_64", str(injector), str(library), str(jar))
archive = next(out.glob("*.zip"))
with zipfile.ZipFile(archive) as bundle:
names = {Path(name).name for name in bundle.namelist()}
self.assertEqual(names, {"OpenPrestigeInjector.exe", library.name, "README.txt"})
readme = bundle.read("openprestige-1.2.3-windows-x86_64/README.txt").decode()
self.assertIn("embeds its payload", readme)
self.assertNotIn("Keep the payload JAR beside", readme)
package_release.checksums(str(out))
expected = f"{hashlib.sha256(archive.read_bytes()).hexdigest()} {archive.name}\n"
self.assertEqual((out / "SHA256SUMS.txt").read_text(), expected)
def test_arm_native_required(self):
with tempfile.TemporaryDirectory() as directory:
jar = Path(directory) / "prestige-1.2.3.jar"
with zipfile.ZipFile(jar, "w"):
pass
with self.assertRaisesRegex(ValueError, "ARM64 payload is missing"):
package_release.make_zip(directory, "1.2.3", "windows-aarch64", "unused.exe", "unused.dll", str(jar))
if __name__ == "__main__":
unittest.main()