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,364 @@
/**
* @zh 网格地图实现
* @en Grid Map Implementation
*/
import type {
IPathfindingMap,
IPathNode,
IPoint,
HeuristicFunction
} from '../core/IPathfinding';
import { createPoint, octileDistance } from '../core/IPathfinding';
// =============================================================================
// 网格节点 | Grid Node
// =============================================================================
/**
* @zh 网格节点
* @en Grid node
*/
export class GridNode implements IPathNode {
readonly id: string;
readonly position: IPoint;
readonly x: number;
readonly y: number;
cost: number;
walkable: boolean;
constructor(x: number, y: number, walkable: boolean = true, cost: number = 1) {
this.x = x;
this.y = y;
this.id = `${x},${y}`;
this.position = createPoint(x, y);
this.walkable = walkable;
this.cost = cost;
}
}
// =============================================================================
// 移动方向 | Movement Directions
// =============================================================================
/**
* @zh 4方向偏移 (上下左右)
* @en 4-directional offsets (up, down, left, right)
*/
export const DIRECTIONS_4 = [
{ dx: 0, dy: -1 }, // Up
{ dx: 1, dy: 0 }, // Right
{ dx: 0, dy: 1 }, // Down
{ dx: -1, dy: 0 } // Left
] as const;
/**
* @zh 8方向偏移 (含对角线)
* @en 8-directional offsets (including diagonals)
*/
export const DIRECTIONS_8 = [
{ dx: 0, dy: -1 }, // Up
{ dx: 1, dy: -1 }, // Up-Right
{ dx: 1, dy: 0 }, // Right
{ dx: 1, dy: 1 }, // Down-Right
{ dx: 0, dy: 1 }, // Down
{ dx: -1, dy: 1 }, // Down-Left
{ dx: -1, dy: 0 }, // Left
{ dx: -1, dy: -1 } // Up-Left
] as const;
// =============================================================================
// 网格地图配置 | Grid Map Options
// =============================================================================
/**
* @zh 网格地图配置
* @en Grid map options
*/
export interface IGridMapOptions {
/** @zh 是否允许对角移动 @en Allow diagonal movement */
allowDiagonal?: boolean;
/** @zh 对角移动代价 @en Diagonal movement cost */
diagonalCost?: number;
/** @zh 是否避免穿角 @en Avoid corner cutting */
avoidCorners?: boolean;
/** @zh 启发式函数 @en Heuristic function */
heuristic?: HeuristicFunction;
}
/**
* @zh 默认网格地图配置
* @en Default grid map options
*/
export const DEFAULT_GRID_OPTIONS: Required<IGridMapOptions> = {
allowDiagonal: true,
diagonalCost: Math.SQRT2,
avoidCorners: true,
heuristic: octileDistance
};
// =============================================================================
// 网格地图 | Grid Map
// =============================================================================
/**
* @zh 网格地图
* @en Grid Map
*
* @zh 基于二维数组的网格地图实现支持4方向和8方向移动
* @en Grid map implementation based on 2D array, supports 4 and 8 directional movement
*
* @example
* ```typescript
* // Create a 10x10 grid
* const grid = new GridMap(10, 10);
*
* // Set some cells as obstacles
* grid.setWalkable(5, 5, false);
* grid.setWalkable(5, 6, false);
*
* // Use with pathfinder
* const pathfinder = new AStarPathfinder(grid);
* const result = pathfinder.findPath(0, 0, 9, 9);
* ```
*/
export class GridMap implements IPathfindingMap {
readonly width: number;
readonly height: number;
private readonly nodes: GridNode[][];
private readonly options: Required<IGridMapOptions>;
constructor(width: number, height: number, options?: IGridMapOptions) {
this.width = width;
this.height = height;
this.options = { ...DEFAULT_GRID_OPTIONS, ...options };
this.nodes = this.createNodes();
}
/**
* @zh 创建网格节点
* @en Create grid nodes
*/
private createNodes(): GridNode[][] {
const nodes: GridNode[][] = [];
for (let y = 0; y < this.height; y++) {
nodes[y] = [];
for (let x = 0; x < this.width; x++) {
nodes[y][x] = new GridNode(x, y, true, 1);
}
}
return nodes;
}
/**
* @zh 获取指定位置的节点
* @en Get node at position
*/
getNodeAt(x: number, y: number): GridNode | null {
if (!this.isInBounds(x, y)) {
return null;
}
return this.nodes[y][x];
}
/**
* @zh 检查坐标是否在边界内
* @en Check if coordinates are within bounds
*/
isInBounds(x: number, y: number): boolean {
return x >= 0 && x < this.width && y >= 0 && y < this.height;
}
/**
* @zh 检查位置是否可通行
* @en Check if position is walkable
*/
isWalkable(x: number, y: number): boolean {
const node = this.getNodeAt(x, y);
return node !== null && node.walkable;
}
/**
* @zh 设置位置是否可通行
* @en Set position walkability
*/
setWalkable(x: number, y: number, walkable: boolean): void {
const node = this.getNodeAt(x, y);
if (node) {
node.walkable = walkable;
}
}
/**
* @zh 设置位置的移动代价
* @en Set movement cost at position
*/
setCost(x: number, y: number, cost: number): void {
const node = this.getNodeAt(x, y);
if (node) {
node.cost = cost;
}
}
/**
* @zh 获取节点的邻居
* @en Get neighbors of a node
*/
getNeighbors(node: IPathNode): GridNode[] {
const neighbors: GridNode[] = [];
const { x, y } = node.position;
const directions = this.options.allowDiagonal ? DIRECTIONS_8 : DIRECTIONS_4;
for (let i = 0; i < directions.length; i++) {
const dir = directions[i];
const nx = x + dir.dx;
const ny = y + dir.dy;
if (!this.isInBounds(nx, ny)) {
continue;
}
const neighbor = this.nodes[ny][nx];
if (!neighbor.walkable) {
continue;
}
// Check corner cutting for diagonal movement
if (this.options.avoidCorners && dir.dx !== 0 && dir.dy !== 0) {
const horizontal = this.getNodeAt(x + dir.dx, y);
const vertical = this.getNodeAt(x, y + dir.dy);
if (!horizontal?.walkable || !vertical?.walkable) {
continue;
}
}
neighbors.push(neighbor);
}
return neighbors;
}
/**
* @zh 计算启发式距离
* @en Calculate heuristic distance
*/
heuristic(a: IPoint, b: IPoint): number {
return this.options.heuristic(a, b);
}
/**
* @zh 计算移动代价
* @en Calculate movement cost
*/
getMovementCost(from: IPathNode, to: IPathNode): number {
const dx = Math.abs(from.position.x - to.position.x);
const dy = Math.abs(from.position.y - to.position.y);
// Diagonal movement
if (dx !== 0 && dy !== 0) {
return to.cost * this.options.diagonalCost;
}
// Cardinal movement
return to.cost;
}
/**
* @zh 从二维数组加载地图
* @en Load map from 2D array
*
* @param data - @zh 0=可通行非0=不可通行 @en 0=walkable, non-0=blocked
*/
loadFromArray(data: number[][]): void {
for (let y = 0; y < Math.min(data.length, this.height); y++) {
for (let x = 0; x < Math.min(data[y].length, this.width); x++) {
this.nodes[y][x].walkable = data[y][x] === 0;
}
}
}
/**
* @zh 从字符串加载地图
* @en Load map from string
*
* @param str - @zh 地图字符串,'.'=可通行,'#'=障碍 @en Map string, '.'=walkable, '#'=blocked
*/
loadFromString(str: string): void {
const lines = str.trim().split('\n');
for (let y = 0; y < Math.min(lines.length, this.height); y++) {
const line = lines[y];
for (let x = 0; x < Math.min(line.length, this.width); x++) {
this.nodes[y][x].walkable = line[x] !== '#';
}
}
}
/**
* @zh 导出为字符串
* @en Export to string
*/
toString(): string {
let result = '';
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
result += this.nodes[y][x].walkable ? '.' : '#';
}
result += '\n';
}
return result;
}
/**
* @zh 重置所有节点为可通行
* @en Reset all nodes to walkable
*/
reset(): void {
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
this.nodes[y][x].walkable = true;
this.nodes[y][x].cost = 1;
}
}
}
/**
* @zh 设置矩形区域的通行性
* @en Set walkability for a rectangle region
*/
setRectWalkable(
x: number,
y: number,
width: number,
height: number,
walkable: boolean
): void {
for (let dy = 0; dy < height; dy++) {
for (let dx = 0; dx < width; dx++) {
this.setWalkable(x + dx, y + dy, walkable);
}
}
}
}
// =============================================================================
// 工厂函数 | Factory Function
// =============================================================================
/**
* @zh 创建网格地图
* @en Create grid map
*/
export function createGridMap(
width: number,
height: number,
options?: IGridMapOptions
): GridMap {
return new GridMap(width, height, options);
}

View File

@@ -0,0 +1,14 @@
/**
* @zh 网格地图模块
* @en Grid Map Module
*/
export {
GridNode,
GridMap,
createGridMap,
DIRECTIONS_4,
DIRECTIONS_8,
type IGridMapOptions,
DEFAULT_GRID_OPTIONS
} from './GridMap';