Kembali
PerformanceManager Roblox Utility Optimasi Script & Anti Memory Leak
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.
-- 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