diff --git a/game-example/hosted/HostedLeaderboardView.cs b/game-example/hosted/HostedLeaderboardView.cs index 35843cc..f634204 100644 --- a/game-example/hosted/HostedLeaderboardView.cs +++ b/game-example/hosted/HostedLeaderboardView.cs @@ -2,5 +2,5 @@ using Godot; using System; public partial class HostedLeaderboardView : VBoxContainer { - private const string CLIENT_KEY = "hvc_dfadce196d372aaaaaa0032657ec1bdb"; + private const string CLIENT_KEY = "hvc_7d973b0af7e07bed9019c8659d94f9f6"; } \ No newline at end of file diff --git a/schema/game.json b/schema/game.json index 2fd64c1..ba0d137 100644 --- a/schema/game.json +++ b/schema/game.json @@ -86,6 +86,11 @@ "description": "The human-readable name of the game, for display in UI.", "ref": "dev.tsunagite.translatable" }, + "nsid": { + "type": "string", + "description": "The namespaced ID of the game used for leaderboard and profile keys.", + "format": "nsid" + }, "logo": { "type": "blob", "description": "The logo of the game, for display in UI.", diff --git a/schema/getChart.json b/schema/getChart.json new file mode 100644 index 0000000..5470f9b --- /dev/null +++ b/schema/getChart.json @@ -0,0 +1,46 @@ +{ + "lexicon": 1, + "id": "dev.tsunagite.getChart", + "defs": { + "main": { + "type": "query", + "description": "Single-record chart fetch", + "parameters": { + "type": "params", + "properties": { + "uri": { + "type": "string", + "description": "The AT URI of the record to fetch. Must be present if `owner` and `key` are absent." + }, + "owner": { + "type": "string", + "description": "The handle or DID of the record owner. Must be present if `uri` is absent.", + "format": "at-identifier" + }, + "key": { + "type": "string", + "description": "The key of the record to fetch. Must be present if `uri` is absent.", + "format": "record-key" + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "dev.tsunagite.chart" + } + }, + "errors": [ + { + "name": "chartNotFound", + "description": "No chart matching the given URI or owner/key was found." + }, + { + "name": "invalidParams", + "description": "Parameters must include a URI or owner and key." + } + ] + } + } +} \ No newline at end of file diff --git a/schema/getDifficulty.json b/schema/getDifficulty.json new file mode 100644 index 0000000..ca182ca --- /dev/null +++ b/schema/getDifficulty.json @@ -0,0 +1,46 @@ +{ + "lexicon": 1, + "id": "dev.tsunagite.getDifficulty", + "defs": { + "main": { + "type": "query", + "description": "Single-record difficulty fetch", + "parameters": { + "type": "params", + "properties": { + "uri": { + "type": "string", + "description": "The AT URI of the record to fetch. Must be present if `owner` and `key` are absent." + }, + "owner": { + "type": "string", + "description": "The handle or DID of the record owner. Must be present if `uri` is absent.", + "format": "at-identifier" + }, + "key": { + "type": "string", + "description": "The key of the record to fetch. Must be present if `uri` is absent.", + "format": "record-key" + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "dev.tsunagite.difficulty" + } + }, + "errors": [ + { + "name": "difficultyNotFound", + "description": "No difficulty matching the given URI or owner/key was found." + }, + { + "name": "invalidParams", + "description": "Parameters must include a URI or owner and key." + } + ] + } + } +} \ No newline at end of file diff --git a/schema/getGame.json b/schema/getGame.json new file mode 100644 index 0000000..4b64066 --- /dev/null +++ b/schema/getGame.json @@ -0,0 +1,46 @@ +{ + "lexicon": 1, + "id": "dev.tsunagite.getGame", + "defs": { + "main": { + "type": "query", + "description": "Single-record game fetch", + "parameters": { + "type": "params", + "properties": { + "uri": { + "type": "string", + "description": "The AT URI of the record to fetch. Must be present if `owner` and `key` are absent." + }, + "owner": { + "type": "string", + "description": "The handle or DID of the record owner. Must be present if `uri` is absent.", + "format": "at-identifier" + }, + "key": { + "type": "string", + "description": "The key of the record to fetch. Must be present if `uri` is absent.", + "format": "record-key" + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "dev.tsunagite.game" + } + }, + "errors": [ + { + "name": "gameNotFound", + "description": "No game matching the given URI or owner/key was found." + }, + { + "name": "invalidParams", + "description": "Parameters must include a URI or owner and key." + } + ] + } + } +} \ No newline at end of file diff --git a/schema/getSong.json b/schema/getSong.json new file mode 100644 index 0000000..d6b65f3 --- /dev/null +++ b/schema/getSong.json @@ -0,0 +1,46 @@ +{ + "lexicon": 1, + "id": "dev.tsunagite.getSong", + "defs": { + "main": { + "type": "query", + "description": "Single-record song fetch", + "parameters": { + "type": "params", + "properties": { + "uri": { + "type": "string", + "description": "The AT URI of the record to fetch. Must be present if `owner` and `key` are absent." + }, + "owner": { + "type": "string", + "description": "The handle or DID of the record owner. Must be present if `uri` is absent.", + "format": "at-identifier" + }, + "key": { + "type": "string", + "description": "The key of the record to fetch. Must be present if `uri` is absent.", + "format": "record-key" + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "ref", + "ref": "dev.tsunagite.song" + } + }, + "errors": [ + { + "name": "songNotFound", + "description": "No song matching the given URI or owner/key was found." + }, + { + "name": "invalidParams", + "description": "Parameters must include a URI or owner and key." + } + ] + } + } +} \ No newline at end of file diff --git a/schema/listCharts.json b/schema/listCharts.json new file mode 100644 index 0000000..170eaa9 --- /dev/null +++ b/schema/listCharts.json @@ -0,0 +1,61 @@ +{ + "lexicon": 1, + "id": "dev.tsunagite.listCharts", + "defs": { + "main": { + "type": "query", + "description": "Batch chart fetch", + "parameters": { + "type": "params", + "properties": { + "game": { + "type": "string", + "description": "The game to find chart records for.", + "format": "at-uri" + }, + "song": { + "type": "string", + "description": "The song to find chart records for.", + "format": "at-uri" + }, + "owner": { + "type": "string", + "description": "The handle or DID of the account to filter results to.", + "format": "at-identifier" + }, + "limit": { + "type": "integer", + "description": "How many records to return.", + "maximum": 100, + "default": 20 + }, + "cursor": { + "type": "string", + "description": "Opaque string - receive from a previous request as the `cursor` value" + } + }, + "required": [ "game", "song" ] + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "properties": { + "charts": { + "type": "array", + "items": { + "type": "ref", + "ref": "dev.tsunagite.chart" + } + }, + "cursor": { + "type": "string", + "description": "Opaque string - pass back in a future request as the `cursor` value" + } + }, + "required": [ "charts" ] + } + } + } + } +} \ No newline at end of file diff --git a/schema/listGames.json b/schema/listGames.json new file mode 100644 index 0000000..a1cd686 --- /dev/null +++ b/schema/listGames.json @@ -0,0 +1,50 @@ +{ + "lexicon": 1, + "id": "dev.tsunagite.listGames", + "defs": { + "main": { + "type": "query", + "description": "Batch game fetch", + "parameters": { + "type": "params", + "properties": { + "player": { + "type": "string", + "description": "The handle or DID of the player to search for profile records on. Will only return games the player has a profile for.", + "format": "at-identifier" + }, + "limit": { + "type": "integer", + "description": "How many records to return.", + "maximum": 100, + "default": 20 + }, + "cursor": { + "type": "string", + "description": "Opaque string - receive from a previous request as the `cursor` value" + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "properties": { + "games": { + "type": "array", + "items": { + "type": "ref", + "ref": "dev.tsunagite.game" + } + }, + "cursor": { + "type": "string", + "description": "Opaque string - pass back in a future request as the `cursor` value" + } + }, + "required": [ "games" ] + } + } + } + } +} \ No newline at end of file diff --git a/schema/listSongs.json b/schema/listSongs.json new file mode 100644 index 0000000..b5951bd --- /dev/null +++ b/schema/listSongs.json @@ -0,0 +1,56 @@ +{ + "lexicon": 1, + "id": "dev.tsunagite.listSongs", + "defs": { + "main": { + "type": "query", + "description": "Batch song fetch", + "parameters": { + "type": "params", + "properties": { + "game": { + "type": "string", + "description": "The game to find song records for.", + "format": "at-uri" + }, + "owner": { + "type": "string", + "description": "The handle or DID of the account to filter results to.", + "format": "at-identifier" + }, + "limit": { + "type": "integer", + "description": "How many records to return.", + "maximum": 100, + "default": 20 + }, + "cursor": { + "type": "string", + "description": "Opaque string - receive from a previous request as the `cursor` value" + } + }, + "required": [ "game" ] + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "properties": { + "songs": { + "type": "array", + "items": { + "type": "ref", + "ref": "dev.tsunagite.song" + } + }, + "cursor": { + "type": "string", + "description": "Opaque string - pass back in a future request as the `cursor` value" + } + }, + "required": [ "songs" ] + } + } + } + } +} \ No newline at end of file diff --git a/schema/profile.json b/schema/profile.json index c3874d0..c42fd67 100644 --- a/schema/profile.json +++ b/schema/profile.json @@ -10,6 +10,11 @@ "record": { "type": "object", "properties": { + "game": { + "type": "string", + "description": "The URI of the game this profile is for.", + "format": "at-uri" + }, "displayName": { "type": "string", "description": "The display name for the player on leaderboards.", @@ -18,7 +23,7 @@ "maxGraphemes": 50 } }, - "required": ["displayName"] + "required": ["game", "displayName"] } } } diff --git a/scripts/getChart.lua b/scripts/getChart.lua new file mode 100644 index 0000000..39d4000 --- /dev/null +++ b/scripts/getChart.lua @@ -0,0 +1,21 @@ +collection = "dev.tsunagite.chart" + +function handle() + local uri = params.uri + + if not uri and params.owner and params.key then + local resp = http.get("https://slingshot.microcosm.blue/xrpc/blue.microcosm.identity.resolveMiniDoc?identifier=" .. params.owner) + local did = json.decode(resp.body).did + uri = "at://" .. did .. "/" .. collection .. "/" .. params.key + end + + if uri then + local record = db.get(params.uri) + if not record then + error("chartNotFound") + end + return { record = record } + else + error("invalidParams") + end +end diff --git a/scripts/getDifficulty.lua b/scripts/getDifficulty.lua new file mode 100644 index 0000000..3a72595 --- /dev/null +++ b/scripts/getDifficulty.lua @@ -0,0 +1,21 @@ +collection = "dev.tsunagite.difficulty" + +function handle() + local uri = params.uri + + if not uri and params.owner and params.key then + local resp = http.get("https://slingshot.microcosm.blue/xrpc/blue.microcosm.identity.resolveMiniDoc?identifier=" .. params.owner) + local did = json.decode(resp.body).did + uri = "at://" .. did .. "/" .. collection .. "/" .. params.key + end + + if uri then + local record = db.get(params.uri) + if not record then + error("difficultyNotFound") + end + return { record = record } + else + error("invalidParams") + end +end diff --git a/scripts/getGame.lua b/scripts/getGame.lua new file mode 100644 index 0000000..dfa4ed0 --- /dev/null +++ b/scripts/getGame.lua @@ -0,0 +1,21 @@ +collection = "dev.tsunagite.game" + +function handle() + local uri = params.uri + + if not uri and params.owner and params.key then + local resp = http.get("https://slingshot.microcosm.blue/xrpc/blue.microcosm.identity.resolveMiniDoc?identifier=" .. params.owner) + local did = json.decode(resp.body).did + uri = "at://" .. did .. "/" .. collection .. "/" .. params.key + end + + if uri then + local record = db.get(params.uri) + if not record then + error("gameNotFound") + end + return { record = record } + else + error("invalidParams") + end +end diff --git a/scripts/getSong.lua b/scripts/getSong.lua new file mode 100644 index 0000000..4dd85c4 --- /dev/null +++ b/scripts/getSong.lua @@ -0,0 +1,21 @@ +collection = "dev.tsunagite.song" + +function handle() + local uri = params.uri + + if not uri and params.owner and params.key then + local resp = http.get("https://slingshot.microcosm.blue/xrpc/blue.microcosm.identity.resolveMiniDoc?identifier=" .. params.owner) + local did = json.decode(resp.body).did + uri = "at://" .. did .. "/" .. collection .. "/" .. params.key + end + + if uri then + local record = db.get(params.uri) + if not record then + error("songNotFound") + end + return { record = record } + else + error("invalidParams") + end +end diff --git a/scripts/listCharts.lua b/scripts/listCharts.lua new file mode 100644 index 0000000..59426b3 --- /dev/null +++ b/scripts/listCharts.lua @@ -0,0 +1,25 @@ +collection = "dev.tsunagite.chart" + +function handle() + local did = nil + if params.owner then + local resp = http.get("https://slingshot.microcosm.blue/xrpc/blue.microcosm.identity.resolveMiniDoc?identifier=" .. params.owner) + did = json.decode(resp.body).did + end + + local unfilteredCharts = db.backlinks({ + collection = collection, + uri = params.song, + did = did, + limit = params.limit, + cursor = params.cursor, + }) + + local filteredCharts = {} + for _, v in unfilteredCharts.records do + if v.game == params.game then + table.insert(filteredCharts, v) + end + end + return {["charts"] = filteredCharts, ["cursor"] = unfilteredCharts.cursor} +end diff --git a/scripts/listGames.lua b/scripts/listGames.lua new file mode 100644 index 0000000..21c3f2d --- /dev/null +++ b/scripts/listGames.lua @@ -0,0 +1,27 @@ +collection = "dev.tsunagite.game" + +function handle() + if params.player then + local resp = http.get("https://slingshot.microcosm.blue/xrpc/blue.microcosm.identity.resolveMiniDoc?identifier=" .. params.player) + local did = json.decode(resp.body).did + local profiles = db.query({ + collection = "dev.tsunagite.profile", + did = did, + limit = params.limit, + cursor = params.cursor + }) + local games = {} + for _, v in profiles.records do + table.insert(games, db.get(v.game)) + end + return {["games"] = games, ["cursor"] = profiles.cursor} + end + + local results = db.query({ + collection = collection, + limit = params.limit, + cursor = params.cursor, + }) + results.games = results.records + results.records = nil +end diff --git a/scripts/listSongs.lua b/scripts/listSongs.lua new file mode 100644 index 0000000..5741103 --- /dev/null +++ b/scripts/listSongs.lua @@ -0,0 +1,20 @@ +collection = "dev.tsunagite.song" + +function handle() + local did = nil + if params.owner then + local resp = http.get("https://slingshot.microcosm.blue/xrpc/blue.microcosm.identity.resolveMiniDoc?identifier=" .. params.owner) + did = json.decode(resp.body).did + end + + local results = db.backlinks({ + collection = collection, + uri = params.game, + did = did, + limit = params.limit, + cursor = params.cursor, + }) + results.songs = results.records + results.records = nil + return results +end diff --git a/scripts/score.lua b/scripts/score.lua index 97cb7f3..87f5d8e 100644 --- a/scripts/score.lua +++ b/scripts/score.lua @@ -37,7 +37,7 @@ function handle() valueOrder = valueOrder .. e.index .. ", " end end - log("Attempted to find enum value " .. v.value .. " among enum set :" .. foundValues) + log("Attempted to find enum value " .. v.value .. " among enum set: " .. foundValues) elseif v["$type"] == "dev.tsunagite.score#text" then -- put strings in quotes valueOrder = valueOrder .. "'" .. v.value .. "', "