Skip to content

Scope

local Scope = require("@game/ReplicatedStorage/Twill").Scope
Scope.Player(player):Connect(someSignal, onSomething)
Scope.Alive(character):Connect(humanoid.Died, onDeath)

Ask for the bag matching how long a thing should live, put what was made into it, and disconnect nothing by hand. Every bag is a Bag, so Connect, Add, Extend and the rest work on it.

ScopeCloses when
PlayerThat player leaves.
CharacterThe character is removed, which is at respawn rather than at death.
AliveThe humanoid dies, or the character is removed.
FrameworkThe server stops. Holds Twill’s own connections.

Character and Alive differ by one death. A healthbar belongs in Character, because it should survive the death that fills it in. A sprint loop belongs in Alive, because it should stop the moment the humanoid does.

Asking twice for the same scope returns the same bag. Two systems share one without knowing about each other.

A character bag attaches itself to the character. An instance destroyed without the bag being closed takes the bag with it, and the bag removes its own entry, so nothing here holds a dead Instance.

A character created outside game is not attached, since there is nothing to watch. Closing it is then the caller’s job.

By default a player’s bag closes on PlayerRemoving. A system that must unwind first, such as saving data, calls OwnPlayerClosing and then closes the bag itself when it is done.

Scope.OwnPlayerClosing()
Players.PlayerRemoving:Connect(function(player)
saveEverything(player)
Scope.Close(player)
end)

This is a one way switch, and it applies to every player from that point on. Lifecycle calls it when a player gate is set.

[Server] | [Client]

Returns the bag that closes when this player leaves.

function Scope.Player(player: Player): Bag

Created on the first ask. For a player who has already left, answers a bag that closes a moment later instead, so nothing put in it outlives them. Throws when given anything but a Player.

[Server] | [Client]

Returns the bag that closes when this character is removed.

function Scope.Character(character: Model): Bag

Attaches itself to the character when the character is in game. Throws when given anything but a Model.

[Server] | [Client]

Returns the bag that closes the instant this character dies.

function Scope.Alive(character: Model): Bag

Waits for the humanoid when the character has none yet. Also closes when the character bag closes, so a character removed without dying does not leave this one open.

Throws when given anything but a Model.

[Server] | [Client]

Returns the bag Twill’s own modules keep their connections in.

function Scope.Framework(): Bag

The same bag every time. Game code can use it for connections that live as long as the server, but a shorter scope is almost always the right one.

[Server] | [Client]

Closes a player’s bag now.

function Scope.Close(player: Player)

Does nothing when there is no bag to close. Throws when given anything but a Player.

[Server] | [Client]

Closes a character’s bag now.

function Scope.CloseCharacter(character: Model)

[Server] | [Client]

Closes a character’s alive bag now, without waiting for a death.

function Scope.CloseAlive(character: Model)

[Server] | [Client]

Closes every bag held here, shortest lifetime first.

function Scope.CloseAll()

Alive bags, then character bags, then player bags, then the framework bag. This is a shutdown rather than a reset: the framework bag is destroyed, not emptied.

[Server] | [Client]

Hands closing of every player bag to the caller, from this point on.

function Scope.OwnPlayerClosing()

There is no way back. Once called, nothing closes a player bag until the caller does.