NPCs Made on client not appearing for other players

Hello everyone, so I was making my npc enemy ai system and decided to create the npcs on the client to optimize stuff.

However, I encountered a problem. When I tried simulating the game in roblox studio with 2 players, npcs appeared on player 1, but not for player 2 (I tried adding print statements as well)

Scripts:

–// the module for the ai of “Scavenger” enemy

local Replicated = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local Deb = game:GetService("Debris")

local AnimationFolder = Replicated:WaitForChild("Assets"):WaitForChild("EnemyAnimations"):FindFirstChild(script.Name)

local StatModule = require(Replicated.Modules.EnemyStats)
local SelfStats = StatModule.Scavenger

local Characters = workspace.Characters:GetChildren()

local module = {}
local CurrentClosest = nil
local CurrentClosestDist = math.huge

module.EnemiesTable = {}
module.Animations = {}

function Bullet(targetpart, originpart)

	local beam = Replicated.Assets.Bullet.Beam0:Clone()
	beam.Parent = workspace.ClientEffects

	local endatt = Instance.new("Attachment", targetpart)

	beam.Attachment0 = originpart.ShootPos
	beam.Attachment1 = endatt

	Deb:AddItem(beam, 0.05)
	Deb:AddItem(endatt, 0.05)
end

function module:AI(Character : Model)
	
	print("init")
	
	local Humanoid = Character:WaitForChild("Humanoid")
	local Animator = Humanoid:WaitForChild("Animator")

	local IdleAnim = Animator:LoadAnimation(AnimationFolder:WaitForChild("Idle"))
	local FireAnim = Animator:LoadAnimation(AnimationFolder:WaitForChild("Fire"))
	local Reload = Animator:LoadAnimation(AnimationFolder:WaitForChild("Reload"))
	local AimAnim = Animator:LoadAnimation(AnimationFolder:WaitForChild("Aim"))
	local AimIdleAnim = Animator:LoadAnimation(AnimationFolder:WaitForChild("AimIdle"))
	local RunAnim = Animator:LoadAnimation(AnimationFolder:WaitForChild("Run"))
	
	local CurrentClosest = nil
	local CurrentClosestDist = math.huge
	
	local ShootInCooldown = false
	
	Character.Name = Character.Name..tostring(math.random(1, 10000))
	IdleAnim:Play()
	
	module.Animations[Character.Name] = {}
	
	module.Animations[Character.Name].Idle = IdleAnim
	module.Animations[Character.Name].Fire = FireAnim
	module.Animations[Character.Name].Reload = Reload
	module.Animations[Character.Name].Aim = AimAnim
	module.Animations[Character.Name].AimIdle = AimIdleAnim
	module.Animations[Character.Name].Run = RunAnim
	
	Humanoid.Running:Connect(function(speed)
		--// play run animation if moving

		if speed > 0 then
			if not RunAnim.IsPlaying then
				RunAnim:Play()
			end
		else
			if RunAnim.IsPlaying then
				RunAnim:Stop()
			end
		end
	end)
	
end

RunService.Stepped:Connect(function()
	for i, v in module.EnemiesTable do
		
		task.spawn(function()
			CurrentClosest = nil
			CurrentClosestDist = math.huge

			for ia, va : Model in Characters do

				local mag = (v.HumanoidRootPart.Position - va.HumanoidRootPart.Position).Magnitude

				if mag < CurrentClosestDist and mag <= SelfStats.AggroRange then
					CurrentClosest = va
					CurrentClosestDist = mag
				end

			end
			--//

			if not CurrentClosest then return end
			
			if not v:GetAttribute("ShootCD") then
				Bullet(CurrentClosest.HumanoidRootPart, v.NeoRifle.Main)
				module.Animations[v.Name].Fire:Play()
				v.NeoRifle.Main.Shoot:Play()
				v:SetAttribute("ShootCD", true)
				task.wait(SelfStats.Firerate)
				v:SetAttribute("ShootCD", false)
			end

			if CurrentClosestDist <= SelfStats.HitRange then

				if module.Animations[v.Name].AimIdle.IsPlaying then else
					module.Animations[v.Name].AimIdle:Play()
					module.Animations[v.Name].Aim:Play()
					module.Animations[v.Name].Idle:Stop()
				end

				local RootPos, Targpos = v.HumanoidRootPart.Position, CurrentClosest:GetPivot().Position
				v.HumanoidRootPart.CFrame = CFrame.new(RootPos, Vector3.new(Targpos.X, RootPos.Y, Targpos.Z))

			else
				module.Animations[v.Name].AimIdle:Stop()
				module.Animations[v.Name].Idle:Play()
			end
		end)
		
		if CurrentClosest then
			v.Humanoid:MoveTo(CurrentClosest:GetPivot().Position, CurrentClosest:FindFirstChild("HumanoidRootPart"))
		end
		
	end
end)

return module

--[[
--//
			CurrentClosest = nil
			CurrentClosestDist = math.huge

			for i, v : Model in Characters do
				
				local mag = (Character.HumanoidRootPart.Position - v.HumanoidRootPart.Position).Magnitude

				if mag < CurrentClosestDist and mag <= SelfStats.AggroRange then
					CurrentClosest = v
					CurrentClosestDist = mag
				end

			end
			--//

			if not CurrentClosest then continue end

			if CurrentClosestDist <= SelfStats.HitRange then

				if AimIdleAnim.IsPlaying then else
					AimIdleAnim:Play()
					AimAnim:Play()
					IdleAnim:Stop()
				end

				local RootPos, Targpos = Character.HumanoidRootPart.Position, CurrentClosest:GetPivot().Position
				Character.HumanoidRootPart.CFrame = CFrame.new(RootPos, Vector3.new(Targpos.X, RootPos.Y, Targpos.Z))

				if not ShootInCooldown then
					Bullet(CurrentClosest.HumanoidRootPart, Character.NeoRifle.Main)
					FireAnim:Play()
					Character.NeoRifle.Main.Shoot:Play()
					ShootInCooldown = true
					task.wait(SelfStats.Firerate)
					ShootInCooldown = false
				end

			else
				AimIdleAnim:Stop()
				IdleAnim:Play()

				Character.Humanoid:MoveTo(CurrentClosest:GetPivot().Position, CurrentClosest:FindFirstChild("HumanoidRootPart"))
			end
			]]

–// Main script controller of all ai.

local Replicated = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local Deb = game:GetService("Debris")

local player = game.Players.LocalPlayer
local character = workspace:WaitForChild("Characters"):WaitForChild(player.Name)

local Map = workspace.Map:FindFirstChildWhichIsA("Model")
local Enemies = Map:FindFirstChild("Enemies")

local StatModule = require(Replicated.Modules.EnemyStats)
local SelfStats = StatModule.Scavenger

local Characters = workspace.Characters:GetChildren()

print("run")

for i, v : Part in Map:FindFirstChild("Enemies"):GetChildren() do
	print("e")
	if script.Parent.AIs:FindFirstChild(v.Name) then
		print("E")
		local NewEnemy : Model = Replicated:WaitForChild("Assets"):WaitForChild("Enemies"):FindFirstChild(v.Name):Clone()
		NewEnemy.Parent = workspace.SpawnedEnemies
		
		NewEnemy:PivotTo(v.CFrame)
		NewEnemy:SetAttribute("ShootCD", false)
		
		require(script.Parent.AIs[v.Name]):AI(NewEnemy)
		table.insert(require(script.Parent.AIs[v.Name]).EnemiesTable, NewEnemy)
		
	end
	
end

Instances created on the client are not replicated to the server and the other clients. The easiest way to get them to appear on both clients would be to create the NPC on the server. That way, the server will replicate the NPC to all clients.

If you really want to avoid creating the NPC model/humanoid/etc on the server. You could have the server send an remote event to all clients with the necessary information and then all the clients can create the NPC on their end. If you go this route though, you may have to implement your own replication system to keep the NPCs in sync between all the clients.

The code runs for every single client, I dont see why it wouldn’t replicate it.

Solved, the code was running before the loop could detect any enemy spawners, thus making the code think that there are no enemies to be spawned for the other player because the “enemies” folder wasn’t fully loaded.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.