跳到主要内容

文件系统

文件系统介绍​

详情请见文件系统概览

文件系统管理​

1. 注入文件系统路径映射规则​

通过 injectFileSystemRule 注入对应协议名的文件路径映射。

接口原型​

gameHandle.injectFileSystemRule(maskPath, realPath)

参数说明​

参数数据类型必填属性说明
maskPathString是虚拟路径协议名
realPathString是实际文件路径

返回值​

返回 Promise<void>

代码示例​

// Inject new file system rule
// The implementation uses string replacement internally.
// When you need to add a mapping between a virtual path and a real path, use 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/

使用场景​

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. 真实路径转虚拟路径​

将真实文件系统路径转换为虚拟路径(协议路径)。

接口原型​

gameHandle.convertRealPathToMaskPath(realPath)

参数说明​

参数数据类型必填属性说明
realPathString是真实文件路径

返回值​

返回 String - 虚拟路径(协议路径)

代码示例​

// 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. 虚拟路径转真实路径​

将虚拟路径(协议路径)转换为真实文件系统路径。

接口原型​

gameHandle.convertMaskPathToRealPath(maskPath)

参数说明​

参数数据类型必填属性说明
maskPathString是虚拟路径协议路径

返回值​

返回 String - 真实文件路径

代码示例​

// 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

文件系统路径规则​

默认路径映射​

PC 版本默认注册以下转换规则:

虚拟路径前缀真实路径说明
tjfile://usr/<用户数据路径>/fs/usr用户数据目录
tjfile://save_<用户数据路径>/fs/store存档目录
tjfile://tmp_<临时文件路径>/fs临时文件目录

路径示例​

macOS:

用户数据路径: /Users/username/Library/Application Support/WebGLHost/userid/appid/
临时文件路径: /Users/username/Library/Caches/WebGLHost/userid/appid/

Windows:

用户数据路径: C:\Users\username\AppData\Roaming\WebGLHost\userid\appid\
临时文件路径: C:\Users\username\AppData\Local\Temp\WebGLHost\userid\appid\

完整示例​

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);

注意事项​

  1. 路径分隔符: 使用 Node.js 的 path 模块处理路径,确保跨平台兼容性

  2. 权限管理: 确保应用有权限访问指定的文件路径

  3. 路径唯一性: 每个虚拟路径前缀应该映射到唯一的真实路径

  4. 路径存在性: 在使用前确保目标目录已创建

  5. 字符串匹配: 路径转换使用字符串替换,确保虚拟路径前缀的唯一性

  6. 性能考虑: 频繁的路径转换可能影响性能,考虑缓存转换结果