Advanced Usage Scenarios of PC SDK
1 Customize Game Window
When creating the host createGameHandle , you can configure the size, position, and style of the game window through the options of RuntimeGameHandle.
const { TJHostHandle, MultiGameLauncher } = require('webglhost-runtime-pc');
// Initialize host
const hostHandle = await new Promise((resolve, reject) => {
TJHostHandle.initialize(SDK_KEY, SDK_SECRET, (handle, error) => {
if (error) reject(error);
else resolve(handle);
});
});
// Create game handle with custom window options
const windowOptions = {
width: 1280,
height: 720,
x: 100,
y: 100,
show: false, // Don't show window immediately
frame: true, // Show window frame
resizable: true
};
const gameHandle = await MultiGameLauncher.createGameHandle(
hostHandle,
gameId,
{ windowOptions }
);
// Set game start options
const options = {};
options[TJConstants.LAUNCH_KEY] = 'launch-key';
options[TJConstants.USER_ID] = 'user-123';
await gameHandle.setGameStartOptions(options);
await gameHandle.start();
// Play game when ready
gameHandle.setOnFirstFrameRenderedListener(async () => {
await gameHandle.play();
});
2 Supports transparent game background
The parameter ENABLE_TRANSPARENT_MODE can be set to true when setGameStartOptions() is called, enabling the game window background to support transparency:
const { TJConstants } = require('webglhost-runtime-pc');
const options = {};
options[TJConstants.LAUNCH_KEY] = 'launch-key';
options[TJConstants.USER_ID] = 'user-123';
options[TJConstants.ENABLE_TRANSPARENT_MODE] = true;
await gameHandle.setGameStartOptions(options);
await gameHandle.start();
await gameHandle.play();
Note: Transparent mode may affect performance. It is recommended to use it only when necessary.
3 Custom Capsule Button
Custom capsule buttons can be implemented by hiding the default capsule buttons and implementing custom UI.
When setGameStartOptions() is called, setting the parameter SHOW_MENU_LAYOUT to false can hide the default capsule button:
const { TJConstants } = require('webglhost-runtime-pc');
const options = {};
options[TJConstants.LAUNCH_KEY] = 'launch-key';
options[TJConstants.USER_ID] = 'user-123';
options[TJConstants.SHOW_MENU_LAYOUT] = false; // Hide default menu button
await gameHandle.setGameStartOptions(options);
await gameHandle.start();
// Add custom menu button UI (using Electron BrowserWindow overlay)
const { BrowserWindow } = require('electron');
const overlayWindow = new BrowserWindow({
parent: gameHandle.getGameWindow(),
modal: false,
show: true,
frame: false,
transparent: true,
width: 200,
height: 60,
x: 10,
y: 10
});
overlayWindow.loadFile('custom-menu-overlay.html');
// Handle menu click in overlay window
// In custom-menu-overlay.html:
// <button onclick="window.parentNotify('menu')">Menu</button>
4 Native Code and JS Mutual Invocation
Refer to CustomCommand Interface
5 Recently Played List
The Host SDK provides interfaces related to recent play, which are used to monitor game launches and query the current user's play list.
const { TJHostHandle } = require('webglhost-runtime-pc');
class RecentlyGameListener {
updateGame(game) {
console.log('Game updated:', game);
// Update UI with recently played game
// game is a HistoryModel instance with properties:
// - id, bundleId, name, gameType, iconUrl, tags
// - briefIntro, versionId, userId, launchKey
// - dev, lastLaunchTime
}
}
// Initialize host with recently played game listener
TJHostHandle.initialize(SDK_KEY, SDK_SECRET, (hostHandle, error) => {
if (error) {
console.error('Initialization failed:', error);
return;
}
// Set recently played game listener
const listener = new RecentlyGameListener();
hostHandle.setRecentlyGameListener(listener);
// Get user's game history
const userId = 'user-123';
const playedGames = hostHandle.getUserHistory(userId);
console.log('User played games:', playedGames);
});
5.1 PlayedGameModel
| Field | Data Type | Description |
|---|---|---|
id | String | Game ID |
userId | String | User Id |
bundleId | String | Game Package ID |
versionId | String | Game Version ID |
name | String | Game Name |
gameType | String | Game Type |
iconUrl | String | Game Icon URL |
tags | Array<String> | Game Tags |
briefIntro | String | Game Introduction |
launchKey | String | Game Launch Options |
dev | Boolean | Game Development Kit |
lastLaunchTime | Number | Last game launch time (Unix timestamp) |
5.2 Explanation of the dev Field in the Game Development Kit
The MiniHost Console provides local development testing functionality in game package management. As a temporary experience version, the download address of the development version game package is temporary.
Since the game will be recorded in the recently played list after it is launched, and the download address of the development package is temporary, when the development package is launched from the recently played list, the game will only launch the locally cached game package and will not re-download it.
When the game version is updated, the new version of the game package needs to be reloaded, and at this time the game list will be updated launchKey replaces the original version as the next startup parameter.
6 Multi-window Game Management
Using MultiGameLauncher allows you to manage multiple game instances simultaneously, with each instance running in a separate window:
const { MultiGameLauncher, TJConstants } = require('webglhost-runtime-pc');
// Configure maximum concurrent games
MultiGameLauncher.configMaxGame(3);
// Create first game instance
const gameHandle1 = await MultiGameLauncher.createGameHandle(
hostHandle,
'game-id-1',
{}
);
// Create second game instance
const gameHandle2 = await MultiGameLauncher.createGameHandle(
hostHandle,
'game-id-2',
{}
);
// Start both games
const options1 = {
[TJConstants.LAUNCH_KEY]: 'launch-key-1',
[TJConstants.USER_ID]: 'user-123'
};
const options2 = {
[TJConstants.LAUNCH_KEY]: 'launch-key-2',
[TJConstants.USER_ID]: 'user-123'
};
await gameHandle1.setGameStartOptions(options1);
await gameHandle1.start();
await gameHandle1.play();
await gameHandle2.setGameStartOptions(options2);
await gameHandle2.start();
await gameHandle2.play();
// Pause/resume games
MultiGameLauncher.pause('game-id-1');
MultiGameLauncher.resume('game-id-1');
// Stop specific game
MultiGameLauncher.stop('game-id-1');
// Destroy specific game instance
MultiGameLauncher.destroy('game-id-2');
7 Custom Splash Screen
The Splash screen when the game starts can be customized:
const { TJConstants } = require('webglhost-runtime-pc');
const options = {};
options[TJConstants.LAUNCH_KEY] = 'launch-key';
options[TJConstants.USER_ID] = 'user-123';
options[TJConstants.SPLASH_ICON_URL] = 'https://example.com/game-icon.png';
options[TJConstants.SPLASH_GAME_TITLE] = 'My Awesome Game';
await gameHandle.setGameStartOptions(options);
await gameHandle.start();
8 Debugging and Development Tools
8.1 Enable vConsole
const { TJConstants } = require('webglhost-runtime-pc');
const options = {};
options[TJConstants.LAUNCH_KEY] = 'launch-key';
options[TJConstants.USER_ID] = 'user-123';
options[TJConstants.ENABLE_VCONSOLE] = true; // Enable vConsole for debugging
await gameHandle.setGameStartOptions(options);
await gameHandle.start();
8.2 Enable Chrome DevTools Inspector
const { TJConstants } = require('webglhost-runtime-pc');
const options = {};
options[TJConstants.LAUNCH_KEY] = 'launch-key';
options[TJConstants.USER_ID] = 'user-123';
options[TJConstants.ENABLE_INSPECTOR] = true; // Enable inspector
options[TJConstants.INSPECTOR_WAIT_FOR_INSPECT] = true; // Wait for inspector to attach
await gameHandle.setGameStartOptions(options);
await gameHandle.start();
8.3 Runtime Debugging Tool
// Show debug menu
gameHandle.showDebugMenu();
// Toggle debug mode
gameHandle.toggleDebugMode();
// Get debug mode status
const isDebugEnabled = gameHandle.getDebugModeStatus();
// Force garbage collection
gameHandle.invokeForceGC();
// Export logs
gameHandle.exportLogManagerLog();
9 Domain Allowlist Configuration
For security reasons, you can configure an allowlist of domains that the game is permitted to access:
const { TJConstants } = require('webglhost-runtime-pc');
const options = {};
options[TJConstants.LAUNCH_KEY] = 'launch-key';
options[TJConstants.USER_ID] = 'user-123';
// Enable domain whitelist
options[TJConstants.ENABLE_DOMAIN_WHITELIST] = true;
// Configure whitelisted domains
options[TJConstants.DOMAIN_WHITELIST_REQUEST] = [
'https://api.example.com',
'https://cdn.example.com'
];
options[TJConstants.DOMAIN_WHITELIST_SOCKET] = [
'wss://socket.example.com'
];
options[TJConstants.DOMAIN_WHITELIST_UPLOAD_FILE] = [
'https://upload.example.com'
];
options[TJConstants.DOMAIN_WHITELIST_DOWNLOAD_FILE] = [
'https://download.example.com'
];
await gameHandle.setGameStartOptions(options);
await gameHandle.start();
10 Game State Monitoring
Listen for various state changes in the game:
const { RuntimeState } = require('webglhost-runtime-pc');
// Listen for first frame rendered
gameHandle.setOnFirstFrameRenderedListener(async () => {
console.log('Game first frame rendered');
const state = gameHandle.getGameState();
console.log('Current state:', state);
});
// Listen for game failure
gameHandle.setOnHostFailureListener((error) => {
console.error('Game failed:', error);
// Handle game crash
});
// Monitor state changes
setInterval(() => {
const state = gameHandle.getGameState();
switch (state) {
case RuntimeState.UNAVAILABLE:
console.log('Game is unavailable');
break;
case RuntimeState.WAITING:
console.log('Game is waiting to start');
break;
case RuntimeState.RUNNING:
console.log('Game is running');
break;
case RuntimeState.PLAYING:
console.log('Game is playing');
break;
}
}, 1000);
11 Complete Example
const {
TJHostHandle,
MultiGameLauncher,
TJConstants,
RuntimeState
} = require('webglhost-runtime-pc');
class AdvancedGameManager {
constructor() {
this.hostHandle = null;
this.games = new Map();
}
async initialize(sdkKey, sdkSecret) {
return new Promise((resolve, reject) => {
TJHostHandle.initialize(sdkKey, sdkSecret, (handle, error) => {
if (error) {
reject(error);
return;
}
this.hostHandle = handle;
this.setupRecentlyPlayedListener();
resolve();
});
});
}
setupRecentlyPlayedListener() {
const listener = {
updateGame: (game) => {
console.log('Recently played game updated:', game);
// Update UI with recently played game
}
};
this.hostHandle.setRecentlyGameListener(listener);
}
async createGame(gameId, launchKey, customOptions = {}) {
// Create game handle
const gameHandle = await MultiGameLauncher.createGameHandle(
this.hostHandle,
gameId,
customOptions
);
// Setup listeners
this.setupGameListeners(gameHandle, gameId);
// Configure game options
const options = {
[TJConstants.LAUNCH_KEY]: launchKey,
[TJConstants.USER_ID]: 'user-123',
[TJConstants.ENABLE_VCONSOLE]: false,
[TJConstants.SHOW_MENU_LAYOUT]: true,
[TJConstants.ENABLE_TRANSPARENT_MODE]: false,
...customOptions
};
await gameHandle.setGameStartOptions(options);
// Store game handle
this.games.set(gameId, gameHandle);
return gameHandle;
}
setupGameListeners(gameHandle, gameId) {
// First frame rendered
gameHandle.setOnFirstFrameRenderedListener(async () => {
console.log(`[${gameId}] First frame rendered`);
await gameHandle.play();
});
// Menu button clicked
gameHandle.setOnTJMenuListener(() => {
console.log(`[${gameId}] Menu clicked`);
this.showGameMenu(gameId);
});
// Close button clicked
gameHandle.setOnTJCloseListener(() => {
console.log(`[${gameId}] Close clicked`);
this.closeGame(gameId);
});
// Game failure
gameHandle.setOnHostFailureListener((error) => {
console.error(`[${gameId}] Game failed:`, error);
this.handleGameFailure(gameId, error);
});
}
async startGame(gameId) {
const gameHandle = this.games.get(gameId);
if (!gameHandle) {
throw new Error(`Game ${gameId} not found`);
}
await gameHandle.start();
}
showGameMenu(gameId) {
const gameHandle = this.games.get(gameId);
if (gameHandle) {
gameHandle.showDebugMenu();
}
}
closeGame(gameId) {
const gameHandle = this.games.get(gameId);
if (gameHandle) {
MultiGameLauncher.stop(gameId);
this.games.delete(gameId);
}
}
handleGameFailure(gameId, error) {
console.error(`Handling game failure for ${gameId}:`, error);
this.closeGame(gameId);
}
getUserHistory(userId) {
return this.hostHandle.getUserHistory(userId);
}
pauseAll() {
MultiGameLauncher.pauseAll();
}
resumeAll() {
for (const gameId of this.games.keys()) {
MultiGameLauncher.resume(gameId);
}
}
stopAll() {
MultiGameLauncher.stopAll();
this.games.clear();
}
destroyAll() {
MultiGameLauncher.destroyAll();
this.games.clear();
}
}
// Usage
async function main() {
const manager = new AdvancedGameManager();
try {
// Initialize
await manager.initialize('sdk-key', 'sdk-secret');
// Configure max concurrent games
MultiGameLauncher.configMaxGame(3);
// Create and start first game
const gameHandle1 = await manager.createGame('game-1', 'launch-key-1', {
[TJConstants.ENABLE_VCONSOLE]: true
});
await manager.startGame('game-1');
// Create and start second game
const gameHandle2 = await manager.createGame('game-2', 'launch-key-2');
await manager.startGame('game-2');
// Get user history
const history = manager.getUserHistory('user-123');
console.log('User history:', history);
} catch (error) {
console.error('Error:', error);
}
}
main().catch(console.error);