Landtrain!!!!
This commit is contained in:
105
tools/java/BaseVehicleConstraintPatch.java
Normal file
105
tools/java/BaseVehicleConstraintPatch.java
Normal file
@@ -0,0 +1,105 @@
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.InsnNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
/**
|
||||
* Patches zombie.vehicles.BaseVehicle so addPointConstraint() no longer force-breaks
|
||||
* both vehicles before creating a new constraint.
|
||||
*/
|
||||
public final class BaseVehicleConstraintPatch {
|
||||
private static final String TARGET_NAME = "addPointConstraint";
|
||||
private static final String BREAK_DESC_OBJECT_BOOL = "(ZLjava/lang/Boolean;)V";
|
||||
private static final String BREAK_DESC_PRIMITIVE_BOOL = "(ZZ)V";
|
||||
private static final String BASE_VEHICLE_OWNER = "zombie/vehicles/BaseVehicle";
|
||||
|
||||
private BaseVehicleConstraintPatch() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length != 2) {
|
||||
System.err.println("Usage: BaseVehicleConstraintPatch <input BaseVehicle.class> <output BaseVehicle.class>");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
Path input = Paths.get(args[0]);
|
||||
Path output = Paths.get(args[1]);
|
||||
byte[] original = Files.readAllBytes(input);
|
||||
|
||||
ClassNode classNode = new ClassNode();
|
||||
new ClassReader(original).accept(classNode, 0);
|
||||
|
||||
int removedCalls = 0;
|
||||
int inspectedAddPointMethods = 0;
|
||||
for (MethodNode method : classNode.methods) {
|
||||
if (!TARGET_NAME.equals(method.name) || !isTargetAddPointConstraint(method.desc)) {
|
||||
continue;
|
||||
}
|
||||
inspectedAddPointMethods++;
|
||||
removedCalls += patchAddPointConstraint(method);
|
||||
}
|
||||
|
||||
if (removedCalls < 2) {
|
||||
throw new IllegalStateException(
|
||||
"Expected to remove 2 breakConstraint calls, removed "
|
||||
+ removedCalls
|
||||
+ " (inspected addPoint methods: "
|
||||
+ inspectedAddPointMethods
|
||||
+ ")");
|
||||
}
|
||||
|
||||
ClassWriter writer = new ClassWriter(0);
|
||||
classNode.accept(writer);
|
||||
|
||||
Files.createDirectories(output.getParent());
|
||||
Files.write(output, writer.toByteArray());
|
||||
System.out.println("Patched BaseVehicle.class; removed breakConstraint calls: " + removedCalls);
|
||||
}
|
||||
|
||||
private static boolean isTargetAddPointConstraint(String methodDesc) {
|
||||
// We only want the 5-arg overload:
|
||||
// (IsoPlayer, BaseVehicle, String, String, boolean|Boolean) -> void
|
||||
return "(Lzombie/characters/IsoPlayer;Lzombie/vehicles/BaseVehicle;Ljava/lang/String;Ljava/lang/String;Z)V"
|
||||
.equals(methodDesc)
|
||||
|| "(Lzombie/characters/IsoPlayer;Lzombie/vehicles/BaseVehicle;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Boolean;)V"
|
||||
.equals(methodDesc);
|
||||
}
|
||||
|
||||
private static int patchAddPointConstraint(MethodNode method) {
|
||||
int patched = 0;
|
||||
InsnList insns = method.instructions;
|
||||
|
||||
for (AbstractInsnNode node = insns.getFirst(); node != null; ) {
|
||||
AbstractInsnNode next = node.getNext();
|
||||
if (node instanceof MethodInsnNode call
|
||||
&& BASE_VEHICLE_OWNER.equals(call.owner)
|
||||
&& "breakConstraint".equals(call.name)) {
|
||||
if (!(BREAK_DESC_OBJECT_BOOL.equals(call.desc)
|
||||
|| BREAK_DESC_PRIMITIVE_BOOL.equals(call.desc))) {
|
||||
node = next;
|
||||
continue;
|
||||
}
|
||||
// Keep stack-map frames valid by preserving stack effect:
|
||||
// breakConstraint(...) consumes objectref + 2 args and returns void.
|
||||
// Replace invoke with POP2 + POP (consume 3 category-1 stack slots).
|
||||
InsnList replacement = new InsnList();
|
||||
replacement.add(new InsnNode(Opcodes.POP2));
|
||||
replacement.add(new InsnNode(Opcodes.POP));
|
||||
insns.insert(node, replacement);
|
||||
insns.remove(node);
|
||||
patched++;
|
||||
}
|
||||
node = next;
|
||||
}
|
||||
|
||||
return patched;
|
||||
}
|
||||
}
|
||||
80
tools/patch-game-basevehicle.ps1
Normal file
80
tools/patch-game-basevehicle.ps1
Normal file
@@ -0,0 +1,80 @@
|
||||
param(
|
||||
[string]$GameRoot = "D:\SteamLibrary\steamapps\common\ProjectZomboid"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
$toolsDir = Join-Path $repoRoot ".tools"
|
||||
$classPatchDir = Join-Path $toolsDir "classpatch"
|
||||
$buildDir = Join-Path $toolsDir "patcher-build"
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $toolsDir | Out-Null
|
||||
New-Item -ItemType Directory -Force -Path $classPatchDir | Out-Null
|
||||
New-Item -ItemType Directory -Force -Path $buildDir | Out-Null
|
||||
|
||||
$javaExe = Join-Path $GameRoot "jre64\bin\java.exe"
|
||||
$gameJar = Join-Path $GameRoot "projectzomboid.jar"
|
||||
if (-not (Test-Path $javaExe)) { throw "java.exe not found at $javaExe" }
|
||||
if (-not (Test-Path $gameJar)) { throw "projectzomboid.jar not found at $gameJar" }
|
||||
|
||||
$ecjJar = Join-Path $toolsDir "ecj.jar"
|
||||
$asmJar = Join-Path $toolsDir "asm.jar"
|
||||
$asmTreeJar = Join-Path $toolsDir "asm-tree.jar"
|
||||
|
||||
if (-not (Test-Path $ecjJar)) {
|
||||
Invoke-WebRequest -Uri "https://repo1.maven.org/maven2/org/eclipse/jdt/ecj/3.38.0/ecj-3.38.0.jar" -OutFile $ecjJar
|
||||
}
|
||||
if (-not (Test-Path $asmJar)) {
|
||||
Invoke-WebRequest -Uri "https://repo1.maven.org/maven2/org/ow2/asm/asm/9.9/asm-9.9.jar" -OutFile $asmJar
|
||||
}
|
||||
if (-not (Test-Path $asmTreeJar)) {
|
||||
Invoke-WebRequest -Uri "https://repo1.maven.org/maven2/org/ow2/asm/asm-tree/9.9/asm-tree-9.9.jar" -OutFile $asmTreeJar
|
||||
}
|
||||
|
||||
$patcherSource = Join-Path $PSScriptRoot "java\BaseVehicleConstraintPatch.java"
|
||||
if (-not (Test-Path $patcherSource)) { throw "Missing patcher source: $patcherSource" }
|
||||
|
||||
& $javaExe -jar $ecjJar -17 -cp "$asmJar;$asmTreeJar" -d $buildDir $patcherSource
|
||||
if ($LASTEXITCODE -ne 0) { throw "Failed to compile BaseVehicleConstraintPatch.java" }
|
||||
|
||||
$inputClass = Join-Path $classPatchDir "BaseVehicle.original.class"
|
||||
$patchedClass = Join-Path $classPatchDir "BaseVehicle.patched.class"
|
||||
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
$zip = [System.IO.Compression.ZipFile]::OpenRead($gameJar)
|
||||
try {
|
||||
$entry = $zip.GetEntry("zombie/vehicles/BaseVehicle.class")
|
||||
if ($null -eq $entry) { throw "zombie/vehicles/BaseVehicle.class not found in $gameJar" }
|
||||
|
||||
$entryStream = $entry.Open()
|
||||
$fileStream = [System.IO.File]::Create($inputClass)
|
||||
try {
|
||||
$entryStream.CopyTo($fileStream)
|
||||
} finally {
|
||||
$fileStream.Close()
|
||||
$entryStream.Close()
|
||||
}
|
||||
} finally {
|
||||
$zip.Dispose()
|
||||
}
|
||||
|
||||
& $javaExe -cp "$buildDir;$asmJar;$asmTreeJar" BaseVehicleConstraintPatch $inputClass $patchedClass
|
||||
if ($LASTEXITCODE -ne 0) { throw "BaseVehicle class patch failed" }
|
||||
|
||||
$targetDir = Join-Path $GameRoot "zombie\vehicles"
|
||||
$targetClass = Join-Path $targetDir "BaseVehicle.class"
|
||||
$backupClass = "$targetClass.landtrain.original"
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $targetDir | Out-Null
|
||||
if (-not (Test-Path $backupClass)) {
|
||||
if (Test-Path $targetClass) {
|
||||
Copy-Item $targetClass $backupClass -Force
|
||||
} else {
|
||||
Copy-Item $inputClass $backupClass -Force
|
||||
}
|
||||
}
|
||||
|
||||
Copy-Item $patchedClass $targetClass -Force
|
||||
Write-Output "Patched BaseVehicle.class deployed to $targetClass"
|
||||
Write-Output "Backup stored at $backupClass"
|
||||
18
tools/restore-game-basevehicle.ps1
Normal file
18
tools/restore-game-basevehicle.ps1
Normal file
@@ -0,0 +1,18 @@
|
||||
param(
|
||||
[string]$GameRoot = "D:\SteamLibrary\steamapps\common\ProjectZomboid"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$targetClass = Join-Path $GameRoot "zombie\vehicles\BaseVehicle.class"
|
||||
$backupClass = "$targetClass.landtrain.original"
|
||||
|
||||
if (Test-Path $backupClass) {
|
||||
Copy-Item $backupClass $targetClass -Force
|
||||
Write-Output "Restored BaseVehicle.class from $backupClass"
|
||||
} elseif (Test-Path $targetClass) {
|
||||
Remove-Item $targetClass -Force
|
||||
Write-Output "Removed override class at $targetClass (game will use class from projectzomboid.jar)"
|
||||
} else {
|
||||
Write-Output "No override or backup found. Nothing to restore."
|
||||
}
|
||||
Reference in New Issue
Block a user