--[[ verify_rwd.luau -- verify the RWD steering fix, bypassing the require cache. check_live_source proved the pushed source IS in the DataModel (40307 bytes, RWD marker present, single copy). But diag_steering still saw 4 motors because require() caches per module INSTANCE for the life of the Play session, and CarManager was first required before the push -- so the old build closure was still running. Clone() produces a NEW instance, so require(clone) re-executes the current source. That gives a true read of the fix without restarting Play mode. ]] local ServerScriptService = game:GetService("ServerScriptService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local RunService = game:GetService("RunService") local out = {} local function log(...) local parts = {} for i = 1, select("#", ...) do parts[#parts + 1] = tostring((select(i, ...))) end table.insert(out, table.concat(parts, " ")) end local Shared = ReplicatedStorage:FindFirstChild("Shared") local Cars = require(Shared.Cars) local Chassis = require(Shared.Chassis) local orig = ServerScriptService:FindFirstChild("Server", true):FindFirstChild("CarManager") local clone = orig:Clone() clone.Parent = orig.Parent local CarManager = require(clone) log("required a FRESH clone of CarManager (" .. #clone.Source .. " bytes)") local pad = Instance.new("Part") pad.Size = Vector3.new(6000, 8, 6000) pad.Position = Vector3.new(0, 5000, 0) pad.Anchored = true pad.Material = Enum.Material.Concrete pad.Parent = workspace local padTop = pad.Position.Y + 4 local car = Cars.Get("gtr_r34") local tuning = Chassis.tuningFor(car) local model = CarManager._buildForDiag(car, tuning) model.Parent = workspace model:PivotTo(CFrame.new(0, padTop + 3, 0)) local servos, motors = {}, {} for _, d in ipairs(model:GetDescendants()) do if d:IsA("HingeConstraint") then if d.Name == Chassis.STEER_SERVO_NAME then table.insert(servos, d) elseif d.Name == Chassis.WHEEL_MOTOR_NAME then table.insert(motors, d) end end end local motorCorners = {} for _, mo in ipairs(motors) do table.insert(motorCorners, mo.Parent and mo.Parent.Name or "?") end log(string.format("servos %d | motors %d [%s]", #servos, #motors, table.concat(motorCorners, ", "))) log( string.format( "expected: 2 servos, 2 motors on REAR corners only -> %s", (#motors == 2 and #servos == 2) and "OK" or "WRONG" ) ) log("") local body = model.PrimaryPart for _ = 1, 90 do RunService.Heartbeat:Wait() end -- Drive the rears and hold a steady steer angle, exactly like diag_steering. local TARGET = -14 local wheelR = Chassis.WHEEL_RADIUS local topW = tuning.MaxWheelAngularVelocity log("t(s) speed yawRate targetAng curAng radius") local samples = 0 local errSum, errMax = 0, 0 local startLook = body.CFrame.LookVector for step = 1, 300 do for _, mo in ipairs(motors) do mo.MotorMaxTorque = tuning.MotorMaxTorque mo.AngularVelocity = -topW end for _, s in ipairs(servos) do s.TargetAngle = TARGET end if step % 30 == 0 then local speed = body.AssemblyLinearVelocity.Magnitude local yaw = body.AssemblyAngularVelocity.Y local cur = servos[1].CurrentAngle local radius = (math.abs(yaw) > 1e-4) and (speed / math.abs(yaw)) or math.huge log(string.format("%4.1f %6.1f %7.3f %8.1f %8.1f %9.1f", step / 60, speed, yaw, TARGET, cur, radius)) samples += 1 local e = math.abs(cur - TARGET) errSum += e errMax = math.max(errMax, e) end RunService.Heartbeat:Wait() end local finalLook = body.CFrame.LookVector local heading = math.deg(math.acos(math.clamp(startLook:Dot(finalLook), -1, 1))) log("") log(string.format("SERVO tracking: mean err %.2f deg, max %.2f deg", errSum / samples, errMax)) log(string.format("heading change over run: %.1f deg", heading)) local speed = body.AssemblyLinearVelocity.Magnitude local yaw = math.abs(body.AssemblyAngularVelocity.Y) local actualR = (yaw > 1e-4) and (speed / yaw) or math.huge local wheelbase = Chassis.WHEELBASE or 11.8 local ackR = wheelbase / math.tan(math.rad(math.abs(servos[1].CurrentAngle))) log( string.format( "Ackermann radius at %.1f deg = %.1f | ACTUAL %.1f | understeer %.2fx", servos[1].CurrentAngle, ackR, actualR, actualR / ackR ) ) model:Destroy() pad:Destroy() clone:Destroy() return table.concat(out, "\n")