refactor: reorganize package structure and decouple framework packages (#338)

* refactor: reorganize package structure and decouple framework packages

## Package Structure Reorganization
- Reorganized 55 packages into categorized subdirectories:
  - packages/framework/ - Generic framework (Laya/Cocos compatible)
  - packages/engine/ - ESEngine core modules
  - packages/rendering/ - Rendering modules (WASM dependent)
  - packages/physics/ - Physics modules
  - packages/streaming/ - World streaming
  - packages/network-ext/ - Network extensions
  - packages/editor/ - Editor framework and plugins
  - packages/rust/ - Rust WASM engine
  - packages/tools/ - Build tools and SDK

## Framework Package Decoupling
- Decoupled behavior-tree and blueprint packages from ESEngine dependencies
- Created abstracted interfaces (IBTAssetManager, IBehaviorTreeAssetContent)
- ESEngine-specific code moved to esengine/ subpath exports
- Framework packages now usable with Cocos/Laya without ESEngine

## CI Configuration
- Updated CI to only type-check and lint framework packages
- Added type-check:framework and lint:framework scripts

## Breaking Changes
- Package import paths changed due to directory reorganization
- ESEngine integrations now use subpath imports (e.g., '@esengine/behavior-tree/esengine')

* fix: update es-engine file path after directory reorganization

* docs: update README to focus on framework over engine

* ci: only build framework packages, remove Rust/WASM dependencies

* fix: remove esengine subpath from behavior-tree and blueprint builds

ESEngine integration code will only be available in full engine builds.
Framework packages are now purely engine-agnostic.

* fix: move network-protocols to framework, build both in CI

* fix: update workflow paths from packages/core to packages/framework/core

* fix: exclude esengine folder from type-check in behavior-tree and blueprint

* fix: update network tsconfig references to new paths

* fix: add test:ci:framework to only test framework packages in CI

* fix: only build core and math npm packages in CI

* fix: exclude test files from CodeQL and fix string escaping security issue
This commit is contained in:
YHH
2025-12-26 14:50:35 +08:00
committed by GitHub
parent a84ff902e4
commit 155411e743
1936 changed files with 4147 additions and 11578 deletions

View File

@@ -0,0 +1,556 @@
/**
* @zh 空间查询蓝图节点
* @en Spatial Query Blueprint Nodes
*
* @zh 提供空间查询功能的蓝图节点
* @en Provides blueprint nodes for spatial query functionality
*/
import type { BlueprintNodeTemplate, BlueprintNode, INodeExecutor, ExecutionResult } from '@esengine/blueprint';
import type { ISpatialQuery, IBounds } from '../ISpatialQuery';
// =============================================================================
// 执行上下文接口 | Execution Context Interface
// =============================================================================
/**
* @zh 空间查询上下文
* @en Spatial query context
*/
interface SpatialContext {
spatialQuery: ISpatialQuery<unknown>;
evaluateInput(nodeId: string, pinName: string, defaultValue?: unknown): unknown;
setOutputs(nodeId: string, outputs: Record<string, unknown>): void;
}
// =============================================================================
// FindInRadius 节点 | FindInRadius Node
// =============================================================================
/**
* @zh FindInRadius 节点模板
* @en FindInRadius node template
*/
export const FindInRadiusTemplate: BlueprintNodeTemplate = {
type: 'FindInRadius',
title: 'Find In Radius',
category: 'entity',
description: 'Find all objects within radius / 查找半径内的所有对象',
keywords: ['spatial', 'find', 'radius', 'range', 'query'],
menuPath: ['Spatial', 'Find In Radius'],
isPure: true,
inputs: [
{
name: 'centerX',
displayName: 'Center X',
type: 'float',
defaultValue: 0
},
{
name: 'centerY',
displayName: 'Center Y',
type: 'float',
defaultValue: 0
},
{
name: 'radius',
displayName: 'Radius',
type: 'float',
defaultValue: 100
}
],
outputs: [
{
name: 'results',
displayName: 'Results',
type: 'array'
},
{
name: 'count',
displayName: 'Count',
type: 'int'
}
],
color: '#4a9eff'
};
/**
* @zh FindInRadius 节点执行器
* @en FindInRadius node executor
*/
export class FindInRadiusExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as SpatialContext;
const centerX = ctx.evaluateInput(node.id, 'centerX', 0) as number;
const centerY = ctx.evaluateInput(node.id, 'centerY', 0) as number;
const radius = ctx.evaluateInput(node.id, 'radius', 100) as number;
const results = ctx.spatialQuery?.findInRadius({ x: centerX, y: centerY }, radius) ?? [];
return {
outputs: {
results,
count: results.length
}
};
}
}
// =============================================================================
// FindInRect 节点 | FindInRect Node
// =============================================================================
/**
* @zh FindInRect 节点模板
* @en FindInRect node template
*/
export const FindInRectTemplate: BlueprintNodeTemplate = {
type: 'FindInRect',
title: 'Find In Rect',
category: 'entity',
description: 'Find all objects within rectangle / 查找矩形区域内的所有对象',
keywords: ['spatial', 'find', 'rect', 'rectangle', 'area', 'query'],
menuPath: ['Spatial', 'Find In Rect'],
isPure: true,
inputs: [
{
name: 'minX',
displayName: 'Min X',
type: 'float',
defaultValue: 0
},
{
name: 'minY',
displayName: 'Min Y',
type: 'float',
defaultValue: 0
},
{
name: 'maxX',
displayName: 'Max X',
type: 'float',
defaultValue: 100
},
{
name: 'maxY',
displayName: 'Max Y',
type: 'float',
defaultValue: 100
}
],
outputs: [
{
name: 'results',
displayName: 'Results',
type: 'array'
},
{
name: 'count',
displayName: 'Count',
type: 'int'
}
],
color: '#4a9eff'
};
/**
* @zh FindInRect 节点执行器
* @en FindInRect node executor
*/
export class FindInRectExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as SpatialContext;
const minX = ctx.evaluateInput(node.id, 'minX', 0) as number;
const minY = ctx.evaluateInput(node.id, 'minY', 0) as number;
const maxX = ctx.evaluateInput(node.id, 'maxX', 100) as number;
const maxY = ctx.evaluateInput(node.id, 'maxY', 100) as number;
const bounds: IBounds = { minX, minY, maxX, maxY };
const results = ctx.spatialQuery?.findInRect(bounds) ?? [];
return {
outputs: {
results,
count: results.length
}
};
}
}
// =============================================================================
// FindNearest 节点 | FindNearest Node
// =============================================================================
/**
* @zh FindNearest 节点模板
* @en FindNearest node template
*/
export const FindNearestTemplate: BlueprintNodeTemplate = {
type: 'FindNearest',
title: 'Find Nearest',
category: 'entity',
description: 'Find nearest object to a point / 查找距离点最近的对象',
keywords: ['spatial', 'find', 'nearest', 'closest', 'query'],
menuPath: ['Spatial', 'Find Nearest'],
isPure: true,
inputs: [
{
name: 'centerX',
displayName: 'Center X',
type: 'float',
defaultValue: 0
},
{
name: 'centerY',
displayName: 'Center Y',
type: 'float',
defaultValue: 0
},
{
name: 'maxDistance',
displayName: 'Max Distance',
type: 'float',
defaultValue: 1000
}
],
outputs: [
{
name: 'result',
displayName: 'Result',
type: 'any'
},
{
name: 'found',
displayName: 'Found',
type: 'bool'
}
],
color: '#4a9eff'
};
/**
* @zh FindNearest 节点执行器
* @en FindNearest node executor
*/
export class FindNearestExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as SpatialContext;
const centerX = ctx.evaluateInput(node.id, 'centerX', 0) as number;
const centerY = ctx.evaluateInput(node.id, 'centerY', 0) as number;
const maxDistance = ctx.evaluateInput(node.id, 'maxDistance', 1000) as number;
const result = ctx.spatialQuery?.findNearest({ x: centerX, y: centerY }, maxDistance) ?? null;
return {
outputs: {
result,
found: result !== null
}
};
}
}
// =============================================================================
// FindKNearest 节点 | FindKNearest Node
// =============================================================================
/**
* @zh FindKNearest 节点模板
* @en FindKNearest node template
*/
export const FindKNearestTemplate: BlueprintNodeTemplate = {
type: 'FindKNearest',
title: 'Find K Nearest',
category: 'entity',
description: 'Find K nearest objects to a point / 查找距离点最近的 K 个对象',
keywords: ['spatial', 'find', 'nearest', 'k', 'query'],
menuPath: ['Spatial', 'Find K Nearest'],
isPure: true,
inputs: [
{
name: 'centerX',
displayName: 'Center X',
type: 'float',
defaultValue: 0
},
{
name: 'centerY',
displayName: 'Center Y',
type: 'float',
defaultValue: 0
},
{
name: 'k',
displayName: 'K',
type: 'int',
defaultValue: 5
},
{
name: 'maxDistance',
displayName: 'Max Distance',
type: 'float',
defaultValue: 1000
}
],
outputs: [
{
name: 'results',
displayName: 'Results',
type: 'array'
},
{
name: 'count',
displayName: 'Count',
type: 'int'
}
],
color: '#4a9eff'
};
/**
* @zh FindKNearest 节点执行器
* @en FindKNearest node executor
*/
export class FindKNearestExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as SpatialContext;
const centerX = ctx.evaluateInput(node.id, 'centerX', 0) as number;
const centerY = ctx.evaluateInput(node.id, 'centerY', 0) as number;
const k = ctx.evaluateInput(node.id, 'k', 5) as number;
const maxDistance = ctx.evaluateInput(node.id, 'maxDistance', 1000) as number;
const results = ctx.spatialQuery?.findKNearest({ x: centerX, y: centerY }, k, maxDistance) ?? [];
return {
outputs: {
results,
count: results.length
}
};
}
}
// =============================================================================
// Raycast 节点 | Raycast Node
// =============================================================================
/**
* @zh Raycast 节点模板
* @en Raycast node template
*/
export const RaycastTemplate: BlueprintNodeTemplate = {
type: 'Raycast',
title: 'Raycast',
category: 'entity',
description: 'Cast a ray and get all hits / 发射射线并获取所有命中',
keywords: ['spatial', 'raycast', 'ray', 'hit', 'query'],
menuPath: ['Spatial', 'Raycast'],
isPure: true,
inputs: [
{
name: 'originX',
displayName: 'Origin X',
type: 'float',
defaultValue: 0
},
{
name: 'originY',
displayName: 'Origin Y',
type: 'float',
defaultValue: 0
},
{
name: 'directionX',
displayName: 'Direction X',
type: 'float',
defaultValue: 1
},
{
name: 'directionY',
displayName: 'Direction Y',
type: 'float',
defaultValue: 0
},
{
name: 'maxDistance',
displayName: 'Max Distance',
type: 'float',
defaultValue: 1000
}
],
outputs: [
{
name: 'hits',
displayName: 'Hits',
type: 'array'
},
{
name: 'hitCount',
displayName: 'Hit Count',
type: 'int'
},
{
name: 'hasHit',
displayName: 'Has Hit',
type: 'bool'
}
],
color: '#4a9eff'
};
/**
* @zh Raycast 节点执行器
* @en Raycast node executor
*/
export class RaycastExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as SpatialContext;
const originX = ctx.evaluateInput(node.id, 'originX', 0) as number;
const originY = ctx.evaluateInput(node.id, 'originY', 0) as number;
const directionX = ctx.evaluateInput(node.id, 'directionX', 1) as number;
const directionY = ctx.evaluateInput(node.id, 'directionY', 0) as number;
const maxDistance = ctx.evaluateInput(node.id, 'maxDistance', 1000) as number;
const hits = ctx.spatialQuery?.raycast(
{ x: originX, y: originY },
{ x: directionX, y: directionY },
maxDistance
) ?? [];
return {
outputs: {
hits,
hitCount: hits.length,
hasHit: hits.length > 0
}
};
}
}
/**
* @zh RaycastFirst 节点模板
* @en RaycastFirst node template
*/
export const RaycastFirstTemplate: BlueprintNodeTemplate = {
type: 'RaycastFirst',
title: 'Raycast First',
category: 'entity',
description: 'Cast a ray and get first hit / 发射射线并获取第一个命中',
keywords: ['spatial', 'raycast', 'ray', 'first', 'hit', 'query'],
menuPath: ['Spatial', 'Raycast First'],
isPure: true,
inputs: [
{
name: 'originX',
displayName: 'Origin X',
type: 'float',
defaultValue: 0
},
{
name: 'originY',
displayName: 'Origin Y',
type: 'float',
defaultValue: 0
},
{
name: 'directionX',
displayName: 'Direction X',
type: 'float',
defaultValue: 1
},
{
name: 'directionY',
displayName: 'Direction Y',
type: 'float',
defaultValue: 0
},
{
name: 'maxDistance',
displayName: 'Max Distance',
type: 'float',
defaultValue: 1000
}
],
outputs: [
{
name: 'hit',
displayName: 'Hit',
type: 'object'
},
{
name: 'target',
displayName: 'Target',
type: 'any'
},
{
name: 'hitPointX',
displayName: 'Hit Point X',
type: 'float'
},
{
name: 'hitPointY',
displayName: 'Hit Point Y',
type: 'float'
},
{
name: 'distance',
displayName: 'Distance',
type: 'float'
},
{
name: 'hasHit',
displayName: 'Has Hit',
type: 'bool'
}
],
color: '#4a9eff'
};
/**
* @zh RaycastFirst 节点执行器
* @en RaycastFirst node executor
*/
export class RaycastFirstExecutor implements INodeExecutor {
execute(node: BlueprintNode, context: unknown): ExecutionResult {
const ctx = context as SpatialContext;
const originX = ctx.evaluateInput(node.id, 'originX', 0) as number;
const originY = ctx.evaluateInput(node.id, 'originY', 0) as number;
const directionX = ctx.evaluateInput(node.id, 'directionX', 1) as number;
const directionY = ctx.evaluateInput(node.id, 'directionY', 0) as number;
const maxDistance = ctx.evaluateInput(node.id, 'maxDistance', 1000) as number;
const hit = ctx.spatialQuery?.raycastFirst(
{ x: originX, y: originY },
{ x: directionX, y: directionY },
maxDistance
) ?? null;
return {
outputs: {
hit,
target: hit?.target ?? null,
hitPointX: hit?.point.x ?? 0,
hitPointY: hit?.point.y ?? 0,
distance: hit?.distance ?? 0,
hasHit: hit !== null
}
};
}
}
// =============================================================================
// 节点定义集合 | Node Definition Collection
// =============================================================================
/**
* @zh 空间查询节点定义
* @en Spatial query node definitions
*/
export const SpatialQueryNodeDefinitions = [
{ template: FindInRadiusTemplate, executor: new FindInRadiusExecutor() },
{ template: FindInRectTemplate, executor: new FindInRectExecutor() },
{ template: FindNearestTemplate, executor: new FindNearestExecutor() },
{ template: FindKNearestTemplate, executor: new FindKNearestExecutor() },
{ template: RaycastTemplate, executor: new RaycastExecutor() },
{ template: RaycastFirstTemplate, executor: new RaycastFirstExecutor() }
];

View File

@@ -0,0 +1,23 @@
/**
* @zh 空间查询蓝图节点导出
* @en Spatial Query Blueprint Nodes Export
*/
export {
// Templates
FindInRadiusTemplate,
FindInRectTemplate,
FindNearestTemplate,
FindKNearestTemplate,
RaycastTemplate,
RaycastFirstTemplate,
// Executors
FindInRadiusExecutor,
FindInRectExecutor,
FindNearestExecutor,
FindKNearestExecutor,
RaycastExecutor,
RaycastFirstExecutor,
// Collection
SpatialQueryNodeDefinitions
} from './SpatialQueryNodes';