if isServer() then return end if not TowBarMod then TowBarMod = {} end TowBarMod.Sync = TowBarMod.Sync or {} if TowBarMod.Sync._towSyncClientLoaded then return end TowBarMod.Sync._towSyncClientLoaded = true local function resolveVehicle(id) if not id then return nil end return getVehicleById(id) end local function ensureAttachment(vehicle, attachmentId) if not vehicle or not attachmentId then return false end local script = vehicle:getScript() if not script then return false end if script:getAttachmentById(attachmentId) ~= nil then return true end local wheelCount = script:getWheelCount() local yOffset = -0.5 if wheelCount > 0 then local wheel = script:getWheel(0) if wheel and wheel:getOffset() then yOffset = wheel:getOffset():y() + 0.1 end end local chassis = script:getPhysicsChassisShape() if not chassis then return false end local attach = ModelAttachment.new(attachmentId) if attachmentId == "trailer" then attach:getOffset():set(0, yOffset, -chassis:z() / 2 - 0.1) attach:setZOffset(-1) else attach:getOffset():set(0, yOffset, chassis:z() / 2 + 0.1) attach:setZOffset(1) end script:addAttachment(attach) return true end local function isLinked(vehicleA, vehicleB) if not vehicleA or not vehicleB then return false end return vehicleA:getVehicleTowing() == vehicleB and vehicleB:getVehicleTowedBy() == vehicleA end local function reconcilePairState(vehicleA, vehicleB, attachmentA, attachmentB) if TowBarMod.Utils and TowBarMod.Utils.updateAttachmentsForRigidTow then TowBarMod.Utils.updateAttachmentsForRigidTow(vehicleA, vehicleB, attachmentA, attachmentB) end local towingMd = vehicleA:getModData() local towedMd = vehicleB:getModData() local currentScript = vehicleB:getScriptName() if towingMd then towingMd["isTowingByTowBar"] = true vehicleA:transmitModData() end if towedMd then if towedMd.towBarOriginalScriptName == nil and currentScript ~= "notTowingA_Trailer" then towedMd.towBarOriginalScriptName = currentScript end if towedMd.towBarOriginalMass == nil then towedMd.towBarOriginalMass = vehicleB:getMass() end if towedMd.towBarOriginalBrakingForce == nil then towedMd.towBarOriginalBrakingForce = vehicleB:getBrakingForce() end towedMd["isTowingByTowBar"] = true towedMd["towed"] = true vehicleB:transmitModData() end vehicleB:setScriptName("notTowingA_Trailer") if TowBarMod.Hook and TowBarMod.Hook.setVehiclePostAttach then pcall(TowBarMod.Hook.setVehiclePostAttach, nil, vehicleB) end end local function applyAttachSync(args) if not args then return end local vehicleA = resolveVehicle(args.vehicleA) local vehicleB = resolveVehicle(args.vehicleB) if not vehicleA or not vehicleB then return end local attachmentA = args.attachmentA or "trailer" local attachmentB = args.attachmentB or "trailerfront" if not ensureAttachment(vehicleA, attachmentA) or not ensureAttachment(vehicleB, attachmentB) then return end if not isLinked(vehicleA, vehicleB) then vehicleA:addPointConstraint(nil, vehicleB, attachmentA, attachmentB) end reconcilePairState(vehicleA, vehicleB, attachmentA, attachmentB) end local function safeBreak(vehicle) if not vehicle then return end if vehicle:getVehicleTowing() == nil and vehicle:getVehicleTowedBy() == nil then return end vehicle:breakConstraint(true, true) end local function applyDetachSync(args) if not args then return end safeBreak(resolveVehicle(args.vehicleA)) safeBreak(resolveVehicle(args.vehicleB)) end local function onServerCommand(module, command, args) if module ~= "towbar" then return end if command == "forceAttachSync" then applyAttachSync(args) elseif command == "forceDetachSync" then applyDetachSync(args) end end Events.OnServerCommand.Add(onServerCommand)