How to fix client-server inconsistencies with character models?

I’m probably going to do not a great job of describing this issue.

Basically sometimes in my game, there will be weird glitches where explosions will not kill the player - he/she will survive in their client view, but server-sided, it will appear that they remain in place. Meaning he/she can go on to kill other players while basically invisible, until he/she dies one way or another clientsided.

Is there a way I can write a client-sided script that will periodically check if said player’s client-sided character model is significantly further away than the server-sided one, and then have the client/server (I am unsure of which would be more appropriate) forcibly respawn them?

Obviously, the best way to fix this is to locate the issues in my scripts that could break client-server consistency, but until I get around to doing that (very busy), I would like to use a somewhat quick fix.

I’d also imagine that this said script could be useful for helping prevent certain exploits like ;cbring?

To expand on this issue, here are some images:


as you can see, the character model hovers in the air after supposedly being hit by the AA gun.

however,


the player continues to move around and be alive.

Oddly enough, the plane can be seen both client and server-sided, it’s just the character model stays stuck. At the right angle, an explosion hit will destroy both the plane and the character.

do you have a script that is constantly look for something and possibly that something gets destroyed when the player dies

What do you mean by this? I do not believe so

If it helps, this is the client sided script for the plane’s controls:

local Camera = game.Workspace.CurrentCamera
local VehicleValue = script:WaitForChild("Vehicle")
local Vehicle = VehicleValue.Value

function GetBankAngle(M) --This function calculates the Bank Angle
	local VSX,X = M.ViewSizeX,M.X
	local Ratio = (((VSX/2) - X)/(VSX/2))
	Ratio = (Ratio < -1 and -1 or Ratio > 1 and 1 or Ratio)
	return math.rad(Ratio * 80)
end

if Vehicle then
	local Player = game.Players.LocalPlayer
	local Character = Player.Character
	local Humanoid = Character:WaitForChild("Humanoid")
	local PlayerGui = Player:WaitForChild("PlayerGui")
	local PilotGui = PlayerGui:WaitForChild("Pilot")
	local Controls = PilotGui:WaitForChild("Controls")
	local Mouse = Player:GetMouse()
	local USINS = game:GetService("UserInputService")
	local Engine = Vehicle:WaitForChild("Engine")
	local Control = Vehicle:WaitForChild("Control")
	local BodyGyro = Engine:WaitForChild("BodyGyro")
	local BodyVelocity = Engine:WaitForChild("BodyVelocity")
	local Keys = unpack(require(Vehicle:WaitForChild("PilotScript"):WaitForChild("Keys")))
	local Down = false
	local ClickMove = false --If enabled, your jet will only move towards the last clicked location.
	if USINS.TouchEnabled then
		ClickMove = true
	end
	local ButtonControls = {}
	local LastHit
	local MinSpeed = Vehicle.speeds.min.Value --100
	local MaxSpeed = Vehicle.speeds.max.Value --400
	local Turn = 0
	local ForwardSpeed = 0
	Character:WaitForChild("Humanoid").Died:Connect(function()
		local KeyName = Enum.KeyCode.E
		Control:InvokeServer(KeyName,Character.Head.Position)
	end)
	USINS.InputBegan:connect(function(Key)
		if not USINS:GetFocusedTextBox()then
			local KeyName = Key and Key.KeyCode.Name
			for _,TryTable in pairs(KeyName and Keys or {})do
				for _,TryValue in pairs(type(TryTable) == "table" and TryTable or {TryTable})do
					if TryValue == KeyName then
						Control:InvokeServer(KeyName,Character.Head.Position)
						print(KeyName)
						break
					end
				end
			end
		end
	end)
	function OneOfControlsDown(Table)
		for _,Value in pairs(type(Table) == "table" and Table or {Table})do
			if USINS:IsKeyDown(Value)then
				return true
			end
		end
	end
	for _,Button in pairs(Controls:GetDescendants())do
		if Button:IsA("TextButton")then
			local Key = type(Keys[Button.Name]) == "table" and Keys[Button.Name][1] or Keys[Button.Name]
			Button.MouseButton1Down:connect(function()
				ButtonControls[Button.Name] = true
				if Key then
					Control:InvokeServer(Key,Character.Head.Position)
				end
			end)
			Button.MouseButton1Up:connect(function()
				ButtonControls[Button.Name] = false
			end)
			Button.MouseLeave:connect(function()
				ButtonControls[Button.Name] = false
			end)
		end
	end
	if ClickMove then
		Mouse.Button1Down:connect(function()
			Down = true
			LastHit = Mouse.Hit
			while Down do
				LastHit = Mouse.Hit
				wait()
			end
		end)
		Mouse.Button1Up:connect(function()
			Down = false
		end)
--		USINS.TouchTap:connect(function()
--			print("Tap down!")
--			LastHit = Mouse.Hit
--		end)
	end
	while VehicleValue.Value do
		local Wait = game["Run Service"].RenderStepped:wait()
		if BodyVelocity.MaxForce == Vector3.new(math.huge,math.huge,math.huge) and not USINS:GetFocusedTextBox()then
			Mouse.TargetFilter = game.Workspace
			local GoingStraight = ButtonControls.Speed or OneOfControlsDown(Keys.Speed)
			local Left = ButtonControls.Left or OneOfControlsDown(Keys.Left)
			local Right = ButtonControls.Right or OneOfControlsDown(Keys.Right)
			local speed = Vehicle.speeds.current.Value
			if ButtonControls.Fire then
				Humanoid.Jump = true
			end
			if Left then
				Turn = Turn+Wait
			end
			if Right then
				Turn = Turn-Wait
			end
			if not(Left or Right)then
				Turn = Turn*.9
			end
			if GoingStraight then
				--ForwardSpeed = math.min(MaxSpeed,math.max(1,ForwardSpeed)*(1+Wait))
				ForwardSpeed = speed
			else
				--ForwardSpeed = math.max(MinSpeed,ForwardSpeed*.9)
				ForwardSpeed = speed
			end
			local BankAngle = GetBankAngle(Mouse)
			BodyVelocity.Velocity = Engine.CFrame.LookVector*ForwardSpeed+Engine.CFrame.RightVector*Turn
			if USINS.TouchEnabled then
				BodyGyro.CFrame = CFrame.new(Engine.Position,ClickMove and (LastHit and LastHit.Position) or Mouse.Hit.Position)*CFrame.Angles(0,math.atan(Turn),Turn) --Camera.CFrame
			else
				BodyGyro.CFrame = CFrame.new(Engine.Position,ClickMove and (LastHit and LastHit.Position) or Mouse.Hit.Position)*CFrame.Angles(0,math.atan(Turn),BankAngle) --Camera.CFrame
			end
		end
	end
else
	warn("Uh... where is the vehicle?",VehicleValue:GetFullName())
end

I believe the only relevant part of the script is while VehicleValue.Value do and after. If it’s necessary, I have the server-sided code too.

Continuing on, this is the AI antiaircraft script’s targeting system that will still pick up the player in the plane, even though the player is hovering near by it:

while wait(0.5) do
	if not on then repeat wait(1) until on end
	local target = findPlayer()
	if hrp and target ~= nil then
		--if object.Position.Y >= 14 then
			hrp:SetPrimaryPartCFrame(CFrame.new(hrp.MAIN.CFrame.p, target.CFrame.p))
			local shootfrom = math.random(1,8)
		local hole = script.Parent.turret["b"..shootfrom]
		print("???")
		local ray = Ray.new(hole.CFrame.p, (hole.CFrame.p - target.CFrame.p).unit * 100)
		local part, position = workspace:FindPartOnRayWithIgnoreList(ray, ignore_list, false, true)
		
		local rocket = Instance.new("Part", script.Parent)
		rocket.BrickColor = BrickColor.new("Black")
		rocket.FormFactor = "Custom"
		rocket.Anchored = true
		rocket.Locked = true
		rocket.CanCollide = false
		rocket.Name = "Tracer"
		rocket.Transparency = 0
		rocket.Size = Vector3.new(1,1,3)
		rocket.CFrame = script.Parent.turret.MAIN.CFrame
		rocket.CFrame = CFrame.new(hole.CFrame.p, target.CFrame.p)
		script.Mesh:Clone().Parent = rocket
		
		local dir = CFrame.new(rocket.CFrame.p,rocket.CFrame.p + rocket.CFrame.lookVector).lookVector
			
		rocket.Velocity = dir * 1250
		
		local bf = Instance.new("BodyForce", rocket)
		bf.force = Vector3.new(0, rocket:GetMass() * workspace.Gravity, 0)
		
		rocket.Anchored = false
		
		rocket.Touched:connect(function(hit)
			if hit.Parent.Name:lower() == "turret" then return end
			local ex = Instance.new("Explosion",workspace)
			ex.Position = rocket.Position
			ex.BlastPressure = 10000
			ex.BlastRadius = 7
			ex.Hit:Connect(function(hit)
				if hit.Parent:FindFirstChild("Humanoid") then
					local creator = Instance.new("ObjectValue",hit.Parent.Humanoid)
					local debris = game:GetService("Debris")
					local plr = game.Players:WaitForChild(owner.Value)
					if owner and plr then
						creator.Value = game.Players[owner.Value]	
					end
					debris:AddItem(creator,3)
				end
			end)
			local s = Instance.new("Sound",hit)
			s.SoundId = "rbxasset://sounds/collide.wav"
			s.PlayOnRemove = true
			s.Volume = 1
			s:Play()
			rocket:Destroy()
		end)
		--end
	end
end
```lua

I think there is a script that when the player dies runs and runs in a loop forever not allowing anything else to run (just a though tho)

Maybe, but when a player dies from anything else this does not happen. It’s 50-50 with getting shot down by this AA gun.

In addition, I made an edit to the server-sided pilot code that was supposed to detect differences between the server character model and client one:

if Humanoid.Parent:FindFirstChild("Head") then
		local head = Humanoid.Parent.Head
		print(client)
		print(head.Position)
		if math.abs((client - head.Position).Magnitude) > 25 then
			print("error occurred, fixing...")
			local plrre = game.Players:GetPlayerFromCharacter(Humanoid.Parent)
			plrre:LoadCharacter()
		end
	end

This code would say that the position between the client head and server head was the same, even though that was false.

Any updates on this issue? I also get this problem with explosion instances.

The update is, that you cannot use ROBLOX’s default explosion as the sole way to damage players. You will have to add code to whatever program you have handling explosions, in where you will have to use Explosion.Hit:Connect() and have lines to break the hit object’s joints or do damage, manually.

1 Like

Awesome, thanks for replying. I wouldn’t have thought of doing that. I’ll test that out.

1 Like

Let me know how it goes for you!