portals-mcp 1.3.4 → 1.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -119,6 +119,67 @@ PrepareEffector({{Object}}, 'SetTransform', '{"scale":[2,2,2]}', 'grow');
|
|
|
119
119
|
|
|
120
120
|
**Targets:** `{{Object}}`, `{{Parent}}`, `{{Child}}` are unquoted template tokens. String IDs like `'760'` are quoted.
|
|
121
121
|
|
|
122
|
+
## Reading Leaderboard Data (async)
|
|
123
|
+
|
|
124
|
+
`GetLeaderboard(name, options?)` fetches a leaderboard's ranked rows from the backend and resolves to a **real JS array** of entries. Use it to build a custom leaderboard UI with `displayHtml(...)` — for example a styled overlay, a "your rank" callout, or a top-N mini board — instead of the native `OpenLeaderboard` panel. It reads leaderboard scores that were posted with `PostScoreToLeaderboard` (numeric) or recorded automatically by `StopTimer` (time-based); see `docs://logic/js-use-effector` for posting.
|
|
125
|
+
|
|
126
|
+
`GetLeaderboard` is **async** — it does an HTTP fetch — so you must `await` it. Top-level `await` is NOT supported in the sandbox, so wrap it in an async IIFE and `catch` errors (the Promise **rejects** on network/parse failure, so a bare `await` throws and can surface a visible error overlay).
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
(async function () {
|
|
130
|
+
try {
|
|
131
|
+
const rows = await GetLeaderboard('MyBoard'); // whole board, best-first
|
|
132
|
+
// rows: [{ rank, name, score, isLocalPlayer }, ...]
|
|
133
|
+
var html = '<ol>' + rows.map(function (r) {
|
|
134
|
+
return '<li>' + r.rank + '. ' + r.name + ' — ' + r.score +
|
|
135
|
+
(r.isLocalPlayer ? ' (you)' : '') + '</li>';
|
|
136
|
+
}).join('') + '</ol>';
|
|
137
|
+
displayHtml('<div id="lb">' + html + '</div>');
|
|
138
|
+
} catch (e) {
|
|
139
|
+
logFromJS('GetLeaderboard failed: ' + e);
|
|
140
|
+
}
|
|
141
|
+
})();
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Arguments
|
|
145
|
+
|
|
146
|
+
| Arg | Type | Required | Description |
|
|
147
|
+
|-----|------|----------|-------------|
|
|
148
|
+
| `name` | string | Yes | Leaderboard name — must match the leaderboard's `ln` field (the score/timer label it tracks). |
|
|
149
|
+
| `options` | object | No | `{ competitionId?, offset?, limit? }`. A bare string is also accepted and treated as `competitionId`. Omit for the whole board. |
|
|
150
|
+
|
|
151
|
+
Options fields:
|
|
152
|
+
- `competitionId` (string) — scope to a specific competition; omit/empty for the standard board.
|
|
153
|
+
- `offset` (int, default `0`) — skip this many rows from the top. Out-of-range offset yields an empty array.
|
|
154
|
+
- `limit` (int, default `-1`) — max rows to return; `-1` means everything from `offset` onward.
|
|
155
|
+
|
|
156
|
+
Paging is applied client-side today, so `rank` stays **absolute** across pages (see below).
|
|
157
|
+
|
|
158
|
+
### Returned entry shape
|
|
159
|
+
|
|
160
|
+
Each entry is `{ rank, name, score, isLocalPlayer }`:
|
|
161
|
+
|
|
162
|
+
| Field | Type | Description |
|
|
163
|
+
|-------|------|-------------|
|
|
164
|
+
| `rank` | int | Absolute 1-based position across the whole board — stays correct on any page. |
|
|
165
|
+
| `name` | string | Player's display name. |
|
|
166
|
+
| `score` | number | The posted score. **Time-based leaderboards return `score` in milliseconds** — divide/format as needed. |
|
|
167
|
+
| `isLocalPlayer` | bool | `true` for the row belonging to the current player (handy for highlighting "you"). |
|
|
168
|
+
|
|
169
|
+
### Paging example
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
(async function () {
|
|
173
|
+
const page2 = await GetLeaderboard('MyBoard', { offset: 10, limit: 10 }); // ranks 11–20
|
|
174
|
+
const comp = await GetLeaderboard('MyBoard', { competitionId: 'abc', limit: 5 });
|
|
175
|
+
})();
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
> **Gotchas.**
|
|
179
|
+
> - `GetLeaderboard` **reads** scores; it does not post them. Post with `UseEffector(..., 'PostScoreToLeaderboard', ...)` (numeric) or record automatically via `StopTimer` (time-based).
|
|
180
|
+
> - Object options do NOT marshal on the Jint fallback engine (exotic platforms) — name-only calls still work there. Prefer name-only when you don't need paging.
|
|
181
|
+
> - It's the async, list-shaped analogue of the synchronous `$N{...}` variable read: leaderboard data is remote and a list, so it can't come through the `$N{...}` substitution path.
|
|
182
|
+
|
|
122
183
|
## Critical Rules
|
|
123
184
|
|
|
124
185
|
### 1. Code formatting
|
|
@@ -485,6 +485,8 @@ UseEffector({{Object}}, 'AttachItemToPlayer', '{"bodyPart":"RightHand","localOff
|
|
|
485
485
|
#### PostScoreToLeaderboard 🟡
|
|
486
486
|
`'PostScoreToLeaderboard'` — `label` (string, default `"Score"`), `ow` (bool) (UE004 — JS call executed without error, but the leaderboard row did NOT visibly appear. Keep docs conservative; consider a custom `displayHtml` leaderboard built from `SelectPlayersParameters({{Players}}, ...)` until row display is confirmed.)
|
|
487
487
|
|
|
488
|
+
> **Reading leaderboard rows into JS:** to build a custom leaderboard UI, use the async `GetLeaderboard(name, options?)` bridge function — it resolves to `[{ rank, name, score, isLocalPlayer }, ...]` (time-based boards return `score` in ms). See `docs://logic/js-function-reference` → "Reading Leaderboard Data".
|
|
489
|
+
|
|
488
490
|
#### OpenLeaderboard ✅
|
|
489
491
|
`'OpenLeaderboard'` — `lb` (string), `ci` (string), `tb` (bool, default `true`) (UE004 — native leaderboard panel opens with the requested label as title.)
|
|
490
492
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "system:function-effector-js",
|
|
3
3
|
"name": "FunctionEffectorJS",
|
|
4
4
|
"description": "JavaScript runtime for game logic. More powerful than NCalc — supports loops, string manipulation, UseEffector calls, and multi-step procedural logic.",
|
|
5
|
-
"aliases": ["js", "javascript", "functioneffectorjs", "js function", "click function", "onclick js", "on click js", "js logic", "js script", "js code", "javascript function", "js onclick", "js variable", "reactive watcher", "S true", "R true", "activate on start"],
|
|
5
|
+
"aliases": ["js", "javascript", "functioneffectorjs", "js function", "click function", "onclick js", "on click js", "js logic", "js script", "js code", "javascript function", "js onclick", "js variable", "reactive watcher", "S true", "R true", "activate on start", "getleaderboard", "read leaderboard", "leaderboard data", "custom leaderboard", "leaderboard rows"],
|
|
6
6
|
"structure": {
|
|
7
7
|
"$type": "FunctionEffectorJS",
|
|
8
8
|
"V": "JavaScript code (single-line or multi-line both supported)",
|
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
"PrepareEffector": "PrepareEffector(target, 'EffectName', '{json params}', 'customId') — create/cache the temporary effector without activating it.",
|
|
22
22
|
"SetPlayerParameter": "SetPlayerParameter('paramName', value) — set a parameter on the local player."
|
|
23
23
|
},
|
|
24
|
+
"async_functions": {
|
|
25
|
+
"GetLeaderboard": "await GetLeaderboard('BoardName', { competitionId?, offset?, limit? }) — fetches leaderboard rows and resolves to a JS array [{ rank, name, score, isLocalPlayer }, ...]. rank is absolute 1-based (stable across pages). Time-based boards return score in milliseconds. MUST be awaited inside an async IIFE (no top-level await); rejects on network/parse error, so wrap in try/catch. Use to build custom displayHtml leaderboards. Options don't marshal on the Jint fallback engine — name-only calls still work. Reads scores posted via PostScoreToLeaderboard / recorded by StopTimer; it does not post."
|
|
26
|
+
},
|
|
24
27
|
"targets": {
|
|
25
28
|
"{{Object}}": "The item the JS is on (unquoted)",
|
|
26
29
|
"{{Parent}}": "Parent of the item (unquoted)",
|
|
@@ -47,7 +50,8 @@
|
|
|
47
50
|
"Missing $N{x} reads return null, not throws — coerce defaults (Number($N{x}) || 0)",
|
|
48
51
|
"Player-list token: {{Players}} (NOT NCalc [Players]); validator rejects the wrong form at upload",
|
|
49
52
|
"Sandbox: window/localStorage/fetch/requestAnimationFrame/getComputedStyle/AudioContext/Audio/Date.now() are NOT available — use globalThis and setInterval",
|
|
50
|
-
"RESERVED VARIABLE NAMES: 'position', 'rotation', 'scale' are engine builtins. $N{position} returns the player's world [x,y,z] array, not a custom variable. Use 'tile', 'spot', 'progress' etc. instead."
|
|
53
|
+
"RESERVED VARIABLE NAMES: 'position', 'rotation', 'scale' are engine builtins. $N{position} returns the player's world [x,y,z] array, not a custom variable. Use 'tile', 'spot', 'progress' etc. instead.",
|
|
54
|
+
"GetLeaderboard is async — must be awaited inside an async IIFE (top-level await is unsupported) and wrapped in try/catch (it rejects on network/parse error). Time-based boards return score in ms. It reads scores only; post with PostScoreToLeaderboard or record via StopTimer."
|
|
51
55
|
],
|
|
52
56
|
"common_patterns": {
|
|
53
57
|
"add_to_variable": "var h = Number($N{health}) || 0.0; SetVariable('health', h + 30.0, 0.0);",
|
|
@@ -57,7 +61,8 @@
|
|
|
57
61
|
"change_movement": "UseEffector({{Object}}, 'ChangeMovementProfile', '{\"mvmtProfile\":\"speedy\"}');",
|
|
58
62
|
"play_sound": "UseEffector({{Object}}, 'PlaySoundOnce', '{\"Url\":\"https://example.com/sound.mp3\",\"volume\":1.0}');",
|
|
59
63
|
"move_item": "UseEffector({{Object}}, 'MoveToSpot', '{\"transformState\":{\"position\":[0,5,0],\"rotation\":[0,0,0,1],\"scale\":[1,1,1],\"duration\":2.0},\"relative\":true}');",
|
|
60
|
-
"prepare_transform": "PrepareEffector({{Object}}, 'SetTransform', '{\"scale\":[2,2,2]}', 'grow');"
|
|
64
|
+
"prepare_transform": "PrepareEffector({{Object}}, 'SetTransform', '{\"scale\":[2,2,2]}', 'grow');",
|
|
65
|
+
"custom_leaderboard": "(async function(){ try { var rows = await GetLeaderboard('MyBoard'); var html = rows.map(function(r){ return r.rank + '. ' + r.name + ' — ' + r.score + (r.isLocalPlayer ? ' (you)' : ''); }).join('<br>'); displayHtml('<div id=\"lb\">' + html + '</div>'); } catch(e){ logFromJS('GetLeaderboard failed: ' + e); } })();"
|
|
61
66
|
},
|
|
62
67
|
"deep_dive_uri": "docs://logic/js-function-reference"
|
|
63
68
|
}
|