Roblox Luau lifecycle utility

GarbageMan

A typed cleanup scope for Roblox systems. Track connections, instances, promises, tweens, render bindings and temporary resources, then clean them up with one clear lifetime owner.

  • Typed Luau API
  • v0.1.4 release
  • Wally, Rojo and RBXM
Scope: Weapon Tracking resources
Connections, tweens and instances enter the scope.

Install

Drop it into a Roblox project fast.

[dependencies] GarbageMan = "virtualdesign0/garbageman@0.1.4"

Core idea

One scope owns one lifetime.

Roblox systems often create resources that should not live forever. A UI screen has connections and tweens. A weapon has input bindings. An NPC has controllers and events. A projectile has a short lifetime. GarbageMan gives each of those systems a clear owner.

Features

Small enough for gameplay code, useful enough for larger systems.

Typed API

Use typed Luau methods with predictable cleanup semantics and named scopes.

Resource tracking

Track instances, connections, functions, threads, promises, tweens and custom objects.

Tagged resources

Replace, get, remove or drop resources when only one object should own a tag.

Child scopes

Create nested lifetimes with Extend and Adopt. Parent destruction cleans children.

Delayed cleanup

Use AddTemporary, CleanAfter and DestroyAfter for hitboxes, UI, VFX and projectiles.

Batched cleanup

Split larger cleanup work across scheduler steps with CleanBatched or DestroyBatched.

Signal helpers

Connect, Once, DestroyOnSignal and ReplaceConnection keep event cleanup close.

Debug tools

Inspect summaries, leak warnings, failed cleanup info and optional profiling data.

Clean vs Destroy

Reusable cleanup or final shutdown.

Clean

Clean removes current resources, but the scope can be used again.

local scope = GarbageMan.new("Round")

scope:Add(connection)
scope:Clean()

scope:Add(newConnection) -- valid

Destroy

Destroy is final. Mutating methods should not be used afterwards.

local scope = GarbageMan.new("Weapon")

scope:Add(connection)
scope:Destroy("Weapon unequipped")

scope:Add(otherConnection) -- error

Installation

Use it with Rojo, Wally or a direct RBXM file.

Rojo

Mount the module into ReplicatedStorage and require the folder directly.

{
  "name": "GarbageMan",
  "tree": {
    "$className": "DataModel",
    "ReplicatedStorage": {
      "$className": "ReplicatedStorage",
      "GarbageMan": {
        "$path": "src/GarbageMan"
      }
    }
  }
}

Wally

Add GarbageMan as a dependency, then install packages.

[dependencies]
GarbageMan = "virtualdesign0/garbageman@0.1.4"

# then
wally install

RBXM

Download the latest release asset and drag it into ReplicatedStorage.

https://github.com/Virtualdesign0/GarbageMan/releases/latest/download/GarbageMan.rbxm

Examples

Common Roblox lifetimes.

UI Scope

local scope = GarbageMan.new("InventoryUI")

local frame = scope:Copy(InventoryTemplate)
frame.Parent = playerGui

scope:DestroyOnSignal(closeButton.Activated, "Inventory closed")

local tween = TweenService:Create(frame, tweenInfo, {
	Position = UDim2.fromScale(0.5, 0.5),
})

scope:ReplaceTween("Tween:Open", tween)
tween:Play()

Temporary Hitbox

local scope = GarbageMan.new("Hitbox")

local hitbox = Instance.new("Part")
hitbox.Name = "TemporaryHitbox"
hitbox.Anchored = true
hitbox.CanCollide = false
hitbox.Parent = workspace

scope:AddTemporary(hitbox, 0.25)

scope:Connect(hitbox.Touched, function(hit)
	print("Touched:", hit.Name)
end)

Projectile Scope

local projectileScope = GarbageMan.new("Projectile")

projectileScope:DestroyAfter(10, "Projectile expired")

local projectile = projectileScope:Construct(Instance, "Part")
projectile.Name = "Projectile"
projectile.Parent = workspace

projectileScope:Connect(projectile.Touched, function(hit)
	projectileScope:Destroy("Projectile hit")
end)

Batched Round Cleanup

local roundScope = GarbageMan.new("Round")

for _, npc in npcs do
	roundScope:Add(npc)
end

roundScope:DestroyBatched("Round ended", 50)

API Preview

The methods you will probably use the most.

Method Purpose
Add()Track one resource.
AddMany()Track multiple default-cleanable resources.
Replace()Store one tagged resource and clean the old one.
Connect()Connect to a signal and track the connection.
ReplaceTween()Replace a tagged tween and cancel the previous one.
AddTemporary()Clean one resource after a delay.
Clean()Clean current resources while keeping the scope reusable.
Destroy()Final cleanup. The scope should not be used afterwards.
Extend()Create a child scope owned by the current scope.
DestroyBatched()Destroy the scope and split cleanup work into batches.

Debugging

Find leaks before they become annoying.

Scope summary

Use summaries for safer debug panels without printing real object references.

for _, summary in GarbageMan.Debug.getSummary() do
	print(
		summary.name,
		summary.size,
		summary.age,
		summary.destroyed
	)
end

Configuration

Enable tracebacks, leak warnings or profiling while testing.

GarbageMan.configure({
	tracebacks = true,
	captureAddTracebacks = true,
	leakWarnings = true,
	profiling = true,
})

Behavior Notes

Simple rules that keep scopes clean.

Do not create a scope every frame.

Create scopes for real lifetimes like UI screens, weapons, NPCs or projectiles.

Use Drop only for ownership transfer.

Remove cleans the resource. Drop releases it without cleaning.

Keep tags for cleanup ownership.

Tags are useful for replacement cleanup, not for all gameplay state.

Destroy final lifetimes.

If the system is finished for good, prefer Destroy over Clean.

Ready to use

Add clear cleanup ownership to Roblox systems.

GarbageMan stays light for gameplay code while still covering debugging, child scopes, delayed cleanup and batched shutdown.