Skip to content
🌊 You are viewing documentation for the Maelstrom pre-release channel. Pin a stable release for production.

Server Service

This example shows the recommended Maelstrom-2 shape for a server module. Server modules receive the side-specific server API in lifecycle hooks, so Riptide.Network, Riptide.State, and Riptide.GetService autocomplete server-only methods.

-- ServerScriptService/Services/CoinsService.lua
--!strict
local CoinsService = {}
function CoinsService:Init(Riptide)
Riptide.Network.RegisterTyped("BuyItem", {
Riptide.Guard.String(50),
Riptide.Guard.Number(0, 1000),
}, function(player: Player, itemId: string, price: number)
local coins = Riptide.State:Get("coins", player) or 0
if coins < price then
return
end
Riptide.State:SetForPlayer(player, "coins", coins - price)
print(player.Name .. " bought " .. itemId)
end)
end
function CoinsService:OnPlayerAdded(Riptide, player: Player)
Riptide.State:SetForPlayer(player, "coins", 100)
end
function CoinsService:OnPlayerRemoving(_Riptide, player: Player)
print("Saving coins for", player.Name)
end
return CoinsService

Launch it from a server script:

-- ServerScriptService/main.server.lua
--!strict
local ServerScriptService = game:GetService("ServerScriptService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Riptide = require(ReplicatedStorage.Packages.Riptide).Server
Riptide.Launch({
ModulesFolder = ServerScriptService.Services,
})

Init should stay non-yielding. Put loops, WaitForChild, and async work in Start or player hooks.