Skip to main content

CustomCommand Interface

The process of implementing a custom API interface using CustomCommand is as follows:

1 Inject CustomCommand listener into the host​

1.1 Method 1: Set a listener for a specific command​

const gameHandle = /* RuntimeGameHandle instance */;

gameHandle.setCustomCommandListener('sampleLogin', (request, handle) => {
// Validate parameters
if (!request.username) {
handle.fail(20001, 'invalid username');
return;
}

if (!request.nonce) {
handle.fail(20001, 'invalid nonce');
return;
}

// Simulate async operation
setTimeout(() => {
handle.success({
username: request.username + '_login',
age: 18,
nonce: request.nonce
});
}, 100);
});

1.2 Method 2: Register Multiple Custom Commands​

const gameHandle = /* RuntimeGameHandle instance */;

class CustomCommandHandler {
constructor(gameHandle) {
this.gameHandle = gameHandle;
this.registerCommands();
}

registerCommands() {
// Register multiple commands
this.gameHandle.setCustomCommandListener('sampleLogin', (request, handle) => {
this.handleLogin(request, handle);
});

this.gameHandle.setCustomCommandListener('getUserInfo', (request, handle) => {
this.handleGetUserInfo(request, handle);
});
}

handleLogin(request, handle) {
// Validate parameters
if (!request.username) {
handle.fail(20001, 'invalid username');
return;
}

if (!request.nonce) {
handle.fail(20001, 'invalid nonce');
return;
}

// Perform login
performLogin(request.username, request.password)
.then(result => {
handle.success({
username: result.username + '_login',
age: 18,
nonce: request.nonce
});
})
.catch(error => {
handle.fail(20002, error.message);
});
}

handleGetUserInfo(request, handle) {
// Get user info
getUserInfo(request.userId)
.then(userInfo => {
handle.success(userInfo);
})
.catch(error => {
handle.fail(20003, error.message);
});
}
}

// Register handler
const handler = new CustomCommandHandler(gameHandle);

2 Implement API Interfaces in JavaScript​

In JavaScript, you can access tj and cusObj variables. tj is used to call and add APIs, and cusObj is used to store functions for internal use.

2.1 tj.customCommand​

Call the corresponding host method via tj.customCommand.

tj.sampleLogin = function(obj) {
tj.customCommand('sampleLogin', obj);
};

The obj parameter contains not only the corresponding interface parameters but also interface callbacks. The callback parameters are defined as follows:

Parameter NameTypeRequiredDescription
successFunctionYesCallback function for successful interface call
failFunctionNoCallback function for interface call failure
completeFunctionNoCallback function for the end of interface call (executed upon both successful and failed calls)

2.2 API Call Example​

tj.sampleLogin({
username: 'test',
nonce: 123,
success: function(res) {
console.log(`sampleLogin: user=${res.username}, age=${res.age}, nonce=${res.nonce}`);
},
fail: function(res) {
console.error(`login fail: ${res.errorCode}, ${res.errorMsg}`);
},
complete: function() {
console.log('sampleLogin completed');
}
});

2.3 tj.customCommandSync​

Synchronous calls to the host interface are implemented through tj.customCommandSync.

tj.sampleLoginSync = function(obj) {
return tj.customCommandSync('sampleLogin', obj);
};

Example of Synchronous Call

try {
const result = tj.sampleLoginSync({
username: 'test',
nonce: 123
});
console.log('Login result:', result);
} catch (error) {
console.error('Login failed:', error);
}

3 Pass in Custom Initialization JavaScript​

3.1 Injection via file path​

const { TJConstants } = require('webglhost-runtime-pc');
const path = require('path');

const options = {
[TJConstants.CUSTOM_SCRIPT_FILES]: [
path.join(__dirname, 'scripts', 'auth.js'),
path.join(__dirname, 'scripts', 'ad.js')
]
};

await gameHandle.setGameStartOptions(options);

Example of auth.js

// auth.js
tj.sampleLogin = function(obj) {
tj.customCommand('sampleLogin', obj);
};

tj.getUserInfo = function(obj) {
tj.customCommand('getUserInfo', obj);
};

ad.js Example

// ad.js
tj.showAd = function(obj) {
tj.customCommand('showAd', obj);
};

tj.hideAd = function(obj) {
tj.customCommand('hideAd', obj);
};

3.2 Injection via string content​

const { TJConstants } = require('webglhost-runtime-pc');

const options = {
[TJConstants.CUSTOM_SCRIPT_CONTENTS]: [
`
tj.sampleLogin = function(obj) {
tj.customCommand('sampleLogin', obj);
};
`,
`
tj.getUserInfo = function(obj) {
tj.customCommand('getUserInfo', obj);
};
`
]
};

await gameHandle.setGameStartOptions(options);

3.3 Mixed Use of Files and Strings​

const { TJConstants } = require('webglhost-runtime-pc');
const path = require('path');

const options = {
// Load from files
[TJConstants.CUSTOM_SCRIPT_FILES]: [
path.join(__dirname, 'scripts', 'auth.js')
],

// Inject inline scripts
[TJConstants.CUSTOM_SCRIPT_CONTENTS]: [
`
tj.quickLogin = function(obj) {
tj.customCommand('sampleLogin', obj);
};
`
]
};

await gameHandle.setGameStartOptions(options);

4 Execute JavaScript during runtime​

When the host is running, you can call gameHandle.runCustomScript to execute JavaScript code. The code can access the tj and cusObj variables.

4.1 Execute Simple Script​

const script = `
console.log('Running custom script');
return { success: true, timestamp: Date.now() };
`;

gameHandle.runCustomScript(script, (result, error) => {
if (error) {
console.error('Script execution error:', error);
return;
}

console.log('Script result:', result);
});

4.2 Execute Complex Object Return​

const script = `
console.log('Getting user data');
return {
name: 'john',
age: 18,
isActive: true,
scores: [40.2, 50, 70.2],
profile: {
email: 'john@example.com',
level: 5
}
};
`;

gameHandle.runCustomScript(script, (result, error) => {
if (error) {
console.error('Error:', error);
return;
}

console.log('Name:', result.name);
console.log('Age:', result.age);
console.log('Active:', result.isActive);
console.log('Scores:', result.scores);
console.log('Email:', result.profile.email);
});

4.3 Interact with the Game​

// Call game API from host
const script = `
// Call game's custom method
if (typeof window.gameAPI !== 'undefined') {
const playerInfo = window.gameAPI.getPlayerInfo();
return playerInfo;
}
return null;
`;

gameHandle.runCustomScript(script, (result, error) => {
if (error) {
console.error('Failed to get player info:', error);
return;
}

console.log('Player info:', result);
});

5 Complete Example​

const { TJHostHandle, TJConstants } = require('webglhost-runtime-pc');
const path = require('path');

class CustomAPIManager {
constructor() {
this.gameHandle = null;
}

async initialize(hostHandle) {
// Create game handle
this.gameHandle = await hostHandle.createGameHandle();

// Setup custom command listeners
this.setupCustomCommands();

// Setup game start options with custom scripts
await this.setupGameOptions();
}

setupCustomCommands() {
// Register login command
this.gameHandle.setCustomCommandListener('login', (request, handle) => {
this.handleLogin(request, handle);
});

// Register getUserInfo command
this.gameHandle.setCustomCommandListener('getUserInfo', (request, handle) => {
this.handleGetUserInfo(request, handle);
});

// Register showAd command
this.gameHandle.setCustomCommandListener('showAd', (request, handle) => {
this.handleShowAd(request, handle);
});
}

async setupGameOptions() {
const options = {
[TJConstants.LAUNCH_KEY]: 'launch-key',
[TJConstants.USER_ID]: 'user-123',

// Load custom scripts from files
[TJConstants.CUSTOM_SCRIPT_FILES]: [
path.join(__dirname, 'custom-scripts', 'auth.js'),
path.join(__dirname, 'custom-scripts', 'ad.js')
],

// Inject inline custom scripts
[TJConstants.CUSTOM_SCRIPT_CONTENTS]: [
`
tj.quickLogin = function(obj) {
tj.customCommand('login', obj);
};
`
]
};

await this.gameHandle.setGameStartOptions(options);
}

handleLogin(request, handle) {
// Validate
if (!request.username || !request.password) {
handle.fail(20001, 'Missing credentials');
return;
}

// Perform login (async)
this.performLogin(request.username, request.password)
.then(result => {
handle.success({
userId: result.userId,
token: result.token,
username: result.username
});
})
.catch(error => {
handle.fail(20002, error.message);
});
}

handleGetUserInfo(request, handle) {
if (!request.userId) {
handle.fail(20003, 'Missing userId');
return;
}

// Get user info
this.getUserInfo(request.userId)
.then(userInfo => {
handle.success(userInfo);
})
.catch(error => {
handle.fail(20004, error.message);
});
}

handleShowAd(request, handle) {
// Show ad
console.log('Showing ad:', request.adId);
handle.success({ shown: true });
}

async performLogin(username, password) {
// Simulate async login
return new Promise((resolve) => {
setTimeout(() => {
resolve({
userId: '12345',
token: 'token-abc',
username: username
});
}, 100);
});
}

async getUserInfo(userId) {
// Simulate async getUserInfo
return new Promise((resolve) => {
setTimeout(() => {
resolve({
userId: userId,
name: 'John Doe',
level: 10,
score: 1000
});
}, 50);
});
}

// Execute custom script at runtime
executeScript(script) {
return new Promise((resolve, reject) => {
this.gameHandle.runCustomScript(script, (result, error) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
}

// Usage
async function main() {
// Initialize SDK
const hostHandle = await new Promise((resolve, reject) => {
TJHostHandle.initialize('sdk-key', 'sdk-secret', (handle, error) => {
if (error) reject(error);
else resolve(handle);
});
});

// Create and setup custom API manager
const apiManager = new CustomAPIManager();
await apiManager.initialize(hostHandle);

// Start game
await apiManager.gameHandle.start();
apiManager.gameHandle.play();

// Execute custom script later
const result = await apiManager.executeScript(`
return { message: 'Hello from runtime script' };
`);
console.log('Script result:', result);
}

main().catch(console.error);