feat: 添加跨平台运行时、资产系统和UI适配功能 (#256)

* feat(platform-common): 添加WASM加载器和环境检测API

* feat(rapier2d): 新增Rapier2D WASM绑定包

* feat(physics-rapier2d): 添加跨平台WASM加载器

* feat(asset-system): 添加运行时资产目录和bundle格式

* feat(asset-system-editor): 新增编辑器资产管理包

* feat(editor-core): 添加构建系统和模块管理

* feat(editor-app): 重构浏览器预览使用import maps

* feat(platform-web): 添加BrowserRuntime和资产读取

* feat(engine): 添加材质系统和着色器管理

* feat(material): 新增材质系统和着色器编辑器

* feat(tilemap): 增强tilemap编辑器和动画系统

* feat(modules): 添加module.json配置

* feat(core): 添加module.json和类型定义更新

* chore: 更新依赖和构建配置

* refactor(plugins): 更新插件模板使用ModuleManifest

* chore: 添加第三方依赖库

* chore: 移除BehaviourTree-ai和ecs-astar子模块

* docs: 更新README和文档主题样式

* fix: 修复Rust文档测试和添加rapier2d WASM绑定

* fix(tilemap-editor): 修复画布高DPI屏幕分辨率适配问题

* feat(ui): 添加UI屏幕适配系统(CanvasScaler/SafeArea)

* fix(ecs-engine-bindgen): 添加缺失的ecs-framework-math依赖

* fix: 添加缺失的包依赖修复CI构建

* fix: 修复CodeQL检测到的代码问题

* fix: 修复构建错误和缺失依赖

* fix: 修复类型检查错误

* fix(material-system): 修复tsconfig配置支持TypeScript项目引用

* fix(editor-core): 修复Rollup构建配置添加tauri external

* fix: 修复CodeQL检测到的代码问题

* fix: 修复CodeQL检测到的代码问题
This commit is contained in:
YHH
2025-12-03 22:15:22 +08:00
committed by GitHub
parent caf7622aa0
commit 63f006ab62
496 changed files with 77601 additions and 4067 deletions

View File

@@ -0,0 +1,275 @@
/**
* Runtime Catalog for Asset Resolution
* 资产解析的运行时目录
*
* Provides GUID-based asset lookup at runtime.
* 提供运行时基于 GUID 的资产查找。
*/
import { AssetGUID, AssetType } from '../types/AssetTypes';
import {
IRuntimeCatalog,
IRuntimeAssetLocation,
IRuntimeBundleInfo
} from '../bundle/BundleFormat';
/**
* Runtime Catalog Manager
* 运行时目录管理器
*
* Loads and manages the asset catalog for runtime GUID resolution.
*/
export class RuntimeCatalog {
private _catalog: IRuntimeCatalog | null = null;
private _loadedBundles = new Map<string, ArrayBuffer>();
private _loadingBundles = new Map<string, Promise<ArrayBuffer>>();
private _baseUrl: string = './';
/**
* Set base URL for loading catalog and bundles
* 设置加载目录和包的基础 URL
*/
setBaseUrl(url: string): void {
this._baseUrl = url.endsWith('/') ? url : `${url}/`;
}
/**
* Load catalog from URL
* 从 URL 加载目录
*/
async loadCatalog(catalogUrl?: string): Promise<void> {
const url = catalogUrl || `${this._baseUrl}asset-catalog.json`;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to load catalog: ${response.status}`);
}
const data = await response.json();
this._catalog = this._parseCatalog(data);
console.log(`[RuntimeCatalog] Loaded catalog with ${Object.keys(this._catalog.assets).length} assets`);
} catch (error) {
console.error('[RuntimeCatalog] Failed to load catalog:', error);
throw error;
}
}
/**
* Initialize with pre-loaded catalog data
* 使用预加载的目录数据初始化
*/
initWithData(catalogData: IRuntimeCatalog): void {
this._catalog = catalogData;
}
/**
* Check if catalog is loaded
* 检查目录是否已加载
*/
isLoaded(): boolean {
return this._catalog !== null;
}
/**
* Get asset location by GUID
* 根据 GUID 获取资产位置
*/
getAssetLocation(guid: AssetGUID): IRuntimeAssetLocation | null {
if (!this._catalog) {
console.warn('[RuntimeCatalog] Catalog not loaded');
return null;
}
return this._catalog.assets[guid] || null;
}
/**
* Check if asset exists in catalog
* 检查资产是否存在于目录中
*/
hasAsset(guid: AssetGUID): boolean {
return this._catalog?.assets[guid] !== undefined;
}
/**
* Get all assets of a specific type
* 获取特定类型的所有资产
*/
getAssetsByType(type: AssetType): AssetGUID[] {
if (!this._catalog) return [];
return Object.entries(this._catalog.assets)
.filter(([_, loc]) => loc.type === type)
.map(([guid]) => guid);
}
/**
* Get bundle info
* 获取包信息
*/
getBundleInfo(bundleName: string): IRuntimeBundleInfo | null {
return this._catalog?.bundles[bundleName] || null;
}
/**
* Load a bundle
* 加载包
*/
async loadBundle(bundleName: string): Promise<ArrayBuffer> {
// Return cached bundle
const cached = this._loadedBundles.get(bundleName);
if (cached) {
return cached;
}
// Return pending load
const pending = this._loadingBundles.get(bundleName);
if (pending) {
return pending;
}
// Start new load
const bundleInfo = this.getBundleInfo(bundleName);
if (!bundleInfo) {
throw new Error(`Bundle not found in catalog: ${bundleName}`);
}
const loadPromise = this._fetchBundle(bundleInfo);
this._loadingBundles.set(bundleName, loadPromise);
try {
const data = await loadPromise;
this._loadedBundles.set(bundleName, data);
return data;
} finally {
this._loadingBundles.delete(bundleName);
}
}
/**
* Load asset data by GUID
* 根据 GUID 加载资产数据
*/
async loadAssetData(guid: AssetGUID): Promise<ArrayBuffer> {
const location = this.getAssetLocation(guid);
if (!location) {
throw new Error(`Asset not found in catalog: ${guid}`);
}
// Load the bundle containing this asset
const bundleData = await this.loadBundle(location.bundle);
// Extract asset data from bundle
return bundleData.slice(location.offset, location.offset + location.size);
}
/**
* Preload bundles marked for preloading
* 预加载标记为预加载的包
*/
async preloadBundles(): Promise<void> {
if (!this._catalog) return;
const preloadPromises: Promise<void>[] = [];
for (const [name, info] of Object.entries(this._catalog.bundles)) {
if (info.preload) {
preloadPromises.push(
this.loadBundle(name).then(() => {
console.log(`[RuntimeCatalog] Preloaded bundle: ${name}`);
})
);
}
}
await Promise.all(preloadPromises);
}
/**
* Unload a bundle from memory
* 从内存卸载包
*/
unloadBundle(bundleName: string): void {
this._loadedBundles.delete(bundleName);
}
/**
* Clear all loaded bundles
* 清除所有已加载的包
*/
clearBundles(): void {
this._loadedBundles.clear();
}
/**
* Get catalog statistics
* 获取目录统计信息
*/
getStatistics(): {
totalAssets: number;
totalBundles: number;
loadedBundles: number;
assetsByType: Record<string, number>;
} {
if (!this._catalog) {
return {
totalAssets: 0,
totalBundles: 0,
loadedBundles: 0,
assetsByType: {}
};
}
const assetsByType: Record<string, number> = {};
for (const loc of Object.values(this._catalog.assets)) {
assetsByType[loc.type] = (assetsByType[loc.type] || 0) + 1;
}
return {
totalAssets: Object.keys(this._catalog.assets).length,
totalBundles: Object.keys(this._catalog.bundles).length,
loadedBundles: this._loadedBundles.size,
assetsByType
};
}
/**
* Parse catalog JSON to typed structure
* 将目录 JSON 解析为类型化结构
*/
private _parseCatalog(data: unknown): IRuntimeCatalog {
const raw = data as Record<string, unknown>;
return {
version: (raw.version as string) || '1.0',
createdAt: (raw.createdAt as number) || Date.now(),
bundles: (raw.bundles as Record<string, IRuntimeBundleInfo>) || {},
assets: (raw.assets as Record<AssetGUID, IRuntimeAssetLocation>) || {}
};
}
/**
* Fetch bundle data
* 获取包数据
*/
private async _fetchBundle(info: IRuntimeBundleInfo): Promise<ArrayBuffer> {
const url = info.url.startsWith('http')
? info.url
: `${this._baseUrl}${info.url}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to load bundle: ${url} (${response.status})`);
}
return response.arrayBuffer();
}
}
/**
* Global runtime catalog instance
* 全局运行时目录实例
*/
export const runtimeCatalog = new RuntimeCatalog();