How would I change the Invoke client in this with making it still work?

A really helpful person helped me make a function that allows people to throw things.
He used a remote function and it went like this:

Local script:

local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local rfunc = script.Parent:WaitForChild('RemoteFunction')

rfunc.OnClientInvoke = function()  -- not safe
	return mouse.Hit.Position 
end

Normal script:

local tool = script.Parent
local part = tool.Handle
local rfunc = tool:WaitForChild('RemoteFunction')

local ratio = 2 
tool.Activated:Connect(function()
	
	local player = game.Players:GetPlayerFromCharacter(tool.Parent)
	local mousepos = rfunc:InvokeClient(player)
	local direction = (mousepos - tool.Parent.PrimaryPart.Position)
	local force = direction * ratio + Vector3.new(0, game.Workspace.Gravity * 0.5 / ratio, 0)
	
	tool.Parent = workspace
	part.AssemblyLinearVelocity = force
	

end)

It works amazingly well, but I’ve been trying to find a way to make it work while removing the invoke client. (although it doesnt really matter for the purpose that it is serving, I want to learn how to do this anyway)
I’ve watched multiple videos on remote events, functions, etc but no matter what I do i cant really get it to work.

I’ve been trying this for around one hour so but I cant and im really stumped

Thanks!

You can’t make it work if you remove the OnClientInvoke function. Also, Invoking on any side should be wrapped in a pcall():

local DidInvoke, Result = pcall(function()
    return RemoteFunction:InvokeClient() -- Could also be :InvokeServer()
end)

You can’t get the mouse’s worldspace position from a server script so at the very least you’ll need to use a RemoteEvent/RemoteFunction instance in order to facilitate communication between the server and the client, are you intending to use a RemoteEvent instance instead?

--CLIENT

local players = game:GetService("Players")
local player = players.LocalPlayer
local mouse = player:GetMouse()

local tool = script.Parent
local remote = tool:WaitForChild("RemoteEvent")

remote.OnClientEvent:Connect(function()
	remote:FireServer(mouse.Hit.Position)
end)
--SERVER

local players = game:GetService("Players")
local tool = script.Parent
local handle = tool.Handle
local remote = tool.RemoteEvent

tool.Activated:Connect(function()
	if not tool.Enabled then return end
	tool.Enabled = false
	
	local character = tool.Parent
	local player = players:GetPlayerFromCharacter(character)
	if player then
		remote:FireClient(player)
	end
end)

remote.OnServerEvent:Connect(function(player, mousePosition)
	local direction = (mousePosition - handle.Position)
	local force = direction * 2 + Vector3.new(0, workspace.Gravity / 4, 0)
	handle.AssemblyLinearVelocity = force
	tool.Parent = workspace
end)

doesn’t this need something in the parameter?

Yeah, just pass the player variable as an argument.

an Error im getting is

I’m pretty sure it might be impossible to change the invoke client with the script still working

This is terribly incorrect, and InvokeClient() is unsafe no matter what. Also, here’s the fixed version of @Forummer’s code.

Server: (client is fine)

local players = game:GetService("Players")
local tool = script.Parent
local handle = tool.Handle
local remote = tool.RemoteEvent

tool.Activated:Connect(function()
	if not tool.Enabled then return end
	tool.Enabled = false
	
	local character = tool.Parent
	local player = players:GetPlayerFromCharacter(character)
	if player then
		remote:FireClient(player)
	end
end)

remote.OnServerEvent:Connect(function(Player, mousePosition)
	local direction = (mousePosition - handle.Position)
	local force = direction * 2 + Vector3.new(0, workspace.Gravity / 4, 0)
	handle.AssemblyLinearVelocity = force
	tool.Parent = workspace
end)

edit: The “player” argument (1st argument) of the OnServerEvent connection wasn’t set; just for clarification.

Both local & server scripts were previously incorrect on my end, when I wrote the code I initially used FireAllClients() which doesn’t require the player argument on the firing or listening side, I later switched to FireClient() but forgot to include the player argument.

Just to let you know, you’d be better off using InvokeClient() for this while utilising the pcall() global.

--CLIENT

local players = game:GetService("Players")
local player = players.LocalPlayer
local mouse = player:GetMouse()

local tool = script.Parent
local remote = tool:WaitForChild("RemoteEvent")

remote.OnClientInvoke = function()
	return mouse.Hit.Position
end
--SERVER

local players = game:GetService("Players")
local tool = script.Parent
local handle = tool.Handle
local remote = tool.RemoteEvent

tool.Activated:Connect(function()
	if not tool.Enabled then return end
	tool.Enabled = false

	local character = tool.Parent
	local player = players:GetPlayerFromCharacter(character)
	if player then
		local success, result = pcall(function()
			return remote:InvokeClient(player)
		end)
		
		task.delay(10, function()
			if (result == nil) then
				player:Kick("Disconnected.")
			end
		end)
		
		if success then
			if result then
				local direction = (result - handle.Position)
				local force = direction * 2 + Vector3.new(0, workspace.Gravity / 4, 0)
				handle.AssemblyLinearVelocity = force
				tool.Parent = workspace
			end
		else
			warn(result)
		end
	end
end)

InvokeClient() can be made safe by kicking the player if they attempt to infinitely yield the invocation.

4 Likes

Thank you for linking this topic, I have a question, would wrapping the method you showed in a coroutine as well be useful? Just so other players aren’t affected if someone tries to yield and don’t have to wait the 10 seconds if a yielder is caught?

Just call whatever function the InvokeClient() method is called within in an alternate thread of execution.