Transformer

A transformer decides which pooled RemoteEvent a carrier's traffic rides on, and it is the switch that turns a group of carriers off. Create one with Pigeon.Transformer.

Most games never touch this object. Every carrier makes its own transformer from its channel name, so it is already handled. You only need one when you want several channels to share a remote, or to drop several channels in one call. See the Transformers guide for when that is worth doing.

Properties

transformer.uuid: string

The identity of the transformer. This string is hashed to pick a remote, so two transformers with the same uuid always land on the same one. If you build a transformer on both the server and the client, pass the same uuid to both.

transformer.destroyed: boolean

false until Destroy is called, then true forever. Carriers read this to decide whether they are still allowed to send.

Methods

Track

transformer:Track(disconnect: () -> ()) -> () -> ()

Registers a function to run when the transformer is destroyed, and returns a handle that removes it again.

This is how a group teardown works. Anything that subscribes on the transformer's behalf hands its disconnect function here, so destroying the transformer runs all of them at once. Every carrier riding on a transformer tracks its own cleanup this way.

ParameterTypeWhat it is
disconnect() -> ()Runs once when the transformer is destroyed.

Returns a function. Call it to remove your teardown again.

local transformer = Pigeon.Transformer("Combat")

local connection = someSignal:Connect(onHit)

local untrack = transformer:Track(function()
	connection:Disconnect()
end)

-- Later, if you disconnect early, drop the teardown too.
connection:Disconnect()
untrack()

Note

Call the returned handle whenever you unsubscribe early. If you do not, the list keeps growing by one entry for every subscription you ever made, and it holds those functions in memory until the transformer is destroyed.

Tracking on a dead transformer

If the transformer is already destroyed, your function runs straight away and the handle you get back does nothing. That is on purpose. It means a late registration can never leave a live connection behind.

local transformer = Pigeon.Transformer()
transformer:Destroy()

transformer:Track(function()
	print("this runs right now, not later")
end)

Destroy

transformer:Destroy() -> ()

Runs every tracked teardown, marks the transformer as destroyed, and lets the remote pool shrink again.

Every carrier built on this transformer is dropped as part of that. This is the one call that turns off a whole group of channels.

local combat = Pigeon.Transformer("Combat")

local damage = Pigeon.new("Damage", { Transformer = combat })
local status = Pigeon.new("Status", { Transformer = combat })
local effects = Pigeon.new("Effects", { Transformer = combat })

-- One call drops all three channels and every listener on them.
combat:Destroy()

Calling it twice does nothing the second time. Teardowns run once, and the count the remote pool is sized from is only lowered once.

A failing teardown does not stop the rest

Each teardown runs on its own. If one of them errors, Pigeon warns and carries on with the next. One broken cleanup function cannot leave the others connected.

Ownership

Who destroys the transformer depends on who made it.

How the carrier got itWho owns itWhat carrier:Destroy() does
Pigeon.new("Shop") The carrier Destroys the transformer too.
Pigeon.new("Shop", { Transformer = t }) You Leaves the transformer alone.

The rule is simple. A transformer a carrier made for itself has no other owner, so it goes when the carrier goes. One you passed in belongs to you, so Pigeon does not touch it.

-- Owned by the carrier.
local shop = Pigeon.new("Shop")
shop:Destroy()
print(shop._transformer.destroyed) --> true

-- Owned by you.
local mine = Pigeon.Transformer("Mine")
local other = Pigeon.new("Other", { Transformer = mine })
other:Destroy()
print(mine.destroyed) --> false

Building a carrier on a dead transformer

If you pass in a transformer that is already destroyed, the carrier you get back is inert from the moment it is made. It will not send and it will not receive. It does not error, because tearing things down often races with work already in flight and an error there would spread.

local transformer = Pigeon.Transformer()
transformer:Destroy()

local carrier = Pigeon.new("TooLate", { Transformer = transformer })

carrier:Emit("Anything")  -- does nothing, does not error