Function returns immediately for some reason

So I have this function that starts when the player needs to aim to cast a certain skill. This function should return, when the player clicks the left mouse button or taps on the screen, a Vector3.
The problem I have is that for some reason when I call the function, it doesn’t yield what comes AFTER said function, which is important because it needs to wait for the Vector3 returned. So what happens is that, instead, it returns nil.

This is the function:

function M:clickToAim()
	local canSelect = true--TargetSelection:Start(), ignore this
	if canSelect == true then
		Service.CAS:SetTitle("startTargetSelection","Exit")
		
		if Service.UIS.TouchEnabled then
			click2Aim = Service.UIS.TouchTapInWorld:Connect(function(pos,ui)
				if ui then return end
				
				targetRaycast.FilterDescendantsInstances = {}
				local ray = workspace.Camera:ViewportPointToRay(pos.X, pos.Y)
				local result = workspace:Raycast(ray.Origin,ray.Direction*100)

				if result then
					click2Aim:Disconnect(); click2Aim = nil
					return result
				end
			end)
		else
			
			click2Aim = Service.UIS.InputBegan:Connect(function(input,isTyping)
				if isTyping then return end
	
				if input.UserInputType == Enum.UserInputType.MouseButton1 then
					local pos = Service.UIS:GetMouseLocation();
					local ray = workspace.Camera:ViewportPointToRay(pos.X, pos.Y)
					local result = workspace:Raycast(ray.Origin,ray.Direction*100)
					if result then
						warn(result)
						click2Aim:Disconnect(); click2Aim = nil
						TargetSelection:End()
						return result
					end
					
				end
			end)
			
		end
	end
end

Ideally the function should yield until I press the mouse button, so that it returns the raycast’s result value.

This is the function that calls the previous one, attached to a RemoteFunction, InvokeClient:

local function selectTarget(targetMode)
	if targetMode == "clickToAim" then
		local result = targeting:clickToAim() --should yield the function here until it returns the vector3 value i need, but it doesn't
		if result then
			print("pls work :))")
		else
			warn("oh me oh my") --instead this is printed
		end
		warn(result) --and this is equal to nil
		return result
	end
end

RemoteFunction.OnClientInvoke = selectTarget

Before I get people saying “but you shouldn’t be using onClientInvoke!!”: I know. I have a mechanic on the server-end of the system to prevent errors (for example, wrapping the whole thing in a pcall & a time limit for how long the server should wait for the client’s response).
I’m open to alternative systems to implement to avoid using OnClientInvoke of course, but I’m mostly interested in understand why my first function returns nil immediately instead of yielding until the player clicks\taps on the screen.

You made connected function which do not yield, the returns inside of connected functions do not return to outer functions. In fact returns inside of connected functions do nothing expect end the function early. You need to either use :Wait() or create a new bindable event and :Wait() on it.

I am not sure the best solution since you aren’t giving us why you are doing this, chances are you should just be using a :FireServer(localRaycast) to avoid OnClientInvoke

Ah, that makes a lot of sense actually… could you show me where to put the :Wait() exactly? I’m not sure where to place them to be honest.
I also did consider bindables but wanted to avoid them to keep the code less cluttered. If necessary, I’ll use them though.

To put it simply, my system works like this:

  • player presses skill keybind
  • client tells to server they selected X spell and the server checks if the player unlocked the skill and can use the skill.
  • If the conditions are met, the server will fire a RemoteFunction:InvokeClient(), where it’ll wait for the client to return where the player aimed to fire the skill in that direction.
  • if the position is returned within the time limit (15 seconds), the skill will be fired. Otherwise, the skill casting is canceled.

I used InvokeClient() because it made the most sense based on my needs tbh and it felt unnecessary to have 3-4 different remotes passing around data when I could’ve just used 2.

Since you have conditions in each connection it’s difficult to just use wait. Here’s an example of using a bindable event to wait for deeper connected result.

For your skill system I would say reducing the back-and-forth talking is very important, try out letting the client send both X spell and position at the same time. Do all the client side information gathering in one batch and all the server side checking in one batch.

  • player presses skill keybind
  • :FireServer(spellX, localRaycast)
  • Server checks conditions, done!

This list is much simpler and chances are players will know locally if they can use spells.

function M:clickToAim()
	local resultEvent = Instance.new("BindableEvent")

	Service.CAS:SetTitle("startTargetSelection","Exit")

	if Service.UIS.TouchEnabled then
		click2Aim = Service.UIS.TouchTapInWorld:Connect(function(pos,ui)
			if ui then return end

			targetRaycast.FilterDescendantsInstances = {}
			local ray = workspace.Camera:ViewportPointToRay(pos.X, pos.Y)
			local result = workspace:Raycast(ray.Origin,ray.Direction*100)

			if result then
				click2Aim:Disconnect(); click2Aim = nil
				resultEvent:Fire(result)
			end
		end)
	else
		click2Aim = Service.UIS.InputBegan:Connect(function(input,isTyping)
			if isTyping then return end

			if input.UserInputType == Enum.UserInputType.MouseButton1 then
				local pos = Service.UIS:GetMouseLocation();
				local ray = workspace.Camera:ViewportPointToRay(pos.X, pos.Y)
				local result = workspace:Raycast(ray.Origin,ray.Direction*100)
				if result then
					warn(result)
					click2Aim:Disconnect(); click2Aim = nil
					TargetSelection:End()
					resultEvent:Fire(result)
				end
			end
		end)
	end
	
	-- waiting for result extracted by :Fire
	local result = resultEvent.Event:Wait()
	resultEvent:Destroy()
	return result
end
1 Like