Core Concepts: Events
Events in NetRay are unidirectional, "fire-and-forget" messages, analogous to Roblox's RemoteEvent
. Use them to broadcast information or trigger actions where an immediate response isn't required.
Key Features
- Type Safety: Optionally define data structures (
typeDefinition
) for automatic validation upon receiving. - Prioritization: Control client-side processing order using
priority
levels. - Batching: Server-to-client events are automatically grouped (
batchable = true
) to reduce network calls. - Compression: Large payloads may be automatically compressed (
compression = true
) by theDynamicSender
.
Server-Side Usage
Registering an Event
Use NetRay:RegisterEvent(eventName, options?)
on the server.
-- Server Script
local damageDealtEvent = NetRay:RegisterEvent("DamageDealt", {
-- Recommended: Define the data structure for clarity and validation
typeDefinition = {
targetInstanceId = "number", -- Use a reliable way to identify instances (e.g., custom attribute ID)
damageAmount = "number",
damageType = "?string", -- Optional: e.g., "Fire", "Physical"
critMultiplier = "number|nil" -- Optional, number or nil
},
priority = NetRay.Priority.HIGH, -- Ensure damage events are handled quickly client-side
batchable = true -- Efficient for frequent damage updates
})
Listening for Client Events
Handle events sent from clients using :OnEvent(callback)
.
-- Server Script
damageDealtEvent:OnEvent(function(player, data)
-- 'player' is the Player who fired the event
-- 'data' is the validated payload (if typeDefinition was provided)
-- You'll need a reliable way to map targetInstanceId back to an Instance
-- This is game-specific logic. FindFirstChild is unreliable for dynamic objects.
-- local target = YourGameSpecificInstanceManager:GetInstanceById(data.targetInstanceId)
local target = game.Workspace:FindFirstChild("Target_" .. data.targetInstanceId) -- Placeholder lookup
if target and target:FindFirstChildOfClass("Humanoid") then
local humanoid = target:FindFirstChildOfClass("Humanoid")
local actualDamage = data.damageAmount * (data.critMultiplier or 1)
print(("%s dealt %.1f %s damage to %s."):format(
player.Name,
actualDamage,
data.damageType or "Unknown",
target.Name
))
humanoid:TakeDamage(actualDamage)
else
warn("Invalid target or missing Humanoid for damage event from", player.Name, "TargetID:", data.targetInstanceId)
end
end)
Firing Events to Clients
Send events to clients using the :Fire...()
methods.
-- Server Script
-- Assuming player references (attackerPlayer, targetPlayer, immunePlayer) exist
if attackerPlayer then
-- Send to a specific player who landed a critical hit
damageDealtEvent:FireClient(attackerPlayer, {
targetInstanceId = 0, -- Use a special ID for UI feedback maybe
damageAmount = 150,
damageType = "CritFeedback",
critMultiplier = 2.5
})
end
-- Broadcast an area effect damage event
damageDealtEvent:FireAllClients({
targetInstanceId = -1, -- Use special ID for AoE
damageAmount = 30,
damageType = "Explosion"
-- Might also include position = Vector3...
})
-- Notify everyone except the target about a status effect
if targetPlayer then
-- Assuming StatusEffectApplied event is registered elsewhere
local statusEffectEvent = NetRay:RegisterEvent("StatusEffectApplied") -- Register ensures it exists
statusEffectEvent:FireAllClientsExcept(targetPlayer, {
targetName = targetPlayer.Name,
effect = "Slowed",
duration = 5
})
end
-- Send event only to players on Team A
local teamUpdateEvent = NetRay:RegisterEvent("TeamUpdate")
teamUpdateEvent:FireFilteredClients(function(p)
-- Make sure player.Team and player.Team.Name exist and are valid
return p.Team and p.Team.Name == "Team A"
end, {
message = "Objective captured!",
points = 100
})
end
Client-Side Usage
Getting an Event Reference
Use NetRay:GetEvent(eventName)
to access an event. Use NetRay:RegisterEvent(eventName, options?)
if you need to set client-specific options before the first event is potentially received or fired (less common).
-- Client Script
-- Get a reference (most common)
local damageDealtEvent = NetRay:GetEvent("DamageDealt")
-- Or register if you need specific client options (like listener priority)
-- local damageDealtEvent = NetRay:RegisterEvent("DamageDealt", { priority = NetRay.Priority.CRITICAL })
Listening for Server Events
Handle events sent from the server using :OnEvent(callback)
.
-- Client Script
local LocalPlayer = game:GetService("Players").LocalPlayer
-- Assume some way to get the local player's character unique ID if needed
-- local localCharacterId = LocalPlayer.Character and LocalPlayer.Character:GetAttribute("InstanceId")
damageDealtEvent:OnEvent(function(data)
-- 'data' is the validated payload from the server
local localCharacterId = LocalPlayer.Character and LocalPlayer.Character:GetAttribute("InstanceId")
-- Show damage numbers or effects
if data.damageType == "CritFeedback" then
print("CRITICAL HIT UI FEEDBACK!")
-- TriggerCritUI()
elseif data.targetInstanceId == localCharacterId then -- Check if this client was the target
print(("Took %.1f %s damage!"):format(data.damageAmount, data.damageType or "Unknown"))
-- UpdateHealthBarUI(data.damageAmount)
-- ShowDamageVignette()
elseif data.targetInstanceId == -1 and data.damageType == "Explosion" then
-- Play explosion sound/visual near event source (if position was included in 'data')
print("Nearby explosion!")
-- PlayExplosionEffect(data.position)
end
end)
-- Also listen for the status effect event
local statusEffectEvent = NetRay:GetEvent("StatusEffectApplied")
statusEffectEvent:OnEvent(function(data)
if data.targetName == LocalPlayer.Name then
print(("You were afflicted with %s for %d seconds!"):format(data.effect, data.duration))
-- Apply visual effect on local player?
elseif data.targetName == LocalPlayer.Name then
print(("%s was afflicted with %s."):format(data.targetName, data.effect))
-- Show indicator on other player's nameplate?
end
end)
Firing Events to Server
Send events to the server using :FireServer(data)
.
-- Client Script
local userInputService = game:GetService("UserInputService")
local requestDamageEvent = NetRay:GetEvent("DamageDealt") -- Event server listens to for damage reports
local function getTargetInstanceId(target)
-- Implement your logic to get a *server-known* ID for the target instance
-- Using attributes is a common method
return target and target:GetAttribute("InstanceId")
end
userInputService.InputBegan:Connect(function(input, gameProcessed)
if gameProcessed then return end
if input.UserInputType == Enum.UserInputType.MouseButton1 then -- Left click
local player = game:GetService("Players").LocalPlayer
local mouse = player:GetMouse()
local targetInstance = mouse.Target
local targetId = getTargetInstanceId(targetInstance) -- Use helper function
if targetId then
print("Attempting to trigger damage event for target ID:", targetId)
-- Client tells server it *hit* something. Server validates/calculates damage.
requestDamageEvent:FireServer({
targetInstanceId = targetId,
damageAmount = 10, -- Client might suggest base damage or weapon type
damageType = "MeleeSwing"
})
end
end)
end)
Example Usage
-- Example of event registration
local damageDealtEvent = NetRay:RegisterEvent("DamageDealt", {
-- Recommended: Define the data structure for clarity and validation
typeDefinition = {
targetInstanceId = "number", -- Use a reliable way to identify instances (e.g., custom attribute ID)
damageAmount = "number",
damageType = "string",
position = "Vector3"
}
})
-- Example of firing the event
local target = game.Workspace:FindFirstChild("Target")
damageDealtEvent:FireAllClients({
targetInstanceId = target:GetAttribute("ID"),
damageAmount = 10,
damageType = "Physical",
position = target.Position
})