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?
--// 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?
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.
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!
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.
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?
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!
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)
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
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)
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?
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!