Skip to main content

TutorialServer

This item only works when running on the server. Server

Where tutorial progress becomes something you can save.

Require it directly
const TutorialServer = require(Packages.TutorialKit.Server)

Never reach it through the kit's facade. require(Packages.TutorialKit) pulls the spotlight, the pointer and ezvisualz, none of which have any business running on a server.

Step 1 — start the bridge at boot

Do this before any client can report, otherwise early reports are lost.

TutorialServer:Start()

Step 2 — save what the client reports

TutorialServer.PlayerStepAdvanced:Connect(function(player, step, previousStep)
	DataService:Set(player, "TutorialStep", step)
end)

TutorialServer.TutorialCompleted:Connect(function(player)
	DataService:Set(player, "TutorialDone", true)
end)

Step 3 — put returning players back where they were

Players.PlayerAdded:Connect(function(player)
	if DataService:Get(player, "TutorialDone") then
		TutorialServer:Complete(player)
		return
	end

	const saved = DataService:Get(player, "TutorialStep")
	if saved then
		TutorialServer:SetStep(player, saved)
	end
end)

The direction of authority

The client reports what it did; it never dictates what happens next. The server is the only side that decides, and TutorialServer:SetStep is how it says so.

Everything arriving from a remote is parsed and dropped when malformed, so neither side ever sees a bad packet. A client can still lie about which step it reached, so treat these signals as progress tracking, not as a place to hand out rewards.

Properties

PlayerStepAdvanced

events
TutorialServer.PlayerStepAdvanced: Signal<Player,number,number?>

Fires when a player reports reaching a step. The third argument is the step they came from, and is nil on the first one.

TutorialServer.PlayerStepAdvanced:Connect(function(player, step, previousStep)
	DataService:Set(player, "TutorialStep", step)
end)

Steps the server itself ordered with TutorialServer:SetStep do not come back through here, so this never echoes your own decisions.

TutorialCompleted

events
TutorialServer.TutorialCompleted: Signal<Player>

Fires once when a player walks past the last step.

Functions

Start

lifecycle
TutorialServer:Start() → ()

Creates the kit's RemoteEvent and starts listening.

Call it once during server boot. Reports that arrive before this are lost, and since a client only reports after its own tutorial starts, "before boot finishes" is a real window.

The kit carries its own RemoteEvent, so it needs no slot in your network layer.

Errors

TypeDescription
"TutorialKit.Server is server only"Called from a client

Stop

lifecycle
TutorialServer:Stop() → ()

Disconnects and forgets tracked progress. Rarely needed outside tests.

SetStep

TutorialServer:SetStep(
playerPlayer,--

Who to move

stepnumber--

Order number to put them on

) → ()

Puts a client on a given step.

This is the server telling the client where to be, and it is how you resume a returning player from saved data. It requires the client's tutorial to have been built with Replicate = true.

Errors

TypeDescription
"TutorialKit.Server is server only"Called from a client

Complete

TutorialServer:Complete(
playerPlayer--

Whose tutorial to end

) → ()

Ends a client's tutorial, for instance when saved data says they already finished it.

Errors

TypeDescription
"TutorialKit.Server is server only"Called from a client

GetStep

TutorialServer:GetStep(
playerPlayer--

Who to look up

) → number?--

Last step they reported, or nil

Reads tracked progress without a round trip.

This is in-memory only and is cleared when the player leaves, so it is a convenience for save hooks, not a substitute for your data store.

Show raw api
{
    "functions": [
        {
            "name": "Start",
            "desc": "Creates the kit's RemoteEvent and starts listening.\n\nCall it once during server boot. Reports that arrive before this are lost, and since a client\nonly reports after its own tutorial starts, \"before boot finishes\" is a real window.\n\nThe kit carries its own RemoteEvent, so it needs no slot in your network layer.",
            "params": [],
            "returns": [],
            "function_type": "method",
            "tags": [
                "lifecycle"
            ],
            "errors": [
                {
                    "lua_type": "\"TutorialKit.Server is server only\"",
                    "desc": "Called from a client"
                }
            ],
            "source": {
                "line": 107,
                "path": "src/Server.luau"
            }
        },
        {
            "name": "Stop",
            "desc": "Disconnects and forgets tracked progress. Rarely needed outside tests.",
            "params": [],
            "returns": [],
            "function_type": "method",
            "tags": [
                "lifecycle"
            ],
            "source": {
                "line": 115,
                "path": "src/Server.luau"
            }
        },
        {
            "name": "SetStep",
            "desc": "Puts a client on a given step.\n\nThis is the server telling the client where to be, and it is how you resume a returning player\nfrom saved data. It requires the client's tutorial to have been built with `Replicate = true`.",
            "params": [
                {
                    "name": "player",
                    "desc": "Who to move",
                    "lua_type": "Player"
                },
                {
                    "name": "step",
                    "desc": "Order number to put them on",
                    "lua_type": "number"
                }
            ],
            "returns": [],
            "function_type": "method",
            "errors": [
                {
                    "lua_type": "\"TutorialKit.Server is server only\"",
                    "desc": "Called from a client"
                }
            ],
            "source": {
                "line": 128,
                "path": "src/Server.luau"
            }
        },
        {
            "name": "Complete",
            "desc": "Ends a client's tutorial, for instance when saved data says they already finished it.",
            "params": [
                {
                    "name": "player",
                    "desc": "Whose tutorial to end",
                    "lua_type": "Player"
                }
            ],
            "returns": [],
            "function_type": "method",
            "errors": [
                {
                    "lua_type": "\"TutorialKit.Server is server only\"",
                    "desc": "Called from a client"
                }
            ],
            "source": {
                "line": 137,
                "path": "src/Server.luau"
            }
        },
        {
            "name": "GetStep",
            "desc": "Reads tracked progress without a round trip.\n\nThis is in-memory only and is cleared when the player leaves, so it is a convenience for save\nhooks, not a substitute for your data store.",
            "params": [
                {
                    "name": "player",
                    "desc": "Who to look up",
                    "lua_type": "Player"
                }
            ],
            "returns": [
                {
                    "desc": "Last step they reported, or nil",
                    "lua_type": "number?"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 149,
                "path": "src/Server.luau"
            }
        }
    ],
    "properties": [
        {
            "name": "PlayerStepAdvanced",
            "desc": "Fires when a player reports reaching a step. The third argument is the step they came from, and\nis nil on the first one.\n\n```lua\nTutorialServer.PlayerStepAdvanced:Connect(function(player, step, previousStep)\n\tDataService:Set(player, \"TutorialStep\", step)\nend)\n```\n\nSteps the server itself ordered with [TutorialServer:SetStep] do not come back through here, so\nthis never echoes your own decisions.",
            "lua_type": "Signal<Player, number, number?>",
            "tags": [
                "events"
            ],
            "source": {
                "line": 85,
                "path": "src/Server.luau"
            }
        },
        {
            "name": "TutorialCompleted",
            "desc": "Fires once when a player walks past the last step.",
            "lua_type": "Signal<Player>",
            "tags": [
                "events"
            ],
            "source": {
                "line": 93,
                "path": "src/Server.luau"
            }
        }
    ],
    "types": [],
    "name": "TutorialServer",
    "desc": "Where tutorial progress becomes something you can save.\n\n:::caution Require it directly\n```lua\nconst TutorialServer = require(Packages.TutorialKit.Server)\n```\nNever reach it through the kit's facade. `require(Packages.TutorialKit)` pulls the spotlight, the\npointer and ezvisualz, none of which have any business running on a server.\n:::\n\n### Step 1 — start the bridge at boot\n\nDo this before any client can report, otherwise early reports are lost.\n\n```lua\nTutorialServer:Start()\n```\n\n### Step 2 — save what the client reports\n\n```lua\nTutorialServer.PlayerStepAdvanced:Connect(function(player, step, previousStep)\n\tDataService:Set(player, \"TutorialStep\", step)\nend)\n\nTutorialServer.TutorialCompleted:Connect(function(player)\n\tDataService:Set(player, \"TutorialDone\", true)\nend)\n```\n\n### Step 3 — put returning players back where they were\n\n```lua\nPlayers.PlayerAdded:Connect(function(player)\n\tif DataService:Get(player, \"TutorialDone\") then\n\t\tTutorialServer:Complete(player)\n\t\treturn\n\tend\n\n\tconst saved = DataService:Get(player, \"TutorialStep\")\n\tif saved then\n\t\tTutorialServer:SetStep(player, saved)\n\tend\nend)\n```\n\n### The direction of authority\n\nThe client reports what it *did*; it never dictates what happens next. The server is the only\nside that decides, and [TutorialServer:SetStep] is how it says so.\n\nEverything arriving from a remote is parsed and dropped when malformed, so neither side ever\nsees a bad packet. A client can still lie about which step it reached, so treat these signals as\nprogress tracking, not as a place to hand out rewards.",
    "realm": [
        "Server"
    ],
    "source": {
        "line": 61,
        "path": "src/Server.luau"
    }
}