Intresting

This commit is contained in:
2026-02-07 18:21:03 -05:00
parent afadd2e07c
commit 083e6db613
3 changed files with 131 additions and 16 deletions

View File

@@ -554,7 +554,32 @@ local function breakConstraintSafe(vehicle, reason)
local playerObj = getSpecificPlayer(0) local playerObj = getSpecificPlayer(0)
if playerObj ~= nil then if playerObj ~= nil then
sendClientCommand(playerObj, "towbar", "detachConstraint", { vehicle = vehicle:getId() }) local towingVehicle = vehicle
local towedVehicle = vehicle:getVehicleTowing()
if towedVehicle == nil then
local frontVehicle = vehicle:getVehicleTowedBy()
if frontVehicle ~= nil then
towingVehicle = frontVehicle
towedVehicle = vehicle
end
end
local detachCommand = "detachTowBar"
if TowBarMod and TowBarMod.Hook and TowBarMod.Hook.performDetachTowBar == nil then
detachCommand = "detachConstraint"
end
local args = {}
if towingVehicle ~= nil then
args.towingVehicle = towingVehicle:getId()
end
if towedVehicle ~= nil then
args.vehicle = towedVehicle:getId()
else
args.vehicle = vehicle:getId()
end
sendClientCommand(playerObj, "towbar", detachCommand, args)
return true return true
end end
return false return false
@@ -1208,9 +1233,19 @@ local function menuHasTowbarAttachSlice(menu)
return false return false
end end
local function getTowbarDetachAction()
if TowBarMod == nil or TowBarMod.Hook == nil then return nil end
return TowBarMod.Hook.deattachTowBarAction or TowBarMod.Hook.detachTowBarAction
end
local function getTowbarPerformDetachHook()
if TowBarMod == nil or TowBarMod.Hook == nil then return nil end
return TowBarMod.Hook.performDetachTowBar or TowBarMod.Hook.performDeattachTowBar
end
local function menuHasTowbarDetachSlice(menu) local function menuHasTowbarDetachSlice(menu)
if menu == nil or menu.slices == nil then return false end if menu == nil or menu.slices == nil then return false end
local detachAction = TowBarMod and TowBarMod.Hook and TowBarMod.Hook.deattachTowBarAction or nil local detachAction = getTowbarDetachAction()
if detachAction == nil then if detachAction == nil then
return false return false
end end
@@ -1288,7 +1323,8 @@ end
local function addLandtrainUnhookOptionToMenu(playerObj, vehicle) local function addLandtrainUnhookOptionToMenu(playerObj, vehicle)
if playerObj == nil or vehicle == nil then return end if playerObj == nil or vehicle == nil then return end
if TowBarMod == nil or TowBarMod.Hook == nil or TowBarMod.Hook.deattachTowBarAction == nil then return end local detachAction = getTowbarDetachAction()
if detachAction == nil then return end
local menu = getPlayerRadialMenu(playerObj:getPlayerNum()) local menu = getPlayerRadialMenu(playerObj:getPlayerNum())
if menu == nil then return end if menu == nil then return end
@@ -1300,7 +1336,7 @@ local function addLandtrainUnhookOptionToMenu(playerObj, vehicle)
menu:addSlice( menu:addSlice(
getText("ContextMenu_Vehicle_DetachTrailer", ISVehicleMenu.getVehicleDisplayName(towedVehicle)), getText("ContextMenu_Vehicle_DetachTrailer", ISVehicleMenu.getVehicleDisplayName(towedVehicle)),
getTexture("media/textures/tow_bar_detach.png"), getTexture("media/textures/tow_bar_detach.png"),
TowBarMod.Hook.deattachTowBarAction, detachAction,
playerObj, playerObj,
towedVehicle towedVehicle
) )
@@ -1538,7 +1574,7 @@ local function installLandtrainTowbarPatch()
return resolvedTowing, resolvedTowed return resolvedTowing, resolvedTowed
end end
local originalPerformDetach = TowBarMod.Hook.performDeattachTowBar local originalPerformDetach = getTowbarPerformDetachHook()
if originalPerformDetach and originalPerformDetach ~= TowBarMod.Hook._landtrainPerformDetachWrapper then if originalPerformDetach and originalPerformDetach ~= TowBarMod.Hook._landtrainPerformDetachWrapper then
local performDetachWrapper = function(playerObj, towingVehicle, towedVehicle) local performDetachWrapper = function(playerObj, towingVehicle, towedVehicle)
local resolvedTowingVehicle, resolvedTowedVehicle = resolveTowbarDetachPair(towingVehicle, towedVehicle) local resolvedTowingVehicle, resolvedTowedVehicle = resolveTowbarDetachPair(towingVehicle, towedVehicle)
@@ -1550,11 +1586,11 @@ local function installLandtrainTowbarPatch()
return return
end end
dumpTowState("performDeattachTowBar pre towing", resolvedTowingVehicle) dumpTowState("performDetachTowBar pre towing", resolvedTowingVehicle)
dumpTowState("performDeattachTowBar pre towed", resolvedTowedVehicle) dumpTowState("performDetachTowBar pre towed", resolvedTowedVehicle)
originalPerformDetach(playerObj, resolvedTowingVehicle, resolvedTowedVehicle) originalPerformDetach(playerObj, resolvedTowingVehicle, resolvedTowedVehicle)
dumpTowState("performDeattachTowBar post-original towing", resolvedTowingVehicle) dumpTowState("performDetachTowBar post-original towing", resolvedTowingVehicle)
dumpTowState("performDeattachTowBar post-original towed", resolvedTowedVehicle) dumpTowState("performDetachTowBar post-original towed", resolvedTowedVehicle)
clearLandtrainFrontLinkData(resolvedTowedVehicle) clearLandtrainFrontLinkData(resolvedTowedVehicle)
reconcileTowbarSplitAround(resolvedTowingVehicle) reconcileTowbarSplitAround(resolvedTowingVehicle)
@@ -1573,13 +1609,14 @@ local function installLandtrainTowbarPatch()
refreshTowBarState(resolvedTowedVehicle:getVehicleTowing()) refreshTowBarState(resolvedTowedVehicle:getVehicleTowing())
saveActiveLandtrainTowbarSnapshot() saveActiveLandtrainTowbarSnapshot()
dumpTowState("performDeattachTowBar post-reconcile towing", resolvedTowingVehicle) dumpTowState("performDetachTowBar post-reconcile towing", resolvedTowingVehicle)
dumpTowState("performDeattachTowBar post-reconcile towed", resolvedTowedVehicle) dumpTowState("performDetachTowBar post-reconcile towed", resolvedTowedVehicle)
end end
TowBarMod.Hook.performDetachTowBar = performDetachWrapper
TowBarMod.Hook.performDeattachTowBar = performDetachWrapper TowBarMod.Hook.performDeattachTowBar = performDetachWrapper
TowBarMod.Hook._landtrainPerformDetachWrapper = performDetachWrapper TowBarMod.Hook._landtrainPerformDetachWrapper = performDetachWrapper
ltLog("installLandtrainTowbarPatch patched TowBarMod.Hook.performDeattachTowBar") ltLog("installLandtrainTowbarPatch patched TowBarMod.Hook.performDetachTowBar")
end end
-- If vanilla detach sneaks into the radial menu, redirect it to Towbar timed detach. -- If vanilla detach sneaks into the radial menu, redirect it to Towbar timed detach.
@@ -1592,9 +1629,9 @@ local function installLandtrainTowbarPatch()
end end
local towbarDetachTarget = getTowbarDetachTargetVehicle(vehicleToCheck) local towbarDetachTarget = getTowbarDetachTargetVehicle(vehicleToCheck)
if playerObj ~= nil and towbarDetachTarget ~= nil local detachAction = getTowbarDetachAction()
and TowBarMod and TowBarMod.Hook and TowBarMod.Hook.deattachTowBarAction then if playerObj ~= nil and towbarDetachTarget ~= nil and detachAction ~= nil then
TowBarMod.Hook.deattachTowBarAction(playerObj, towbarDetachTarget) detachAction(playerObj, towbarDetachTarget)
return return
end end

View File

@@ -7,6 +7,7 @@ import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
@@ -17,6 +18,9 @@ import org.objectweb.asm.Opcodes;
*/ */
public final class BaseVehicleConstraintPatch { public final class BaseVehicleConstraintPatch {
private static final String TARGET_NAME = "addPointConstraint"; private static final String TARGET_NAME = "addPointConstraint";
private static final String CLINIT_NAME = "<clinit>";
private static final String VOID_NOARG_DESC = "()V";
private static final String PATCH_LOG_LINE = "[Landtrain][BaseVehiclePatch] BaseVehicle override enabled";
private static final String BREAK_DESC_OBJECT_BOOL = "(ZLjava/lang/Boolean;)V"; 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 BREAK_DESC_PRIMITIVE_BOOL = "(ZZ)V";
private static final String BASE_VEHICLE_OWNER = "zombie/vehicles/BaseVehicle"; private static final String BASE_VEHICLE_OWNER = "zombie/vehicles/BaseVehicle";
@@ -55,13 +59,19 @@ public final class BaseVehicleConstraintPatch {
+ inspectedAddPointMethods + inspectedAddPointMethods
+ ")"); + ")");
} }
if (!ensureClassInitLog(classNode)) {
throw new IllegalStateException("Failed to inject BaseVehicle class-init debug log");
}
ClassWriter writer = new ClassWriter(0); ClassWriter writer = new ClassWriter(0);
classNode.accept(writer); classNode.accept(writer);
Files.createDirectories(output.getParent()); Files.createDirectories(output.getParent());
Files.write(output, writer.toByteArray()); Files.write(output, writer.toByteArray());
System.out.println("Patched BaseVehicle.class; removed breakConstraint calls: " + removedCalls); System.out.println(
"Patched BaseVehicle.class; removed breakConstraint calls: "
+ removedCalls
+ ", class-init debug log: enabled");
} }
private static boolean isTargetAddPointConstraint(String methodDesc) { private static boolean isTargetAddPointConstraint(String methodDesc) {
@@ -102,4 +112,72 @@ public final class BaseVehicleConstraintPatch {
return patched; return patched;
} }
private static boolean ensureClassInitLog(ClassNode classNode) {
MethodNode clinit = null;
for (MethodNode method : classNode.methods) {
if (CLINIT_NAME.equals(method.name) && VOID_NOARG_DESC.equals(method.desc)) {
clinit = method;
break;
}
}
if (clinit == null) {
clinit = new MethodNode(Opcodes.ACC_STATIC, CLINIT_NAME, VOID_NOARG_DESC, null, null);
clinit.instructions = new InsnList();
clinit.instructions.add(createPatchLogInstructions());
clinit.instructions.add(new InsnNode(Opcodes.RETURN));
clinit.maxStack = 2;
clinit.maxLocals = 0;
classNode.methods.add(clinit);
return true;
}
if (hasPatchLog(clinit)) {
return true;
}
boolean inserted = false;
for (AbstractInsnNode node = clinit.instructions.getFirst(); node != null; ) {
AbstractInsnNode next = node.getNext();
if (node.getOpcode() == Opcodes.RETURN) {
clinit.instructions.insertBefore(node, createPatchLogInstructions());
inserted = true;
}
node = next;
}
if (inserted) {
clinit.maxStack = Math.max(clinit.maxStack, 2);
}
return inserted;
}
private static boolean hasPatchLog(MethodNode method) {
for (AbstractInsnNode node = method.instructions.getFirst(); node != null; node = node.getNext()) {
if (node instanceof LdcInsnNode ldc
&& ldc.cst instanceof String text
&& PATCH_LOG_LINE.equals(text)) {
return true;
}
}
return false;
}
private static InsnList createPatchLogInstructions() {
InsnList insns = new InsnList();
insns.add(new org.objectweb.asm.tree.FieldInsnNode(
Opcodes.GETSTATIC,
"java/lang/System",
"out",
"Ljava/io/PrintStream;"));
insns.add(new LdcInsnNode(PATCH_LOG_LINE));
insns.add(new MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
"java/io/PrintStream",
"println",
"(Ljava/lang/String;)V",
false));
return insns;
}
} }

Binary file not shown.