From ce3f31a48cd5e08c6dac48ae39ce064595ed23ef Mon Sep 17 00:00:00 2001 From: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> Date: Mon, 2 Nov 2026 14:20:41 -0500 Subject: [PATCH] fix: Project Zomboid runs on G1 and the C1 JIT instead of ZGC --- .../overlay/usr/local/bin/droiddeck-game-env | 45 +++++++++++++++++++ tools/tests/test_game_environment.py | 34 ++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/tools/linuxfs/overlay/usr/local/bin/droiddeck-game-env b/tools/linuxfs/overlay/usr/local/bin/droiddeck-game-env index 608eefd5f..fe0ad0cf3 100755 --- a/tools/linuxfs/overlay/usr/local/bin/droiddeck-game-env +++ b/tools/linuxfs/overlay/usr/local/bin/droiddeck-game-env @@ -243,6 +243,50 @@ def ubisoft_overlay_off(env): print("droiddeck-game-env: Ubisoft Connect overlay left as is (%s)" % error, file=sys.stderr) +PZ_JVM_FLAGS = ("-XX:+UseG1GC", "-XX:TieredStopAtLevel=1") + + +def zomboid_vm_args(args): + updated = [arg for arg in args if arg != "-XX:+UseZGC"] + for flag in PZ_JVM_FLAGS: + if flag not in updated: + updated.append(flag) + return updated + + +def project_zomboid_jvm(command): + if len(command) <= 2: + return + exe = Path(command[2]) + if not exe.stem.lower().startswith("projectzomboid"): + return + settings = exe.with_suffix(".json") + try: + with settings.open(encoding="utf-8-sig") as source: + data = json.load(source) + args = data.get("vmArgs") + if not isinstance(args, list) or not all(isinstance(arg, str) for arg in args): + return + updated = zomboid_vm_args(args) + if updated == args: + return + data["vmArgs"] = updated + handle, staged = tempfile.mkstemp(prefix=".droiddeck-", dir=settings.parent) + try: + with os.fdopen(handle, "w", encoding="utf-8") as target: + json.dump(data, target, indent=4) + target.write("\n") + os.chmod(staged, settings.stat().st_mode & 0o7777) + os.replace(staged, settings) + except BaseException: + os.unlink(staged) + raise + print("droiddeck-game-env: Project Zomboid: G1 garbage collector and C1 JIT only (ZGC overheats and stalls here)", + file=sys.stderr) + except (OSError, ValueError, AttributeError) as error: + print("droiddeck-game-env: Project Zomboid JVM settings left as is (%s)" % error, file=sys.stderr) + + def offline_without_anticheat(command, env): if (env.get("DROIDDECK_ANTICHEAT_OFFLINE") != "1" or len(command) <= 2 or command[1] != "waitforexitandrun" @@ -931,6 +975,7 @@ def main(): if waiting: command = skip_red_launcher(command) ubisoft_overlay_off(env) + project_zomboid_jvm(command) fixes, extra, notes = engine_fixes(command[2], command[3:]) if len(command) > 2 else ({}, [], []) if not gl_default: fixes.pop("vblank_mode", None) diff --git a/tools/tests/test_game_environment.py b/tools/tests/test_game_environment.py index 1d2ea6441..c07a37371 100644 --- a/tools/tests/test_game_environment.py +++ b/tools/tests/test_game_environment.py @@ -2026,6 +2026,40 @@ class DirectAudioPrefixTest(unittest.TestCase): self.assertIsNone(self.link("system32")) +class ProjectZomboidJvmTest(unittest.TestCase): + ARGS = ["-Djava.awt.headless=true", "-Xmx3072m", "-XX:+UseZGC", "-XX:-CreateCoredumpOnCrash"] + + def launch(self, tmp, args=None, stem="ProjectZomboid64"): + settings = Path(tmp) / (stem + ".json") + settings.write_text(json.dumps({"mainClass": "zombie/gameStates/MainScreenState", "vmArgs": args or self.ARGS})) + command = ["proton", "waitforexitandrun", str(Path(tmp) / (stem + ".exe"))] + return settings, command + + def test_zgc_becomes_g1_and_the_c1_jit_flag_is_added_once(self): + with tempfile.TemporaryDirectory() as tmp: + settings, command = self.launch(tmp) + MODULE["project_zomboid_jvm"](command) + data = json.loads(settings.read_text()) + self.assertEqual(data["vmArgs"], ["-Djava.awt.headless=true", "-Xmx3072m", "-XX:-CreateCoredumpOnCrash", + "-XX:+UseG1GC", "-XX:TieredStopAtLevel=1"]) + self.assertEqual(data["mainClass"], "zombie/gameStates/MainScreenState") + before = settings.read_text() + MODULE["project_zomboid_jvm"](command) + self.assertEqual(settings.read_text(), before) + + def test_other_games_and_unreadable_files_are_left_alone(self): + with tempfile.TemporaryDirectory() as tmp: + settings, command = self.launch(tmp, stem="Other") + before = settings.read_text() + MODULE["project_zomboid_jvm"](command) + self.assertEqual(settings.read_text(), before) + settings.write_text("{") + MODULE["project_zomboid_jvm"](command[:2] + [str(Path(tmp) / "Other.exe")]) + (Path(tmp) / "ProjectZomboid64.json").write_text("{") + MODULE["project_zomboid_jvm"](command[:2] + [str(Path(tmp) / "ProjectZomboid64.exe")]) + self.assertEqual((Path(tmp) / "ProjectZomboid64.json").read_text(), "{") + + class UbisoftOverlayTest(unittest.TestCase): SETTINGS = ( "language:\n code: en-US\n"