diff --git a/skills/rudder-go-sdk/SKILL.md b/skills/rudder-go-sdk/SKILL.md new file mode 100644 index 0000000..26edfba --- /dev/null +++ b/skills/rudder-go-sdk/SKILL.md @@ -0,0 +1,117 @@ +--- +name: rudder-go-sdk +description: Use when working with the Rudder Go server SDK (package rudder, module hub.rudder.build/rudder/rudder-go-sdk) for server-side admin operations — players, wallet, inventory, storage, quests, leaderboards, webhook signature verification. +--- + +# Rudder Go server SDK + +Server-side admin SDK for the Rudder LiveOps platform. Covers the `/game/v1` +admin surface, authenticated with `X-API-Key`. Module: +`hub.rudder.build/rudder/rudder-go-sdk`, package `rudder`. Requires Go 1.21+. + +## Install + +The module is served from a self-hosted Gitea, bypassing proxy.golang.org: + +```bash +go env -w GOPRIVATE=hub.rudder.build/* +go get hub.rudder.build/rudder/rudder-go-sdk@latest +``` + +## Client init + +`AdminKey` is the per-project Admin Key from the dashboard +(app.rudder.build → project → Settings). The key identifies the project; no +separate project ID is needed. Keep it server-side only, never in game clients. + +```go +import rudder "hub.rudder.build/rudder/rudder-go-sdk" + +client, err := rudder.New(rudder.Config{ + AdminKey: os.Getenv("RUDDER_ADMIN_KEY"), + // BaseURL defaults to https://api.rudder.build + // Timeout defaults to 30s; HTTPClient optional +}) +``` + +`client` exposes services: `Players`, `Leaderboards`, `Wallet`, `Inventory`, +`Quests`, `Storage`. All methods take `context.Context` first. + +Public types live in package `rudder` (aliases in `types.go`). The `models` +package is generated wire format — never import it in application code and +never edit it by hand (regenerate via `make generate-openapi` in +liveops-gateway). + +## Batch-first mutation model + +All top-level mutating operations are batch-only. Request body is +`{idempotencyKey?, items:[...]}` with `playerId` inside each item +(leaderboard delete takes `{playerIds:[...]}`, project-storage delete +`{types:[...]}`). Rules: + +- at most 100 items per batch +- all-or-nothing in one transaction — one failing item fails the whole batch +- one optional `IdempotencyKey` per batch; retrying with the same key is safe +- errors report the index and playerId of the failing item + +Method naming is uniform: verb + entity (`List`/`Get`/`Submit`/`Update`/ +`Upsert`/`Delete`/`Adjust`/`History`). + +## Handles + +`client.Player(id)` and `client.Leaderboard(slug)` return handles without any +HTTP request. Handles are sugar over the batch operations with n=1: the +handle injects `playerID`/`slug` into a single-item batch and unwraps the +first result. + +```go +player := client.Player("p1") +wallet, err := player.Wallet().Adjust(ctx, rudder.AdjustPlayerWalletParameters{ + IdempotencyKey: "grant-001", + CurrencyCode: "coins", + Amount: 100, + Reason: "compensation", +}) + +board := client.Leaderboard("weekly") +err = board.Submit(ctx, []rudder.LeaderboardScore{{PlayerID: "p1", Score: 1500}}) +``` + +`client.Player(id)` exposes sub-handles `Wallet()`, `Inventory()`, +`Storage()`, `Quests()` plus `Update`/`Ban`/`Unban`/`Delete`. +`client.Leaderboard(slug)` exposes `List`/`Get`/`GetAround`/`Submit`/ +`Update`/`Delete`. + +## Top-level Storage vs PlayerHandle.Storage() + +`client.Storage` is the **global (project) storage** — shared key/value items +keyed by `Type`, with permissions and versioning. Player storage is reachable +**only** through `client.Player(id).Storage()` — per-player items keyed by +`Type`. They are different backends with different item shapes; do not +confuse them. + +## Errors + +All API failures return `*rudder.APIError` (`Status`, `Code`, `Message`, +`RequestID`); use `errors.As`. Client-side misconfiguration returns +`Code: "sdk/invalid-options"` (`rudder.ErrorCodeInvalidOptions`). Scenario +engine codes surfaced in `Code` include `early_completion`, `forbidden`, +`level_not_reached`, `node_not_active`, `objectives_incomplete`, +`run_expired`, `run_not_active`, `scenario_not_active`, `unknown_run`. + +## Webhook verification + +```go +if !rudder.VerifyWebhookSignature(secret, body, r.Header.Get("X-Signature")) { + // reject: signature is hex HMAC-SHA256 of the raw body +} +``` + +## Reference (exact signatures per service area) + +- `reference/players.md` — list/get/verify-auth, batch update/ban/unban/delete, PlayerHandle +- `reference/wallet.md` — batch adjust, history, PlayerWalletHandle +- `reference/inventory.md` — batch adjust, list, PlayerInventoryHandle +- `reference/storage.md` — project storage (top-level) and player storage (handle) +- `reference/quests.md` — quest definitions list, batch reset/force-complete/force-claim, PlayerQuestsHandle +- `reference/leaderboards.md` — definitions list, LeaderboardHandle entries diff --git a/skills/rudder-go-sdk/reference/inventory.md b/skills/rudder-go-sdk/reference/inventory.md new file mode 100644 index 0000000..3e25339 --- /dev/null +++ b/skills/rudder-go-sdk/reference/inventory.md @@ -0,0 +1,43 @@ +# Inventory + +Source: `inventory.go`, `player_inventory.go`. Types: `InventoryItem`, `InventoryList`, `InventoryAdjustment`. + +## Batch adjust + +```go +func (s *InventoryService) Adjust(ctx context.Context, parameters AdjustInventoryParameters) ([]InventoryItem, error) +type AdjustInventoryParameters struct { + IdempotencyKey string + Items []InventoryAdjustment +} +// InventoryAdjustment: {PlayerID, Slug, Amount int64, Reason} +// Negative Amount removes items. Returns updated items in item order. +``` + +## List + +```go +func (s *InventoryService) List(ctx context.Context, playerID string, parameters ListInventoryParameters) (*InventoryList, error) +type ListInventoryParameters struct { + Limit int + Cursor *string +} +// InventoryList: {Items []InventoryItem, NextCursor string} +// InventoryItem: {ID, Slug, Amount int64, UpdatedAt} +``` + +## PlayerInventoryHandle + +`client.Player(id).Inventory()`: + +```go +func (h *PlayerInventoryHandle) Adjust(ctx context.Context, parameters AdjustPlayerInventoryParameters) (*InventoryItem, error) +type AdjustPlayerInventoryParameters struct { + IdempotencyKey string + Slug string + Amount int64 + Reason string +} + +func (h *PlayerInventoryHandle) List(ctx context.Context, parameters ListInventoryParameters) (*InventoryList, error) +``` diff --git a/skills/rudder-go-sdk/reference/leaderboards.md b/skills/rudder-go-sdk/reference/leaderboards.md new file mode 100644 index 0000000..86cde53 --- /dev/null +++ b/skills/rudder-go-sdk/reference/leaderboards.md @@ -0,0 +1,54 @@ +# Leaderboards + +Source: `leaderboards.go` (definitions + batch entry mutations), `leaderboard.go` (handle). +Types: `Leaderboard`, `LeaderboardList`, `LeaderboardScore`, `Rank`, `RankList`. + +## Definitions + +```go +func (s *LeaderboardsService) List(ctx context.Context, parameters ListLeaderboardsParameters) (*LeaderboardList, error) +type ListLeaderboardsParameters struct { + Limit int + Cursor *string +} +// LeaderboardList: {Items []Leaderboard, NextCursor string} +// Leaderboard: {Slug, Name, Metric, ResetPeriod, SortingOrder, MaxEntries int64} +``` + +## Batch entry mutations (service level) + +Note: leaderboard entry mutations do NOT take an IdempotencyKey — the generated +batch request bodies carry only items/playerIds. + +```go +func (s *LeaderboardsService) Submit(ctx context.Context, slug string, items []LeaderboardScore) error +func (s *LeaderboardsService) Update(ctx context.Context, slug string, items []LeaderboardScore) error +// LeaderboardScore: {PlayerID, Score float64} + +func (s *LeaderboardsService) Delete(ctx context.Context, slug string, playerIDs []string) error +// body: {playerIds: [...]} +``` + +## LeaderboardHandle + +`client.Leaderboard(slug)` — no request at creation. + +```go +func (h *LeaderboardHandle) List(ctx context.Context, parameters ListLeaderboardEntriesParameters) (*RankList, error) +type ListLeaderboardEntriesParameters struct { + Limit int + Cursor *string +} +// RankList: {Entries []Rank, NextCursor string} + +func (h *LeaderboardHandle) Get(ctx context.Context, playerID string) (*Rank, error) + +func (h *LeaderboardHandle) GetAround(ctx context.Context, playerID string, limit int) ([]Rank, error) +// entries around the player's rank + +func (h *LeaderboardHandle) Submit(ctx context.Context, items []LeaderboardScore) error +func (h *LeaderboardHandle) Update(ctx context.Context, items []LeaderboardScore) error +func (h *LeaderboardHandle) Delete(ctx context.Context, playerIDs []string) error +``` + +`Rank`: `{PlayerID, PlayerName, Rank int64, Score float64}`. diff --git a/skills/rudder-go-sdk/reference/players.md b/skills/rudder-go-sdk/reference/players.md new file mode 100644 index 0000000..98512c6 --- /dev/null +++ b/skills/rudder-go-sdk/reference/players.md @@ -0,0 +1,86 @@ +# Players + +Source: `players.go`, `player.go`. Types: `types.go` (`Player`, `PlayerDetails`, `PlayerList`, `PlayerUpdate`, `PlayerBan`, `PlayerAuthVerification`). + +## Reads + +```go +func (s *PlayersService) List(ctx context.Context, parameters ListPlayersParameters) (*PlayerList, error) +type ListPlayersParameters struct { + Limit int // offset-based pagination + Offset int + Search string +} +// PlayerList: {Players []Player, Total int, Limit int, Offset int} + +func (s *PlayersService) Get(ctx context.Context, playerID string) (*PlayerDetails, error) +// PlayerDetails: {Player Player, Wallets []WalletBalance, Inventory []InventoryItem, +// Storages []PlayerStorage, WalletTransactions []WalletTransaction} + +func (s *PlayersService) VerifyAuth(ctx context.Context, accessToken string) (*PlayerAuthVerification, error) +// Single (non-batch) call: POST /players/auth/verify. +// PlayerAuthVerification: {PlayerID, ProjectID, Status} — Status is "active"/"banned"/"deleted". +``` + +## Batch mutations + +```go +func (s *PlayersService) Update(ctx context.Context, parameters UpdatePlayersParameters) ([]Player, error) +type UpdatePlayersParameters struct { + IdempotencyKey string + Items []PlayerUpdate // {PlayerID, Nickname, Data json.RawMessage} +} + +func (s *PlayersService) Ban(ctx context.Context, parameters BanPlayersParameters) ([]Player, error) +type BanPlayersParameters struct { + IdempotencyKey string + Items []PlayerBan // {PlayerID, Reason, BannedUntil string} +} + +func (s *PlayersService) Unban(ctx context.Context, parameters UnbanPlayersParameters) ([]Player, error) +type UnbanPlayersParameters struct { + IdempotencyKey string + PlayerIDs []string +} + +func (s *PlayersService) Delete(ctx context.Context, parameters DeletePlayersParameters) error +type DeletePlayersParameters struct { + IdempotencyKey string + PlayerIDs []string +} +``` + +## PlayerHandle + +`client.Player(id)` — no request at creation. + +```go +h.Wallet() *PlayerWalletHandle +h.Inventory() *PlayerInventoryHandle +h.Storage() *PlayerStorageHandle +h.Quests() *PlayerQuestsHandle + +func (h *PlayerHandle) Update(ctx context.Context, parameters UpdatePlayerParameters) (*Player, error) +type UpdatePlayerParameters struct { + IdempotencyKey string + Nickname string + Data json.RawMessage +} + +func (h *PlayerHandle) Ban(ctx context.Context, parameters BanPlayerParameters) (*Player, error) +type BanPlayerParameters struct { + IdempotencyKey string + Reason string + BannedUntil string +} + +func (h *PlayerHandle) Unban(ctx context.Context, idempotencyKey string) (*Player, error) +func (h *PlayerHandle) Delete(ctx context.Context, idempotencyKey string) error +``` + +## Player fields + +`Player`: `ID`, `StableID`, `ProjectID`, `Nickname`, `Data json.RawMessage`, +`Language`, `Region`, `PayerFlag bool`, `BanReason`, `BannedAt`, +`BannedUntil`, `CreatedAt`, `UpdatedAt`, `LastSeen`, `DeletedAt` (all +string unless noted). diff --git a/skills/rudder-go-sdk/reference/quests.md b/skills/rudder-go-sdk/reference/quests.md new file mode 100644 index 0000000..d7ee34c --- /dev/null +++ b/skills/rudder-go-sdk/reference/quests.md @@ -0,0 +1,52 @@ +# Quests + +Source: `quests.go`, `player_quests.go`. Types: `Quest`, `QuestObjective`, `QuestReward`, `QuestList`, `PlayerQuest`, `PlayerQuestObjective`, `PlayerQuestRef`. + +## Quest definitions (catalog) + +```go +func (s *QuestsService) List(ctx context.Context, parameters ListQuestsParameters) (*QuestList, error) +type ListQuestsParameters struct { + Status *string // optional: "active" or "archived" + Limit int + Cursor *string +} +// QuestList: {Items []Quest, NextCursor string} +// Quest: {ID, Name, Status, NextQuestID, Objectives []QuestObjective, Rewards []QuestReward} +// QuestObjective: {ID, Metric, Target int64} +// QuestReward: {Amount int64, Currency, ItemID} +``` + +## Batch player-quest operations + +All three share the same parameters and return only `error`: + +```go +func (s *QuestsService) Reset(ctx context.Context, parameters PlayerQuestsParameters) error +func (s *QuestsService) ForceComplete(ctx context.Context, parameters PlayerQuestsParameters) error +func (s *QuestsService) ForceClaim(ctx context.Context, parameters PlayerQuestsParameters) error + +type PlayerQuestsParameters struct { + IdempotencyKey string + Items []PlayerQuestRef // {PlayerID, QuestID} +} +``` + +## PlayerQuestsHandle + +`client.Player(id).Quests()`: + +```go +func (h *PlayerQuestsHandle) List(ctx context.Context) ([]PlayerQuest, error) +// PlayerQuest: {QuestID, Name, Status, Objectives []PlayerQuestObjective} +// PlayerQuestObjective: {ObjectiveID, Current int64, Target int64} + +func (h *PlayerQuestsHandle) Reset(ctx context.Context, parameters PlayerQuestParameters) error +func (h *PlayerQuestsHandle) ForceComplete(ctx context.Context, parameters PlayerQuestParameters) error +func (h *PlayerQuestsHandle) ForceClaim(ctx context.Context, parameters PlayerQuestParameters) error + +type PlayerQuestParameters struct { + IdempotencyKey string + QuestID string +} +``` diff --git a/skills/rudder-go-sdk/reference/storage.md b/skills/rudder-go-sdk/reference/storage.md new file mode 100644 index 0000000..91d3481 --- /dev/null +++ b/skills/rudder-go-sdk/reference/storage.md @@ -0,0 +1,59 @@ +# Storage + +Source: `storage.go` (project storage, top-level), `player_storage.go` (player storage, handle only). +Types: `ProjectStorage`, `ProjectStorageList`, `ProjectStorageInput`, `PlayerStorage`. + +Top-level `client.Storage` is **project (global) storage**. Player storage is reachable only via `client.Player(id).Storage()`. + +## Project storage (client.Storage) + +```go +func (s *StorageService) List(ctx context.Context, parameters ListStorageParameters) (*ProjectStorageList, error) +type ListStorageParameters struct { + Limit int + Cursor string // plain string, not *string + Search string +} +// ProjectStorageList: {Items []ProjectStorage, NextCursor string} + +func (s *StorageService) Get(ctx context.Context, itemType string) (*ProjectStorage, error) + +func (s *StorageService) Upsert(ctx context.Context, parameters UpsertStorageParameters) error +type UpsertStorageParameters struct { + IdempotencyKey string + Items []ProjectStorageInput +} +// ProjectStorageInput: {Type, Data, ExpiresAt, ReadPermission, WritePermission} + +func (s *StorageService) Delete(ctx context.Context, parameters DeleteStorageParameters) error +type DeleteStorageParameters struct { + IdempotencyKey string + Types []string // batch delete by item types +} +``` + +`ProjectStorage` (returned item): `{ID, Type, Data, Size int64, Version int64, ReadPermission, WritePermission, ExpiresAt, UpdatedAt}`. + +## Player storage (client.Player(id).Storage()) + +```go +func (h *PlayerStorageHandle) Get(ctx context.Context, storageType string) (*PlayerStorage, error) + +func (h *PlayerStorageHandle) Upsert(ctx context.Context, parameters UpsertPlayerStorageParameters) (*PlayerStorage, error) +type UpsertPlayerStorageParameters struct { + IdempotencyKey string + Type string + Data string + ExpiresAt string +} + +func (h *PlayerStorageHandle) Delete(ctx context.Context, parameters DeletePlayerStorageParameters) error +type DeletePlayerStorageParameters struct { + IdempotencyKey string + Type string +} +``` + +`PlayerStorage`: `{ID, PlayerID, Type, Data, ExpiresAt, UpdatedAt}`. + +The handle's Upsert/Delete send single-item batches to `PUT`/`DELETE /players/storage` — the same batch rules (idempotency key, all-or-nothing) apply. diff --git a/skills/rudder-go-sdk/reference/wallet.md b/skills/rudder-go-sdk/reference/wallet.md new file mode 100644 index 0000000..7599799 --- /dev/null +++ b/skills/rudder-go-sdk/reference/wallet.md @@ -0,0 +1,50 @@ +# Wallet + +Source: `wallet.go`, `player_wallet.go`. Types: `WalletBalance`, `WalletTransaction`, `WalletHistory`, `WalletAdjustment`. + +## Batch adjust + +```go +func (s *WalletService) Adjust(ctx context.Context, parameters AdjustWalletParameters) ([]WalletBalance, error) +type AdjustWalletParameters struct { + IdempotencyKey string + Items []WalletAdjustment +} +// WalletAdjustment: {PlayerID, CurrencyCode, Amount int64, Reason} +// Negative Amount subtracts. Returns updated balances in item order. +``` + +## History + +```go +func (s *WalletService) History(ctx context.Context, playerID string, parameters WalletHistoryParameters) (*WalletHistory, error) +type WalletHistoryParameters struct { + Currency *string // optional filter + Limit int + Cursor *string +} +// WalletHistory: {Entries []WalletTransaction, NextCursor string} +// WalletTransaction: {ID, WalletID, PlayerID, CurrencyCode, Amount int64, +// BalanceBefore int64, BalanceAfter int64, Reason, Source, +// Metadata json.RawMessage, CreatedAt} +``` + +## PlayerWalletHandle + +`client.Player(id).Wallet()`: + +```go +func (h *PlayerWalletHandle) Adjust(ctx context.Context, parameters AdjustPlayerWalletParameters) (*WalletBalance, error) +type AdjustPlayerWalletParameters struct { + IdempotencyKey string + CurrencyCode string + Amount int64 + Reason string +} + +func (h *PlayerWalletHandle) History(ctx context.Context, parameters WalletHistoryParameters) (*WalletHistory, error) +``` + +## WalletBalance fields + +`{ID, PlayerID, CurrencyCode, Balance int64, UpdatedAt}`.