Services
Services are the main building blocks of Cradle. They are exclusively server side code that is used to organise code into logical groups. Services are used to create a modular system that is easy to maintain and expand.
For this tutorial, we will be creating a simple MoneyService. This service will be used to manage in-game currency for players.
Creating a Service
Services can be made on the server side using:
local MoneyService = Cradle:CreateService({
Name = "MoneyService",
Client = {}
})
return MoneyService
The Name
field is required and must be unique. This is used to identify the controller and is used in the Cradle:GetService()
method.
The 'Client' table is used to define client side code. This table can contain RemoteFunctions, RemoteEvents and RemoteProperties. These objects can be created using the RemoteComm module. These objects are usable from the client upon calling Cradle:GetService()
on the client. We will cover this in more detail below.
If there is no client side code, the Client
table can be omitted. This is useful for services that are purely server side.
If the Client
table is omitted, the service will be server only and cannot be accessed from the client.
The last line (return MoneyService
) is required to return the service object. It is recommended to use a ModuleScript to contain the service code.
Adding Methods
To add methods to the service, simply add them to the service object:
function MoneyService:AddMoney(player: Player, amount: number)
-- TODO: Add money
end
function MoneyService:SubtractMoney(player: Player, amount: number)
-- TODO: Subtract money
end
function MoneyService:GetMoney(player: Player): number
return 0
end
Adding Properties
Properties can be added to the service object by simply adding them to the object. You can do these two ways:
Adding them directly to the object:
MoneyService.MoneyPerPlayer = {}
or
self.MoneyPerPlayer = {} -- Inside of a method
or using the CreateService
function:
local MoneyService = Cradle:CreateService({
Name = "MoneyService",
MoneyPerPlayer = {}
})
It is recommended to use the CreateService
function to add properties to your service.
Using Methods and Properties
We can now use the properties and methods we have created. For example, we can add methods to add & subtract money from a player:
local MoneyService = Cradle:CreateService({
Name = "MoneyService",
MoneyPerPlayer = {}
})
function MoneyService:AddMoney(player: Player, amount: number)
local money = self.MoneyPerPlayer[player] or 0 -- Current amount of money
money += amount -- Add amount to current money
self.MoneyPerPlayer[player] = self.MoneyPerPlayer[player] + amount -- Store money
end
function MoneyService:GetMoney(player: Player): number
local money = self.MoneyPerPlayer[player]
return if money ~= nil then money else 0 -- Return 0 if no money found for player
end
Init and Start Methods
Init and Start methods can then be added to this object:
The Init function is called when running Cradle:Start(), however it is not guaranteed that any other service will be fully initialised, so in this stage other services should not be referenced.
function MoneyService:Init()
end
The Start function is called when running Cradle:Start() once all Service :Init()
methods have ran, only at this stage other services are considered initialised and safe to use.
function MoneyService:Start()
end
This is optional, however it is recommended to use these methods or leave them empty.
Client Communication
Client communication is handled by the Client table, this table can contain RemoteFunctions, RemoteEvents and RemoteProperties. These objects can be created using the RemoteComm module. These objects are usable from the client upon calling Cradle:GetService()
.
local MoneyService = Cradle:CreateService({
Name = "MoneyService",
Client = {
updateEvent = Cradle.RemoteComm.RemoteEvent.new()
taxProperty = Cradle.RemoteComm.RemoteProperty.new(0.1) -- 10% tax
}
})
You are also able to group these objects into tables which can be useful for organising your code and making it easier to read:
local MoneyService = Cradle:CreateService({
Name = "MoneyService",
Client = {
Tax = {
updateEvent = Cradle.RemoteComm.RemoteEvent.new()
}
Money = {
taxProperty = Cradle.RemoteComm.RemoteProperty.new(0.1) -- 10% tax
}
}
})
Check out the Networking guide to learn more about the networking system.
Remote Functions
To add client methods to the service, simply add them to the client table:
function MoneyService.Client:GetMoney(player: Player): number
return self:GetMoney(player)
end
This creates a client method that can be called from the client. Inside of this method we are calling the server method GetMoney
that we created earlier and then returning the result to the client through the RemoteFunction.
In this example, we have just made a proxy method that calls the server method, however we can also add client side code to this method:
function MoneyService.Client:GetMoney(player: Player): number
return self:GetMoney(player)
end
Under the hood, Cradle will create a RemoteFunction and bind it to the server method. This means that the client can call the server method as if it was a normal function.
On the client, we can invoke the server method using:
-- LocalScript
local Cradle = require(game:GetService("ReplicatedStorage").Cradle)
local MoneyService = Cradle:GetService("MoneyService")
local money = MoneyService:GetMoney(game.Players.LocalPlayer)
print("Money: " .. money)
Remote Events
Remote Events can be stored under a service's Client table. These can be used to fire events from the server to the client and vice versa.
To create a Remote Event, we can use the RemoteComm module:
local MoneyService = Cradle:CreateService({
Name = "MoneyService",
Client = {
updateEvent = Cradle.RemoteComm.RemoteEvent.new()
}
})
We can then fire the event from the server using:
function MoneyService:Add(player: Player, amount: number)
local money = self.MoneyPerPlayer[player] or 0
money += amount
self.MoneyPerPlayer[player] = self.MoneyPerPlayer[player] + amount
self.Client.updateEvent:FireClient(player, money)
end
function MoneyService:Subtract(player: Player, amount: number)
local money = self.MoneyPerPlayer[player] or 0
money -= amount
self.MoneyPerPlayer[player] = self.MoneyPerPlayer[player] - amount
self.Client.updateEvent:FireClient(player, money)
end
and listen to the event on the client using:
-- LocalScript
local Cradle = require(game:GetService("ReplicatedStorage").Cradle)
local MoneyService = Cradle:GetService("MoneyService")
MoneyService.Client.updateEvent:Connect(function(player: Player, money: number)
print(player.Name .. " has " .. money .. " money")
end)
Remote Properties
Creating Remote Properties is very similar to using Remote Events, however they are used to store values that can be accessed from both the client and the server.
Remote Properties can store the following data types:
table
string
number
boolean
Instance
To create a Remote Property, we can use the RemoteComm module:
local MoneyService = Cradle:CreateService({
Name = "MoneyService",
Client = {
taxProperty = Cradle.RemoteComm.RemoteProperty.new(0.1) -- 10% tax
},
})
We can use this Remote Property to calculate the tax when subtracting money:
function MoneyService:Subtract(player: Player, amount: number, taxable: boolean)
local money = self.MoneyPerPlayer[player] or 0
if taxable then
local tax = self.Client.taxProperty:Get()
amount = amount * (1 + tax)
end
money -= amount
self.MoneyPerPlayer[player] = self.MoneyPerPlayer[player] - amount
self.Client.updateEvent:FireClient(player, money)
end
}
We can then check the tax rate on the client:
-- LocalScript
local Cradle = require(game:GetService("ReplicatedStorage").Cradle)
local MoneyService = Cradle:GetService("MoneyService")
local tax = MoneyService.Client.taxProperty:Get()
print(`Tax: {tax * 100}%`)
Full Example
MoneyService
We can now put all of this together to create a full MoneyService. At the end of this tutorial, we will have a service that can be used to manage in-game currency for players and handle tax on transactions:
local Cradle = require(game:GetService("ReplicatedStorage").Cradle)
local MoneyService = Cradle:CreateService({
Name = "MoneyService",
Client = {
updateEvent = Cradle.RemoteComm.RemoteEvent.new()
taxProperty = Cradle.RemoteComm.RemoteProperty.new(0.1) -- 10% tax
},
MoneyPerPlayer = {},
})
function MoneyService.Client:GetMoney(player: Player): number
return self:GetMoney(player)
end
function MoneyService:GetMoney(player: Player): number
local money = self.MoneyPerPlayer[player] -- Current amount of money
return if money ~= nil then money else 0 -- Return 0 if no money found for player
end
function MoneyService:Add(player: Player, amount: number)
local money = self.MoneyPerPlayer[player] or 0 -- Current amount of money
money += amount -- Add amount to current money
self.MoneyPerPlayer[player] = self.MoneyPerPlayer[player] + amount -- Store money
self.Client.updateEvent:FireClient(player, money) -- Fire money update event
end
function MoneyService:Subtract(player: Player, amount: number, taxable: boolean)
local money = self.MoneyPerPlayer[player] or 0 -- Current amount of money
if taxable then -- Check if taxable
local tax = self.Client.taxProperty:Get() -- Get tax from property
amount = amount * (1 + tax) -- Add tax to amount
end
money -= amount -- Subtract amount from current money
self.MoneyPerPlayer[player] = self.MoneyPerPlayer[player] - amount -- Store money
self.Client.updateEvent:FireClient(player, money) -- Fire money update event
end
function MoneyService:Start()
print(self.Client.taxProperty:Get()) -- Get tax (Will be 0.2)
end
function MoneyService:Init()
print(self.Client.taxProperty:Get()) -- Get tax (Will be 0.1)
self.Client.taxProperty:Set(0.2) -- Set tax to 20%
end
return MoneyService
Client Consumer
Example of a local script that uses the MoneyService:
-- LocalScript
local Cradle = require(game:GetService("ReplicatedStorage").Cradle)
local MoneyService = Cradle:GetService("MoneyService")
local money = MoneyService:GetMoney(game.Players.LocalPlayer)
print("Money: " .. money)
Check out the Boilerplates guide to see how to create a service using a boilerplate.