Question on how to go about projectile windup

I have a little throw animation before I shoot my projectile and initially the event that started the throw animation would give the server the mouse.Hit.Position so I could shoot the projectile to that point. The problem with that is you could not redirect your mouse during the animation and the projectile would go to the position the mouse was at when the animation started.

What I have done now is have a second event that is fired by the local script after a short period after the initial event that starts the animation. This event is now the one that gives the direction instead of the one that starts the animation. It makes the projectile easier to use now but I am worried that exploiters could use this to make an instantaneous and unreactable projectile by firing the 2 events with no delay between them.

The only other solution I can think of is that the server Fires an event to the client once the animation is done and the client then fires the event back to the server to give it the position. I feel this will cause issues with latency however since it has to bounce between the client and server so much. How do you all think I should go about making my projectile feel good to use while keeping it safe from exploiters?

1 Like

I’m not rly qualified but maybe
task.spawn(function()
wait(animLength)
then do the stuff here
End)

(I’m still learning roblox studio so idk if that’ll help idk)

1 Like

Use Debounces to make sure only one thing runs at a time

local debounce = {}

local function function(player)
   if not debounce[player] then
        debounce[player] = true
        -- code here --
        task.wait(1)
        ---------------
        debounce[player = false
   end
end

If you need to cancel a function because you have a new function that will run instead you can use threads and cancel them.

-- Here we have a Railgun. When the player clicks it sends a signal to the server
-- and 3 seconds later it fires the railgun
-- If the player clicks twice in a row, we want to make sure the railgun only fires once

-- Use a thread with task.cancel to cancel functions
local thread = coroutine.create(function()end) --Dummy thread

local chargeTime = 2

local function onEvent(player)
    task.cancel(thread)
    thread = task.spawn(function()
        task.wait(chargeTime)
        fireRailgun()
    end)
end

fireRailgun.OnServerEvent:Connect(onEvent)
1 Like

I think you guys are misunderstanding me(or maybe I am misunderstanding you) but the problem is not that multiple things are running at the same time, my debouce is working perfectly fine, the problem is that if the player has there mouse at point A at the moment they hit the key to shoot the projectile and they move their mouse to point B during the windup animation, the projectile will still shoot to point A since the original event that fired sent over point As location. I currently have a what I think is a bandaid solution where the first event is fired to do the windup while the second event is what actually shoots the script. I need is to be this way because unless you can see 1 second into the future(the windup time), it is practically the same as having 1000ms of ping when trying to aim and instantaneous projectile.

My main concern with this solution is that an exploiter could create their own input where they fire the windup event then immediately fire the shoot event in quick succession and basically skip the entire windup animation. I have something similar to this in the local script :

	Connections.UISConnection = UIS.InputBegan:Connect(function(key,chat)

		if not chat and key.KeyCode.Name == script.AgingKnives.Value then
			
			local ID = game:GetService("HttpService"):GenerateGUID()

			game.ReplicatedStorage.RemoteEvents.AgingKnives:FireServer(ID)
			task.wait(1)	
	game.ReplicatedStorage.RemoteEvents.DirectionEvent:FireServer(root.Position,mouse.Hit.Position,ID)

		end

	end)

What an exploiter could do is this :

	Connections.UISConnection = UIS.InputBegan:Connect(function(key,chat)

		if not chat and key.KeyCode.Name == script.AgingKnives.Value then
			
			local ID = game:GetService("HttpService"):GenerateGUID()

			game.ReplicatedStorage.RemoteEvents.AgingKnives:FireServer(ID)
			task.wait()
			game.ReplicatedStorage.RemoteEvents.DirectionEvent:FireServer(root.Position,mouse.Hit.Position,ID)

		end

	end)

??

local delayTime = 1
local currentTime = tick()

Connections.UISConnection = UIS.InputBegan:Connect(function(key,chat)
	if not chat and key.KeyCode.Name == script.AgingKnives.Value then
		local ID = game:GetService("HttpService"):GenerateGUID()
		game.ReplicatedStorage.RemoteEvents.AgingKnives:FireServer(ID)

        repeat RunService.Heartbeat:Wait() until tick() - currentTime >= delayTime
   
    game.ReplicatedStorage.RemoteEvents.DirectionEvent:FireServer(root.Position,mouse.Hit.Position,ID)
	end
end)

How does that stop cheaters from running the code on their own script with no delay?

I have no clue but tick() returns the time passed.

That’s why I gave you this solution

Cancel the first function and only run the second function with the new mouse position