[Full Release] Ship Fair And Competitive Games with Server Authority

Are .Touched events used to trigger forces on the HumanoidRootPart officially supported using Server Authority? If so, what would be the best way to handle such an event to avoid unnecessary conflicts between the server and client states? For example, in the short clip provided, the force is applied correctly most of the time using the provided code. Is there a way to guarantee more stability by using a different approach instead?

--!strict
local Simulation = {}

local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

function Simulation.Touch(player: Player)
	local character = player.Character or player.CharacterAdded:Wait()
	local rootPart = character:WaitForChild("HumanoidRootPart") :: BasePart
	
	local player = Players:GetPlayerFromCharacter(character)
	if not player then return end
	
	rootPart:SetAttribute("Force", false)
	rootPart:SetAttribute("ForceCooldown", false)
	
	rootPart.Touched:Connect(function(otherPart: BasePart)
		if otherPart.Name == "MeshPart" and rootPart:GetAttribute("Force") == false and rootPart:GetAttribute("ForceCooldown") == false then
			rootPart:SetAttribute("ForceCooldown", true)
			task.spawn(function()
				rootPart:SetAttribute("Force", true)
				
				task.wait(1)
				
				rootPart:SetAttribute("ForceCooldown", false)
			end)
		end
	end)
end

function Simulation.Simulate(deltaTime: number)
	for _, player: Player in Players:GetPlayers() do
		local character = player.Character
		if not character then continue end
		local rootPart = character:FindFirstChild("HumanoidRootPart") :: BasePart
		if not rootPart then continue end
		
		if RunService:GetPredictionStatus(rootPart) == Enum.PredictionStatus.None then return end

		if rootPart:GetAttribute("Force") == true then
			rootPart:SetAttribute("Force", false)
			rootPart:ApplyImpulse(Vector3.new(0.0, rootPart.AssemblyMass * 70.0, 0.0))
		end
	end
end

function Simulation.Init()
	RunService:BindToSimulation(Simulation.Simulate)
	Players.PlayerAdded:Connect(Simulation.Touch)
	for _, player: Player in Players:GetPlayers() do
		Simulation.Touch(player)
	end
end

return Simulation

Additionally, for more complex physics forces/alterations to the character such as ragdolls (which are commonly used upon death across experiences), that use BallSocketConstraints and alter Motor6D’s, how would you ensure the effect is replicated and predicted when many events that are used to run and build the ragdoll are not allowed during a simulation callback from RunService:BindToSimulation?

This information would prove invaluable for developers working on such systems with Server Authority!

I think there’s a bug in Simulate: the GetPredictionStatus check uses return instead of continue, so it exits the entire function instead of skipping just that player. Try that first.

Next .Touched fires server-side, so the attribute isn’t set on the client until replication arrives and the client can’t predict. Try to connect .Touched client-side as well and set the attribute locally there. The client’s BindToSimulation will then apply the impulse immediately without waiting for server state.

For ragdolls: joint/constraint changes can’t happen inside BindToSimulation, that’s by design. Trigger the structural setup outside the simulation via Humanoid.Died or similar, let it replicate normally, and use BindToSimulation only for any force application. Full client-side prediction of ragdoll transitions isn’t supported today.

1 Like

update: i simulated ping on my windows pc with clumsy (clumsy, an utility for simulating broken network for Windows Vista / Windows 7 and above) and it ran well with 200-250 Data Ping (shown when pressing Shift F3), nearly flawlessly so if something in the code got changed in the meantime i guess that must have fixed it
to reproduce the rubber banding i was experiencing before i ran these settings on it


this simulates a way worse connection than i had back then, Data Ping shows about 2000 ms (thousand, not hundred), which again is way more than it was on my previous tests
so unless youre still getting reports or nothing changed in the code i would say its fixed now so good job

1 Like

thx for getting back to us. We’ve indeed been working hard to fix these issues, especially at high pings.

1 Like

I forgot to mention the module is required by both the client and sever, so there already was a .Touched event created for each. Thanks for the bug fix though, but the functionality remains the same. Don’t get me wrong though, the prediction is already fairly good and works most of the time really well. An absolutely powerful feature and will prove invaluable when my game releases.

EDIT: Resolved issue by handling collisions more effectively

However, what is tricky about ragdolls, and what makes them fundamentally challenging, especially alongside Server Authority, is that for a ragdoll state that turns on when the player touches a part, and then turns off after a cooldown, the server must run the simulation to determine where the player has ended up, because otherwise the client can lie about the final position of the ragdoll. Now without Server Authority, I had ragdolls running on the client for the super responsive physics simulation, but with them running on the server, the client tries to predict where each instance (in this case each body part) of the Character ends up, and creates many false predictions and causes jitter even while joint/constraint changes are made outside BindToSimulation.

An example shown here (the left is the server, and the right is the client. notice how the server runs the simulation beautifully, but the client jitters):

What is even more fascinating, and extremely odd (may even be bug behavior), is if running the ragdoll simulation on both the server and client, the ragdoll works perfectly and is predicted well on the FIRST ATTEMPT ONLY up until the moment of the unragdoll, after that, the character jitters violently until exiting the water, and any subsequent attempts will result in jitter in not only the swimming, but also the ragdoll as well (even though it worked on the first attempt!). Example shown here of first attempt:

I tried to explore other approaches to this problem, such as creating a fake “dummy” player to simulate the smooth ragdoll physics on the client, and use the HumanoidRootPart (or perhaps the Torso) for replication only, but have not yet finished an implementation that can stop the server from predicting other body parts from the original player character as well.

I am pretty certain I have come across a bug with either this new Server Authority system or the new AvatarJointsUpgrade. I thought this was because of my implementation at first, but after doing a ton of digging, I pinpointed the exact cause.

EDIT: See Changing isKinematic value on AnimationConstraints inside Limbs causes CanCollide value to Randomly Change

Changing isKinematic = false and then back to true again results in the Character being locked into a buggy state that causes many mispredictions, and can only be fixed upon respawn. I noticed as well that, for some reason, the CanCollide properties of each body part that had isKinematic changed gets turned on during each rollback (intended behavior would be off for the limbs when running), and then off again when the jittering stops. I am not sure if this is more related to Server Authority or the AvatarJointsUpgrade, because the server correctly shows the intended state and does not change any CanCollide values.

Additionally, it does not matter if the simulation is ran on both the server and client, or just the server, because the same result happens either way. I have no way of getting around this behavior, and it is not related to this bug report as functionality does not return. Please let me know if I need to file a bug report for this issue, or if this post is fine.

Here is a video example (watch the full clip to see the change in behavior):

Honestly, it’s pretty good, and I’ve tried it myself. But I decided not to use it because there are still too many client mispredictions, especially when it comes to collisions. A lot of players complained about lagging or blinking, when it wasn’t actually lag cuz it was just position corrections. I don’t want to sacrifice the players experience for something like this when the physics can still be validated manualy on server-side.

2 Likes

I’ve noticed I get significantly better prediction rates when my attributes are on BaseParts vs Folders. Is this a known quirk? It’s possible I just did something wrong, but the difference seems significant on my game.

Looks parts going through more physics are more likely to match attributes. Simply unanchoring a particular part made it mispredict less. Sadly this means I need to weld parts to my character if i want less resims. I can’t just leave unanchored parts holding critical state lying around, and they seem to predict better as they fall towards the void rather than just sit there, so they need welded to my character to undergo physics without falling into the void.

This means I need to rebuild the parts and attributes whenever I die, instead of keeping a static instance with all the states.