Skip to main content

Mechanic

This was deprecated in v1.0.0
Use [Binder](/api/Binder) instead.
Deprecated

Mechanic is no longer maintained and is kept only for existing projects. Use Binder instead; it covers the same use case with a smaller API.

Mechanic attaches behaviour to every instance that carries a CollectionService tag. It wraps CollectionService:GetInstanceAddedSignal / GetInstanceRemovedSignal and filters the tagged instances by class name, by ancestor, or with a custom predicate. Every instance that passes the filters gets an AppliedMechanic object holding its own per-instance Data table and a Trove that is cleaned when the instance is untagged, destroyed, or moved out of the allowed ancestors.

The module returns a table with three constructors: new, newConstructor and bindToClass. Everything else is a method on the Mechanic object they return.

local RunService = game:GetService("RunService")
local Mechanic = require(path.to.Mechanic)

local Spinner = Mechanic.new("Spinner", {
	Shared = { Speed = 2 }, -- copied into every instance's Data table
	ClassNames = { "BasePart" },
	Ancestors = { workspace },
})

Spinner:OnAdded(function(part, applied, trove)
	local data = applied:GetData()
	trove:Connect(RunService.Heartbeat, function(dt)
		part.CFrame *= CFrame.Angles(0, data.Speed * dt, 0)
	end)
end)

Spinner:OnRemoved(function(part)
	print(part.Name, "stopped spinning")
end)

-- Later: tag a part manually (also adds the "Spinner" tag) with custom data
Spinner:Apply(workspace.Windmill, { Speed = 0.5 })

Depends on Sleitnick's Trove and Signal, which Wally installs alongside it.

Credits: Trove and Signal are by sleitnick (sleitnick's RbxUtil). Wally installs both.

Installation and guide: Mechanic package page.

Types​

Constructable​

type Constructable = {new: (U...) → T}

A class table with a new constructor, as accepted by newConstructor and bindToClass. The constructor is called through Trove:Construct as new(instance, trove) and its result becomes the AppliedMechanic's Data.

MechanicConfig​

interface MechanicConfig {
Shared: T?--

Table that is shallow-copied into each instance's Data (and exposed as Shared). Defaults to {}.

Ancestors: {U}?--

Instances the tagged instance must be a descendant of (any one of them). Empty means no ancestor filter.

ClassNames: {string}?--

Class names the instance must match with IsA (any one of them). Empty means no class filter.

CustomPredicate: ((Instance) → boolean)?--

Extra check run after the other filters; return false to reject the instance.

}

Configuration passed to Mechanic.new to decide which tagged instances the mechanic applies to and what data they start with. Every field is optional; an empty config applies to every instance with the tag.

Properties​

Tag​

This item is read only and cannot be modified. Read Only
Mechanic.Tag: string

The CollectionService tag this mechanic watches.

Shared​

Mechanic.Shared: T

The shared table from the config (or {}). Each AppliedMechanic's Data starts as a shallow copy of it.

Applications​

This item is read only and cannot be modified. Read Only
Mechanic.Applications: {[Instance]: AppliedMechanic<T>}

Every instance the mechanic is currently applied to, mapped to its AppliedMechanic. Prefer GetApplied and GetInstances.

Trove​

Mechanic.Trove: Trove

The mechanic's own Trove. Everything added to it is cleaned by Destroy, including every AppliedMechanic.

InstanceAdded​

This item is read only and cannot be modified. Read Only
Mechanic.InstanceAdded: Signal<Instance,AppliedMechanic<T>,Trove>

Fires when the mechanic is applied to an instance. Receives the instance, its AppliedMechanic and the AppliedMechanic's Trove. OnAdded also replays existing applications.

InstanceRemoved​

This item is read only and cannot be modified. Read Only
Mechanic.InstanceRemoved: Signal<Instance>

Fires with the instance after its AppliedMechanic is cleaned (untagged, destroyed, moved out of Ancestors, or revoked).

Functions​

new​

Mechanic.new(
Tag: string,--

The CollectionService tag to watch.

Config: MechanicConfig<T,U>?--

Filters and shared data; defaults to no filters and Shared = {}.

) → Mechanic<T>--

The new mechanic.

Creates a Mechanic for Tag. Instances that already have the tag and pass the filters are applied immediately (each in its own coroutine); instances tagged later are applied as they appear, and instances that lose the tag are revoked. When Ancestors is set, tagged instances that are later parented under one of the ancestors are picked up too.

newConstructor​

Mechanic.newConstructor(
Tag: string,--

The CollectionService tag to watch.

Config: MechanicConfig<A,B>,--

Filters and shared data; pass {} for none.

Constructable: Constructable<C,D...>--

A class table whose new(instance, trove) builds the per-instance object.

) → Mechanic<C>--

The new mechanic; each AppliedMechanic's Data is the constructed object.

Creates a Mechanic and, for every instance it is applied to, constructs Constructable.new(instance, trove) through the AppliedMechanic's Trove and stores the result in the AppliedMechanic's Data. The constructed object is destroyed with the Trove when the instance is removed. This is the closest equivalent to Binder.

local Door = {}
Door.__index = Door

function Door.new(model: Model, trove)
	local self = setmetatable({ Model = model, Open = false }, Door)
	trove:Connect(model.ClickDetector.MouseClick, function() self:Toggle() end)
	return self
end

function Door.Toggle(self)
	self.Open = not self.Open
end

function Door.Destroy(self) end

local DoorMechanic = Mechanic.newConstructor("Door", { ClassNames = { "Model" } }, Door)

bindToClass​

Mechanic.bindToClass(
Tag: string,--

The CollectionService tag to watch.

Config: MechanicConfig<A,B>,--

Filters and shared data; pass {} for none.

Constructable: Constructable<C,D...>--

A class table whose new(instance, trove) builds the per-instance object.

) → Mechanic<C>--

The new mechanic; each AppliedMechanic's Data is the constructed object.

Alias of newConstructor: identical behaviour under a Binder-style name.

GetApplied​

Mechanic:GetApplied(
instance: Instance--

The instance to look up.

) → AppliedMechanic<T>?--

The applied mechanic, or nil.

Returns the AppliedMechanic for instance, or nil if the mechanic is not currently applied to it.

GetInstances​

Mechanic:GetInstances() → {Instance}--

The applied instances.

Returns a new array of every instance the mechanic is currently applied to. The order is not defined.

CanBeApplied​

Mechanic:CanBeApplied(
instance: Instance--

The instance to test.

) → boolean--

Whether the instance passes every filter.

Runs the config filters against instance: it must IsA one of ClassNames (if any were given), be a descendant of one of Ancestors (if any were given), and pass CustomPredicate (if set). The tag itself is not checked.

Apply​

Mechanic:Apply(
instance: Instance,--

The instance to apply the mechanic to.

data: T?--

Initial Data for the instance; defaults to a shallow copy of Shared.

) → AppliedMechanic<T>--

The new or existing applied mechanic.

Adds the tag to instance and applies the mechanic to it right away, skipping the CanBeApplied filters. If the mechanic is already applied to the instance the existing AppliedMechanic is returned and data is ignored.

Revoke​

Mechanic:Revoke(
instance: Instance--

The instance to revoke the mechanic from.

) → ()

Removes the tag from instance and, if the mechanic is applied to it, destroys its AppliedMechanic (cleaning its Trove and firing InstanceRemoved). Safe to call on instances the mechanic is not applied to.

OnAdded​

Mechanic:OnAdded(
callback: (
Trove
) → ()--

Called with the instance, its AppliedMechanic and that AppliedMechanic's Trove.

) → Connection--

The signal connection; disconnect it to stop receiving new instances.

Connects callback to InstanceAdded and also runs it (in a new coroutine each) for every instance the mechanic is already applied to. The callback receives the instance, its AppliedMechanic and the AppliedMechanic's Trove; connect per-instance work to that Trove so it is cleaned up when the instance goes away. The connection is added to the mechanic's Trove.

OnRemoved​

Mechanic:OnRemoved(
callback: (Instance) → ()--

Called with the instance that was removed.

) → Connection--

The signal connection.

Connects callback to InstanceRemoved, so it runs with the instance whenever an AppliedMechanic is cleaned (the instance was untagged, destroyed, moved out of Ancestors, or revoked). The connection is added to the mechanic's Trove.

Destroy​

Mechanic:Destroy() → ()

Cleans the mechanic's Trove: every AppliedMechanic is destroyed (firing InstanceRemoved for each), the CollectionService connections are disconnected and both signals are destroyed. Tags are left on the instances.

Show raw api
{
    "functions": [
        {
            "name": "new",
            "desc": "Creates a Mechanic for `Tag`. Instances that already have the tag and pass the filters are\napplied immediately (each in its own coroutine); instances tagged later are applied as they\nappear, and instances that lose the tag are revoked. When `Ancestors` is set, tagged instances\nthat are later parented under one of the ancestors are picked up too.",
            "params": [
                {
                    "name": "Tag",
                    "desc": "The CollectionService tag to watch.",
                    "lua_type": "string"
                },
                {
                    "name": "Config",
                    "desc": "Filters and shared data; defaults to no filters and `Shared = {}`.",
                    "lua_type": "MechanicConfig<T, U>?"
                }
            ],
            "returns": [
                {
                    "desc": "The new mechanic.",
                    "lua_type": "Mechanic<T>"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 318,
                "path": "packages/src/Mechanic/init.luau"
            }
        },
        {
            "name": "GetApplied",
            "desc": "Returns the AppliedMechanic for `instance`, or `nil` if the mechanic is not currently applied\nto it.",
            "params": [
                {
                    "name": "instance",
                    "desc": "The instance to look up.",
                    "lua_type": "Instance"
                }
            ],
            "returns": [
                {
                    "desc": "The applied mechanic, or nil.",
                    "lua_type": "AppliedMechanic<T>?"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 373,
                "path": "packages/src/Mechanic/init.luau"
            }
        },
        {
            "name": "GetInstances",
            "desc": "Returns a new array of every instance the mechanic is currently applied to. The order is\nnot defined.",
            "params": [],
            "returns": [
                {
                    "desc": "The applied instances.",
                    "lua_type": "{Instance}"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 385,
                "path": "packages/src/Mechanic/init.luau"
            }
        },
        {
            "name": "CanBeApplied",
            "desc": "Runs the config filters against `instance`: it must `IsA` one of `ClassNames` (if any were\ngiven), be a descendant of one of `Ancestors` (if any were given), and pass `CustomPredicate`\n(if set). The tag itself is not checked.",
            "params": [
                {
                    "name": "instance",
                    "desc": "The instance to test.",
                    "lua_type": "Instance"
                }
            ],
            "returns": [
                {
                    "desc": "Whether the instance passes every filter.",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 403,
                "path": "packages/src/Mechanic/init.luau"
            }
        },
        {
            "name": "Apply",
            "desc": "Adds the tag to `instance` and applies the mechanic to it right away, skipping the\n`CanBeApplied` filters. If the mechanic is already applied to the instance the existing\nAppliedMechanic is returned and `data` is ignored.",
            "params": [
                {
                    "name": "instance",
                    "desc": "The instance to apply the mechanic to.",
                    "lua_type": "Instance"
                },
                {
                    "name": "data",
                    "desc": "Initial `Data` for the instance; defaults to a shallow copy of `Shared`.",
                    "lua_type": "T?"
                }
            ],
            "returns": [
                {
                    "desc": "The new or existing applied mechanic.",
                    "lua_type": "AppliedMechanic<T>"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 448,
                "path": "packages/src/Mechanic/init.luau"
            }
        },
        {
            "name": "Revoke",
            "desc": "Removes the tag from `instance` and, if the mechanic is applied to it, destroys its\nAppliedMechanic (cleaning its Trove and firing `InstanceRemoved`). Safe to call on instances\nthe mechanic is not applied to.",
            "params": [
                {
                    "name": "instance",
                    "desc": "The instance to revoke the mechanic from.",
                    "lua_type": "Instance"
                }
            ],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 466,
                "path": "packages/src/Mechanic/init.luau"
            }
        },
        {
            "name": "OnAdded",
            "desc": "Connects `callback` to `InstanceAdded` and also runs it (in a new coroutine each) for every\ninstance the mechanic is already applied to. The callback receives the instance, its\nAppliedMechanic and the AppliedMechanic's Trove; connect per-instance work to that Trove so it\nis cleaned up when the instance goes away. The connection is added to the mechanic's Trove.",
            "params": [
                {
                    "name": "callback",
                    "desc": "Called with the instance, its AppliedMechanic and that AppliedMechanic's Trove.",
                    "lua_type": "(Instance, AppliedMechanic<T>, Trove) -> ()"
                }
            ],
            "returns": [
                {
                    "desc": "The signal connection; disconnect it to stop receiving new instances.",
                    "lua_type": "Connection"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 486,
                "path": "packages/src/Mechanic/init.luau"
            }
        },
        {
            "name": "OnRemoved",
            "desc": "Connects `callback` to `InstanceRemoved`, so it runs with the instance whenever an\nAppliedMechanic is cleaned (the instance was untagged, destroyed, moved out of `Ancestors`, or\nrevoked). The connection is added to the mechanic's Trove.",
            "params": [
                {
                    "name": "callback",
                    "desc": "Called with the instance that was removed.",
                    "lua_type": "(Instance) -> ()"
                }
            ],
            "returns": [
                {
                    "desc": "The signal connection.",
                    "lua_type": "Connection"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 507,
                "path": "packages/src/Mechanic/init.luau"
            }
        },
        {
            "name": "Destroy",
            "desc": "Cleans the mechanic's Trove: every AppliedMechanic is destroyed (firing `InstanceRemoved` for\neach), the CollectionService connections are disconnected and both signals are destroyed. Tags\nare left on the instances.",
            "params": [],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 521,
                "path": "packages/src/Mechanic/init.luau"
            }
        },
        {
            "name": "newConstructor",
            "desc": "Creates a Mechanic and, for every instance it is applied to, constructs\n`Constructable.new(instance, trove)` through the AppliedMechanic's Trove and stores the\nresult in the AppliedMechanic's `Data`. The constructed object is destroyed with the Trove\nwhen the instance is removed. This is the closest equivalent to Binder.\n\n```lua\nlocal Door = {}\nDoor.__index = Door\n\nfunction Door.new(model: Model, trove)\n\tlocal self = setmetatable({ Model = model, Open = false }, Door)\n\ttrove:Connect(model.ClickDetector.MouseClick, function() self:Toggle() end)\n\treturn self\nend\n\nfunction Door.Toggle(self)\n\tself.Open = not self.Open\nend\n\nfunction Door.Destroy(self) end\n\nlocal DoorMechanic = Mechanic.newConstructor(\"Door\", { ClassNames = { \"Model\" } }, Door)\n```",
            "params": [
                {
                    "name": "Tag",
                    "desc": "The CollectionService tag to watch.",
                    "lua_type": "string"
                },
                {
                    "name": "Config",
                    "desc": "Filters and shared data; pass `{}` for none.",
                    "lua_type": "MechanicConfig<A, B>"
                },
                {
                    "name": "Constructable",
                    "desc": "A class table whose `new(instance, trove)` builds the per-instance object.",
                    "lua_type": "Constructable<C, D...>"
                }
            ],
            "returns": [
                {
                    "desc": "The new mechanic; each AppliedMechanic's `Data` is the constructed object.",
                    "lua_type": "Mechanic<C>"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 557,
                "path": "packages/src/Mechanic/init.luau"
            }
        },
        {
            "name": "bindToClass",
            "desc": "Alias of `newConstructor`: identical behaviour under a Binder-style name.",
            "params": [
                {
                    "name": "Tag",
                    "desc": "The CollectionService tag to watch.",
                    "lua_type": "string"
                },
                {
                    "name": "Config",
                    "desc": "Filters and shared data; pass `{}` for none.",
                    "lua_type": "MechanicConfig<A, B>"
                },
                {
                    "name": "Constructable",
                    "desc": "A class table whose `new(instance, trove)` builds the per-instance object.",
                    "lua_type": "Constructable<C, D...>"
                }
            ],
            "returns": [
                {
                    "desc": "The new mechanic; each AppliedMechanic's `Data` is the constructed object.",
                    "lua_type": "Mechanic<C>"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 567,
                "path": "packages/src/Mechanic/init.luau"
            }
        }
    ],
    "properties": [
        {
            "name": "Tag",
            "desc": "The CollectionService tag this mechanic watches.",
            "lua_type": "string",
            "readonly": true,
            "source": {
                "line": 134,
                "path": "packages/src/Mechanic/init.luau"
            }
        },
        {
            "name": "Shared",
            "desc": "The shared table from the config (or `{}`). Each AppliedMechanic's `Data` starts as a shallow copy of it.",
            "lua_type": "T",
            "source": {
                "line": 139,
                "path": "packages/src/Mechanic/init.luau"
            }
        },
        {
            "name": "Applications",
            "desc": "Every instance the mechanic is currently applied to, mapped to its AppliedMechanic. Prefer `GetApplied` and `GetInstances`.",
            "lua_type": "{[Instance]: AppliedMechanic<T>}",
            "readonly": true,
            "source": {
                "line": 145,
                "path": "packages/src/Mechanic/init.luau"
            }
        },
        {
            "name": "Trove",
            "desc": "The mechanic's own Trove. Everything added to it is cleaned by `Destroy`, including every AppliedMechanic.",
            "lua_type": "Trove",
            "source": {
                "line": 150,
                "path": "packages/src/Mechanic/init.luau"
            }
        },
        {
            "name": "InstanceAdded",
            "desc": "Fires when the mechanic is applied to an instance. Receives the instance, its AppliedMechanic and the AppliedMechanic's Trove. `OnAdded` also replays existing applications.",
            "lua_type": "Signal<Instance, AppliedMechanic<T>, Trove>",
            "readonly": true,
            "source": {
                "line": 156,
                "path": "packages/src/Mechanic/init.luau"
            }
        },
        {
            "name": "InstanceRemoved",
            "desc": "Fires with the instance after its AppliedMechanic is cleaned (untagged, destroyed, moved out of `Ancestors`, or revoked).",
            "lua_type": "Signal<Instance>",
            "readonly": true,
            "source": {
                "line": 162,
                "path": "packages/src/Mechanic/init.luau"
            }
        }
    ],
    "types": [
        {
            "name": "Constructable",
            "desc": "A class table with a `new` constructor, as accepted by `newConstructor` and `bindToClass`. The\nconstructor is called through `Trove:Construct` as `new(instance, trove)` and its result becomes\nthe AppliedMechanic's `Data`.",
            "lua_type": "{ new: (U...) -> T }",
            "source": {
                "line": 105,
                "path": "packages/src/Mechanic/init.luau"
            }
        },
        {
            "name": "MechanicConfig",
            "desc": "Configuration passed to `Mechanic.new` to decide which tagged instances the mechanic applies to\nand what data they start with. Every field is optional; an empty config applies to every\ninstance with the tag.",
            "fields": [
                {
                    "name": "Shared",
                    "lua_type": "T?",
                    "desc": "Table that is shallow-copied into each instance's `Data` (and exposed as `Shared`). Defaults to `{}`."
                },
                {
                    "name": "Ancestors",
                    "lua_type": "{U}?",
                    "desc": "Instances the tagged instance must be a descendant of (any one of them). Empty means no ancestor filter."
                },
                {
                    "name": "ClassNames",
                    "lua_type": "{string}?",
                    "desc": "Class names the instance must match with `IsA` (any one of them). Empty means no class filter."
                },
                {
                    "name": "CustomPredicate",
                    "lua_type": "((Instance) -> boolean)?",
                    "desc": "Extra check run after the other filters; return `false` to reject the instance."
                }
            ],
            "source": {
                "line": 121,
                "path": "packages/src/Mechanic/init.luau"
            }
        }
    ],
    "name": "Mechanic",
    "desc": ":::caution Deprecated\nMechanic is no longer maintained and is kept only for existing projects. Use\n[Binder](/api/Binder) instead; it covers the same use case with a smaller API.\n:::\n\nMechanic attaches behaviour to every instance that carries a `CollectionService` tag. It wraps\n`CollectionService:GetInstanceAddedSignal` / `GetInstanceRemovedSignal` and filters the tagged\ninstances by class name, by ancestor, or with a custom predicate. Every instance that passes\nthe filters gets an [AppliedMechanic](/api/AppliedMechanic) object holding its own per-instance\n`Data` table and a [Trove](https://sleitnick.github.io/RbxUtil/api/Trove/) that is cleaned when\nthe instance is untagged, destroyed, or moved out of the allowed ancestors.\n\nThe module returns a table with three constructors: `new`, `newConstructor` and `bindToClass`.\nEverything else is a method on the `Mechanic` object they return.\n\n```lua\nlocal RunService = game:GetService(\"RunService\")\nlocal Mechanic = require(path.to.Mechanic)\n\nlocal Spinner = Mechanic.new(\"Spinner\", {\n\tShared = { Speed = 2 }, -- copied into every instance's Data table\n\tClassNames = { \"BasePart\" },\n\tAncestors = { workspace },\n})\n\nSpinner:OnAdded(function(part, applied, trove)\n\tlocal data = applied:GetData()\n\ttrove:Connect(RunService.Heartbeat, function(dt)\n\t\tpart.CFrame *= CFrame.Angles(0, data.Speed * dt, 0)\n\tend)\nend)\n\nSpinner:OnRemoved(function(part)\n\tprint(part.Name, \"stopped spinning\")\nend)\n\n-- Later: tag a part manually (also adds the \"Spinner\" tag) with custom data\nSpinner:Apply(workspace.Windmill, { Speed = 0.5 })\n```\n\nDepends on Sleitnick's [Trove](https://sleitnick.github.io/RbxUtil/api/Trove/) and\n[Signal](https://sleitnick.github.io/RbxUtil/api/Signal/), which Wally installs alongside it.\n\n**Credits:** [Trove](https://sleitnick.github.io/RbxUtil/api/Trove/) and [Signal](https://sleitnick.github.io/RbxUtil/api/Signal/) are by [sleitnick](https://github.com/Sleitnick) ([sleitnick's RbxUtil](https://sleitnick.github.io/RbxUtil/)). Wally installs both.\n\nInstallation and guide: [Mechanic package page](/docs/packages/mechanic).",
    "deprecated": {
        "version": "v1.0.0",
        "desc": "Use [Binder](/api/Binder) instead."
    },
    "source": {
        "line": 54,
        "path": "packages/src/Mechanic/init.luau"
    }
}