Do you code dashing on the server side or client side?

Right now, I have dashing for my sword game coded on the server, but if there is a slight bit of lag it is a pain to dash… is it recommended to make dashing on the client side?
Here is the current server side code for dashing…

	local dash_data = {
		disable_block_function = false,
		debounce = false,
		other_debounce = false,
		cd = 3,
		other_cd = 1,
		block_cd = 0.001,
		block_debounce = false,
		block_size = Vector3.new(1,1,1),
		len = 0.5,
		other_len = 0.25,
		rate = 200
	}
	self.dash_state = dash_data

	function self:dash()
		local d
		local k

		local done = false
		if self.Player then
			game.ReplicatedStorage.ClientGet:FireClient(self.Player, {
				"workspace",
				"CurrentCamera",
				"CFrame"
			})
			game.ReplicatedStorage.ClientGet.OnServerEvent:Connect(function(p, cf)
				if p ~= Player then
					return
				end

				done = true
				d = cf
			end)

			repeat
				task.wait()
			until done

			local md: Vector3 = self.char.Humanoid.MoveDirection

			local vecs = {front= d.LookVector.Unit, back= d.LookVector.Unit*-1, left= d.RightVector.Unit*-1, right= d.RightVector.Unit}
			local winningClosest
			local winningVector: Vector3

			for i,v in vecs do
				if not winningVector or (winningVector - md).Magnitude > (v - md).Magnitude then
					winningVector = v
					winningClosest = i
				end
			end

			k = winningClosest				
			if md == Vector3.zero then
				k = "front"
			end

		else
			k = "front"
		end
		
		if if k == "front" then self.dash_state.debounce else self.dash_state.other_debounce then
			return
		end

		if k == "front" then
			self.dash_state.debounce = true
		else
			self.dash_state.other_debounce = true
		end

		local rate = self.dash_state.rate
		local goDownBy = 1

		ragdoll.disable(hum.Parent :: Model)
		hum:SetAttribute("invincible", false)
		self.char:FindFirstChild("HumanoidRootPart").AssemblyLinearVelocity = Vector3.zero
		self.invincible = true

		self.char.Humanoid.AutoRotate = false
		
		if self.Player then
			done = false
			d = nil
			game.ReplicatedStorage.AlignCFrame:FireClient(self.Player)
			game.ReplicatedStorage.AlignCFrame.OnServerEvent:Connect(function(p, cf)
				if p ~= Player then
					return
				end

				d = cf
				done = true
			end)

			repeat
				task.wait()
			until done and d
		end
		
		local do_not_handle = dash_data.disable_block_function == true
		local block_dash_flag = false

		if not do_not_handle then
			dash_data.disable_block_function = true
			if self.char:FindFirstChildOfClass("Humanoid"):GetAttribute("blocking") then
				block_dash_flag = true
			end
			self:block(false)
		end

		local a = hum:FindFirstChildOfClass("Animator"):LoadAnimation(
			if k == "front" then game.ReplicatedStorage.config.anims.Dash 
				elseif k == "back" then game.ReplicatedStorage.config.anims.BDash 
				elseif k == "left" then game.ReplicatedStorage.config.anims.LDash 
				elseif k == "right" then game.ReplicatedStorage.config.anims.RDash
				else game.ReplicatedStorage.config.anims.Dash
		);
		a:Play()

		local LV = Instance.new("BodyVelocity")
		LV.MaxForce = Vector3.new(100_000, 0, 100_000)
		LV.Parent = hum.Parent:FindFirstChild("HumanoidRootPart")
		LV.Velocity =  Vector3.new(0,0,0)

		game:GetService("Debris"):AddItem(LV, if k == "front" then self.dash_state.len else self.dash_state.other_len)

		task.delay(1, function()
			self.invincible = false
		end)

		if self.tool.Parent == self.char and k == "front" then
			self.debounce_table[Attack1.key] = true
			self.debounce_table[Attack2.key] = true
		end

		SwordState.play_sound_from_char(self, "rbxassetid://8653395088"):Play()

		task.spawn(function()
			while LV.Parent ~= nil do
				rate ^= goDownBy
				goDownBy*=0.97
				task.wait(0.1)
			end
		end)


		local dir

		task.spawn(function()
			while LV.Parent ~= nil do
				if not dir then
					task.wait()
					continue
				end

				if self.dash_state.block_debounce then
					task.wait()
					continue
				end

				SpawnEffect.as_rect(self.char:GetPivot().Position + dir * (if self.dash_state.block_step then self.dash_state.block_step elseif k == "front" then 23 else 35), self.char:GetPivot().LookVector, self.dash_state.block_size, 5, 1, "Unifrom")
				self.dash_state.block_debounce = true
				task.wait(if k == "front" then self.dash_state.block_cd else 0.01)
				self.dash_state.block_debounce = false
				task.wait()
			end
		end)
		local dialect = d or self.char:GetPivot()

		while LV.Parent ~= nil do
			dir = dialect.LookVector
			if k == "left" then
				dir = (dialect.RightVector * Vector3.new(-1,-1,-1))
			elseif k == "right" then
				dir = dialect.RightVector
			elseif k == "back" then
				dir = dialect.LookVector * Vector3.new(-1,-1,-1)
			end

			LV.Velocity = dir * (Vector3.new(rate,rate,rate))
			task.wait()
		end

		if not do_not_handle then
			dash_data.disable_block_function = false
			self:block(false)
		end
		a:Stop()
		if self.tool.Parent == self.char and k == "front" then
			self.debounce_table[self.attack_I.key] = false
			self.debounce_table[self.attack_II.key] = false
			
			if self.Player then
				self.inv_object.state.is_debounce = true
				ChangeInventoryItem:FireClient(self.Player, self.inv_object)
				self.inv_object.state.is_debounce = false
			end
			if not block_dash_flag then
				self.attack_I:run(self, 2)
				last_attack1 = os.clock()
			else
				self:block(true)
				self.char.Humanoid:SetAttribute("parry", true)
				task.delay(0.5, function()
					self.char.Humanoid:SetAttribute("parry", false)
					self:block(false)
				end)
			end
		end
		self.char.Humanoid.AutoRotate = true

		self.char:FindFirstChild("HumanoidRootPart").AssemblyLinearVelocity = Vector3.zero
		
		if self.Player then
			game.ReplicatedStorage.DashCD:FireClient(self.Player, if k == "front" then self.dash_state.cd else self.dash_state.other_cd)
		end
		
		task.wait(if k == "front" then self.dash_state.cd else self.dash_state.other_cd)
		if k == "front" then
			self.dash_state.debounce = false
		else
			self.dash_state.other_debounce = false
		end
	end		

It depends…

If it’s client sided you wouldn’t be able to see it as another player
(if that doesn’t matter to you then I suggest switching)

if it’s server sided It’ll probably be a bit more laggy but this also could be a script optimization issue.

It’s movement code, as far as I know it will be seeable everywhere no matter what end.

if enoughtStamina < requestStamina then return end
local success = DashFunction:InvokeServer()
-- server side doing check is player have enought stamina, is it player, -stamina, maybe fire all clients to play animation. etc
if not succeess then warn(`{player}: stamina on client > than player stamina on server. Probably cheater`) return end
DoSlide here

I am going to edit my post with the code, so more context is given…

always do it on the client side, then send a remote to the server side which can handle server sided stuff like giving invincibility, then send to other clients to render vfx on their side

3 Likes