How would I make a CFrame set on the client translate to the server

So I’ve made a PropSystem which works well, apart from the fact that it doesn’t register collisions. I’ve realised this is because most of it works on the client for reasons which are too long and boring for me to say but all I know is it won’t work on the server as well as it does on the client.

The basic way it works is that, when picked up, on the client the prop is moved to the location of a part which is constantly moved to an offset of the camera. Now this is great, apart from the fact that you can just move the prop through walls and it won’t collide with anything except from the player itself.

Really, all I want to know is how do I move the CFrame of the prop to the server easily? (I’m pretty sure the answer will be “you will have to rewrite it” but I thought I might as well try).

Scripts:

PropHandler (server, parented to the prop itself
local Players = game:GetService("Players")
local ServerScriptService = game:GetService("ServerScriptService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local StarterPlayer = game:getService("StarterPlayer")
local StarterCharacterScripts = StarterPlayer.StarterCharacterScripts
local RunService = game:GetService("RunService")

local PromptTriggeredEvent = ReplicatedStorage:WaitForChild("PropSystemPromptTriggered")
local PropThrowEvent = ReplicatedStorage:WaitForChild("PropThrow")
local Prop = script.Parent
local Prompt = Prop.ProximityPrompt
local Attachment0 = Prop.Attachment0
local IsPropHeld = false

Players.PlayerAdded:Connect(function(Player)
	Player.CharacterAdded:Connect(function(Character)
	    local VectorForce = Character.PropClientHandler:WaitForChild("VectorForce")
	        Prompt.Triggered:Connect(function()
	    	VectorForce.Attachment0 = Attachment0
	    	PromptTriggeredEvent:FireClient(Player)
	    	print("Prompt Triggered")
		end)
	end)
end)
PropClientHandler (client, parented to StarterCharacterScripts)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ContextActionService = game:GetService("ContextActionService")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")

local PromptTriggeredEvent = ReplicatedStorage.PropSystemPromptTriggered
local PropThrowEvent = ReplicatedStorage.PropThrow
local Camera = workspace.CurrentCamera
local IsPropHeld = false
local PropThrow = false
local RunServiceConnection
local PropPositionCFrame = Instance.new("CFrameValue")

local LocalPlayer = Players.LocalPlayer
local Character = LocalPlayer.Character
local PropPosition = Character:WaitForChild("PropPosition")
local Motor6D = PropPosition.PropMotor6D

local function OnPropThrow()
	if PropThrow == true then
		PropThrowEvent:FireServer()
	end
end

local function OnPromptTriggered(Player)
	local VectorForce = Character.PropClientHandler.VectorForce
	local Prop = VectorForce.Attachment0.Parent
	-- This is the stupidest, smartest thing I have ever done
	if Prop == nil then
		return warn("Prop could not be determined")
	end
	
	local function OnRenderStepped(dt)
		local Value = 0
		for i = 0, 1, 0.001 do 
			wait()
			Prop.CFrame = Prop.CFrame:Lerp(PropPosition.CFrame, i) 
			Value = Value + 0.1
			if Prop.CFrame == CFrame.new(math.round(PropPosition.CFrame.X), math.round(PropPosition.CFrame.Y), math.round(PropPosition.CFrame.Z)) then
				Motor6D.Part0 = Prop
				print(Motor6D.Part0)
				Prop.CFrame = Prop.CFrame
			end
		end
	end
	
	if IsPropHeld == false then
		RunService:BindToRenderStep("ControlScriptRenderstep", Enum.RenderPriority.Input.Value, function(dt)
			OnRenderStepped(dt)
		end)
	elseif IsPropHeld == true then
		RunService:UnbindFromRenderStep("ControlScriptRenderstep")
		Motor6D.Part0 = nil
		IsPropHeld = false
	else 
		warn("Prop status could not be determined by client") 
		Motor6D.Part0 = nil
		IsPropHeld = false
	end
end

local function BindContextActions()

	local HandlePropThrow = function(ActionName, InputState, InputObject)
		PropThrow = (InputState == Enum.UserInputState.Begin) and true or false
		OnPropThrow()
		return Enum.ContextActionResult.Pass
	end

	ContextActionService:BindActionAtPriority("PropThrow", HandlePropThrow, false,
		3, Enum.UserInputType.MouseButton1)
end

function UnbindContextActions()

	ContextActionService:UnbindAction("PropThrow")

end

PromptTriggeredEvent.OnClientEvent:Connect(OnPromptTriggered)
BindContextActions()

PropThrow does not work yet, just ignore it

These scripts will probably have a bunch of leftover stuff from other attempts at working around the server-client boundary, sorry about that

Thanks, all help is appreciated

(please don’t suggest using remote functions/remote events, I have tried many times to use them and unfortunately due to the need to pass a CFrame through them every single frame it makes the game too laggy)

Maybe this will help. :slight_smile:

This would work and I’ve tried this, only problem is I need to constantly send the CFrame through a RemoteEvent every single frame (i.e. using RenderStepped or Heartbeat) to account for the player moving, which is where the lag is coming from. If the prop was moving to a static position then this would work perfectly, but sadly not.

Thanks anyway

1 Like

You can use NetworkOwnership to make the client have control over the part’s physics, and then do collision checks on the server potentially. You can find the article for NetworkOwnership here.

I hope this helps!

I was thinking about doing this but it would mean having every single part with the client as the NetworkOwner. It would definitely work (this is for a singleplayer game, so doing stuff on the client is preferable) it’s just I have no idea whether that’s actually efficient or not.

I’ll try this and get back to you.