Multiple server authority game breaking bugs

Hello, I have been making a vehicle from scratch with server authority in mind and have found multiple bugs with the current implementation, with one being game breaking. Here is the place file where you can try out the chassis and test each bug: Server-Authoritive-Vehicle-Place.rbxl (101.5 KB)

To test the vehicle with server authority disabled, open ReplicatedStorage.CarSystem.CarSystemServer, jump to the onOccupantChanged function, then comment out the start/stop lines and uncomment the SetNetworkOwner line.

VectorForce instances causing levitation in live servers

The chassis runs under the realistic units provided from this webpage, specifically the provided gravity, weight and torque values. This works great, however changing Workspace.Gravity is not ideal because it affects character physics as well, so what the chassis does is instead make a VectorForce on every assembly to compensate, with the final result being the chassis’s physics behaving the same as if Workspace.Gravity was set to 35.

The bug I found occurs with server authority and in live servers only. When joining a game with these forces, the car floats up into the air, then is brought back down, then goes back up, repeating forever.

Prediction cannot handle moving attachments

The vehicle steers by changing an attachments orientation to a desired angle. The attachment is used as the top attachment for a SpringConstraint and a CylindricalConstraint. This method works great with SetNetworkOwner however it does not with Server Authorities prediction. This is not caused by input delay and you can confirm that by setting SteerTime to a longer number.

Pre-spawned vehicles appear in different locations in new public servers

In the test place provided, I have had multiple (albeit rare) cases where I spawned in, only to find the cars I positioned by spawn to be in a different place, maybe they somehow got flung there during server creation?

Getting out of a car sometimes lets you see the void for a split second

This one is pretty self explanatory.

Thanks for the report. We are looking into this and sorry for the trouble!

1 Like

We believe the vector forces bug should be better. Let us know if it’s still broken on you.

Still working on the other issues. Thx for your patience!

1 Like

Finally had some time to dive a bit deeper into this. Something I noticed with your vehicles is that you rely on a Luau cache for things like Steering.

Simulation.Cache = {} :: { [Model]: {
	CharacterParts: { [BasePart]: { [string]: boolean } },
	Connections: { RBXScriptConnection },
	States: { [string]: any }
} }

and

	const returning = math.abs(target) < math.abs(cache.SteerAmount) or target * cache.SteerAmount < 0
	const steerTime = returning and tune.ReturnTime or tune.SteerTime
	cache.SteerAmount = MoveToward(cache.SteerAmount, target, StepForTime(deltaTime, steerTime))

This kind of coding pattern doesn’t work with the current state of Server Authority because the prediction system has no access to your cache to roll it back. So what ends up happening that your Server and Client versions of the “Simulation” module diverge over time by storing this internal state that impacts future simulations, which permanently desynchronizes the system.

If you port most of these cached values that impact run-time simulation to Attributes, you will get a good Server Authoritative car.

For example I added an entry pointing to the Seat in your cache

		local cache = Simulation.Cache[vehicle].States
		cache.InputThrottle = seat:GetAttribute("Throttle") or 0
...
		cache.seat = seat

and then stored Attribute values in your steering script instead of using cache.SteerAmount.

	local steerAmount = cache.seat:GetAttribute("SteerAmount")
	if (not steerAmount) then
		steerAmount = 0
	end
	const returning = math.abs(target) < math.abs(steerAmount) or target * steerAmount < 0
	const steerTime = returning and tune.ReturnTime or tune.SteerTime
	steerAmount = MoveToward(steerAmount, target, StepForTime(deltaTime, steerTime))
	cache.seat:SetAttribute("SteerAmount", steerAmount)
	
	const speed = cache.Velocity.Magnitude * Units.StudToMetres * 3.6
	const angleScale = SampleCurve(tune.MaxAngleCurve, speed)

This lets Server Authority tech do it’s magic time travel in the background, rolling Attributes forward or backward as needed. This fixes the Orientation bug you were mentioning.

EDIT:
I didn’t have time to dive deeper into the code myself, but I was able to convince the Roblox AI Assistant in Studio to modify all of your code that uses the cached values to port them into using Attributes:

Server-Authoritive-Vehicle-Place_RobloxAIAssistedFix.rbxl (97.2 KB)
@VanillaEdge ^ this is a vibe-code fixed version of your car that works with Server Authority. I think it naively converted TOO MANY cache table values into Attributes, but it works really well.

It still mis predicts every frame, but I think this is because your EngineVelocity, EngineInstability and EngineRPM values are very fine-tuned such that they don’t deterministically calculate on Client and Server, so we’re always resimulating because of them. But the resimulation is perfect.
RobloxStudioBeta_zMgKfRrNGE

2 Likes

I understand the solution and will implement it a little differently, but I have a question.

Are the attributes constantly replicated from the server to each client with every change, or are they only replicated when a re-simulation needs to occur? And when they are sent, are only the differences included or is it all the attributes for that frame?

I am wondering if I will run into issues with network load as more features are added which require more attributes that need to be replicated, along with if I will need to clamp all the values to a smaller possible range so large values such as f32’s are not being sent due to some extra unnecessary decimals.

Only attributes that change are replicated from Server to Client.

So if you’re updating something every frame, it will be replicated from Server → Client whenever the replicator can. (This might depend on other load in the game)

Looking at your scripts, there are some configurational values that only initialize once when Module:Start() is called and those can remain cached values. It’s truly only the things you need to influence your frame to frame simulations that need to be put into Attributes.

1 Like

I found another bug with server authority mid way through swapping to attributes, I can only get this to happen in live servers, not studio, and it is the second seat related bug I have found.

Server-Authoritive-Vehicle-Place_SeatBug.rbxl (101.7 KB)