Intresting
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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.
Reference in New Issue
Block a user