Skip to main content

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.

No Client Table forces Server-Only

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 = {}
})
tip

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
info

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
}
}
})
tip

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)
tip

Check out the Boilerplates guide to see how to create a service using a boilerplate.