* refactor(engine): 重构2D渲染管线坐标系统 * feat(engine): 完善2D渲染管线和编辑器视口功能 * feat(editor): 实现Viewport变换工具系统 * feat(editor): 优化Inspector渲染性能并修复Gizmo变换工具显示 * feat(editor): 实现Run on Device移动预览功能 * feat(editor): 添加组件属性控制和依赖关系系统 * feat(editor): 实现动画预览功能和优化SpriteAnimator编辑器 * feat(editor): 修复SpriteAnimator动画预览功能并迁移CI到pnpm * feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm * feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm * feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm * feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm * feat(ci): 迁移项目到pnpm并修复CI构建问题 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 移除 network 相关包 * chore: 移除 network 相关包
432 lines
13 KiB
TypeScript
432 lines
13 KiB
TypeScript
/**
|
||
* Asset database for managing asset metadata
|
||
* 用于管理资产元数据的资产数据库
|
||
*/
|
||
|
||
import {
|
||
AssetGUID,
|
||
AssetType,
|
||
IAssetMetadata,
|
||
IAssetCatalogEntry
|
||
} from '../types/AssetTypes';
|
||
|
||
/**
|
||
* Asset database implementation
|
||
* 资产数据库实现
|
||
*/
|
||
export class AssetDatabase {
|
||
private readonly _metadata = new Map<AssetGUID, IAssetMetadata>();
|
||
private readonly _pathToGuid = new Map<string, AssetGUID>();
|
||
private readonly _typeToGuids = new Map<AssetType, Set<AssetGUID>>();
|
||
private readonly _labelToGuids = new Map<string, Set<AssetGUID>>();
|
||
private readonly _dependencies = new Map<AssetGUID, Set<AssetGUID>>();
|
||
private readonly _dependents = new Map<AssetGUID, Set<AssetGUID>>();
|
||
|
||
/**
|
||
* Add asset to database
|
||
* 添加资产到数据库
|
||
*/
|
||
addAsset(metadata: IAssetMetadata): void {
|
||
const { guid, path, type, labels, dependencies } = metadata;
|
||
|
||
// 存储元数据 / Store metadata
|
||
this._metadata.set(guid, metadata);
|
||
this._pathToGuid.set(path, guid);
|
||
|
||
// 按类型索引 / Index by type
|
||
if (!this._typeToGuids.has(type)) {
|
||
this._typeToGuids.set(type, new Set());
|
||
}
|
||
this._typeToGuids.get(type)!.add(guid);
|
||
|
||
// 按标签索引 / Index by labels
|
||
labels.forEach((label) => {
|
||
if (!this._labelToGuids.has(label)) {
|
||
this._labelToGuids.set(label, new Set());
|
||
}
|
||
this._labelToGuids.get(label)!.add(guid);
|
||
});
|
||
|
||
// 建立依赖关系 / Establish dependencies
|
||
this.updateDependencies(guid, dependencies);
|
||
}
|
||
|
||
/**
|
||
* Remove asset from database
|
||
* 从数据库移除资产
|
||
*/
|
||
removeAsset(guid: AssetGUID): void {
|
||
const metadata = this._metadata.get(guid);
|
||
if (!metadata) return;
|
||
|
||
// 清理元数据 / Clean up metadata
|
||
this._metadata.delete(guid);
|
||
this._pathToGuid.delete(metadata.path);
|
||
|
||
// 清理类型索引 / Clean up type index
|
||
const typeSet = this._typeToGuids.get(metadata.type);
|
||
if (typeSet) {
|
||
typeSet.delete(guid);
|
||
if (typeSet.size === 0) {
|
||
this._typeToGuids.delete(metadata.type);
|
||
}
|
||
}
|
||
|
||
// 清理标签索引 / Clean up label indices
|
||
metadata.labels.forEach((label) => {
|
||
const labelSet = this._labelToGuids.get(label);
|
||
if (labelSet) {
|
||
labelSet.delete(guid);
|
||
if (labelSet.size === 0) {
|
||
this._labelToGuids.delete(label);
|
||
}
|
||
}
|
||
});
|
||
|
||
// 清理依赖关系 / Clean up dependencies
|
||
this.clearDependencies(guid);
|
||
}
|
||
|
||
/**
|
||
* Update asset metadata
|
||
* 更新资产元数据
|
||
*/
|
||
updateAsset(guid: AssetGUID, updates: Partial<IAssetMetadata>): void {
|
||
const metadata = this._metadata.get(guid);
|
||
if (!metadata) return;
|
||
|
||
// 如果路径改变,更新索引 / Update index if path changed
|
||
if (updates.path && updates.path !== metadata.path) {
|
||
this._pathToGuid.delete(metadata.path);
|
||
this._pathToGuid.set(updates.path, guid);
|
||
}
|
||
|
||
// 如果类型改变,更新索引 / Update index if type changed
|
||
if (updates.type && updates.type !== metadata.type) {
|
||
const oldTypeSet = this._typeToGuids.get(metadata.type);
|
||
if (oldTypeSet) {
|
||
oldTypeSet.delete(guid);
|
||
}
|
||
|
||
if (!this._typeToGuids.has(updates.type)) {
|
||
this._typeToGuids.set(updates.type, new Set());
|
||
}
|
||
this._typeToGuids.get(updates.type)!.add(guid);
|
||
}
|
||
|
||
// 如果依赖改变,更新关系 / Update relations if dependencies changed
|
||
if (updates.dependencies) {
|
||
this.updateDependencies(guid, updates.dependencies);
|
||
}
|
||
|
||
// 合并更新 / Merge updates
|
||
Object.assign(metadata, updates);
|
||
metadata.lastModified = Date.now();
|
||
metadata.version++;
|
||
}
|
||
|
||
/**
|
||
* Get asset metadata
|
||
* 获取资产元数据
|
||
*/
|
||
getMetadata(guid: AssetGUID): IAssetMetadata | undefined {
|
||
return this._metadata.get(guid);
|
||
}
|
||
|
||
/**
|
||
* Get metadata by path
|
||
* 通过路径获取元数据
|
||
*/
|
||
getMetadataByPath(path: string): IAssetMetadata | undefined {
|
||
const guid = this._pathToGuid.get(path);
|
||
return guid ? this._metadata.get(guid) : undefined;
|
||
}
|
||
|
||
/**
|
||
* Find assets by type
|
||
* 按类型查找资产
|
||
*/
|
||
findAssetsByType(type: AssetType): AssetGUID[] {
|
||
const guids = this._typeToGuids.get(type);
|
||
return guids ? Array.from(guids) : [];
|
||
}
|
||
|
||
/**
|
||
* Find assets by label
|
||
* 按标签查找资产
|
||
*/
|
||
findAssetsByLabel(label: string): AssetGUID[] {
|
||
const guids = this._labelToGuids.get(label);
|
||
return guids ? Array.from(guids) : [];
|
||
}
|
||
|
||
/**
|
||
* Find assets by multiple labels (AND operation)
|
||
* 按多个标签查找资产(AND操作)
|
||
*/
|
||
findAssetsByLabels(labels: string[]): AssetGUID[] {
|
||
if (labels.length === 0) return [];
|
||
|
||
let result: Set<AssetGUID> | null = null;
|
||
|
||
for (const label of labels) {
|
||
const labelGuids = this._labelToGuids.get(label);
|
||
if (!labelGuids || labelGuids.size === 0) return [];
|
||
|
||
if (!result) {
|
||
result = new Set(labelGuids);
|
||
} else {
|
||
// 交集 / Intersection
|
||
const intersection = new Set<AssetGUID>();
|
||
labelGuids.forEach((guid) => {
|
||
if (result!.has(guid)) {
|
||
intersection.add(guid);
|
||
}
|
||
});
|
||
result = intersection;
|
||
}
|
||
}
|
||
|
||
return result ? Array.from(result) : [];
|
||
}
|
||
|
||
/**
|
||
* Search assets by query
|
||
* 通过查询搜索资产
|
||
*/
|
||
searchAssets(query: {
|
||
name?: string;
|
||
type?: AssetType;
|
||
labels?: string[];
|
||
path?: string;
|
||
}): AssetGUID[] {
|
||
let results = Array.from(this._metadata.keys());
|
||
|
||
// 按名称过滤 / Filter by name
|
||
if (query.name) {
|
||
const nameLower = query.name.toLowerCase();
|
||
results = results.filter((guid) => {
|
||
const metadata = this._metadata.get(guid)!;
|
||
return metadata.name.toLowerCase().includes(nameLower);
|
||
});
|
||
}
|
||
|
||
// 按类型过滤 / Filter by type
|
||
if (query.type) {
|
||
const typeGuids = this._typeToGuids.get(query.type);
|
||
if (!typeGuids) return [];
|
||
results = results.filter((guid) => typeGuids.has(guid));
|
||
}
|
||
|
||
// 按标签过滤 / Filter by labels
|
||
if (query.labels && query.labels.length > 0) {
|
||
const labelResults = this.findAssetsByLabels(query.labels);
|
||
const labelSet = new Set(labelResults);
|
||
results = results.filter((guid) => labelSet.has(guid));
|
||
}
|
||
|
||
// 按路径过滤 / Filter by path
|
||
if (query.path) {
|
||
const pathLower = query.path.toLowerCase();
|
||
results = results.filter((guid) => {
|
||
const metadata = this._metadata.get(guid)!;
|
||
return metadata.path.toLowerCase().includes(pathLower);
|
||
});
|
||
}
|
||
|
||
return results;
|
||
}
|
||
|
||
/**
|
||
* Get asset dependencies
|
||
* 获取资产依赖
|
||
*/
|
||
getDependencies(guid: AssetGUID): AssetGUID[] {
|
||
const deps = this._dependencies.get(guid);
|
||
return deps ? Array.from(deps) : [];
|
||
}
|
||
|
||
/**
|
||
* Get asset dependents (assets that depend on this one)
|
||
* 获取资产的依赖者(依赖此资产的其他资产)
|
||
*/
|
||
getDependents(guid: AssetGUID): AssetGUID[] {
|
||
const deps = this._dependents.get(guid);
|
||
return deps ? Array.from(deps) : [];
|
||
}
|
||
|
||
/**
|
||
* Get all dependencies recursively
|
||
* 递归获取所有依赖
|
||
*/
|
||
getAllDependencies(guid: AssetGUID, visited = new Set<AssetGUID>()): AssetGUID[] {
|
||
if (visited.has(guid)) return [];
|
||
visited.add(guid);
|
||
|
||
const result: AssetGUID[] = [];
|
||
const directDeps = this.getDependencies(guid);
|
||
|
||
for (const dep of directDeps) {
|
||
result.push(dep);
|
||
const transitiveDeps = this.getAllDependencies(dep, visited);
|
||
result.push(...transitiveDeps);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* Check for circular dependencies
|
||
* 检查循环依赖
|
||
*/
|
||
hasCircularDependency(guid: AssetGUID): boolean {
|
||
const visited = new Set<AssetGUID>();
|
||
const recursionStack = new Set<AssetGUID>();
|
||
|
||
const checkCycle = (current: AssetGUID): boolean => {
|
||
visited.add(current);
|
||
recursionStack.add(current);
|
||
|
||
const deps = this.getDependencies(current);
|
||
for (const dep of deps) {
|
||
if (!visited.has(dep)) {
|
||
if (checkCycle(dep)) return true;
|
||
} else if (recursionStack.has(dep)) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
recursionStack.delete(current);
|
||
return false;
|
||
};
|
||
|
||
return checkCycle(guid);
|
||
}
|
||
|
||
/**
|
||
* Update dependencies
|
||
* 更新依赖关系
|
||
*/
|
||
private updateDependencies(guid: AssetGUID, newDependencies: AssetGUID[]): void {
|
||
// 清除旧的依赖关系 / Clear old dependencies
|
||
this.clearDependencies(guid);
|
||
|
||
// 建立新的依赖关系 / Establish new dependencies
|
||
if (newDependencies.length > 0) {
|
||
this._dependencies.set(guid, new Set(newDependencies));
|
||
|
||
// 更新被依赖关系 / Update dependent relations
|
||
newDependencies.forEach((dep) => {
|
||
if (!this._dependents.has(dep)) {
|
||
this._dependents.set(dep, new Set());
|
||
}
|
||
this._dependents.get(dep)!.add(guid);
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Clear dependencies
|
||
* 清除依赖关系
|
||
*/
|
||
private clearDependencies(guid: AssetGUID): void {
|
||
// 清除依赖 / Clear dependencies
|
||
const deps = this._dependencies.get(guid);
|
||
if (deps) {
|
||
deps.forEach((dep) => {
|
||
const dependents = this._dependents.get(dep);
|
||
if (dependents) {
|
||
dependents.delete(guid);
|
||
if (dependents.size === 0) {
|
||
this._dependents.delete(dep);
|
||
}
|
||
}
|
||
});
|
||
this._dependencies.delete(guid);
|
||
}
|
||
|
||
// 清除被依赖 / Clear dependents
|
||
const dependents = this._dependents.get(guid);
|
||
if (dependents) {
|
||
dependents.forEach((dependent) => {
|
||
const dependencies = this._dependencies.get(dependent);
|
||
if (dependencies) {
|
||
dependencies.delete(guid);
|
||
if (dependencies.size === 0) {
|
||
this._dependencies.delete(dependent);
|
||
}
|
||
}
|
||
});
|
||
this._dependents.delete(guid);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Get database statistics
|
||
* 获取数据库统计
|
||
*/
|
||
getStatistics(): {
|
||
totalAssets: number;
|
||
assetsByType: Map<AssetType, number>;
|
||
totalDependencies: number;
|
||
assetsWithDependencies: number;
|
||
circularDependencies: number;
|
||
} {
|
||
const assetsByType = new Map<AssetType, number>();
|
||
this._typeToGuids.forEach((guids, type) => {
|
||
assetsByType.set(type, guids.size);
|
||
});
|
||
|
||
let circularDependencies = 0;
|
||
this._metadata.forEach((_, guid) => {
|
||
if (this.hasCircularDependency(guid)) {
|
||
circularDependencies++;
|
||
}
|
||
});
|
||
|
||
return {
|
||
totalAssets: this._metadata.size,
|
||
assetsByType,
|
||
totalDependencies: Array.from(this._dependencies.values()).reduce(
|
||
(sum, deps) => sum + deps.size,
|
||
0
|
||
),
|
||
assetsWithDependencies: this._dependencies.size,
|
||
circularDependencies
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Export to catalog entries
|
||
* 导出为目录条目
|
||
*/
|
||
exportToCatalog(): IAssetCatalogEntry[] {
|
||
const entries: IAssetCatalogEntry[] = [];
|
||
|
||
this._metadata.forEach((metadata) => {
|
||
entries.push({
|
||
guid: metadata.guid,
|
||
path: metadata.path,
|
||
type: metadata.type,
|
||
size: metadata.size,
|
||
hash: metadata.hash
|
||
});
|
||
});
|
||
|
||
return entries;
|
||
}
|
||
|
||
/**
|
||
* Clear database
|
||
* 清空数据库
|
||
*/
|
||
clear(): void {
|
||
this._metadata.clear();
|
||
this._pathToGuid.clear();
|
||
this._typeToGuids.clear();
|
||
this._labelToGuids.clear();
|
||
this._dependencies.clear();
|
||
this._dependents.clear();
|
||
}
|
||
}
|