Skip to main content

File System

File System Intro​

See File System Overview for details.

File System Management​

1. Inject file system path mapping rules​

Use injectFileSystemRule to inject a mapping for a protocol name to a file path.

API​

gameHandle.injectFileSystemRule(maskPath, realPath)

Parameters​

ParamTypeRequiredDescription
maskPathStringYesVirtual path prefix
realPathStringYesReal file system path

Returns​

Promise<void>

Example​

// Inject new file system rule
// Implementation uses string replacement inside.
// Add a mapping between a virtual path and a real path with this method.

// Built-in conversion rules:
// 1. tjfile://usr/ -> <host user data path>/fs/usr
// 2. tjfile://save_ -> <host user data path>/fs/store
// 3. tjfile://tmp_ -> <host temp file path>/fs

// Example:
// maskPath: tjfile://usr/
// realPath: /Users/username/Library/Application Support/myapp/userid/appid/

await gameHandle.injectFileSystemRule('tjfile://usr/', '/path/to/user/data/');

// After injection, convertRealPathToMaskPath will return tjfile://usr/
const maskPath = gameHandle.convertRealPathToMaskPath('/path/to/user/data/');
console.log('Mask path:', maskPath); // tjfile://usr/

Usage scenario​

const path = require('path');
const os = require('os');

async function setupCustomFileSystem(gameHandle) {
// Define custom file system paths
const appDataPath = path.join(os.homedir(), '.myapp', 'data');
const cachePath = path.join(os.homedir(), '.myapp', 'cache');
const logPath = path.join(os.homedir(), '.myapp', 'logs');

// Inject custom rules
await gameHandle.injectFileSystemRule('myapp://data/', appDataPath);
await gameHandle.injectFileSystemRule('myapp://cache/', cachePath);
await gameHandle.injectFileSystemRule('myapp://logs/', logPath);

console.log('Custom file system rules injected');
}

2. Real path to virtual path​

Convert a real file system path to a virtual (protocol) path.

API​

gameHandle.convertRealPathToMaskPath(realPath)

Parameters​

ParamTypeRequiredDescription
realPathStringYesReal file path

Returns​

String - virtual (protocol) path

Example​

// Convert real path to mask path
const realPath = '/Users/username/Library/Application Support/myapp/userid/appid/data.json';
const maskPath = gameHandle.convertRealPathToMaskPath(realPath);

console.log('Real path:', realPath);
console.log('Mask path:', maskPath); // e.g., tjfile://usr/data.json

3. Virtual path to real path​

Convert a virtual (protocol) path to a real file system path.

API​

gameHandle.convertMaskPathToRealPath(maskPath)

Parameters​

ParamTypeRequiredDescription
maskPathStringYesVirtual path prefix

Returns​

String - real file path

Example​

// Convert mask path to real path
const maskPath = 'tjfile://usr/data.json';
const realPath = gameHandle.convertMaskPathToRealPath(maskPath);

console.log('Mask path:', maskPath);
console.log('Real path:', realPath); // e.g., /Users/username/Library/Application Support/myapp/userid/appid/data.json

File system path rules​

Default mappings​

The PC host registers these conversion rules by default:

Virtual prefixReal pathNote
tjfile://usr/<user data path>/fs/usrUser data dir
tjfile://save_<user data path>/fs/storeSave dir
tjfile://tmp_<temp file path>/fsTemp dir

Path examples​

macOS:

User data path: /Users/username/Library/Application Support/WebGLHost/userid/appid/
Temp file path: /Users/username/Library/Caches/WebGLHost/userid/appid/

Windows:

User data path: C:\Users\username\AppData\Roaming\WebGLHost\userid\appid\
Temp file path: C:\Users\username\AppData\Local\Temp\WebGLHost\userid\appid\

Full example​

const { TJHostHandle } = require('webglhost-runtime-pc');
const path = require('path');
const os = require('os');
const fs = require('fs').promises;

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

async initialize(gameHandle) {
this.gameHandle = gameHandle;
await this.setupCustomPaths();
}

async setupCustomPaths() {
// Define custom application paths
const appName = 'MyGame';
const baseDir = path.join(os.homedir(), `.${appName.toLowerCase()}`);

const paths = {
data: path.join(baseDir, 'data'),
save: path.join(baseDir, 'save'),
cache: path.join(baseDir, 'cache'),
logs: path.join(baseDir, 'logs'),
config: path.join(baseDir, 'config')
};

// Create directories if they don't exist
for (const [key, dirPath] of Object.entries(paths)) {
await fs.mkdir(dirPath, { recursive: true });
console.log(`Created directory: ${dirPath}`);
}

// Inject custom file system rules
await this.gameHandle.injectFileSystemRule('game://data/', paths.data);
await this.gameHandle.injectFileSystemRule('game://save/', paths.save);
await this.gameHandle.injectFileSystemRule('game://cache/', paths.cache);
await this.gameHandle.injectFileSystemRule('game://logs/', paths.logs);
await this.gameHandle.injectFileSystemRule('game://config/', paths.config);

console.log('File system rules injected successfully');
}

// Test path conversions
testPathConversions() {
// Test real path to mask path
const realDataPath = path.join(os.homedir(), '.mygame', 'data', 'player.json');
const maskDataPath = this.gameHandle.convertRealPathToMaskPath(realDataPath);
console.log('Real -> Mask:', realDataPath, '->', maskDataPath);

// Test mask path to real path
const maskSavePath = 'game://save/slot1.dat';
const realSavePath = this.gameHandle.convertMaskPathToRealPath(maskSavePath);
console.log('Mask -> Real:', maskSavePath, '->', realSavePath);

// Test built-in tjfile paths
const tjfilePath = 'tjfile://usr/userdata.json';
const tjfileRealPath = this.gameHandle.convertMaskPathToRealPath(tjfilePath);
console.log('tjfile -> Real:', tjfilePath, '->', tjfileRealPath);
}

// Example: Save player data
async savePlayerData(playerId, data) {
const maskPath = `game://save/player_${playerId}.json`;
const realPath = this.gameHandle.convertMaskPathToRealPath(maskPath);

await fs.writeFile(realPath, JSON.stringify(data, null, 2), 'utf-8');
console.log(`Player data saved to: ${realPath}`);
console.log(`Virtual path: ${maskPath}`);
}

// Example: Load player data
async loadPlayerData(playerId) {
const maskPath = `game://save/player_${playerId}.json`;
const realPath = this.gameHandle.convertMaskPathToRealPath(maskPath);

try {
const content = await fs.readFile(realPath, 'utf-8');
const data = JSON.parse(content);
console.log(`Player data loaded from: ${realPath}`);
return data;
} catch (error) {
console.error(`Failed to load player data:`, error);
return null;
}
}

// Example: Clear cache
async clearCache() {
const maskCachePath = 'game://cache/';
const realCachePath = this.gameHandle.convertMaskPathToRealPath(maskCachePath);

try {
const files = await fs.readdir(realCachePath);
for (const file of files) {
await fs.unlink(path.join(realCachePath, file));
}
console.log(`Cache cleared: ${realCachePath}`);
} catch (error) {
console.error('Failed to clear cache:', error);
}
}

// Example: Write log
async writeLog(message) {
const timestamp = new Date().toISOString();
const logEntry = `[${timestamp}] ${message}\n`;

const maskLogPath = 'game://logs/game.log';
const realLogPath = this.gameHandle.convertMaskPathToRealPath(maskLogPath);

await fs.appendFile(realLogPath, logEntry, 'utf-8');
}
}

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

const gameHandle = await hostHandle.createGameHandle();

// Setup file system manager
const fsManager = new FileSystemManager();
await fsManager.initialize(gameHandle);

// Test path conversions
fsManager.testPathConversions();

// Save player data
await fsManager.savePlayerData('player123', {
name: 'John',
level: 10,
score: 1000
});

// Load player data
const playerData = await fsManager.loadPlayerData('player123');
console.log('Loaded player data:', playerData);

// Write log
await fsManager.writeLog('Game started successfully');

// Clear cache
await fsManager.clearCache();
}

main().catch(console.error);

Tips​

  1. Path Separators: Use Node.js path utilities so the handling stays cross-platform compatible.

  2. Permissions: Ensure the app has access to the target file paths.

  3. Path uniqueness: Each virtual path prefix should map to exactly one real path.

  4. Path existence: Make sure the target directory exists before you use it.

  5. String matching: Path conversion works via string replacement, so keep the virtual prefixes unique.

  6. Performance: Frequent conversions can cost you—cache results when practical.