Remote Function Returning Nil?

Hey

So I am creating a gun system and the script needs to use a remote function to get the bullet direction from the player. The problem is that the remote function returns nil.

Server

tool.Activated:Connect(function()
	if equipped and tool.Parent:FindFirstChild("Humanoid") and not cooldown then
		tool.Parent.Humanoid:LoadAnimation(tool.animations.fire):Play(0)

		cooldown = true

		local endpos, startpos = rs.events.localShot:InvokeClient(game.Players:GetPlayerFromCharacter(tool.Parent))
		print(endpos, startpos) -- returns nil here
		rs.events.globalShot:FireAllClients(muzzle, endpos, startpos)

		local spreadVal = tool.Parent.PlayerValues.SpreadVal
		
		spreadVal.Value += spreadPerShot

		delay(0.4, function()
			cooldown = false
		end)
	end
end)

Local Script (it might not even be running):

rs.events.localShot.OnClientInvoke = function()
	print('12111') -- does not even print these number
	return mainMod.getRaySpread(char)
end

Module Script (also might not be running?):

function main.getRaySpread(char)
	print("ewjuhbdfgvb8ywbrnteb") -- does not print here either???
	local spreadVal = char.PlayerValues.SpreadVal
	
	local function getSpreadDirection(input)
		local sens = 100
		local screenSizeMid = Vector2.new(camera.ViewportSize.X / 2, camera.ViewportSize.Y / 2  - (game:GetService("GuiService"):GetGuiInset().Y))

		local upOff = (math.random(-input*sens,input*sens)/sens)
		local riOff = (math.random(-input*sens,input*sens)/sens)
		local ray = camera:ScreenPointToRay(screenSizeMid.X + riOff,screenSizeMid.Y + upOff)
		print(ray)
		return ray.Direction * 1000
	end

	local percent = spreadVal.Value / 10

	local tool = char:FindFirstChildOfClass("Tool")
	if tool and percent * 10 > tool.values.maxSpread.Value then
		percent = tool.values.maxSpread.Value / 10
	end

	local spreadInput = 5
	if spreadVal.Value > 10 then
		spreadInput = 40 
	elseif spreadVal ~= 0 then
		spreadInput += 35 * percent
	end

	if tool and tool.values.aiming.Value >= 1 then
		spreadInput = 0
	end

	return getSpreadDirection(spreadInput), camera.CFrame.Position
end

I think the problem is that the local script isn’t running when the remote function is invoked? How can I fix this?

2 Likes

the code logically looks correct, weird also client invoking is very unsafe and should not be used

edit: can you provide more code in the server since if the client doesnt get invoked the problem cant be in the client

1 Like
--// Variables \\--
local ts = game:GetService("TweenService")
local rs = game:GetService("ReplicatedStorage")

local tool = script.Parent.Parent
local bodyAttach = tool.BodyAttach
local muzzle = bodyAttach.Muzzle
local events = tool.events
local values = tool.values

local spreadPerShot = values.spreadPerShot.Value

local equipped = false
local isAiming = false
local cooldown = false

local currentTween = nil

--// Code \\--
function round(num)
	return math.ceil(num * 10) / 10
end

tool.Equipped:Connect(function()
	equipped = true
end)

tool.Unequipped:Connect(function()
	equipped = false
	values.aiming.Value = 0
	if currentTween ~= nil then
		currentTween:Pause()
		currentTween = nil
	end
end)

events.aiming.OnServerEvent:Connect(function(plr, bool)
	if equipped and plr == game:GetService("Players"):GetPlayerFromCharacter(tool.Parent) then
		if currentTween ~= nil then
			currentTween:Pause()
			currentTween = nil
		end

		local goal = 0 
		if bool then goal = 1 end

		local length
		if bool then 
			length = 0.5 - values.aiming.Value / 2
		else
			length = values.aiming.Value / 2
		end

		currentTween = ts:Create(values.aiming, TweenInfo.new(length , Enum.EasingStyle.Cubic), {Value = goal})
		currentTween:Play()
	end
end)

tool.Activated:Connect(function()
	if equipped and tool.Parent:FindFirstChild("Humanoid") and not cooldown then
		tool.Parent.Humanoid:LoadAnimation(tool.animations.fire):Play(0)

		cooldown = true

		local endpos, startpos = rs.events.localShot:InvokeClient(game.Players:GetPlayerFromCharacter(tool.Parent))
		print(endpos, startpos) -- returns nil here
		rs.events.globalShot:FireAllClients(muzzle, endpos, startpos)

		local spreadVal = tool.Parent.PlayerValues.SpreadVal
		
		spreadVal.Value += spreadPerShot

		delay(0.4, function()
			cooldown = false
		end)
	end
end)

This is the entire server script. I dont think there is anything else that could be interfering with the remote function. However, if remote functions are unsafe what else can I use to replace it? Maybe when I replace it the problem will go with it?

1 Like

Hi! My guess is that if it returns nil, then it probably means that it is unable to actually retrieve what you are looking from the remote event. Additionally, I would say in terms of the module script running, make sure you are requiring it in some server script so it can start running. I think perhaps a reason why it is returning nil, could be that the tool has not loaded yet by the time the script is running.

require(--path to module script)
1 Like

Hi,
I am already requiring it properly:

--// Variables \\--
local mods = rs.moduleScripts
local mainMod = require(mods:WaitForChild("main"))

I would use remote event because they are easier. However, I want the player to return a value to the server, and remote events cannot do that.

1 Like

I don’t think they are inherently unsafe, a good practice would be using sanity checks to make sure that exploiters don’t put values that are impossible to achieve. I think what @solidracer meant was that InvokeClient is not the best practice as the client can manipulate their data. I would say you can perhaps try to have the client fire a remote event when they meet a specific condition (I’m not sure on the specifics of your goal), then the server do some sanity checks rather than server depending on the client for information. I hope this helps!

1 Like

My goal is to get the player’ camera’s cframe, and calculate the spread of the bullet. I can only get that info locally, which is why I am using remote functions. I do not really care about sanity check because I just want to have a game I can play with friends. :wink:

1 Like

after doing research I read that invoke client shouldnt be used for callback functions, you should listen to mouse in client and use InvokeServer instead (InvokeServer is totally safe and fine)

or just use remote events and send mouse data to calculate in the server?

1 Like

remote functions arent unsafe its just the invoking client function that isnt considered safe

1 Like

Alright, I’m going to change my script so that it detects the tool activation from the client, and invokes the server with the info. I’ll get back to you afterwards!

1 Like

Why are you using invoke client to communicate a local script to a server script? Shouldn’t you use invoke server?

1 Like

No, it’s the other way around, I have the server communicating to the client. :wink:

1 Like

invoke client is a function that calls the OnClientInvoke function of the given player used when communicating server to client

2 Likes

Oh ok I see, in that case for your module script here is the code I used to get it to work (note I defined the function as getRaySpread instead of main.getRaySpread):

local rs = game:GetService("ReplicatedStorage")

local MainMod = require(rs["module"]:WaitForChild("main"))

MainMod.getRaySpread()

For Server to client, why not use FireClient(player, info)

1 Like

he doesnt use FireClient cuz he used InvokeClient to make the client calculate the info for the server to work, but he should normally do listen to events on client and send needed data to server and calculate in server instead

2 Likes

It worked! Thank you a ton. Here is the code:

Server:

rs.events.localShot.OnServerEvent:Connect(function(plr, endpos, startpos)
	if equipped and tool.Parent:FindFirstChild("Humanoid") and not cooldown and plr == game.Players:GetPlayerFromCharacter(tool.Parent) then
		tool.Parent.Humanoid:LoadAnimation(tool.animations.fire):Play(0)

		cooldown = true
		values.canShoot.Value = false

		rs.events.localShot:FireClient(plr)
		rs.events.globalShot:FireAllClients(muzzle, endpos, startpos)

		local spreadVal = tool.Parent.PlayerValues.SpreadVal
		
		if values.aiming.Value < 1 then
			spreadVal.Value += spreadPerShot
		end

		delay(0.4, function()
			cooldown = false
			values.canShoot.Value = true
		end)
	end
end)

Local:

tool.Activated:Connect(function()
	if values.canShoot.Value == false then return end
	
	local endpos, startpos = mainMod.getRaySpread(character)
	rs.events.localShot:FireServer(endpos, startpos)
end)
1 Like

looks good! as I dont know the whole code I cant really say you must make this addition but can you connect localShot and globalShot remotes as globalShot already gets fired to every player? Or is there a reason why localShot is fired for “plr” differently?

1 Like

i made local shot fire only for the player so i can apply recoil to the players camera, but adding a player parameter in the global shot event would let me check if the player is the one you shot, and then apply the recoil. so i guess it would lower the amount of events firing. thanks for the idea!

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.