Forest Logo
search
package_2

playpath

By @gmackie

Roblox

Mirrored

PlayPath SDK for Roblox

Server-side Lua SDK for integrating PlayPath adaptive learning into Roblox games.

Installation

With Wally (recommended)

Add to your wally.toml:

[dependencies]
PlayPath = "gmackie/playpath@0.1.0"

Then run:

wally install

Manual Installation

Copy src/PlayPath.lua into ReplicatedStorage.PlayPath.

Quick Start

-- ServerScriptService/PlayPathSetup.lua
local Players = game:GetService("Players")
local PlayPath = require(game.ReplicatedStorage.PlayPath)

PlayPath.init({
    gameKeyId = "your-game-key-id",
    apiKeySecret = "your-api-secret",
})

local sessions = {}

Players.PlayerAdded:Connect(function(player)
    PlayPath.createSession(player)
        :andThen(function(session)
            sessions[player] = session
            
            if not session.linked then
                -- Show pairing UI with session.pairingCode
            end
            
            return session:getNextQuestion()
        end)
        :andThen(function(response)
            local question = response.question
            -- Display question to player
        end)
        :catch(function(err)
            warn("PlayPath error:", err.message)
        end)
end)

Players.PlayerRemoving:Connect(function(player)
    local session = sessions[player]
    if session then
        session:endSession()
        sessions[player] = nil
    end
end)

API Reference

PlayPath.init(config)

Initialize the SDK. Call once at server start.

PlayPath.init({
    gameKeyId = "your-key",        -- Required
    apiKeySecret = "your-secret",  -- Required
    baseUrl = "https://...",       -- Optional, default: production
    maxRetries = 3,                -- Optional
    retryBackoffMs = 1000,         -- Optional
    eventFlushInterval = 5,        -- Optional, seconds
    eventFlushThreshold = 10,      -- Optional, events
    logLevel = "warn",             -- Optional: "none"|"error"|"warn"|"debug"
    mockMode = false,              -- Optional, for testing
})

PlayPath.createSession(player, options?)

Create a session for a player. Returns a Promise.

PlayPath.createSession(player, { launchToken = "optional-lti-token" })
    :andThen(function(session)
        print(session.sessionId)   -- string
        print(session.linked)      -- boolean
        print(session.pairingCode) -- string or nil
        print(session.student)     -- {id, displayName} or nil
        print(session.config)      -- {theme, focusSkills}
    end)

Session Methods

All methods return Promises except trackEvent.

session:getNextQuestion(count?)

session:getNextQuestion():andThen(function(response)
    local question = response.question
    print(question.id, question.prompt, question.choices)
end)

session:submitAnswer(questionId, answer, responseTimeMs)

session:submitAnswer(questionId, "b", 3500)
    :andThen(function(result)
        print(result.correct)      -- boolean
        print(result.feedback)     -- string
        print(result.masteryUpdates) -- array
    end)

session:skipQuestion(questionId, reason)

session:skipQuestion(questionId, "too_hard")

session:getHint(questionId, hintIndex?)

session:getHint(questionId, 0):andThen(function(hint)
    print(hint.hint)        -- string
    print(hint.hintIndex)   -- number
    print(hint.totalHints)  -- number
    print(hint.isLastHint)  -- boolean
end)

session:trackEvent(event)

Fire-and-forget event tracking. Events are batched automatically.

session:trackEvent({
    type = "skill_demo",
    questionId = questionId,
    correct = true,
})

session:flush()

Manually flush pending events.

session:flush():andThen(function(result)
    print(result.accepted, result.rejected)
end)

session:verifyPairingCode(code)

Link an unlinked account.

session:verifyPairingCode("ABC123"):andThen(function(result)
    if result.success then
        print("Linked to:", result.student.displayName)
    end
end)

session:endSession()

End the session. Flushes pending events first.

session:endSession():andThen(function()
    print("Session ended")
end)

Error Handling

All errors are structured:

session:submitAnswer(...):catch(function(err)
    print(err.code)       -- "RATE_LIMITED", "UNAUTHORIZED", etc.
    print(err.message)    -- Human-readable message
    print(err.statusCode) -- HTTP status or nil
    print(err.retryable)  -- boolean
end)

Error Codes

CodeRetryableDescription
VALIDATION_ERRORNoInvalid request
UNAUTHORIZEDNoInvalid credentials
NOT_FOUNDNoResource not found
RATE_LIMITEDYesToo many requests
INTERNAL_ERRORYesServer error
NETWORK_ERRORYesNetwork failure
SESSION_ENDEDNoSession already ended
PLAYER_LEFTNoPlayer left game

Mock Mode

For testing without API credentials:

PlayPath.init({
    gameKeyId = "test",
    apiKeySecret = "test",
    mockMode = true,
})

Testing

Run crypto self-tests in Studio:

local PlayPath = require(game.ReplicatedStorage.PlayPath)
PlayPath._internal.runCryptoTests()

License

MIT

Package Details

Install command (Click to copy)


Version

0.1.0

License

MIT

check_circle

Safe for commercial use

Automated license review — not legal advice.