Tutorial
Tutorial APIs live under client.automation — a namespace that holds long-running drivers (auto-tutorial) plus the paired read-only state queries.
Why a namespace?
clientitself stays focused on plain account/world ops (warp, leave, send packets). Drivers + their state live onclient.automationso IDE autocomplete onc:doesn't drown in tutorial methods.
TutorialState enum
PlayerData.TutorialState is the server-side enum (sourced from the il2cpp dump and pushed via GPd). Exposed as a global TutorialState table so scripts don't have to hardcode the integer values.
| Constant | Value | Meaning |
|---|---|---|
TutorialState.NOT_STARTED | 0 | Brand new account, no character yet |
TutorialState.CHARACTER_CREATION | 1 | Picked outfit, hasn't spawned |
TutorialState.TUTORIAL_WORLD | 2 | Inside TUTORIAL2, mid-flow |
TutorialState.COMPLETED | 3 | Cleared the chain — cutoff for "done" |
TutorialState.PHASE_1 | 4 | Post-intro FTUE checkpoint 1 |
TutorialState.PHASE_2 | 5 | Post-intro FTUE checkpoint 2 |
TutorialState.PHASE_3 | 6 | Post-intro FTUE checkpoint 3 |
TutorialState.PHASE_4 | 7 | Post-intro FTUE checkpoint 4 |
The < 3 cutoff is what isInTutorial() checks. >= 3 is what isTutorialDone() checks.
State queries
client.automation:tutorialState()
| Field | Type |
|---|---|
| Signature | (self: Automation) → number? |
| Returns | number? — raw enum int (0..7), or nil before the first GPd profile blob lands |
| Errors | none — pure snapshot read |
local state = c.automation:tutorialState()
if state == TutorialState.COMPLETED then
print("done")
elseif state == nil then
print("not authenticated yet")
end
client.automation:tutorialStateLabel()
| Field | Type |
|---|---|
| Signature | (self: Automation) → string? |
| Returns | string? — canonical CamelCase name, or nil before first GPd |
| Possible values | "NotStarted", "CharacterCreation", "TutorialWorld", "Completed", "Phase1", "Phase2", "Phase3", "Phase4" |
print(c.automation:tutorialStateLabel()) -- e.g. "Phase4"
client.automation:isInTutorial()
| Field | Type |
|---|---|
| Signature | (self: Automation) → boolean |
| Returns | boolean — true when state is 0..2 (mid-tutorial); false when >= 3 OR state is nil |
if c.automation:isInTutorial() then
-- account hasn't finished the intro
end
client.automation:isTutorialDone()
| Field | Type |
|---|---|
| Signature | (self: Automation) → boolean |
| Returns | boolean — true when state is >= 3; false when < 3 OR state is nil |
Pairs with
isInTutorial()— both returnfalsewhen state is unknown so post-tutorial gates don't trip onnil.
while not c.automation:isTutorialDone() do
task.wait(0.5)
end
c:warp("ASDSA")
Auto-driver
client.automation:tutorial()
Drives the entire intro flow end-to-end (character creation → tutorial world → BasicClothes pack → leave → PIXELSTATION → backpack upgrade).
| Field | Type |
|---|---|
| Signature | (self: Automation) → string? |
| Returns | string? — nil on success, or an Errors.* code on failure |
| Common errors | Errors.TUTORIAL_BUSY (another driver is already running), Errors.NOT_CONNECTED, Errors.DISCONNECTED, Errors.TIMEOUT |
| Idempotent | yes — finished accounts return immediately |
| Async | yes — yields until the chain finishes or fails |
local err = c.automation:tutorial()
if err == Errors.TUTORIAL_BUSY then
print("already running")
elseif err then
print("tutorial failed:", err)
else
print("done")
end
client.automation:tutorialRunning()
| Field | Type |
|---|---|
| Signature | (self: Automation) → boolean |
| Returns | boolean — true while the auto-driver is active for this bot |
if c.automation:tutorialRunning() then
return -- driver owns the bot right now
end
client.automation:tutorialStage()
| Field | Type |
|---|---|
| Signature | (self: Automation) → string? |
| Returns | string? — human-readable label of the current driver step, or nil when no driver is running |
| Possible values | "walking to NPC", "buying clothes pack", "equipping cosmetics", "leaving tutorial world", "post-tutorial spawn world", "buying inventory slot upgrade", "complete", … (non-exhaustive — labels are advisory, don't == compare in production) |
c:on("status", function()
if c.automation:tutorialRunning() then
print("step:", c.automation:tutorialStage())
end
end)
End-to-end recipe
local c = getClient()
local a = c.automation
-- 1. Wait for the first GPd profile blob.
while a:tutorialState() == nil do task.wait(0.2) end
-- 2. Auto-run if mid-tutorial.
if a:isInTutorial() then
local err = a:tutorial()
if err then
print("auto-tutorial failed:", err)
return
end
end
-- 3. Now safe to do post-tutorial things.
print("state:", a:tutorialStateLabel()) -- "Completed" or "Phase1+"
c:warp("WORLD_OF_CHOICE")
See also
Client.client— full Client method referenceTutorialStateglobal enum constantsErrors— error code reference