Services
Services act as the authoritative backend for your game. They handle data management, physics calculations, server-side validation, and state management.
Every Service is a singleton, meaning only one instance of it exists per server.
Defining a Service
A standard Service requires, at minimum, a table to hold its structure. The CLI loren make service command generates the following boilerplate:
local PlayerDataService = {
Dependencies = {},
-- Signals are declared as an array of names (strings).
-- Loren swaps this array for ready-to-fire Signal objects on ignition.
Signals = {},
Client = {},
Middleware = {}
}
function PlayerDataService:LorenIgnite()
-- Synchronous setup
end
function PlayerDataService:LorenBurn()
-- Asynchronous execution
end
return PlayerDataService
The Client Table
By default, everything inside a Service is strictly private to the server. To allow a Client Controller to interact with your Service, you must explicitly define functions inside the Client table.
The framework intercepts calls to these functions and automatically manages the underlying RemoteEvent data packing.
Method Structure
When a Client invokes a Service method, the framework guarantees that the first argument received by the server is the Player object who made the request.
-- The client only passes 'itemId'
function PlayerDataService.Client:PurchaseItem(player, itemId)
-- The server securely receives the player context
local hasFunds = self.Server:CheckFunds(player)
if hasFunds then
return true, "Item purchased."
else
return false, "Insufficient funds."
end
end
Notice the use of self.Server in the example above. Methods executed within the Client table have their self context shifted. To access the top-level Service functions and variables, you must route through self.Server.
Firing Signals
Signals provide a highly optimized alternative to standard RemoteEvents. First declare the signal name as a string in the Signals array. During ignition, Loren replaces that array with live Signal objects you can fire by name.
local PlayerDataService = {
Signals = {"PointsUpdated"}, -- declare the name here
}
function PlayerDataService:AwardPoints(player, amount)
-- Logic to award points...
-- Notify the specific player
self.Signals.PointsUpdated:Fire(player, amount)
-- Or notify every connected client
self.Signals.PointsUpdated:FireAll(amount)
end
The example above fires from a top-level Service method, so self.Signals is correct. Inside a Client method self is the Client table — reach signals through self.Server.Signals instead.
See the Signal API reference for every method (Fire, FireAll, Connect, Once).