Accessing player's mouse through a server script

-- Local script for firing player's mouse
local LAZAR = script.Parent
local Fire_LAZAR_Remote = script:WaitForChild("Fire_LAZAR")

local player = game.Players.LocalPlayer
local Mouse = player:GetMouse()

local debounce = false

LAZAR.Activated:Connect(function()
	if debounce == true then return end
	debounce = true
	
	Fire_LAZAR_Remote:FireServer(Mouse)
	task.wait(10)
	debounce = false
end)
-- Server script for creating the lazar beam
local SoundService = game:GetService("SoundService")
local TweenService = game:GetService("TweenService")
local Debris = game:GetService("Debris")

local Fire_LAZAR_Remote = script.Parent
local LAZAR = Fire_LAZAR_Remote.Parent.Parent
local Handle = LAZAR:WaitForChild("Handle")

local Laser_Charge_Up = Handle:WaitForChild("Laser_Charge_Up")
local Laser_Fire = Handle:WaitForChild("Laser_Fire")
local Starting_Point = Handle:WaitForChild("Starting_Position")

local MAX_RANGE = 300

local function Toggle_Scripts(character, Toggle_Value)
	for i, v in character:GetDescendants() do
		if not v:IsA("LocalScript") or not v:IsA("Script") or v == script then continue end
		v.Enabled = Toggle_Value
	end
end

local function Fire_Laser_Sound(HRP)
	local Laser_Fire_Clone = Laser_Fire:Clone()
	Laser_Fire_Clone.Parent = HRP
	Laser_Fire_Clone:Play()
end

local function Laser_Warning(character, Humanoid)
	Toggle_Scripts(character, false)
	
	local Highlight = Instance.new("Highlight")
	Highlight.Parent = character
	Humanoid.WalkSpeed = 0
	
	task.wait(Laser_Charge_Up.TimeLength)
	
	Toggle_Scripts(character, true)
	Highlight:Destroy()
end

Fire_LAZAR_Remote.OnServerEvent:Connect(function(player, playerMouse)
	local character = player.Character
	local Humanoid = character:WaitForChild("Humanoid")
	local HRP = character:WaitForChild("HumanoidRootPart")
	
	local LAZAR_BEAM = Instance.new("Part")
	LAZAR_BEAM.Anchored = true
	LAZAR_BEAM.Color = Color3.fromRGB(0, 255, 255)
	LAZAR_BEAM.Material = Enum.Material.Neon
	LAZAR_BEAM.CanCollide = false
	LAZAR_BEAM.Size = Vector3.new(1, 1, 1)
	LAZAR_BEAM.Position = Starting_Point.WorldCFrame.Position + Vector3.new(0, 0, 6)
	
	local Laser_Charge_Up_Clone = Laser_Charge_Up:Clone()
	Laser_Charge_Up_Clone.Parent = HRP
	Laser_Charge_Up_Clone:Play()
	
	local Charge_LAZAR_Beam = TweenService:Create(LAZAR_BEAM, TweenInfo.new(Laser_Charge_Up_Clone.TimeLength, Enum.EasingStyle.Linear), {Size = Vector3.new(5, 5, 5)})
	Charge_LAZAR_Beam:Play()
	task.spawn(Laser_Warning, character, Humanoid)
	Charge_LAZAR_Beam.Completed:Wait()
	
	local RaycastParameters = RaycastParams.new()
	RaycastParameters.FilterType = Enum.RaycastFilterType.Exclude
	RaycastParameters.FilterDescendantsInstances = {LAZAR, character, LAZAR_BEAM}
	RaycastParameters.IgnoreWater = true
	
	local Direction = (playerMouse.Hit.Position - Starting_Point.WorldCFrame.Position)
	
	local RayResults = game.Workspace:Raycast(Starting_Point.WorldCFrame.Position, Direction, RaycastParameters)
	task.spawn(Fire_Laser_Sound, HRP)
	
	LAZAR_BEAM.CFrame = CFrame.lookAt(Starting_Point.WorldCFrame.Position, playerMouse.Hit.Position)
	
	if not RayResults then
		LAZAR_BEAM.Size = Vector3.new(5, 5, MAX_RANGE)
	else
		LAZAR_BEAM.Size = Vector3.new(5, 5, RayResults.Distance)
	end
	
	local Disappear_Tween = TweenService:Create(LAZAR_BEAM, TweenInfo.new(Laser_Fire.TimeLength, Enum.EasingStyle.Linear), {Transparency = 1})
	Disappear_Tween:Play()
	Disappear_Tween.Completed:Wait()
	
	LAZAR_BEAM:Destroy()
end)

I’m trying to create a laser beam where it has to charge up and then fires a beam so I’m attempting to access the player’s mouse in case their mouse position has changed while charging up the beam but it errored at line 68.

local Direction = (playerMouse.Hit.Position - Starting_Point.WorldCFrame.Position)

I’m not sure how to fix this?

1 Like

I’m not sure you can pass a mouse object like that directly to the server. I think your solution lies in a remoteFunction. You can ask the client where the mouse’s position on screen / in the workspace straight from the server.

Add a remoteFunction to the LAZAR’s parent. Please just name it remoteFunction for now.

Modified localscript (I included the whole script and added a callback for the RemoteFunction at the bottom):

-- Local script for firing player's mouse
local LAZAR = script.Parent
local Fire_LAZAR_Remote = script:WaitForChild("Fire_LAZAR")

local player = game.Players.LocalPlayer
local Mouse = player:GetMouse()

local debounce = false

LAZAR.Activated:Connect(function()
	if debounce == true then return end
	debounce = true
	
	Fire_LAZAR_Remote:FireServer(Mouse)
	task.wait(10)
	debounce = false
end)

-- this makes the remoteFunction do stuff
script.Parent.RemoteFunction.OnClientInvoke = function()
	return Mouse.Hit.Position
end

Modified script (I changed some stuff in the OnServerEvent):

Fire_LAZAR_Remote.OnServerEvent:Connect(function(player)
	local character = player.Character
	local Humanoid = character:WaitForChild("Humanoid")
	local HRP = character:WaitForChild("HumanoidRootPart")

	local LAZAR_BEAM = Instance.new("Part")
	LAZAR_BEAM.Anchored = true
	LAZAR_BEAM.Color = Color3.fromRGB(0, 255, 255)
	LAZAR_BEAM.Material = Enum.Material.Neon
	LAZAR_BEAM.CanCollide = false
	LAZAR_BEAM.Size = Vector3.new(1, 1, 1)
	LAZAR_BEAM.Position = Starting_Point.WorldCFrame.Position + Vector3.new(0, 0, 6)

	local Laser_Charge_Up_Clone = Laser_Charge_Up:Clone()
	Laser_Charge_Up_Clone.Parent = HRP
	Laser_Charge_Up_Clone:Play()

	local Charge_LAZAR_Beam = TweenService:Create(LAZAR_BEAM, TweenInfo.new(Laser_Charge_Up_Clone.TimeLength, Enum.EasingStyle.Linear), {Size = Vector3.new(5, 5, 5)})
	Charge_LAZAR_Beam:Play()
	task.spawn(Laser_Warning, character, Humanoid)
	Charge_LAZAR_Beam.Completed:Wait()

	local RaycastParameters = RaycastParams.new()
	RaycastParameters.FilterType = Enum.RaycastFilterType.Exclude
	RaycastParameters.FilterDescendantsInstances = {LAZAR, character, LAZAR_BEAM}
	RaycastParameters.IgnoreWater = true
	
	-- here's the change: we get mouseHit from the remoteFunction
	local mouseHit = script.Parent.RemoteFunction:InvokeClient(player)
	local Direction = (mouseHit - Starting_Point.WorldCFrame.Position)

	local RayResults = game.Workspace:Raycast(Starting_Point.WorldCFrame.Position, Direction, RaycastParameters)
	task.spawn(Fire_Laser_Sound, HRP)
	
	-- also this gets changed to be mouseHit
	LAZAR_BEAM.CFrame = CFrame.lookAt(Starting_Point.WorldCFrame.Position, mouseHit)

	if not RayResults then
		LAZAR_BEAM.Size = Vector3.new(5, 5, MAX_RANGE)
	else
		LAZAR_BEAM.Size = Vector3.new(5, 5, RayResults.Distance)
	end

	local Disappear_Tween = TweenService:Create(LAZAR_BEAM, TweenInfo.new(Laser_Fire.TimeLength, Enum.EasingStyle.Linear), {Transparency = 1})
	Disappear_Tween:Play()
	Disappear_Tween.Completed:Wait()

	LAZAR_BEAM:Destroy()
end)
1 Like

Wait, I’ve barely known how return works. Does mouseHit become the player’s Mouse.Hit.Position when the local script returns it?

1 Like

Exactly right. Return is a value that a function “returns”.

In the following example, addTheseTwoNumbers is a function that adds the numbers and returns them.

local function addTheseTwoNumbers(a, b)
    return a + b
end

local addedNumbers = addTheseTwoNumbers(5, 5)
print(addedNumbers)

When run, the program outputs 10. The function “returns” a + b, or in this case 5 + 5 (which is 10) and the program stores it in the variable addedNumbers.

To answer your question, yes, mouseHit is the variable made to store Mouse.Hit.Position.

This is technical and not quite related to the solution, but I felt it was necessary to include

Be careful in saying that MouseHit “becomes” the value. Here’s why:

local a = {3, 4}
local b = a

table.insert(a, 3)
print(b[1], b[2], b[3])

In this code example, b does “become” a. This is because a is a table. Actions taken on a will affect b. I believe this is because both a and b point to the same place in the memory. (At least, this is how it works in the coding language Python). The reason for this is because tables are mutable - they can be changed after being created. (That’s why table.insert() works).

This is not always the case!

local a = 1
local b = a

a = 2
print(b)

This outputs 1, not 2. When creating b, b does not “become” a, rather, b becomes a copy of a (in this case). This is what happens to strings too (and I think booleans as well). Variable types like these are called immutable - they can’t be changed after they’re created. When setting one variable (b) to another immutable variable’s value (a), b becomes a copy of a, and b’s location in memory is different from a’s location in memory. This means that actions taken on a will not affect b.

To sum up:

  1. Mutable variables can be changed after creation, and when setting one variable to another mutable variable, both variables will point to the same place in memory.
  2. Immutable variables cannot be changed after creation, and are copied when one variable is set to an immutable variable.

My main point here is to be careful in saying that one variable “becomes” another. In this case, mouse.Hit.Position returns a CFrame, which is an immutable data type. So mouseHit becomes a copy of mouse.Hit.Position.

1 Like
-- Local script
local LAZAR = script.Parent

local Fire_Lazar_Remote = script:WaitForChild("Fire_LAZAR")
local Obtain_Mouse_Pos_Function = script:WaitForChild("Obtain_Mouse_Pos")

local player = game.Players.LocalPlayer
local Mouse = player:GetMouse()

local debounce = false

LAZAR.Activated:Connect(function()
	if debounce == true then return end
	debounce = true
	
	Fire_Lazar_Remote:FireServer()
	
	task.wait(10)
	debounce = false
end)

Obtain_Mouse_Pos_Function.OnClientInvoke = function(player)
	print(Mouse.Hit.Position)
	return Mouse.Hit.Position
end
-- Server Script
local SoundService = game:GetService("SoundService")
local TweenService = game:GetService("TweenService")
local Debris = game:GetService("Debris")

local Fire_Beam_Script = script.Parent
local LAZAR = Fire_Beam_Script.Parent
local Handle = LAZAR:WaitForChild("Handle")

local Obtain_Mouse_Pos_Function = Fire_Beam_Script:WaitForChild("Obtain_Mouse_Pos")
local Fire_LAZAR_Remote = Fire_Beam_Script:WaitForChild("Fire_LAZAR")

local Laser_Charge_Up = Handle:WaitForChild("Laser_Charge_Up")
local Laser_Fire = Handle:WaitForChild("Laser_Fire")
local Starting_Point = Handle:WaitForChild("Starting_Position")
local Beam_Charge_Position = Handle:WaitForChild("Charge_Up_Position")

local LAZAR_Beam = script:WaitForChild("LAZAR_Beam")

local MAX_RANGE = 300

local function Toggle_Scripts(character, Toggle_Value)
	for i, v in character:GetDescendants() do
		if not v:IsA("LocalScript") or not v:IsA("Script") or v == script then continue end
		v.Enabled = Toggle_Value
	end
end

local function Fire_Laser_Sound(HRP)
	local Laser_Fire_Clone = Laser_Fire:Clone()
	Laser_Fire_Clone.Parent = HRP
	Laser_Fire_Clone:Play()
end

local function Laser_Warning(character, Humanoid)
	Toggle_Scripts(character, false)

	local Highlight = Instance.new("Highlight")
	Highlight.Parent = character
	Humanoid.WalkSpeed = 0

	task.wait(Laser_Charge_Up.TimeLength)

	Toggle_Scripts(character, true)
	Highlight:Destroy()
	Humanoid.WalkSpeed = 16
end

Fire_LAZAR_Remote.OnServerEvent:Connect(function(player)
	local character = player.Character
	local Humanoid = character:WaitForChild("Humanoid")
	local HRP = character:WaitForChild("HumanoidRootPart")

	local LAZAR_BEAM_Clone = LAZAR_Beam:Clone()
	LAZAR_BEAM_Clone.Transparency = 0
	LAZAR_BEAM_Clone.Position = Beam_Charge_Position.WorldCFrame.Position
	LAZAR_BEAM_Clone.Parent = game.Workspace

	local Laser_Charge_Up_Clone = Laser_Charge_Up:Clone()
	Laser_Charge_Up_Clone.Parent = HRP
	Laser_Charge_Up_Clone:Play()

	local Charge_LAZAR_Beam = TweenService:Create(LAZAR_BEAM_Clone, TweenInfo.new(Laser_Charge_Up_Clone.TimeLength, Enum.EasingStyle.Linear), {Size = Vector3.new(5, 5, 5)})
	Charge_LAZAR_Beam:Play()
	task.spawn(Laser_Warning, character, Humanoid)
	Charge_LAZAR_Beam.Completed:Wait()

	local RaycastParameters = RaycastParams.new()
	RaycastParameters.FilterType = Enum.RaycastFilterType.Exclude
	RaycastParameters.FilterDescendantsInstances = {LAZAR, LAZAR_BEAM_Clone, character}
	RaycastParameters.IgnoreWater = true

	local playerMouse_Pos = Obtain_Mouse_Pos_Function:InvokeClient(player)
	print(playerMouse_Pos)
	local Direction = playerMouse_Pos - Starting_Point.WorldCFrame.Position

	local RayResults = game.Workspace:Raycast(Starting_Point.WorldCFrame.Position, Direction, RaycastParameters)
	task.spawn(Fire_Laser_Sound, HRP)

	LAZAR_BEAM_Clone.CFrame = CFrame.lookAt(Starting_Point.WorldCFrame.Position, playerMouse_Pos)

	if not RayResults then
		LAZAR_BEAM_Clone.Size = Vector3.new(5, 5, MAX_RANGE)
	else
		LAZAR_BEAM_Clone.Size = Vector3.new(5, 5, RayResults.Distance)
	end

	local Disappear_Tween = TweenService:Create(LAZAR_BEAM_Clone, TweenInfo.new(Laser_Fire.TimeLength, Enum.EasingStyle.Linear), {Transparency = 1})
	Disappear_Tween:Play()
	Disappear_Tween.Completed:Wait()

	LAZAR_BEAM_Clone:Destroy()
end)


I tried your script and it errored

1 Like

Hmm. Based on what I read here I think the issue is that the localScript is firing the Fire_LAZAR event, so then the server invokes the Obtain_Mouse_Pos function, causing something funny to happen in the localScript.

Try putting the remoteFunction’s callback in its own localScript (I’ll call it mouseScript here). I recommend parenting mouseScript to starterPlayerScripts, although if that doesn’t work you should still be able to parent it to the tool (or anywhere that localScripts can run).

Make the localScript in the LAZAR this:

-- Local script
local LAZAR = script.Parent

local Fire_Lazar_Remote = script:WaitForChild("Fire_LAZAR")
local Obtain_Mouse_Pos_Function = script:WaitForChild("Obtain_Mouse_Pos")

local player = game.Players.LocalPlayer
local Mouse = player:GetMouse()

local debounce = false

LAZAR.Activated:Connect(function()
	if debounce == true then return end
	debounce = true
	
	Fire_Lazar_Remote:FireServer()
	
	task.wait(10)
	debounce = false
end)

-- take out the remoteFunction callback

and make mouseScript this:

local Mouse = player:GetMouse()
local Obtain_Mouse_Pos_Function = script:WaitForChild("Obtain_Mouse_Pos")

Obtain_Mouse_Pos_Function.OnClientInvoke = function(player)
	print(Mouse.Hit.Position)
	return Mouse.Hit.Position
end

I don’t have any changes for the server script. Please see if this works!

2 Likes

I only want it to be able to be used only for the player who has the tool though

1 Like

image
Explorer of LAZAR tool, if needed

1 Like

I don’t understand what you mean.

If you mean that you only want the mouseScript to be usable by the player using the tool, it’s a localscript and it will only run on that particular player’s device. Also, the client (player) will fire the event Fire_LAZAR, which will tell the server what player to invoke the Obtain_Mouse_Pos for, essentially making it possible for only the player with the tool to fire it and use mouseScript.

Also, looking at the explorer of the LAZAR tool, mouseScript needs to be parented to the tool and it needs to look like this:

local Mouse = player:GetMouse()
local Obtain_Mouse_Pos_Function = script.Parent.Fire_Beam_Position:WaitForChild("Obtain_Mouse_Pos")

Obtain_Mouse_Pos_Function.OnClientInvoke = function(player)
	print(Mouse.Hit.Position)
	return Mouse.Hit.Position
end

I don’t understand what you’re asking for, but does this help at all?

1 Like

Oh okay, I will try that! I’ll see if it works :thinking:

1 Like


I got the same error again?? (Roblox why)

1 Like

I’m not even sure what is causing this error? :thinking:

1 Like