Kembali

PerformanceManager Roblox Utility Optimasi Script & Anti Memory Leak

lua Uploaded by Neo 15 views 06 Jun 2026

PerformanceManager adalah ModuleScript Roblox yang dirancang untuk membantu developer menjaga performa game tetap stabil. Script ini menyediakan fitur throttle, debounce, pengelolaan koneksi event, auto cleanup instance, cache instance, batch loop, dan cleanup otomatis saat player keluar. Cocok digunakan untuk project Roblox yang memiliki banyak event, loop, efek, atau sistem real-time agar lebih ringan, terstruktur, dan tidak mudah menyebabkan lag.

PerformanceManager Roblox Utility Optimasi Script & Anti Memory Leak LUA
-- ModuleScript: PerformanceManager
-- Letakkan di: ReplicatedStorage atau ServerScriptService
-- Cara pakai:
-- local Perf = require(path.to.PerformanceManager)
-- Perf.init()

local PerformanceManager = {}

local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local Debris = game:GetService("Debris")

local connections = {}
local throttleRegistry = {}
local debounceRegistry = {}
local instanceCache = {}
local playerCleanupCallbacks = {}

local initialized = false

-- =====================
-- THROTTLE / DEBOUNCE
-- =====================

function PerformanceManager.throttle(key, interval, fn)
	if typeof(key) ~= "string" then return end
	if typeof(interval) ~= "number" then return end
	if typeof(fn) ~= "function" then return end

	local now = os.clock()

	if not throttleRegistry[key] or (now - throttleRegistry[key]) >= interval then
		throttleRegistry[key] = now

		local success, err = pcall(fn)
		if not success then
			warn("[PerformanceManager.throttle]", err)
		end
	end
end

function PerformanceManager.debounce(key, delayTime, fn)
	if typeof(key) ~= "string" then return end
	if typeof(delayTime) ~= "number" then return end
	if typeof(fn) ~= "function" then return end

	if debounceRegistry[key] then
		task.cancel(debounceRegistry[key])
	end

	debounceRegistry[key] = task.delay(delayTime, function()
		debounceRegistry[key] = nil

		local success, err = pcall(fn)
		if not success then
			warn("[PerformanceManager.debounce]", err)
		end
	end)
end

-- =====================
-- CONNECTION MANAGER
-- =====================

function PerformanceManager.addConnection(key, conn)
	if typeof(key) ~= "string" then return end

	if connections[key] then
		connections[key]:Disconnect()
	end

	connections[key] = conn
end

function PerformanceManager.removeConnection(key)
	if connections[key] then
		connections[key]:Disconnect()
		connections[key] = nil
	end
end

function PerformanceManager.clearConnections()
	for key, conn in pairs(connections) do
		if conn and conn.Connected then
			conn:Disconnect()
		end
		connections[key] = nil
	end
end

-- =====================
-- INSTANCE CLEANER / CACHE
-- =====================

function PerformanceManager.autoClean(instance, delayTime)
	if instance then
		Debris:AddItem(instance, delayTime or 5)
	end
end

function PerformanceManager.getCache(key, fetchFn)
	if typeof(key) ~= "string" then return nil end
	if typeof(fetchFn) ~= "function" then return nil end

	local cached = instanceCache[key]

	if cached and cached.Parent then
		return cached
	end

	local result = fetchFn()

	if result then
		instanceCache[key] = result
	end

	return result
end

function PerformanceManager.removeCache(key)
	instanceCache[key] = nil
end

function PerformanceManager.clearCache()
	table.clear(instanceCache)
end

function PerformanceManager.pruneCache()
	for key, inst in pairs(instanceCache) do
		if not inst or not inst.Parent then
			instanceCache[key] = nil
		end
	end
end

-- =====================
-- LOOP THROTTLER
-- =====================

function PerformanceManager.batchLoop(list, fn, batchSize)
	if typeof(list) ~= "table" then return end
	if typeof(fn) ~= "function" then return end

	batchSize = batchSize or 10

	local i = 1

	while i <= #list do
		for j = i, math.min(i + batchSize - 1, #list) do
			local success, err = pcall(fn, list[j])

			if not success then
				warn("[PerformanceManager.batchLoop]", err)
			end
		end

		i += batchSize

		if i <= #list then
			task.wait()
		end
	end
end

-- =====================
-- RUNSERVICE GUARD
-- =====================

function PerformanceManager.addSteppedLoop(key, minInterval, fn)
	if typeof(key) ~= "string" then return end
	if typeof(minInterval) ~= "number" then return end
	if typeof(fn) ~= "function" then return end

	local elapsed = 0

	local conn = RunService.Heartbeat:Connect(function(dt)
		elapsed += dt

		if elapsed >= minInterval then
			elapsed = 0

			local success, err = pcall(fn, dt)
			if not success then
				warn("[PerformanceManager.addSteppedLoop]", err)
			end
		end
	end)

	PerformanceManager.addConnection("stepped_" .. key, conn)
end

-- =====================
-- PLAYER CLEANUP
-- =====================

function PerformanceManager.onPlayerRemoving(fn)
	if typeof(fn) == "function" then
		table.insert(playerCleanupCallbacks, fn)
	end
end

local function handlePlayerRemoving(player)
	for _, cb in ipairs(playerCleanupCallbacks) do
		local success, err = pcall(cb, player)

		if not success then
			warn("[PerformanceManager.PlayerRemoving]", err)
		end
	end
end

-- =====================
-- INIT
-- =====================

function PerformanceManager.init()
	if initialized then return end
	initialized = true

	task.spawn(function()
		while initialized do
			task.wait(30)
			PerformanceManager.pruneCache()
		end
	end)

	task.spawn(function()
		while initialized do
			task.wait(60)

			local now = os.clock()

			for key, lastTime in pairs(throttleRegistry) do
				if typeof(lastTime) == "number" and (now - lastTime) > 120 then
					throttleRegistry[key] = nil
				end
			end
		end
	end)

	if RunService:IsServer() then
		PerformanceManager.addConnection(
			"playerRemoving",
			Players.PlayerRemoving:Connect(handlePlayerRemoving)
		)
	end
end

function PerformanceManager.destroy()
	initialized = false
	PerformanceManager.clearConnections()
	PerformanceManager.clearCache()
	table.clear(throttleRegistry)

	for key, threadRef in pairs(debounceRegistry) do
		task.cancel(threadRef)
		debounceRegistry[key] = nil
	end

	table.clear(playerCleanupCallbacks)
end

return PerformanceManager