Local script somehow interfering with server script and lags npc

So I am having this issue where I simply locally highlight npcs I click on, but for some very random reason, when I do that the server script controlling npc’s pathfinding starts to weirdly lag, before I will continue, let me say that there is no error or anything in output and the path for npc IS FOUND so its not like it recalculates or something. The thing is, the npc “lags” after every waypoint, but it DOES NOT happen when the local script is disabled. Here are clips for better understanding:

Clip where npc lags and I have local script enabled:

Clip where npc works normally with local script disabled:

the local script I am using:

local player = game:GetService("Players").LocalPlayer
local mouse = player:GetMouse()
local userInputService = game:GetService("UserInputService")
local connectionInputBegan = nil
local connectionInputEnded = nil
local userInput = nil
local isActivated = false
local startMousePosition = nil
local isMultipleSelecting = false
local listOfHighlightes = {}
local selectedNPCs = {}

local function addHighlight(object, color)
	local highlight = Instance.new("Highlight")
	highlight.FillColor = color.Color
	highlight.FillTransparency = 0.9
	highlight.OutlineColor = color.Color
	table.insert(listOfHighlightes, highlight)
	highlight.Parent = object
end

local function moveMause()
	if isActivated == false then return end
	if (startMousePosition - userInputService:GetMouseLocation()).Magnitude > 25 then
		isMultipleSelecting = true
	end
end

local function inputEnded(input, processed)
	if userInput ~= input then return end
	userInput = nil
	isActivated = false
	connectionInputEnded:Disconnect()
	startMousePosition = nil
	print("Deactivated")
	if not userInputService:IsKeyDown(Enum.KeyCode.LeftShift) then
		for i,v in pairs(listOfHighlightes) do
			v:Destroy()
		end
		for i,v in pairs(selectedNPCs) do
			table.clear(selectedNPCs)
		end
	end
	if isMultipleSelecting == false then
		local mouseHit = mouse.Target
		if mouseHit.Parent.Name ~= "Workspace" and mouseHit.Parent:FindFirstChild("Humanoid") then
			--mouseHit.Parent:FindFirstChild("Team").Value == game:GetService("ReplicatedStorage"):WaitForChild("UserData"):FindFirstChild(player.Name):WaitForChild("Team").Value
			table.insert(selectedNPCs, mouseHit.Parent)
			for i,v in pairs(selectedNPCs) do
				addHighlight(v, v:FindFirstChild("Team").Value)
			end
		end
	end
	isMultipleSelecting = false
end

local function inputBegan(input, processed)
	if userInput ~= nil then return end
	if processed == true then return end
	if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
	startMousePosition = userInputService:GetMouseLocation()
	userInput = input
	isActivated = true
	connectionInputEnded = userInputService.InputEnded:Connect(inputEnded)
	print("Activated")
end

mouse.Move:Connect(moveMause)
connectionInputBegan = userInputService.InputBegan:Connect(inputBegan)

I think with this I can cay for sure its not server script’s fault but I will put it here anyway because I am sure someone will ask for it, but I am saying it in front that its pretty messy:

local humanoid = script.Parent:WaitForChild("Humanoid")
local npcRoot = script.Parent:WaitForChild("HumanoidRootPart")
local team = script.Parent:WaitForChild("Team")
local inDanger = script.Parent:WaitForChild("inDanger")
local pathService = game:GetService("PathfindingService")
local stats = script.Parent.stats:GetChildren()
local walkStatus = 0
local requireToLook = true
local FoodGatherAnim = humanoid.Animator:LoadAnimation(script.Parent.Animations:WaitForChild("FoodSource"))
local waveAnim = humanoid.Animator:LoadAnimation(script.Parent.Animations:WaitForChild("WaveAnim"))
local PhysicsService = game:GetService("PhysicsService")
local blockedConnection
local nextWaypointIndex

local home = workspace:WaitForChild("Home")

task.wait(2)
for _, child in ipairs(script.Parent:GetChildren()) do
	if child:IsA("MeshPart") or child:IsA("Part") or child:IsA("BasePart") then
		PhysicsService:SetPartCollisionGroup(child, "npcGroup")
	end
end


local function getStat(stat)
	for i,v in pairs(stats) do
		if v.Name == stat then
			return v
		end
	end
end

local GatherType = getStat("GatherType")

local function findNearestGather(object)
	local currentNearest = nil
	local objects = workspace:FindFirstChild(object)
	if objects == nil then
		return nil
	end
	objects = objects:GetChildren()
	for i,v in pairs(objects) do
		if v:FindFirstChild("Amount").Value > 0 then
			if currentNearest == nil then
				currentNearest = v
			elseif (npcRoot.Position - v.PrimaryPart.Position).Magnitude < (npcRoot.Position - currentNearest.PrimaryPart.Position).Magnitude then
				currentNearest = v
			end
		end
	end
	return currentNearest
end

local function pathFind(destination)
	requireToLook = true
	if walkStatus ~= 0 then walkStatus = 2 end
	repeat task.wait(0.1) until walkStatus == 0
	nextWaypointIndex = 1
	walkStatus = 1
	local path = pathService:CreatePath({
		AgentCatJump = false
	})
	local success, errorMessage = pcall(function()
		path:ComputeAsync(npcRoot.Position, destination.Position - CFrame.new(npcRoot.Position, destination.Position).LookVector * 4)
	end)
	if success and path.Status == Enum.PathStatus.Success then
		local waypoints = path:GetWaypoints()
		blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
			if blockedWaypointIndex >= nextWaypointIndex then
				blockedConnection:Disconnect()
				print("hitler")
				pathFind(destination)
			end
		end)
		for i, waypoint in ipairs(waypoints) do
			if walkStatus == 2 then
				walkStatus = 0
				break
			end
			humanoid:MoveTo(waypoint.Position)
			humanoid.MoveToFinished:Wait()
			nextWaypointIndex += 1
		end
		walkStatus = 0
	elseif path.Status == Enum.PathStatus.NoPath then
		waveAnim:Play()
		task.wait(1.5)
		waveAnim:Stop()
		walkStatus = 0
	end
end

local function isFull(resource)
	if resource == "FoodSource" then
		local maxfood = getStat("Food")
		local food = getStat("MaxFood")
		if maxfood.Value == food.Value then
			return true
		else
			return false
		end
	elseif resource == "WoodSource" then
		local maxfood = getStat("Wood")
		local food = getStat("MaxWood")
		if maxfood.Value == food.Value then
			return true
		else
			return false
		end
	end
end

local function gather(Source)
	if requireToLook == true then
		npcRoot.CFrame = CFrame.lookAt(npcRoot.Position, Source.PrimaryPart.Position)
		requireToLook = false
	end
	if GatherType.Value == "FoodSource" then
		FoodGatherAnim:Play()
	elseif GatherType.Value == "WoodSource" then
		FoodGatherAnim:Play()
	end
	task.wait(getStat("GatherTime").Value)
	if Source:FindFirstChild("Amount").Value <= 0 then
		return
	end
	Source:FindFirstChild("Amount").Value -= 1
	if GatherType.Value == "FoodSource" then
		local food = getStat("Food")
		food.Value += 1
	elseif GatherType.Value == "WoodSource" then
		local wood = getStat("Wood")
		wood.Value += 1
	end
end

local function returnResources()
	local food = getStat("Food")
	food.Value = 0
	local wood = getStat("Wood")
	wood.Value = 0
end


local function mainLoop()
	local Source = findNearestGather(GatherType.Value)
	if inDanger.Value == true then
		pathFind(home)
		
	elseif isFull(GatherType.Value) == false then
		if Source ~= nil and (npcRoot.Position - Source.PrimaryPart.Position).Magnitude > 6 then
			pathFind(Source.PrimaryPart)
		end
		if (npcRoot.Position - Source.PrimaryPart.Position).Magnitude < 7 then
			gather(Source)
		end
	elseif isFull(GatherType.Value) == true then
		pathFind(home)
		if (npcRoot.Position - home.Position).Magnitude < 5 then
			returnResources()
		end
	end
end


while true do
	mainLoop()
end
1 Like

Did you make sure to set the network owner of the NPC to the server? (call :SetNetworkOwner(nil) on a basepart in the NPC) This makes sure that the server calculates the NPC’s movements, which is usually a ‘must’ when dealing with pathfinding.

4 Likes