function ZoneController.updateDetection(A)local J={enterDetection="\095\099\117\114\114\101\110\116\069\110\116\101\114\068\101\116\101\099\116\105\111\110";exitDetection="\095\099\117\114\114\101\110\116\069\120\105\116\068\101\116\101\099\116\105\111\110"}for J,X in pairs(J)do local o=A[J]local l=Tracker.getCombinedTotalVolumes()if o==enum.Detection.Automatic then if l>WHOLE_BODY_DETECTION_LIMIT then o=enum.Detection.Centre else o=enum.Detection.WholeBody end end A[X]=o end end function ZoneController._formHeartbeat(A)local J=heartbeatConnections[A]if J then return end local X=0 J=heartbeat:Connect(function()local J=os.clock()if J>=X then local o local l for J,X in pairs(activeZones)do if J.activeTriggers[A]then local A=J.accuracy if o==nil or A<o then o=A end ZoneController.updateDetection(J)local X=J._currentEnterDetection if l==nil or X<l then l=X end end end local P=o local u=heartbeatActions[A](l)local y={}local k={}for A,J in pairs(u)do local X=A.settingsGroupName and ZoneController.getGroup(A.settingsGroupName)if X and X.onlyEnterOnceExitedAll==true then for J,X in pairs(J)do local o=y[A.settingsGroupName]if not o then o={}y[A.settingsGroupName]=o end o[J]=A end k[A]=J end end for A,J in pairs(k)do local X=y[A.settingsGroupName]if X then for o,l in pairs(J)do local P=X[o]if P and P~=A then J[o]=nil end end end end local N={{},{}}for J,X in pairs(activeZones)do if J.activeTriggers[A]then local X=J.accuracy local o=u[J]or{}local l=false for A,J in pairs(o)do l=true break end if l and X>P then P=X end local y=J:_updateOccupants(A,o)N[1][J]=y.exited N[2][J]=y.entered end end local Y={"\069\120\105\116\101\100";"\069\110\116\101\114\101\100"}for J,X in pairs(N)do local o=Y[J]local l=A..o for A,J in pairs(X)do local X=A[l]if X then for A,J in pairs(J)do X:Fire(J)end end end end local f=enum.Accuracy.getProperty(P)X=J+f end end)heartbeatConnections[A]=J end function ZoneController._deregisterConnection(A,J)activeConnections-=1 if activeTriggers[J]==1 then activeTriggers[J]=nil local A=heartbeatConnections[J]if A then heartbeatConnections[J]=nil A:Disconnect()end else activeTriggers[J]-=1 end A.activeTriggers[J]=nil if dictLength(A.activeTriggers)==0 then activeZones[A]=nil ZoneController._updateZoneDetails()end if A.touchedConnectionActions[J]then A:_disconnectTouchedConnection(J)end end function ZoneController._updateZoneDetails()activeParts={}activePartToZone={}allParts={}allPartToZone={}activeZonesTotalVolume=0 for A,J in pairs(registeredZones)do local X=activeZones[A]if X then activeZonesTotalVolume+=A.volume end for J,o in pairs(A.zoneParts)do if X then table.insert(activeParts,o)activePartToZone[o]=A end table.insert(allParts,o)allPartToZone[o]=A end end end function ZoneController._getZonesAndItems(A,J,X,o,l)local P=X if not P then for A,J in pairs(J)do P+=A.volume end end local u={}local y=trackers[A]if y.totalVolume<P then for J,X in pairs(y.items)do local P=ZoneController.getTouchingZones(X,o,l,y)for J,l in pairs(P)do if not o or l.activeTriggers[A]then local J=X if A=="\112\108\097\121\101\114"then J=players:GetPlayerFromCharacter(X)end if J then fillOccupants(u,l,J)end end end end else for J,X in pairs(J)do if not o or J.activeTriggers[A]then local X=CollectiveWorldModel:GetPartBoundsInBox(J.region.CFrame,J.region.Size,y.whitelistParams)local o={}for A,J in pairs(X)do local X=y.partToItem[J]if not o[X]then o[X]=true end end for X,o in pairs(o)do if A=="\112\108\097\121\101\114"then local A=players:GetPlayerFromCharacter(X)if J:findPlayer(A)then fillOccupants(u,J,A)end elseif J:findItem(X)then fillOccupants(u,J,X)end end end end end return u end

function ZoneController.updateDetection(zone)
	local detectionTypes = {
		["enterDetection"] = "_currentEnterDetection",
		["exitDetection"] = "_currentExitDetection",
	}
	for detectionType, currentDetectionName in pairs(detectionTypes) do
		local detection = zone[detectionType]
		local combinedTotalVolume = Tracker.getCombinedTotalVolumes()
		if detection == enum.Detection.Automatic then
			if combinedTotalVolume > WHOLE_BODY_DETECTION_LIMIT then
				detection = enum.Detection.Centre
			else
				detection = enum.Detection.WholeBody
			end
		end
		zone[currentDetectionName] = detection
	end
end

function ZoneController._formHeartbeat(registeredTriggerType)
	local heartbeatConnection = heartbeatConnections[registeredTriggerType]
	if heartbeatConnection then return end
	-- This will only ever connect once per triggerType per server
	-- This means instead of initiating a loop per-zone we can handle everything within
	-- a singular connection. This is particularly beneficial for player/item-orinetated
	-- checking, where a check only needs to be cast once per interval, as apposed
	-- to every zone per interval
	-- I utilise heartbeat with os.clock() to provide precision (where needed) and flexibility
	local nextCheck = 0
	heartbeatConnection = heartbeat:Connect(function()
		local clockTime = os.clock()
		if clockTime >= nextCheck then
			local lowestAccuracy
			local lowestDetection
			for zone, _ in pairs(activeZones) do
				if zone.activeTriggers[registeredTriggerType] then
					local zAccuracy = zone.accuracy
					if lowestAccuracy == nil or zAccuracy < lowestAccuracy then
						lowestAccuracy = zAccuracy
					end
					ZoneController.updateDetection(zone)
					local zDetection = zone._currentEnterDetection
					if lowestDetection == nil or zDetection < lowestDetection then
						lowestDetection = zDetection
					end
				end
			end
			local highestAccuracy = lowestAccuracy
			local zonesAndOccupants = heartbeatActions[registeredTriggerType](lowestDetection)

			-- If a zone belongs to a settingsGroup with 'onlyEnterOnceExitedAll = true' , and the occupant already exists in a member group, then
			-- ignore all incoming occupants for the other zones (preventing the enteredSignal from being fired until the occupant has left
			-- all other zones within the same settingGroup)
			local occupantsToBlock = {}
			local zonesToPotentiallyIgnore = {}
			for zone, newOccupants in pairs(zonesAndOccupants) do
				local settingsGroup = (zone.settingsGroupName and ZoneController.getGroup(zone.settingsGroupName))
				if settingsGroup and settingsGroup.onlyEnterOnceExitedAll == true then
					--local currentOccupants = zone.occupants[registeredTriggerType]
					--if currentOccupants then
						for newOccupant, _ in pairs(newOccupants) do
							--if currentOccupants[newOccupant] then
								local groupDetail = occupantsToBlock[zone.settingsGroupName]
								if not groupDetail then
									groupDetail = {}
									occupantsToBlock[zone.settingsGroupName] = groupDetail
								end
								groupDetail[newOccupant] = zone
							--end
						end
						zonesToPotentiallyIgnore[zone] = newOccupants
					--end
				end
			end
			for zone, newOccupants in pairs(zonesToPotentiallyIgnore) do
				local groupDetail = occupantsToBlock[zone.settingsGroupName]
				if groupDetail then
					for newOccupant, _ in pairs(newOccupants) do
						local occupantToKeepZone = groupDetail[newOccupant]
						if occupantToKeepZone and occupantToKeepZone ~= zone then
							newOccupants[newOccupant] = nil
						end
					end
				end
			end

			-- This deduces what signals should be fired
			local collectiveSignalsToFire = {{}, {}}
			for zone, _ in pairs(activeZones) do
				if zone.activeTriggers[registeredTriggerType] then
					local zAccuracy = zone.accuracy
					local occupantsDict = zonesAndOccupants[zone] or {}
					local occupantsPresent = false
					for k,v in pairs(occupantsDict) do
						occupantsPresent = true
						break
					end
					if occupantsPresent and zAccuracy > highestAccuracy then
						highestAccuracy = zAccuracy
					end
					local signalsToFire = zone:_updateOccupants(registeredTriggerType, occupantsDict)
					collectiveSignalsToFire[1][zone] = signalsToFire.exited
					collectiveSignalsToFire[2][zone] = signalsToFire.entered
				end
			end

			-- This ensures all exited signals and called before entered signals
			local indexToSignalType = {"Exited", "Entered"}
			for index, zoneAndOccupants in pairs(collectiveSignalsToFire) do
				local signalType = indexToSignalType[index]
				local signalName = registeredTriggerType..signalType
				for zone, occupants in pairs(zoneAndOccupants) do
					local signal = zone[signalName]
					if signal then
						for _, occupant in pairs(occupants) do
							signal:Fire(occupant)
						end
					end
				end
			end

			local cooldown = enum.Accuracy.getProperty(highestAccuracy)
			nextCheck = clockTime + cooldown
		end
	end)
	heartbeatConnections[registeredTriggerType] = heartbeatConnection
end

function ZoneController._deregisterConnection(registeredZone, registeredTriggerType)
	activeConnections -= 1
	if activeTriggers[registeredTriggerType] == 1 then
		activeTriggers[registeredTriggerType] = nil
		local heartbeatConnection = heartbeatConnections[registeredTriggerType]
		if heartbeatConnection then
			heartbeatConnections[registeredTriggerType] = nil
			heartbeatConnection:Disconnect()
		end
	else
		activeTriggers[registeredTriggerType] -= 1
	end
	registeredZone.activeTriggers[registeredTriggerType] = nil
	if dictLength(registeredZone.activeTriggers) == 0 then
		activeZones[registeredZone] = nil
		ZoneController._updateZoneDetails()
	end
	if registeredZone.touchedConnectionActions[registeredTriggerType] then
		registeredZone:_disconnectTouchedConnection(registeredTriggerType)
	end
end

function ZoneController._updateZoneDetails()
	activeParts = {}
	activePartToZone = {}
	allParts = {}
	allPartToZone = {}
	activeZonesTotalVolume = 0
	for zone, _ in pairs(registeredZones) do
		local isActive = activeZones[zone]
		if isActive then
			activeZonesTotalVolume += zone.volume
		end
		for _, zonePart in pairs(zone.zoneParts) do
			if isActive then
				table.insert(activeParts, zonePart)
				activePartToZone[zonePart] = zone
			end
			table.insert(allParts, zonePart)
			allPartToZone[zonePart] = zone
		end
	end
end

function ZoneController._getZonesAndItems(trackerName, zonesDictToCheck, zoneCustomVolume, onlyActiveZones, recommendedDetection)
	local totalZoneVolume = zoneCustomVolume
	if not totalZoneVolume then
		for zone, _ in pairs(zonesDictToCheck) do
			totalZoneVolume += zone.volume
		end
	end
	local zonesAndOccupants = {}
	local tracker = trackers[trackerName]
	if tracker.totalVolume < totalZoneVolume then
		-- If the volume of all *characters/items* within the server is *less than* the total
		-- volume of all active zones (i.e. zones which listen for .playerEntered)
		-- then it's more efficient cast checks within each character and
		-- then determine the zones they belong to
		for _, item in pairs(tracker.items) do
			local touchingZones = ZoneController.getTouchingZones(item, onlyActiveZones, recommendedDetection, tracker)
			for _, zone in pairs(touchingZones) do
				if not onlyActiveZones or zone.activeTriggers[trackerName] then
					local finalItem = item
					if trackerName == "player" then
						finalItem = players:GetPlayerFromCharacter(item)
					end
					if finalItem then
						fillOccupants(zonesAndOccupants, zone, finalItem)
					end
				end
			end
		end
	else
		-- If the volume of all *active zones* within the server is *less than* the total
		-- volume of all characters/items, then it's more efficient to perform the
		-- checks directly within each zone to determine players inside
		for zone, _ in pairs(zonesDictToCheck) do
			if not onlyActiveZones or zone.activeTriggers[trackerName] then
				local result = CollectiveWorldModel:GetPartBoundsInBox(zone.region.CFrame, zone.region.Size, tracker.whitelistParams)
				local finalItemsDict = {}
				for _, itemOrChild in pairs(result) do
					local correspondingItem = tracker.partToItem[itemOrChild]
					if not finalItemsDict[correspondingItem] then
						finalItemsDict[correspondingItem] = true
					end
				end
				for item, _ in pairs(finalItemsDict) do
					if trackerName == "player" then
						local player = players:GetPlayerFromCharacter(item)
						if zone:findPlayer(player) then
							fillOccupants(zonesAndOccupants, zone, player)
						end
					elseif zone:findItem(item) then
						fillOccupants(zonesAndOccupants, zone, item)
					end
				end
			end
		end
	end
	return zonesAndOccupants
end


