Commit 346cba0
authored
feat: support scene named spawn points (#9369)
# Pull Request Description
## What does this PR change?
Adds **named spawn point selection** to the teleport pipeline (part of #6848). Scenes already declare named spawn points in `scene.json`; until now the explorer always ran the default selection (default-flagged → closest). This PR lets a teleport request a spawn point **by name**, end-to-end.
**Core mechanism** (single chokepoint, applies to every entry point below):
- `TeleportUtils.PickSpawnPoint` gains `string? spawnPointName`. When set and matched (**case-insensitive**), that point is used directly, bypassing the default/closest logic.
- Unmatched name → **soft fallback** to the default selection + `ReportHub` warning (`Spawn point '<name>' not found in scene, falling back to default spawn point selection`). The teleport itself never fails because of a bad name.
- Duplicate names → first in declaration order wins (+ warning).
- Everything *after* selection is unchanged: coordinate-range points (`"x": [min, max]`) still scatter arriving players randomly within the range; `cameraTarget` still applies.
**Entry points wired in this PR:**
| Entry point | Syntax |
|---|---|
| `/goto` (Genesis) | `/goto x,y/spawnPoint` |
| `/goto` (worlds) | `/goto world/spawnPoint`, `/goto world/x,y/spawnPoint` |
| `/goto-local` | `/goto-local x,y/spawnPoint`, `/goto-local spawnPoint` (current scene) |
| Deep link (cold start + runtime) | `decentraland://?realm=...&spawnpoint=spawnPoint` |
| Launch parameter (CLI / CI) | `Explorer.exe --realm ... --spawnpoint spawnPoint` |
**Also included:**
- `/goto` parsing extracted into pure `ChatParamUtils.ParseGotoTarget` with full grammar-table unit tests (`ChatParamUtilsShould`); `GoToChatCommand` is now a thin dispatcher.
- The chat command handler now trims trailing whitespace, so `/goto 0,0 ` (trailing space) no longer fails validation — for all commands.
- **Already-in-realm named teleport**: `/goto world/name` (and the runtime deep link with `spawnpoint` but no `position`) used while already inside the target realm now teleports to the named spawn point of the scene the player stands in, instead of stopping at `🟡 You are already in ...`. Without a name the old message is kept.
- Unit tests: `TeleportUtilsShould` (named selection core), `ChatParamUtilsShould` (grammar), `RealmLaunchSettingsShould` (deep link / CLI arg parsing), `ChatTeleporterShould` (already-in-realm branching).
**Out of scope:** SDK entry points (`teleportTo` / `changeRealm` with `spawnPoint`) — they require protocol + js-sdk-toolchain changes first; nothing for them in this repo yet.
## Test Instructions
**Steps (standard run)**:
```bash
metaforge explorer run 9369
```
### Prerequisites
- A world with named spawn points. **Test world: `flutterecho.dcl.eth`** (4x4 parcels, spotlight cubes in each corner, physics playground near the origin corner), deployed with:
| Name | Position | What it's for |
|---|---|---|
| `spawn` (default) | 2, 0, 2 | default landing (regression) |
| `center` | 32, 0, 32 | scene center |
| `physics` | 15, 0, 12 | inside the physics playground |
| `far-light` | 60, 0, 60 | far corner; has `cameraTarget` looking back at scene center |
| `mid-corner` | 4, 0, 60 | another corner |
| `scatter` | x∈[26,38], z∈[26,38] | coordinate-range scatter |
| `twin` (declared twice) | 10, 0, 50 and 50, 0, 10 | duplicate-name resolution |
- The "not found" / "duplicate" warnings go to **logs, not chat**:
```bash
metaforge explorer logs tail --filter "spawn point"
```
### Test Cases
#### 1. `/goto` — world forms
1. From Genesis: `/goto flutterecho/center` → realm change, land at scene center (32, 32). No coordinates given: the world manifest picks the parcel, the name picks the point.
2. From Genesis: `/goto flutterecho.dcl.eth/0,0/far-light` → land at the far corner **and** the camera faces back toward scene center (`cameraTarget`).
3. Already inside the world: `/goto flutterecho/0,0/physics` → teleport within the world to the physics playground.
4. Already inside the world: `/goto flutterecho/center` → **in-world teleport** to the scene center, no realm change (the name targets a spawn point of the scene you are standing in).
5. Already inside the world: `/goto flutterecho` (no name) → `🟡 You are already in...`, no teleport (pre-existing behavior, unchanged).
6. Regression: `/goto flutterecho` from Genesis → land at the default `spawn` (2, 2), exactly as before.
#### 2. `/goto` — Genesis form
1. `/goto x,y/name` against any Genesis scene that declares named spawn points → lands at the named point.
2. Fallback path works on **any** parcel: `/goto 0,0/nosuch` → normal teleport to 0,0 + warning in logs.
3. Regression: `/goto 0,0`, `/goto random`, `/goto crowd` behave exactly as before.
4. From inside a world: `/goto 0,0/name` → realm change to Genesis carries the name (it is not dropped).
#### 3. `/goto-local` (inside the world / any realm)
1. `/goto-local 0,0/center` → parcel + name form.
2. `/goto-local physics` → **bare-name form**: teleports to the named point of the scene you are currently standing in.
3. `/goto-local far-light` → also verify camera orientation.
4. Reserved words are not names: `/goto-local random` / `/goto-local crowd` → `🔴 Invalid parameters` + usage.
5. `/goto-local nosuch` → teleport to your current parcel, default spawn selection + warning in logs (soft fallback).
6. Regression: `/goto-local 0,0` unchanged.
#### 4. Deep link — cold start (client closed)
```
decentraland://?realm=flutterecho.dcl.eth&dclenv=org&spawnpoint=physics
```
1. Client starts and the **initial** landing is the physics playground (not the default `spawn`).
2. With a position: `decentraland://?realm=flutterecho.dcl.eth&dclenv=org&position=0,0&spawnpoint=far-light`.
3. Bad name: `...&spawnpoint=nosuch` → lands at default spawn + warning in logs.
#### 5. Deep link — runtime (client already running)
1. While standing in another realm (e.g. Genesis), open the same deep link from the OS → realm change, land at the named point.
2. Already **inside** the world, deep link with `position` + `spawnpoint` → in-world teleport to the named point.
3. Already **inside** the world, deep link with `spawnpoint` but **without** `position` (`decentraland://?realm=flutterecho.dcl.eth&dclenv=org&spawnpoint=far-light`) → in-world teleport to the named spawn point of the current scene (previously this silently did nothing).
4. Already inside the world, deep link with `realm` only (no `position`, no `spawnpoint`) → nothing happens (pre-existing "already in realm" behavior, unchanged).
#### 6. Launch parameters (cmd / CI)
```powershell
Explorer.exe --realm flutterecho.dcl.eth --dclenv org --spawnpoint physics
```
1. Same expectations as the cold-start deep link (CLI args and deep link query params share one parser).
2. Genesis variant: `--realm <genesis peer> --position 0,0 --spawnpoint <name>`.
#### 7. Cross-cutting edge cases (any entry point)
1. **Case-insensitivity**: `/goto-local PHYSICS`, `/goto flutterecho/Far-Light`, `...&spawnpoint=CENTER` → all match.
2. **Range scatter**: `/goto-local scatter` several times in a row → each landing is a *different* spot within the 26..38 square (players must not stack on one point).
3. **Duplicate names**: `/goto-local twin` repeatedly → always lands at (10, 50) (first declared), never (50, 10); warning about duplicates in logs.
4. **Unknown name**: never breaks the teleport — always default selection + `Spawn point '<name>' not found...` in logs. Nothing red in chat.
5. **Trailing whitespace**: `/goto flutterecho/center ` (trailing spaces) → works (previously any trailing space failed validation of any command).
6. **Grammar junk** stays world-names (unchanged behavior): `/goto myworld//lobby`, `/goto myworld/0,0/` → treated as a (non-existent) world → `🔴 ... doesn't exist or not reachable`.
7. **`/goto flutterecho/random`** → `random` here is a spawn point *name* (soft fallback if the scene has none), not the random-parcel flag.
8. **Initial world entry without a name** (jump-in, navmap, plain deep link) → default spawn, no behavior change.1 parent c821324 commit 346cba0
49 files changed
Lines changed: 1093 additions & 253 deletions
File tree
- .github/workflows
- Explorer/Assets/DCL
- ApplicationGuards/ApplicationMinimumSpecsGuard/Tests
- AuthenticationScreenFlow/Tests
- AvatarAnimation/Editor
- Character/CharacterMotion
- Components
- Systems
- Chat
- Commands
- Tests
- MessageBus
- Infrastructure
- Global
- AppArgs
- Dynamic
- Tests/EditMode
- SceneLifeCycle
- Realm
- Tests
- RuntimeDeepLink
- UserInAppInitializationFlow/StartupOperations
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
70 | 70 | | |
71 | 71 | | |
72 | 72 | | |
| 73 | + | |
73 | 74 | | |
74 | | - | |
| 75 | + | |
75 | 76 | | |
76 | 77 | | |
77 | 78 | | |
| |||
85 | 86 | | |
86 | 87 | | |
87 | 88 | | |
88 | | - | |
| 89 | + | |
89 | 90 | | |
90 | 91 | | |
91 | | - | |
92 | | - | |
93 | 92 | | |
94 | | - | |
| 93 | + | |
95 | 94 | | |
96 | | - | |
97 | | - | |
98 | | - | |
| 95 | + | |
| 96 | + | |
99 | 97 | | |
100 | 98 | | |
101 | 99 | | |
102 | | - | |
| 100 | + | |
103 | 101 | | |
| 102 | + | |
104 | 103 | | |
105 | 104 | | |
106 | 105 | | |
107 | | - | |
| 106 | + | |
108 | 107 | | |
109 | 108 | | |
110 | 109 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
444 | 444 | | |
445 | 445 | | |
446 | 446 | | |
| 447 | + | |
447 | 448 | | |
448 | 449 | | |
449 | 450 | | |
| |||
455 | 456 | | |
456 | 457 | | |
457 | 458 | | |
458 | | - | |
| 459 | + | |
459 | 460 | | |
460 | 461 | | |
461 | 462 | | |
| |||
691 | 692 | | |
692 | 693 | | |
693 | 694 | | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
694 | 754 | | |
695 | 755 | | |
696 | 756 | | |
| |||
Lines changed: 4 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
36 | | - | |
| 36 | + | |
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
| |||
194 | 194 | | |
195 | 195 | | |
196 | 196 | | |
197 | | - | |
| 197 | + | |
198 | 198 | | |
199 | 199 | | |
200 | | - | |
| 200 | + | |
201 | 201 | | |
202 | 202 | | |
203 | | - | |
| 203 | + | |
204 | 204 | | |
205 | 205 | | |
206 | 206 | | |
Lines changed: 1 addition & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
4 | 3 | | |
5 | 4 | | |
6 | 5 | | |
| |||
20 | 19 | | |
21 | 20 | | |
22 | 21 | | |
23 | | - | |
24 | 22 | | |
25 | | - | |
| 23 | + | |
26 | 24 | | |
27 | 25 | | |
28 | 26 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
2 | | - | |
| 1 | + | |
3 | 2 | | |
4 | 3 | | |
5 | | - | |
| 4 | + | |
6 | 5 | | |
7 | 6 | | |
8 | 7 | | |
| |||
Lines changed: 3 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
2 | | - | |
| 1 | + | |
3 | 2 | | |
4 | 3 | | |
5 | 4 | | |
6 | 5 | | |
7 | | - | |
| 6 | + | |
8 | 7 | | |
9 | 8 | | |
10 | 9 | | |
11 | 10 | | |
12 | 11 | | |
13 | 12 | | |
14 | 13 | | |
15 | | - | |
| 14 | + | |
16 | 15 | | |
17 | 16 | | |
18 | 17 | | |
| |||
Lines changed: 2 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
2 | | - | |
| 1 | + | |
3 | 2 | | |
4 | 3 | | |
5 | 4 | | |
6 | 5 | | |
7 | 6 | | |
8 | | - | |
| 7 | + | |
9 | 8 | | |
10 | 9 | | |
11 | 10 | | |
| |||
Lines changed: 3 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
2 | | - | |
| 1 | + | |
3 | 2 | | |
4 | 3 | | |
5 | 4 | | |
6 | | - | |
7 | 5 | | |
| 6 | + | |
8 | 7 | | |
9 | | - | |
| 8 | + | |
10 | 9 | | |
11 | 10 | | |
12 | 11 | | |
| |||
Lines changed: 2 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
2 | | - | |
| 1 | + | |
3 | 2 | | |
4 | 3 | | |
5 | 4 | | |
6 | 5 | | |
7 | | - | |
| 6 | + | |
8 | 7 | | |
9 | 8 | | |
10 | 9 | | |
| |||
0 commit comments