decentraland / decentraland/creator-hub
Support Epic Games Store client detection and launch as fallback for decentraland:// protocol
- Dominant language
- TypeScript
- Stars
- 7
- Forks
- 14
- Avg merge
- 2d 8h
- Merged PRs (30d)
- 44
Description
## Summary
When Decentraland is installed via the Epic Games Store (EGS), the Creator Hub cannot detect it or launch previews. The `decentraland://` protocol handler is registered by the **Decentraland Launcher** (launcher-rust), which is not bundled with the EGS distribution. This means:
1. The Creator Hub's preview button fails with _"Failed to start preview. Check your scene code for errors"_ — a misleading message since the actual cause is client detection failure
2. dApps and the Content Hub also fail to launch via deeplink for the same reason
3. The EGS-installed client is completely invisible to the Creator Hub
### Screenshots from thread
The error modal shows: `Command "reg query HKEY_CLASSES_ROOT\\decentraland /ve" exited with code 1` — confirming the `decentraland://` protocol isn't registered for EGS installs.
## Root Cause
In `packages/creator-hub/main/src/modules/bin.ts`, the `dclDeepLink()` function checks for the `HKEY_CLASSES_ROOT\decentraland` registry key (Windows) or uses `open` (macOS). This only works when the Decentraland Launcher has registered the protocol. Epic Games Store installs don't include the launcher, so the protocol is never registered.
Additionally, in `packages/creator-hub/renderer/src/modules/store/snackbar/slice.ts`, the `editorActions.runScene.rejected` handler **always** shows the "Failed to start preview" snackbar, even for `CLIENT_NOT_INSTALLED` errors — which are already handled by the `InstallClient` modal in the EditorPage. This means users see both a misleading error toast AND the install modal.
## Proposed Solution
### Priority Detection Flow
Extend client detection with the following priority order:
1. **`decentraland://` protocol** → If resolvable, use it (current behavior, keep as top priority)
2. **Epic Games Store fallback** → If `decentraland://` fails, check for EGS installation + Decentraland installed via EGS → launch via `com.epicgames.launcher://` protocol
3. **Fallback** → Show InstallClient modal (current fallback behavior)
### Implementation Plan
**Repos involved:**
- `decentraland/creator-hub` — all changes are in this repo
#### 1. New module: `packages/creator-hub/main/src/modules/epic.ts`
Create an Epic Games detection/launch module:
```typescript
// Detection functions:
async function isEpicLauncherInstalled(): Promise
// Windows: reg query "HKEY_CLASSES_ROOT\com.epicgames.launcher"
// macOS: existsSync('/Applications/Epic Games Launcher.app')
async function findDecentralandEpicManifest(): Promise
// Windows: scan C:\ProgramData\Epic\EpicGamesLauncher\Data\Manifests\*.item
// (or derive path from HKLM\SOFTWARE\WOW6432Node\EpicGames\EpicGamesLauncher AppDataPath)
// macOS: scan ~/Library/Application Support/Epic/EpicGamesLauncher/Data/Manifests/*.item
// Match by CatalogNamespace, verify bIsIncompleteInstall === false && InstallLocation exists
async function launchViaEpic(catalogNamespace: string): Promise
// Open com.epicgames.launcher://apps/{namespace}?action=launch&silent=true
```
**Key constant needed:** The Decentraland EGS `CatalogNamespace` — this must be obtained from the team that publishes to EGS and hardcoded as a constant.
#### 2. Modify `dclDeepLink()` in `packages/creator-hub/main/src/modules/bin.ts`
Add EGS fallback when native protocol check fails:
```typescript
export async function dclDeepLink(deepLink: string) {
try {
if (process.platform === 'win32') {
await exec('reg query "HKEY_CLASSES_ROOT\\decentraland"');
}
const command = process.platform === 'win32' ? 'start' : 'open';
await exec(`${command} decentraland://"${deepLink}"`);
} catch (e) {
// NEW: Try Epic Games Store fallback
const epicInstalled = await isEpicLauncherInstalled();
if (epicInstalled) {
const manifest = await findDecentralandEpicManifest();
if (manifest) {
await launchViaEpic(manifest.CatalogNamespace);
return; // Successfully launched via EGS
}
}
throw new ClientError('CLIENT_NOT_INSTALLED', CLIENT_NOT_INSTALLED_ERROR);
}
}
```
**Note:** When launching via EGS, the preview server URL (realm parameter) needs to be passed to the game. Investigate whether `com.epicgames.launcher://` supports passing launch arguments to the game, or whether the preview server URL can be communicated via a temporary file or environment variable.
#### 3. Fix the misleading snackbar in `packages/creator-hub/renderer/src/modules/store/snackbar/slice.ts`
Suppress the "Failed to start preview" snackbar when the error is `CLIENT_NOT_INSTALLED` (since the EditorPage already handles this with the InstallClient modal):
```typescript
.addCase(editorActions.runScene.rejected, (state, payload) => {
// Don't show snackbar for CLIENT_NOT_INSTALLED — handled by InstallClient modal
if (payload.error.name === 'CLIENT_NOT_INSTALLED') return;
const { requestId } = payload.meta;
state.notifications = state.notifications.filter($ => $.id !== requestId);
state.notifications.push(
createGenericNotification('error', t('snackbar.generic.preview_scene_failed'), {
requestId,
duration: 0,
description: payload.error.message,
}),
);
})
```
#### 4. Modify `start()` in `packages/creator-hub/main/src/modules/cli.ts`
The `start()` function has **two paths** that produce `CLIENT_NOT_INSTALLED`:
- **Path A**: `dclDeepLink()` call when re-launching an existing preview (line ~218) — automatically benefits from the `dclDeepLink` changes
- **Path B**: `sdk-commands` CLI stdout contains the error string (line ~237) — this is the first-launch case where the CLI itself detects no client
For Path B, consider adding the same EGS fallback before throwing:
```typescript
if (resultLogs.includes(CLIENT_NOT_INSTALLED_ERROR)) {
// Try EGS fallback before giving up
const epicFallbackSucceeded = await tryEpicFallback(/* preview server URL */);
if (!epicFallbackSucceeded) {
throw new ClientError('CLIENT_NOT_INSTALLED', CLIENT_NOT_INSTALLED_ERROR);
}
}
```
### Files to Create/Modify
| File | Action | Description |
|------|--------|-------------|
| `packages/creator-hub/main/src/modules/epic.ts` | Create | EGS detection & launch module |
| `packages/creator-hub/main/src/modules/bin.ts` | Modify | Add EGS fallback to `dclDeepLink()` |
| `packages/creator-hub/main/src/modules/cli.ts` | Modify | Add EGS fallback to `start()` Path B |
| `packages/creator-hub/renderer/src/modules/store/snackbar/slice.ts` | Modify | Suppress snackbar for `CLIENT_NOT_INSTALLED` |
| `packages/creator-hub/main/tests/modules/epic.spec.ts` | Create | Unit tests for EGS detection |
| `packages/creator-hub/main/tests/modules/bin.spec.ts` | Modify | Add tests for EGS fallback in `dclDeepLink` |
### Configuration
| Item | Description |
|------|-------------|
| `DECENTRALAND_EGS_CATALOG_NAMESPACE` | Hardcoded constant — the CatalogNamespace from EGS for Decentraland. Must be obtained from the team that publishes to EGS. |
### Open Questions
1. **What is Decentraland's `CatalogNamespace` on EGS?** This is needed to detect the game in EGS manifests and to construct the launch URL. Someone with EGS access can find it in `C:\ProgramData\Epic\EpicGamesLauncher\Data\Manifests\*.item` on a machine with DCL installed via EGS.
2. **Can launch arguments be passed via `com.epicgames.launcher://` protocol?** The preview needs to pass the realm URL to the Explorer. If EGS protocol doesn't support extra args, alternatives include: writing params to a temp file, using a localhost HTTP handshake, or launching the executable directly from `InstallLocation` in the manifest.
3. **Does the EGS-installed Explorer binary support the same command-line arguments as the launcher-distributed one?** This affects whether the preview server URL can be passed directly.
### Testing Strategy
- **Unit tests**: Mock `exec` and `fs` to test all detection paths (Windows/macOS, EGS installed/not, game installed/not, corrupt manifests)
- **Manual testing**: Install DCL via EGS, uninstall the native launcher, verify Creator Hub preview works via EGS fallback
- **Edge cases**: EGS installed but DCL not, DCL partially installed (`bIsIncompleteInstall`), both native launcher and EGS installed (native should take priority)
Requested by Gabriel Díaz via Slack
Contributor guide
Assessment
This issue has not been assessed yet.