Help with client rendering system

I recently rewrote my enemy system, and I switched from parts on the server to an table on the server. But, i came across an problem that i have spent close to an month trying to fix. The client stop updating the enemies. So, visually, some enemies just stop walking, becomes stuck. Enemies are on an OOP class, and the said class updates the position. Than, I have an module to compile all the enemies, and send the enemies to the client for rendering. I have tracked the problem to the enemy compiler, and some positions on the compiled table returns nil. On the server, the position is still updating. Also, it is not bridgenet2 problem. the compiled table, before sending it off to the client, the frozen enemy position is nil.

Tl;DR, I rewrote my enemy system, and sometimes the server stops sending positions to the client. Server is still updating the positions of bugged enemies.

Enemy Replicator.lua:

--!native
--[[
      __   _  _                                   
  _____  __/ /_ | || |  ___                             
 / _ \ \/ / '_ \| || |_/ __|                            
|  __/>  <| (_) |__   _\__ \                            
 \___/_/\_\\___/   |_| |___/      _   _                 
 _ __  _ __ ___   __| |_   _  ___| |_(_) ___  _ __  ___ 
| '_ \| '__/ _ \ / _` | | | |/ __| __| |/ _ \| '_ \/ __|
| |_) | | | (_) | (_| | |_| | (__| |_| | (_) | | | \__ \
| .__/|_|  \___/ \__,_|\__,_|\___|\__|_|\___/|_| |_|___/
|_|                                                     
]]
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local replicator = {}
replicator.enemies = {}
replicator.pathTable = {}
local BezierPath = require(ReplicatedStorage.Packages:WaitForChild("BezierPath"))
local BridgeNet = require(ReplicatedStorage.Packages:WaitForChild("bridgenet2"))
local enemyclass = require(ServerStorage:WaitForChild("EnemyClass"))
local packetBridge = BridgeNet.ServerBridge("enemyReplication")
--packetBridge.Logging = true
local createBridge = BridgeNet.ServerBridge("enemyCreation")
local deleteBridge = BridgeNet.ServerBridge("enemyDeletion")
local deleteEvent = ReplicatedStorage.deletedCompletedEnemy

--[=[ 
@class replicator

This handles enemy Replication, sends packets to the client, so the client can render and position
All the functions in this module handle enemies, and do things like damage or heal effects.
This module also handles creating enemies.

]=]


--[=[
@function setUp
This function sets up the Replicator, which connects BindableEvent
to deleteEnemies, and reset the EnemyCount.

@within replicator

]=]
replicator.setUp = function(_)
	deleteEvent.Event:Connect(function(hash)
		replicator.enemies[hash] = nil
		replicator.enemyCount -= 1
	end)
	replicator.enemyCount = 0
end

--[=[
@function createnavpath
This function creates an path with the BezierPath Module.
This function is called for each path in the map.

@param nodefolder Folder
@param index number

@within replicator
]=]
replicator.createnavpath = function(nodefolder: Folder, index)
	local nodePositions = { nodefolder.enemyspawn.Position }

	for i = 1, #nodefolder:GetChildren() - 2 do
		table.insert(nodePositions, nodefolder[i].Position)
	end

	table.insert(nodePositions, nodefolder.exit.Position)
	local pathtable = {
		["path"] = BezierPath.new(nodePositions, 3),
		["nodefolder"] = nodefolder,
	}
	replicator.pathTable[index] = pathtable
end

--[=[
@function getEnemyCount
Retrives the amount of enemies active

@within replicator
]=]
replicator.getEnemyCount = function()
	return replicator.enemyCount
end

--[=[
@function getTargetingData

Compiles an table with all the enemies, 
with condensed info like the index of translation, the position, the health, and the translated index.

@within replicator
]=]
replicator.getTargetingData = function()
	task.desynchronize()
	local enemyData = {}
	for hash, enemy: enemyclass.enemy in pairs(replicator.enemies) do
		table.insert(enemyData, { hash, enemy.position, enemy.health, enemy.translated })
	end
	task.synchronize()
	return enemyData
end

--[=[
@function enemyEffect

Changes an property in the enemy with the hash applied.
If the effect is health, than it checks if the health is under 0, and if it is, deletes the enemy.
TODO give cash to everyone based on enemy

@param hash string
@param effect any
@param new any

@within replicator
]=]
replicator.enemyEffect = function(hash, effect, new)
	local enemy = replicator.enemies[hash]
	if enemy then
		enemy[effect] = new
		if effect == "health" then
			if enemy["health"] <= 0 then
				deleteBridge:Fire(BridgeNet.AllPlayers(), hash)
				replicator.enemyCount -= 1
				replicator.enemies[hash] = nil
			end
		end
	end
end

--[=[
@function getSingleEnemy

SelfExplanatory, retrives an single enemy with the hash provided

@param enemyHash string

@within replicator
]=]
replicator.getSingleEnemy = function(enemyHash)
	local enemy = replicator.enemies[enemyHash]
	return enemy
end

--[=[
@function update

Compiles all the enemies into one condensed table, with the CFrame. Than, using bridgeNet2, sends
the infomation to the client, so it can be rendered on the client.

@within replicator
]=]
local enemyData = {}
replicator.update = function()
	table.clear(enemyData)
	for hash:string, enemy: enemyclass.enemy in pairs(replicator.enemies) do
		enemyData[hash] = enemy.position
		--print("hash: "..tostring(hash).." ".."position: "..tostring(enemy.position))
	end
	
	packetBridge:Fire(BridgeNet.AllPlayers(), enemyData)
end

--[=[
@function addEnemy

Creates an enemy, and adds it to the replicator table in the module. Than, 
fires an bridge to the client to create an enemy, and increases the enemyCount.

@param enemyName string
@param pathNum number

@within replicator
]=]

replicator.addEnemy = function(enemyName, pathNum)
	local eClass = enemyclass.create(enemyName, replicator.pathTable[pathNum])
	createBridge:Fire(BridgeNet.AllPlayers(), { eClass["hash"], eClass["class"] })
	replicator.enemyCount += 1
	local hash = eClass["hash"]
	replicator.enemies[hash] = eClass
end

return replicator

EnemyClass.lua:

--!native
--!strict
--[[
      __   _  _                                   
  _____  __/ /_ | || |  ___                             
 / _ \ \/ / '_ \| || |_/ __|                            
|  __/>  <| (_) |__   _\__ \                            
 \___/_/\_\\___/   |_| |___/      _   _                 
 _ __  _ __ ___   __| |_   _  ___| |_(_) ___  _ __  ___ 
| '_ \| '__/ _ \ / _` | | | |/ __| __| |/ _ \| '_ \/ __|
| |_) | | | (_) | (_| | |_| | (__| |_| | (_) | | | \__ \
| .__/|_|  \___/ \__,_|\__,_|\___|\__|_|\___/|_| |_|___/
|_|                                                     


]]
--[=[
@class Enemy

Enemy Class. Updates the Translated Value of Enemies, 
and are in the .enemies in the enemyReplicator Script.
addEnemy on the enemyReplicator module creates these classes.

]=]
local Enemy = {}
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local BezierPath = require(ReplicatedStorage.Packages:WaitForChild("BezierPath"))
local BridgeNet = require(ReplicatedStorage.Packages:WaitForChild("bridgenet2"))
local enemystats = require(ServerStorage:WaitForChild("EnemyStats"))
local bosshealthbar = ReplicatedStorage.AddHealthBar
local AbilityManager = require(ServerStorage.AbilityManager)
local Enemies = ReplicatedStorage.Enemies
local HashCreator = require(ServerStorage.HashCreator)
local deleteBridge = BridgeNet.ServerBridge("enemyDeletion")
local refresh_rate = 0.1
local deleteEvent = ReplicatedStorage.deletedCompletedEnemy
Enemy.__index = Enemy

export type enemy = {
	speed: number,
	health: number,
	hash: string,
	position: CFrame,
	translated:number,
	class:string,
	renderID:number,
	updateConnection: RBXScriptConnection,
}
--[=[
Creates an enemy
@param enemyName string --The class name of the enemy
@param path table --The path that the enemy follows
]=]
function Enemy.create(enemyName, path: BezierPath.Path):enemy
	ReplicatedStorage.DebugValues.EnemyCount.Value += 1
	local self = setmetatable({}, Enemy)
	self.position = CFrame.new(0, 0, 0)
	self.class = enemyName
	self.StartTime = os.clock()
	local enemyData = enemystats.get(enemyName)
	self.speed = enemyData.speed
	self.health = enemyData.health
	self.hash = HashCreator.retriveHash()
	self.renderID = 1
	local _, boundingBox = Enemies[enemyName]:GetBoundingBox()
	local sizeAddition = boundingBox.Y / 2
	local rotationalOffset
	local offset = math.random(-1, 1)
	if enemyData.modifiers["boss"] then
		bosshealthbar:FireAllClients(enemyData.name, self, self.health)
	end
	if enemyData.rotationOffset ~= nil then
		rotationalOffset = enemyData.rotationOffset
	else
		rotationalOffset = Vector3.zero
	end
	if enemyData["abilities"] then
		task.spawn(function()
			for ability, _ in pairs(enemyData["abilities"]) do
				AbilityManager.register("enemies", ability, self)
			end
		end)
	end
	local rotationalCFrame = CFrame.Angles(rotationalOffset.X, rotationalOffset.Y, rotationalOffset.Z)
	local offsetCFrame = CFrame.new(offset, sizeAddition, 0)
	self.updateConnection = task.spawn(function()
		local PreviousTime = os.clock()
		self.translated = 0
		while self.translated < 1 do
			task.desynchronize()
			self.translated += (os.clock() - PreviousTime) * self.speed / path["path"]:GetPathLength()
			PreviousTime = os.clock()
			local transformedcframe: CFrame = path["path"]:CalculateUniformCFrame(self.translated)
				* offsetCFrame
				* rotationalCFrame
			self.position = transformedcframe
			task.wait(refresh_rate)
			task.synchronize()
		end
		ReplicatedStorage.DebugValues.EnemyCount.Value -= 1
		deleteBridge:Fire(BridgeNet.AllPlayers(), self.hash)
		deleteEvent:Fire(self.hash)
		if self.health > 0 then
			ReplicatedStorage.BaseHealth.Value -= self.health
			self.health = 0
		end
	end)
	return self
end

--[=[
Damaging function. Unused function, just use enemyReplicator.enemyEffect(health)
@param damage number --damage that will be given to the enemy
]=]

function Enemy:damage(damage):nil
	local clampedDamage = math.clamp(self.health - damage, 0, 9999999999)
	self.health -= clampedDamage
	if self.health <= 0 then
		self.health = 0
	end
end

return Enemy

EnemyRender.client.lua:

local Players = game:GetService("Players")
local player = Players.LocalPlayer
repeat
	task.wait()
until player:WaitForChild("loaded").Value == true
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Map = ReplicatedStorage:WaitForChild("Map")
local Enemies = ReplicatedStorage:WaitForChild("Enemies")
local loadedEnemies = {}
local Workspace = game:GetService("Workspace")
local MapObject = Workspace:WaitForChild(Map.Value)
local BridgeNet2 = require(ReplicatedStorage.Packages.bridgenet2)
local enemyPacketBridge = BridgeNet2.ReferenceBridge("enemyReplication")
local enemyCreationBridge = BridgeNet2.ReferenceBridge("enemyCreation")
local enemyDeletionBridge = BridgeNet2.ReferenceBridge("enemyDeletion")
local RunService = game:GetService("RunService")
local cframeData = {}
local function createEnemy(content)
	local hash, name = content[1], content[2]
	local enemyModel: Model = Enemies:FindFirstChild(name):Clone()
	enemyModel.Name = hash
	enemyModel:SetAttribute("class", name)
	enemyModel.Parent = MapObject.enemies
	loadedEnemies[hash] = enemyModel.PrimaryPart
	local animation = enemyModel.Animations.walk
	local track = enemyModel.AnimationController.Animator:LoadAnimation(animation)
	track:Play()
end

local function deleteEnemy(content)
	local enemy = loadedEnemies[content]
	if enemy then
		enemy.Parent:Destroy()
		loadedEnemies[content] = nil
	end
end

local function updatePosition(delta)
	local Parts = {}
	local CFrames = {}
	local index = 1
	for hash, cframe in pairs(cframeData) do
		local enemyPrimary = loadedEnemies[hash]
		if enemyPrimary then
			local alpha = math.clamp(delta / 0.1, 0, 1)
			Parts[index] = enemyPrimary
			CFrames[index] = enemyPrimary.CFrame:Lerp(cframe, alpha)
			index += 1
		else
			print(hash)
			print(cframeData[hash])
		end
	end
	task.synchronize()
	Workspace:BulkMoveTo(Parts, CFrames, Enum.BulkMoveMode.FireCFrameChanged)
end

local function updateData(content)
	cframeData = content
end
enemyCreationBridge:Connect(createEnemy)
enemyPacketBridge:Connect(updateData)
enemyDeletionBridge:Connect(deleteEnemy)
RunService.Heartbeat:ConnectParallel(updatePosition)

Thank you for your help in advance!

I have been banging my head on an table for 2 weeks plz help

Where is your EnemyRender.client located?

1 Like

Starter player scripts under an actor

Does the client receive the remote event to create the enemy?

1 Like

yes, the client recives an event, and creates an enemy on the client

So what’s the problem? If the client receives the data then what do you mean by “stops sending positions to the client”?

1 Like

the enemies stop moving. some enemies freeze up and they stop moving

here is an video:

@Katrist

Can you print something when the player receives a packet and record it until the bug happens?

1 Like

there are 4 frozen enemies in this output dump:
Server:

                    ["039"] = -140.857956, 150.376587, 1647.58533, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["041"] = -142.857956, 150.376587, 1634.82788, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["046"] = -142.857956, 150.376587, 1665.17456, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["051"] = -142.857956, 150.376587, 1617.68164, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["054"] = -140.857956, 150.376587, 1594.55017, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["064"] = -142.857941, 150.376587, 1214.66602, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["069"] = -140.857956, 150.376587, 1638.92285, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1006"] = -141.857956, 150.376587, 1639.46301, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["10103"] = -140.857956, 150.376587, 1649.21643, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["10106"] = -142.857956, 150.376587, 1604.76978, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["10110"] = -142.857956, 150.376587, 1635.53162, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1014"] = -141.857956, 150.376587, 1643.15601, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1015"] = -141.857956, 150.376587, 1628.21948, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1017"] = -140.857941, 150.376587, 1249.3606, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1021"] = -140.857956, 150.376587, 1667.22083, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["104"] = -142.857956, 150.376587, 1652.49792, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1046"] = -140.857956, 150.376587, 1658.47266, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1065"] = -140.857956, 150.376587, 1633.29578, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1067"] = -142.857956, 150.376587, 1610.79041, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1071"] = -141.857956, 150.376587, 1615.18628, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1077"] = -142.857956, 150.376587, 1665.73389, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["11010"] = -140.857956, 150.376587, 1647.04041, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1109"] = -142.857956, 150.376587, 1664.89819, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["11102"] = -142.857956, 150.376587, 1603.07922, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1111"] = -141.857956, 150.376587, 1659.9801, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1118"] = -141.857956, 150.376587, 1601.54932, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1119"] = -141.857956, 150.376587, 1637.98364, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1124"] = -142.857956, 150.376587, 1619.12073, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1139"] = -142.857956, 150.376587, 1640.05725, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["11510"] = -142.857956, 150.376587, 1611.41418, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["11611"] = -142.857956, 150.376587, 1625.04846, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1173"] = -141.857956, 150.376587, 1598.66638, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1182"] = -142.857956, 150.376587, 1662.00037, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1191"] = -140.857956, 150.376587, 1656.96313, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1211"] = -142.857956, 150.376587, 1607.9187, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["126"] = -142.857956, 150.376587, 1599.26099, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["129"] = -142.857956, 150.376587, 1612.80725, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["130"] = -140.857956, 150.376587, 1623.78845, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1511"] = -140.857956, 150.376587, 1650.02539, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["163"] = -142.857956, 150.376587, 1654.22693, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["178"] = -140.857956, 150.376587, 1636.5144, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["212"] = -141.857956, 150.376587, 1648.95288, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["252"] = -141.857956, 150.376587, 1609.28076, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["277"] = -141.857956, 150.376587, 1655.78125, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["296"] = -140.857956, 150.376587, 1626.64197, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["3011"] = -141.857941, 150.376587, 1231.85193, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["305"] = -142.857956, 150.376587, 1606.80103, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["3109"] = -141.857956, 150.376587, 1616.71094, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["312"] = -141.857956, 150.376587, 1659.42322, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["324"] = -141.857956, 150.376587, 1646.11877, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["347"] = -141.857956, 150.376587, 1606.28381, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["349"] = -140.857956, 150.376587, 1631.43018, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["364"] = -141.857941, 150.376587, 1268.00171, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["385"] = -142.857956, 150.376587, 1614.65515, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["390"] = -140.857956, 150.376587, 1656.40308, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["4101"] = -141.857956, 150.376587, 1630.37646, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["4110"] = -141.857956, 150.376587, 1641.83887, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["427"] = -140.857956, 150.376587, 1641.06152, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["439"] = -141.857956, 150.376587, 1663.93933, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["477"] = -140.857956, 150.376587, 1661.47656, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["484"] = -142.857956, 150.376587, 1625.64563, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["492"] = -140.857956, 150.376587, 1668.17908, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["495"] = -140.857956, 150.376587, 1603.51782, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["498"] = -142.857956, 150.376587, 1667.85828, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["503"] = -142.857956, 150.376587, 1623.31616, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["5114"] = -140.857956, 150.376587, 1637.22351, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["540"] = -142.857956, 150.376587, 1654.8623, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["5510"] = -140.857956, 150.376587, 1662.51233, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["573"] = -142.857956, 150.376587, 1668.66711, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["574"] = -140.857956, 150.376587, 1643.96497, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["594"] = -142.857956, 150.376587, 1633.29004, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["607"] = -141.857956, 150.376587, 1661.47192, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["609"] = -140.857956, 150.376587, 1613.24622, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["614"] = -140.857956, 150.376587, 1650.77087, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["619"] = -141.857956, 150.376587, 1595.81921, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["6411"] = -140.857956, 150.376587, 1664.39441, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["6810"] = -141.857956, 150.376587, 1629.32812, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["718"] = -141.857956, 150.376587, 1663.0448, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["725"] = -142.857956, 150.376587, 1592.75366, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["7311"] = -142.857956, 150.376587, 1627.44519, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["743"] = -141.857956, 150.376587, 1659.08838, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["746"] = -142.857956, 150.376587, 1610.3197, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["7811"] = -140.857956, 150.376587, 1623.32068, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["791"] = -141.857956, 150.376587, 1663.38477, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["81011"] = -140.857956, 150.376587, 1648.18237, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["8111"] = -140.857956, 150.376587, 1598.66125, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["8210"] = -142.857956, 150.376587, 1668.6698, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["823"] = -140.857956, 150.376587, 1657.93469, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["8610"] = -140.857956, 150.376587, 1644.79138, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["879"] = -142.857956, 150.376587, 1633.82166, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["8811"] = -141.857956, 150.376587, 1655.16223, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["886"] = -141.857956, 150.376587, 1666.82178, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["904"] = -140.857956, 150.376587, 1657.36316, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["909"] = -142.857956, 150.376587, 1653.35266, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["920"] = -140.857956, 150.376587, 1651.73303, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["924"] = -142.857956, 150.376587, 1660.51306, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["936"] = -141.857956, 150.376587, 1621.31311, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["980"] = -142.857956, 150.376587, 1642.82776, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["982"] = -142.857956, 150.376587, 1651.23218, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["9910"] = -142.857941, 150.376587, 1285.18762, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["993"] = -140.857956, 150.376587, 1666.54382, -1, 0, 0, 0, 1, 0, 0, 0, -1

Client:

                    ["039"] = -140.857956, 150.376587, 1613.48071, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["041"] = -142.857956, 150.376587, 1600.72461, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["046"] = -142.857956, 150.376587, 1631.0719, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["051"] = -142.857956, 150.376587, 1583.57739, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["054"] = -140.857956, 150.376587, 1560.44568, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["064"] = -142.857941, 150.376587, 1180.56628, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["069"] = -140.857956, 150.376587, 1604.81836, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1006"] = -141.857956, 150.376587, 1605.35852, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["10103"] = -140.857956, 150.376587, 1615.11194, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["10106"] = -142.857956, 150.376587, 1570.66541, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["10110"] = -142.857956, 150.376587, 1601.42712, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1014"] = -141.857956, 150.376587, 1609.05164, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1015"] = -141.857956, 150.376587, 1594.11475, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1017"] = -140.857941, 150.376587, 1215.25977, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1021"] = -140.857956, 150.376587, 1633.11865, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["104"] = -142.857956, 150.376587, 1618.3949, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1046"] = -140.857956, 150.376587, 1624.37, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1065"] = -140.857956, 150.376587, 1599.19238, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1067"] = -142.857956, 150.376587, 1576.68567, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1071"] = -141.857956, 150.376587, 1581.08191, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1077"] = -142.857956, 150.376587, 1631.63159, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["11010"] = -140.857956, 150.376587, 1612.93628, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1109"] = -142.857956, 150.376587, 1630.79578, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["11102"] = -142.857956, 150.376587, 1568.97461, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1111"] = -141.857956, 150.376587, 1625.87708, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1118"] = -141.857956, 150.376587, 1567.4447, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1119"] = -141.857956, 150.376587, 1603.87915, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1124"] = -142.857956, 150.376587, 1585.01624, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1139"] = -142.857956, 150.376587, 1605.95276, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["11510"] = -142.857956, 150.376587, 1577.3103, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["11611"] = -142.857956, 150.376587, 1590.94458, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1173"] = -141.857956, 150.376587, 1564.56201, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1182"] = -142.857956, 150.376587, 1627.89685, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1191"] = -140.857956, 150.376587, 1622.86047, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1211"] = -142.857956, 150.376587, 1573.81409, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["126"] = -142.857956, 150.376587, 1565.15662, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["129"] = -142.857956, 150.376587, 1578.70264, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["130"] = -140.857956, 150.376587, 1589.6842, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["1511"] = -140.857956, 150.376587, 1615.92102, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["163"] = -142.857956, 150.376587, 1620.12402, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["178"] = -140.857956, 150.376587, 1602.40979, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["212"] = -141.857956, 150.376587, 1614.8501, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["252"] = -141.857956, 150.376587, 1575.17688, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["277"] = -141.857956, 150.376587, 1621.67786, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["296"] = -140.857956, 150.376587, 1592.53845, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["3011"] = -141.857941, 150.376587, 1197.75183, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["305"] = -142.857956, 150.376587, 1572.69714, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["3109"] = -141.857956, 150.376587, 1582.60645, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["312"] = -141.857956, 150.376587, 1625.32092, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["324"] = -141.857956, 150.376587, 1612.01538, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["347"] = -141.857956, 150.376587, 1572.17944, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["349"] = -140.857956, 150.376587, 1597.3269, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["364"] = -141.857941, 150.376587, 1233.901, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["385"] = -142.857956, 150.376587, 1580.55103, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["390"] = -140.857956, 150.376587, 1622.30029, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["4101"] = -141.857956, 150.376587, 1596.27173, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["4110"] = -141.857956, 150.376587, 1607.73438, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["427"] = -140.857956, 150.376587, 1606.95703, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["439"] = -141.857956, 150.376587, 1629.83521, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["477"] = -140.857956, 150.376587, 1627.37427, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["484"] = -142.857956, 150.376587, 1591.54089, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["492"] = -140.857956, 150.376587, 1634.07581, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["495"] = -140.857956, 150.376587, 1569.41345, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["498"] = -142.857956, 150.376587, 1633.755, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["503"] = -142.857956, 150.376587, 1589.2124, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["5114"] = -140.857956, 150.376587, 1603.11914, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["540"] = -142.857956, 150.376587, 1620.7594, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["5510"] = -140.857956, 150.376587, 1628.4093, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["573"] = -142.857956, 150.376587, 1634.5647, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["574"] = -140.857956, 150.376587, 1609.86047, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["594"] = -142.857956, 150.376587, 1599.18665, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["607"] = -141.857956, 150.376587, 1627.36963, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["609"] = -140.857956, 150.376587, 1579.14197, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["614"] = -140.857956, 150.376587, 1616.66772, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["619"] = -141.857956, 150.376587, 1561.7146, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["6411"] = -140.857956, 150.376587, 1630.29126, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["6810"] = -141.857956, 150.376587, 1595.22351, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["718"] = -141.857956, 150.376587, 1628.94275, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["725"] = -142.857956, 150.376587, 1558.64929, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["7311"] = -142.857956, 150.376587, 1593.34058, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["743"] = -141.857956, 150.376587, 1624.98596, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["746"] = -142.857956, 150.376587, 1576.21606, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["7811"] = -140.857956, 150.376587, 1589.21741, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["791"] = -141.857956, 150.376587, 1629.2821, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["81011"] = -140.857956, 150.376587, 1614.078, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["8111"] = -140.857956, 150.376587, 1564.55676, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["8210"] = -142.857956, 150.376587, 1634.56726, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["823"] = -140.857956, 150.376587, 1623.83142, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["8610"] = -140.857956, 150.376587, 1610.68701, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["879"] = -142.857956, 150.376587, 1599.71716, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["8811"] = -141.857956, 150.376587, 1621.05957, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["886"] = -141.857956, 150.376587, 1632.71948, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["904"] = -140.857956, 150.376587, 1623.26086, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["909"] = -142.857956, 150.376587, 1619.24805, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["920"] = -140.857956, 150.376587, 1617.62854, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["924"] = -142.857956, 150.376587, 1626.40979, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["936"] = -141.857956, 150.376587, 1587.2085, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["980"] = -142.857956, 150.376587, 1608.72351, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["982"] = -142.857956, 150.376587, 1617.12805, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["9910"] = -142.857941, 150.376587, 1251.0863, -1, 0, 0, 0, 1, 0, 0, 0, -1,
                    ["993"] = -140.857956, 150.376587, 1632.44128, -1, 0, 0, 0, 1, 0, 0, 0, -1

This is 105 enemies
Also im sending an entire table to the client, containing the positioning table

also thx for helping me with this.

So does it stop receiving packets when the freeze starts? I’m trying to figure out if the issue is with your processing, or just the packet not arriving in the first place.

1 Like

the enemy is not appearing in the table, so the when the table is sent to the client, it stops moving the enemy

Are you still there?

Srry for bothering you

Sorry I’ve been busy these few days. Can you include the code where you call these functions?

1 Like
task.spawn(function()
	while true do
		enemyReplicator.update()
		task.wait(0.1)
	end
end)

And to spawn enemies it’s this:


local function spawnenemies(name, amount, nodes, spawninterval)
	for _ = 1, amount, 1 do
		local enemyWrap = coroutine.wrap(enemyReplicator.addEnemy)
		enemyWrap(name, tonumber(nodes.Name))
		task.wait(spawninterval)
	end
end

I also did a bit of debugging, and I found out the frozen enemy is just missing from the table on the server, so the client doesn’t move the enemy.

are you still there? i kinda need help on this

alright, a bit of an update on this. i rewrote it for the 4th time. I found out that the replicator was skipping over some enemies because i was using the hash as an index, and apparently my hashes are kinda weird.

anyway, now its an problem of identifying which model is associated with the packet I’m sending to the client. I tried straight up looking in the datamodel folder, but that didn’t work, and the renderer started to confuse enemies with each other. Eventually, I landed on an function to search through the loaded enemies, but that’s really laggy.

edit:
oh yeah, i forgot the code:

render.client.lua

--!native
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local loadedValue = player:WaitForChild("loaded")

repeat
	task.wait()
until loadedValue.Value == true

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Packages = ReplicatedStorage:WaitForChild("Packages")
local bridgenet2 = require(Packages:WaitForChild("bridgenet2")) :: any
local createBridge = bridgenet2.ReferenceBridge("enemyCreation")
local deleteBridge = bridgenet2.ReferenceBridge("enemyDeletion")
local packetBridge = bridgenet2.ReferenceBridge("enemyReplication")
local enemyModels = ReplicatedStorage.Enemies
local MapName = ReplicatedStorage:WaitForChild("Map").Value
local Workspace = game:GetService("Workspace")
local map = Workspace:WaitForChild(MapName)
local EnemyFolder = map:WaitForChild("enemies")
local packetData = {}
local loadedEnemies = {}
local RunService = game:GetService("RunService")

local function findEnemyModelFromHashParallal(hash)
	task.desynchronize()
	for _, scannedEnemy in pairs(loadedEnemies) do
		if scannedEnemy.Name == hash then
			return scannedEnemy
		end
	end
	task.synchronize()
	return nil
end

local function findEnemyModelFromHash(hash)
	for _, scannedEnemy in pairs(loadedEnemies) do
		if scannedEnemy.Name == hash then
			return scannedEnemy
		end
	end
	return nil
end


local function createEnemy(content)
    print("creating Enemy On Client")
	local enemyHash = content[1]
	local enemyClass = content[2]
	local EnemyModel = enemyModels:FindFirstChild(enemyClass)
	if EnemyModel then
		EnemyModel = EnemyModel:Clone()
		EnemyModel.Name = enemyHash
	else
		error("Enemy " .. enemyClass .. " " .. "dosent exist")
	end
	loadedEnemies[#loadedEnemies + 1] = EnemyModel
    EnemyModel.Parent = EnemyFolder
	local Animations: Folder = EnemyModel:WaitForChild("Animations")
	local walkAnimation = Animations:WaitForChild("walk")
	local AnimationController = EnemyModel:WaitForChild("AnimationController")
	local Animator: Animator = AnimationController:WaitForChild("Animator")
	local walkTrack = Animator:LoadAnimation(walkAnimation)
	walkTrack:Play()
end

local function deleteEnemy(hash)
	local enemyModel: Model | Actor = findEnemyModelFromHash(hash)
	if enemyModel then
		enemyModel:Destroy()
	else
		error("deletionEnemy does not exist, attempted deletion: " .. hash)
	end
end

local function updatePackets(newPacketData)
	packetData = newPacketData
end

local function updateEnemyPosition(delta)
	local primaryParts = {}
	local primaryCFrames = {}
	local index = 1
	for _, packet in pairs(packetData) do
		local hash = packet[1]
		local model:Model = findEnemyModelFromHashParallal(hash)
		local alpha = math.clamp(delta / 0.1, 0, 1)
		local position = packet[2]
		local modelPrimary = model.PrimaryPart
		primaryParts[index] = modelPrimary
		primaryCFrames[index] = modelPrimary.CFrame:Lerp(position, alpha)
        index += 1
	end
	task.synchronize()
	Workspace:BulkMoveTo(primaryParts, primaryCFrames, Enum.BulkMoveMode.FireCFrameChanged)
end

createBridge:Connect(createEnemy)
deleteBridge:Connect(deleteEnemy)
packetBridge:Connect(updatePackets)
RunService.Heartbeat:ConnectParallel(updateEnemyPosition)

EnemyClass.lua

--!native
--!strict
--[[
      __   _  _                                   
  _____  __/ /_ | || |  ___                             
 / _ \ \/ / '_ \| || |_/ __|                            
|  __/>  <| (_) |__   _\__ \                            
 \___/_/\_\\___/   |_| |___/      _   _                 
 _ __  _ __ ___   __| |_   _  ___| |_(_) ___  _ __  ___ 
| '_ \| '__/ _ \ / _` | | | |/ __| __| |/ _ \| '_ \/ __|
| |_) | | | (_) | (_| | |_| | (__| |_| | (_) | | | \__ \
| .__/|_|  \___/ \__,_|\__,_|\___|\__|_|\___/|_| |_|___/
|_|                                                     


]]
--[=[
@class Enemy

Enemy Class. Updates the Translated Value of Enemies, 
and are in the .enemies in the enemyReplicator Script.
addEnemy on the enemyReplicator module creates these classes.

]=]
local Enemy = {}
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local BezierPath = require(ReplicatedStorage.Packages:WaitForChild("BezierPath")) :: any
local BridgeNet = require(ReplicatedStorage.Packages:WaitForChild("bridgenet2")) :: any
local enemystats = require(ServerStorage:WaitForChild("EnemyStats"))
local bosshealthbar = ReplicatedStorage.AddHealthBar
local AbilityManager = require(ServerStorage.AbilityManager)
local Enemies = ReplicatedStorage.Enemies
local HashCreator = require(ServerStorage.HashCreator)
local deleteBridge = BridgeNet.ServerBridge("enemyDeletion")
local refresh_rate = 0.1
local deleteEvent = ReplicatedStorage.deletedCompletedEnemy
Enemy.__index = Enemy

export type enemy = {
	speed: number,
	health: number,
	hash: string,
	position: CFrame,
	translated: number,
	class: string,
	renderID: number,
	updateConnection: RBXScriptConnection,
}
--[=[
Creates an enemy
@param enemyName string --The class name of the enemy
@param path table --The path that the enemy follows
]=]
function Enemy.create(enemyName, path)
	ReplicatedStorage.DebugValues.EnemyCount.Value += 1
	local self = setmetatable({}, Enemy)
	self.position = path["path"]:CalculateUniformCFrame(0.01)
	self.class = enemyName
	local enemyData = enemystats.get(enemyName)
	self.speed = enemyData.speed
	self.health = enemyData.health
	self.hash = HashCreator.retriveHash()
	local _, boundingBox = Enemies[enemyName]:GetBoundingBox()
	local sizeAddition = boundingBox.Y / 2
	local rotationalOffset
	local offset = math.random(-1, 1)
	if enemyData.modifiers["boss"] then
		bosshealthbar:FireAllClients(enemyData.name, self, self.health)
	end
	if enemyData.rotationOffset ~= nil then
		rotationalOffset = enemyData.rotationOffset
	else
		rotationalOffset = Vector3.zero
	end
	if enemyData["abilities"] then
		task.spawn(function()
			for ability, _ in pairs(enemyData["abilities"]) do
				AbilityManager.register("enemies", ability, self)
			end
		end)
	end
	local rotationalCFrame = CFrame.Angles(rotationalOffset.X, rotationalOffset.Y, rotationalOffset.Z)
	local offsetCFrame = CFrame.new(offset, sizeAddition, 0)
	local pathLength = path["path"]:GetPathLength()
	self.updateConnection = task.spawn(function()
		local PreviousTime = os.clock()
		self.translated = 0
		while self.translated < 1 do
			task.desynchronize()
			self.translated += (os.clock() - PreviousTime) * self.speed / pathLength
			PreviousTime = os.clock()
			local transformedcframe: CFrame = path["path"]:CalculateUniformCFrame(self.translated)
				* offsetCFrame
				* rotationalCFrame
			self.position = transformedcframe
			task.wait(refresh_rate)
			task.synchronize()
		end
		ReplicatedStorage.DebugValues.EnemyCount.Value -= 1
		deleteEvent:Fire(self.hash)
		if self.health > 0 then
			ReplicatedStorage.BaseHealth.Value -= self.health
			self.health = 0
		end
	end)
	return self
end

--[=[
Damaging function. Unused function, just use enemyReplicator.enemyEffect(health)
@param damage number --damage that will be given to the enemy
]=]

function Enemy:damage(damage): nil
	local clampedDamage = math.clamp(self.health - damage, 0, 9999999999)
	self.health -= clampedDamage
	if self.health <= 0 then
		self.health = 0
	end
	return nil
end

--[=[
Retrives data about the enemy, such as positioning,
to send to the client
]=]

function Enemy:GetPacketData()
	return {
		self.hash,
		self.position,
	}
end

--[=[
Retrives data for targeting data for towers
]=]

function Enemy:GetTargetingData()
	return {
		self.hash,
		self.position,
		self.translated,
		self.health,
	}
end

return Enemy

EnemyReplicator.lua

--!native
--[[
      __   _  _                                   
  _____  __/ /_ | || |  ___                             
 / _ \ \/ / '_ \| || |_/ __|                            
|  __/>  <| (_) |__   _\__ \                            
 \___/_/\_\\___/   |_| |___/      _   _                 
 _ __  _ __ ___   __| |_   _  ___| |_(_) ___  _ __  ___ 
| '_ \| '__/ _ \ / _` | | | |/ __| __| |/ _ \| '_ \/ __|
| |_) | | | (_) | (_| | |_| | (__| |_| | (_) | | | \__ \
| .__/|_|  \___/ \__,_|\__,_|\___|\__|_|\___/|_| |_|___/
|_|                                                     
]]
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local replicator = {}
replicator.pathTable = {}
replicator.enemyCount = 0
local BezierPath = require(ReplicatedStorage.Packages:WaitForChild("BezierPath")) :: any
local BridgeNet = require(ReplicatedStorage.Packages:WaitForChild("bridgenet2")) :: any
local enemyclass = require(ServerStorage:WaitForChild("EnemyClass"))
local packetBridge = BridgeNet.ServerBridge("enemyReplication")
--packetBridge.Logging = true
local createBridge = BridgeNet.ServerBridge("enemyCreation")
local deleteBridge = BridgeNet.ServerBridge("enemyDeletion")
local deleteEvent = ReplicatedStorage.deletedCompletedEnemy
local enemies = {}
--[=[ 
@class replicator

This handles enemy Replication, sends packets to the client, so the client can render and position
All the functions in this module handle enemies, and do things like damage or heal effects.
This module also handles creating enemies.

]=]

--[=[
@function setUp
This function sets up the Replicator, which connects BindableEvent
to deleteEnemies, and reset the EnemyCount.

@within replicator

]=]
replicator.setUp = function(_)
	deleteEvent.Event:Connect(function(hash)
		for index, enemy in pairs(enemies) do
			if enemy.hash == hash then
				deleteBridge:Fire(BridgeNet.AllPlayers(), enemy["hash"])
				enemies[index] = nil
			end
		end
	end)
end

--[=[
@function createnavpath
This function creates an path with the BezierPath Module.
This function is called for each path in the map.

@param nodefolder Folder
@param index number

@within replicator
]=]

replicator.createnavpath = function(nodefolder, index)
	local nodePositions = { nodefolder.enemyspawn.Position }
	for i = 1, #nodefolder:GetChildren() - 2 do
		table.insert(nodePositions, nodefolder[i].Position)
	end

	table.insert(nodePositions, nodefolder.exit.Position)
	local BezPath = BezierPath.new(nodePositions, 3)
	local pathtable = {
		["path"] = BezPath,
		["nodefolder"] = nodefolder,
	}
	replicator.pathTable[index] = pathtable
end
--[=[
@function getSingleEnemy

SelfExplanatory, retrives an single enemy with the hash provided

@param enemyHash string

@within replicator
]=]
replicator.getSingleEnemy = function(enemyHash)
	task.desynchronize()
	for index, enemy in pairs(enemies) do
		if enemy.hash == enemyHash then
			return enemy, index
		end
	end
	task.synchronize()
	return nil
end

--[=[
@function getEnemyCount
Retrives the amount of enemies active

@within replicator
]=]
replicator.getEnemyCount = function()
	return replicator.enemyCount
end

--[=[
@function getTargetingData

Compiles an table with all the enemies, 
with condensed info like the index of translation, the position, the health, and the translated index.

@within replicator
]=]
replicator.getTargetingData = function()
	local targetingPacket = {}
	for _, enemy in pairs(enemies) do
		targetingPacket[#targetingPacket + 1] = enemy:GetTargetingData()
	end
	return targetingPacket
end

--[=[
@function enemyEffect

Changes an property in the enemy with the hash applied.
If the effect is health, than it checks if the health is under 0, and if it is, deletes the enemy.
TODO give cash to everyone based on enemy

@param hash string
@param effect any
@param new any

@within replicator
]=]
replicator.enemyEffect = function(hash, effect, new)
	local retrivedEnemy, retrivedIndex = replicator.getSingleEnemy(hash)
	retrivedEnemy[effect] = new
	if (effect == "health") and retrivedEnemy["health"] <= 0 then
		enemies[retrivedIndex] = nil
	end
end
--[=[
@function update

Compiles all the enemies into one condensed table, with the CFrame. Than, using bridgeNet2, sends
the infomation to the client, so it can be rendered on the client.

@within replicator
]=]

replicator.update = function()
	task.desynchronize()
	local packet = {}
	for _, enemyClass in pairs(enemies) do
		packet[#packet + 1] = enemyClass:GetPacketData()
	end
	task.synchronize()
	if #packet > 0 then
		packetBridge:Fire(BridgeNet.AllPlayers(), packet)
	end
end

--[=[
@function addEnemy

Creates an enemy, and adds it to the replicator table in the module. Than, 
fires an bridge to the client to create an enemy, and increases the enemyCount.

@param enemyName string
@param pathNum number

@within replicator
]=]

replicator.addEnemy = function(enemyName, pathNum)
	local path = replicator.pathTable[pathNum]
	local enemyClass = enemyclass.create(enemyName, path)
	enemies[#enemies + 1] = enemyClass
	createBridge:Fire(BridgeNet.AllPlayers(), { enemyClass["hash"], enemyClass["class"], enemyClass.index })
	replicator.enemyCount += 1
end

return replicator