Events and Hooks
Summary
An Event is something that happens. The source of the event can be many things, from an object, broadcast, to even Player input. For example, when the Player presses a key, any event listeners connected to a binding event will be fired, allowing you to react and perform any additional tasks based on which binding was pressed.
A Hook is like an event, but the purpose is to view or change something that's already going on. For example, modifying the player direction in a movement hook.
Broadcasting and Connecting
Core has a robust event system for communicating between scripts by using functions from the Events namespace. There are functions in the Events namespace that help with server-to-client and client-to-server communication. Along with that, broadcasting between scripts in the same network context can help with modularizing your systems.
-
Events.Broadcast
This function allows for user-created events to be broadcasted in the same network context. These events have an added benefit where they are not networked, so can be used as much as needed. This is helpful when there are scripts in the same network context but need a way to talk to each other or pass data.
Events.Broadcast
also supports any number of arguments that will get passed to the listener function.Events.Broadcast("PrintMessage", "Hello World!")
The code above would be in one script that handles broadcasting to the
PrintMessage
event.local function PrintMessage(msg) print(msg) end Events.Connect("PrintMessage", PrintMessage)
The above code would be in another script that listens for the
PrintMessage
event. -
Events.Connect
This function allows for user created events to be listened to by passing the event name as the first argument, and the event listener function as the second argument. When the event is broadcasted too, the listener function will be fired.
When setting up a listener, an
EventListener
object is returned that can be used to determine if the listener is connected and disconnecting for the event to stop listening.-- Script "A" -- Wait 1 frame to give other script a chance to setup the listener. Task.Wait() Events.Broadcast("CallOnce", "Hello World!") Events.Broadcast("CallOnce", "Hello Again!")
-- Script "B" local myEvent local function CallOnce(msg) print(msg) -- Check if myEvent is connected, if so, disconnect. if myEvent.isConnected then myEvent:Disconnect() myEvent = nil end end myEvent = Events.Connect("CallOnce", CallOnce)
In the above code, the event
CallOnce
is broadcasted which will fire all listeners attached to that event. The connected listener will only be fired once. A reference to theeventListener
is stored in themyEvent
variable so that the eventCallOnce
can be disconnected later. -
Events.BroadcastToAllPlayers
This function broadcasts the event to all clients over the network and fires all listeners attached to that event. Any arguments after the event name are passed to the listener on the client. The function returns a result code and a message. This is a networked event so will count against all player's rate limits.
-- Server script Events.BroadcastToAllPlayers("RoundCompleted", time())
-- Client script local function RoundCompleted(roundEndTime) print("Round Completed", roundEndTime) end Events.Connect("RoundCompleted", RoundCompleted)
-
Events.BroadcastToPlayer
This function broadcasts the event to a specific client over the network and fires all listeners attached to the client. The first argument specifies the Player who will receive the event. The arguments after the event name will be passed to the listener on the client. The function returns a result code and a message. This is a networked event so will count against the receiving player's rate limits.
-- Client script local function Say(message) print(message) end Events.Connect("OnJoinSay", Say) ```lua linenums="1" -- Server script -- Broadcast to the player when they join the game. local function OnPlayerJoined(player) Events.BroadcastToPlayer(player, "OnJoinSay", "Welcome") end Game.playerJoinedEvent:Connect(OnPlayerJoined)
-
Events.BroadcastToServer
This function broadcasts the event to the server from the client over the network and fires all listeners attached to the server. Any arguments after the event name are passed to the listener on the server. The function returns a result code and a message. This is a networked event so will count against the rate limit for the server.
-- Client script Events.BroadcastToServer("PurchaseItem", item_id)
-- Server script local function PurchaseItem(item_id) print(item_id) end Events.Connect("PurchaseItem", PurchaseItem)
-
Events.ConnectForPlayer
This function registers a listener for the event that will be called every time the event is fired using
BroadcastToServer
. The first parameter the function receives will be the Player that fired the event. Any arguments after the event name are passed to the listener.-- Client script Events.BroadcastToServer("PurchaseItem", item_id)
-- Server script local function PurchaseItem(player, item_id) print(player.name, item_id) end Events.ConnectForPlayer("PurchaseItem", PurchaseItem)
Rate Limits
Functions in the Events namespace that communicate across the network have rate limits in place. This is to control the amount of network traffic between the server and the client.
When approaching the allowed rate limit, a warning will be displayed in the Event Log.
Each of the following functions return a BroadcastEventResultCode
and error message. See the enums for the result code here.
-
BroadcastToAllPlayers
Rate limited to 10 per second. Will fail to broadcast if one or more connections are exceeding the rate limit. This means that each Player has a rate limit of 10 per second, but if any Player reaches the rate limit, then events will fail to broadcast.
-
BroadcastToServer
Rate limited to 10 per second.
-
BroadcastToPlayer
Rate limited to 10 per second. The rate limit is per Player and not for all players.
See Events API and Platform Limits for more information.
Data Transfer
The functions in the Events namespace have a limit of 128 bytes per broadcast. Broadcasts have higher priority over other methods for transferring data over the network. When needing to transfer far more data over the network, there are better alternatives that can be used.
-
Dynamic Properties
Using dynamic properties is a way to transfer data to all clients. This is very useful when clients need to know about the game state, or setting AI animations. Dynamic properties are replicated to all clients, this has the benefit of all clients receiving the change, however, it increases network traffic. The data is replicated as soon as possible, but changing a lot of properties, or having a large amount of data can cause a delay.
-
Private Networked Data
This is ideal for transferring large chunks of data to the player. For example, loading the player's inventory from Storage which could contain many items. This has the added benefit where data is replicated to the receiving client and not all clients.
See SetCustomProperty and SetPrivateNetworkedData.
Object Events
There are various objects in Core that have their own events.
Ability
Abilities can be added to players and guide the player's animation in sync with the ability's state machine. Abilities can be assigned an action binding so that when the player presses that specific binding, the ability state machine will go through the phases. Each phase of an ability will fire any attached listeners.
local ABILITY = script.parent
local WEAPON = script:FindAncestorByType("weapon")
local LOW_AMMO_SOUND = script:GetCustomProperty("lowAmmoSound")
local lowAmmoPercent = 0.2
function OnExecute(ability)
if weapon.currentAmmo / weapon.maxAmmo <= lowAmmoPercent then
World.SpawnAsset(LOW_AMMO_SOUND, { position = weapon:GetWorldPosition() })
end
end
ability.executeEvent:Connect(OnExecute)
In the example code above, the Ability will fire executeEvent
each time the Ability is used. The listener function will check the amount of ammo for the weapon, if the ammo is less than or equal to lowAmmoPercent
a sound will be spawned. This is useful to provide feedback to the player.
See Ability API for more information.
AnimatedMesh
AnimatedMesh objects are skeletal meshes with parameterized animations baked into them. Some animations have events that fire when certain parts of the animations are reached. This allows you to sync up hit effects with animations.
-
animationEvent
This event is fired with the animated mesh that triggered it and the name of the event at those points.
local RAPTOR = script:GetCustomProperty("RaptorMob") local raptorMesh = World.SpawnAsset(RAPTOR) function AnimEventListener(mesh, eventName) print("Animated Mesh just hit event " .. eventName) end raptorMesh.animationEvent:Connect(AnimEventListener) raptorMesh:PlayAnimation("unarmed_claw")
In the code above, the
AnimEventListener
will fire if the animation has an event. In this case, theunarmed_claw
animation has animpact
event. This could be useful for playing a slash effect when the raptor impacts an object such as a Player.
See AnimatedMesh API for more information.
CoreObject
CoreObject is an object placed in the scene hierarchy during edit mode or can be part of a template. For example, a Cube is a CoreObject, it can be placed in the hierarchy or be part of a template.
There are a lot of useful events that a CoreObject can use, one of those being destroyEvent
. The destroyEvent
is fired when this object is about to be destroyed.
local RAPTOR = script:GetCustomProperty("RaptorMob"):WaitForObject()
local function OnDestroyed(obj)
print(obj .. " Destroyed")
end
RAPTOR.destroyEvent:Connect(OnDestroyed)
Task.Wait(1)
RAPTOR:Destroy()
In the example code above, a Raptor is destroyed by calling the Destroy
function after 1 second. The destroyEvent
gets the object that was destroyed.
See CoreObject API for more information.
Equipment
Equipment is a CoreObject representing an equippable item for players. They generally have a visual component that attaches to the Player, but a visual component is not a requirement. Any Ability objects added as children of the Equipment are added/removed from the Player automatically as it becomes equipped/unequipped.
See Equipment API for more information.
Game
The Game namespace has a collection of functions and events related to players in the game, rounds of a game, and team scoring. Below are 2 useful events that you will find yourself using a lot of the time.
-
Game.playerJoinedEvent
Fired when a Player has joined the game and their character is ready. When used in the client context it will fire off for each player already connected to the server.
local function OnPlayerJoined(player) print(player.name .. " joined the game.") end Game.playerJoinedEvent:Connect(OnPlayerJoined)
Knowing when a Player has joined the game allows you to do many things. For example, updating a UI player list so other players can see who has joined the game.
Another useful way to use the
playerJoinedEvent
is loading the player's data from Storage when they join the server, and then sending that data to them (that is Private Networked Data). -
Game.playerLeftEvent
Fired when a player has disconnected from the game or their character has been destroyed. This event fires before the player has been removed.
local function OnPlayerLeft(player) print(player.name .. " has left the game") end Game.playerLeftEvent:Connect(OnPlayerLeft)
Knowing when a player has left the game can be used in many ways. From updating the player's Storage, to cleaning up variables and tables that may hold references to the Player leaving the game.
See Game API for more information.
IK Anchor
IKAnchors are objects that can be used to control player animations. They can be used to specify the position of a specific hand, foot, or the hips of a Player. IKAnchors have events that can be listened too to detect when an IKAnchor is activated on a player, and deactivated from the player.
See IKAnchors API for more information.
Player
bindingPressedEvent
is deprecated. Use Action Bindings instead.
Player is an object representation of the state of a player connected to the game, as well as their avatar in the world. The Player object contains a lot of useful events that can be listened to. For example, a useful thing to know is what key binding the Player has pressed. Listening to the bindingPressedEvent
when a Player presses a specific key binding is an easy way to do this.
local localPlayer = Game.GetLocalPlayer()
local function OnBindingPressed(player, binding)
print("The player pressed " .. binding)
end
localPlayer.bindingPressedEvent:Connect(OnBindingPressed)
In the code above, when a player presses a binding, it will print out to the Event Log what the binding was. This is useful if you need to perform a specific action based on what binding the Player has pressed.
See Player and Bindings API for more information.
Trigger
A Trigger is an invisible and non-colliding CoreObject which fires events when it interacts with another object (for example A Player walks into it). Triggers can be used in a wide range of cases, one of them being a way to detect when a Player has entered the Trigger to show an interaction label to open a door.
A Trigger will also work in a Client Context by setting the Collision property to Force On.
local TRIGGER = script:GetCustomProperty("pickupTrigger")
local function OnBeginOverlap(trigger, obj)
if Object.IsValid(obj) and obj:IsA("Player") then
print("Player entered the trigger").
end
end
local function OnEndOverlap(trigger, obj)
if Object.IsValid(obj) and obj:IsA("Player") then
print("Player left the trigger").
end
end
TRIGGER.beginOverlapEvent:Connect(OnBeginOverlap)
TRIGGER.endOverlapEvent:Connect(OnEndOverlap)
In the example code above, the listeners will display a message in the Event Log when the Player enters and exits the Trigger.
When a Vehicle enters or exits a Trigger, it will fire the listener twice.
See Trigger API for more information.
Vehicle
Vehicle is a CoreObject representing a vehicle that can be occupied and driven by a Player. Events can be setup to listen for when a Player enters or exits a Vehicle.
See Vehicle API for more information.
Weapon
A Weapon is an Equipment that comes with built-in Abilities and fires Projectiles. Some weapons may have projectiles, so it may be useful to know when a projectile has spawned, or when it has impacted something like a wall.
See Weapon API for more information.
UI Events
The UI namespace contains a set of class functions allowing you to get information about a Player's display and push information to their HUD. Most functions require the script to be inside a Client Context and execute for the local Player.
Button
A UIButton is a useful UI component that allows players to interact with the UIButton, and various listeners can fire depending on the state of the UIButton. For example, detecting when a Player has clicked the button. Or when a player has hovered over the UIButton to grow the size of the UIButton for a nice animation effect.
See UIButton API for more information.
Input
The Input namespace contains functions, events, and hooks for responding to player input. For example, what input type is connected to the device.
See Input API for more information.
Touch
The Input namespace has functions and events that can be used to handle the player's input when the input type is TOUCH. This means that when the player uses their finger on the screen, creators can respond to those touch events. For example, detecting when a player starts and stops touching the screen.
local function OnTouchStarted(location, touchIndex)
print(string.format("Finger touched at %s", location))
end
local function OnTouchStopped(location, touchIndex)
print(string.format("Finger lifted at %s", location))
end
Input.touchStartedEvent:Connect(OnTouchStarted)
Input.touchStoppedEvent:Connect(OnTouchStopped)
Hooks
Hooks appear as properties on several objects. Similar to Events, functions may be registered that will be called whenever that hook is fired. Hooks are similar to Events, the difference being that a Hook is used to view or change something that's already going on in a system.
See Hook and Hook API for more information.
Chat
The Chat namespace contains functions and hooks for sending and reacting to chat messages. This could be used for example, as a way for an admin to use special commands in the chat to perform actions either on the server (moderation), or in game (give a player a resource). Chat hooks can even be used to turn a Chat into a game.
See Chat API for more information.
Movement
The Movement hook is called when processing a player's movement. The parameter's table contains a Vector3 named "direction", indicating the direction the player will move. For example, in a click to move game, the client script would detect mouse input and modify the player's direction.
See Movement API for more information.
Input
The Input namespace contains hooks for responding to player input. For example, in a lot of games, the Esc key is used to open up a game's menu. In Core the default behavior is to open up the browse games page. Using an Input hook, this can be changed.
Players may press Shift + Esc to force the pause menu to open if the default behavior has been changed for the Esc key.
See Input API for more information.
Vehicle
Vehicles have 2 movement hooks that work on the client and server. The client hook clientMovementHook
is called when processing the driver's input. serverMovementHook
is called when on the server for a vehicle with no driver. This has the same parameters as clientMovementHook
.
See Vehicle API for more information.
Disconnecting
It's a good idea to disconnect any events that will no longer be used. Some events that aren't disconnected will continue to be fired even when the script that set up the listener is destroyed. A build up of events can lead to poor game performance for your players, so it's recommended to disconnect any events that no longer have a use.
Below is an example that sets up some listeners, and later when the script has been destroyed, disconnects any listeners still connected.
-- A table will be used to store all listeners
local myEvents = {}
-- When a listener is setup, it gets added to the myEvents table
myEvents[#myEvents + 1] = Events.Connect("YourEvent", SomeFunction)
myEvents[#myEvents + 1] = Events.Connect("YourOtherEvent", SomeOtherFunction)
-- When the script is destroyed, disconnect all events by looping through the table
local function OnDestroyed()
for index, event in ipairs(myEvents) do
if event.isConnected then
event:Disconnect()
end
end
end
script.destroyEvent:Connect(OnDestroyed)
Learn More
Event | Hook | HookListener | EventListener