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:
73
packages/framework/network/src/systems/NetworkInputSystem.ts
Normal file
73
packages/framework/network/src/systems/NetworkInputSystem.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { EntitySystem, Matcher } from '@esengine/ecs-framework';
|
||||
import type { IPlayerInput } from '@esengine/network-protocols';
|
||||
import type { NetworkService } from '../services/NetworkService';
|
||||
|
||||
/**
|
||||
* 网络输入系统
|
||||
* Network input system
|
||||
*
|
||||
* 收集本地玩家输入并发送到服务器。
|
||||
* Collects local player input and sends to server.
|
||||
*/
|
||||
export class NetworkInputSystem extends EntitySystem {
|
||||
private _networkService: NetworkService;
|
||||
private _frame: number = 0;
|
||||
private _inputQueue: IPlayerInput[] = [];
|
||||
|
||||
constructor(networkService: NetworkService) {
|
||||
// 不查询任何实体,此系统只处理输入
|
||||
// Don't query any entities, this system only handles input
|
||||
super(Matcher.nothing());
|
||||
this._networkService = networkService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理输入队列
|
||||
* Process input queue
|
||||
*/
|
||||
protected override process(): void {
|
||||
if (!this._networkService.isConnected) return;
|
||||
|
||||
this._frame++;
|
||||
|
||||
// 发送队列中的输入
|
||||
// Send queued inputs
|
||||
while (this._inputQueue.length > 0) {
|
||||
const input = this._inputQueue.shift()!;
|
||||
input.frame = this._frame;
|
||||
this._networkService.sendInput(input);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加移动输入
|
||||
* Add move input
|
||||
*/
|
||||
public addMoveInput(x: number, y: number): void {
|
||||
this._inputQueue.push({
|
||||
frame: 0,
|
||||
moveDir: { x, y }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加动作输入
|
||||
* Add action input
|
||||
*/
|
||||
public addActionInput(action: string): void {
|
||||
const lastInput = this._inputQueue[this._inputQueue.length - 1];
|
||||
if (lastInput) {
|
||||
lastInput.actions = lastInput.actions || [];
|
||||
lastInput.actions.push(action);
|
||||
} else {
|
||||
this._inputQueue.push({
|
||||
frame: 0,
|
||||
actions: [action]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected override onDestroy(): void {
|
||||
this._inputQueue.length = 0;
|
||||
}
|
||||
}
|
||||
101
packages/framework/network/src/systems/NetworkSpawnSystem.ts
Normal file
101
packages/framework/network/src/systems/NetworkSpawnSystem.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { EntitySystem, Entity, type Scene, Matcher } from '@esengine/ecs-framework';
|
||||
import type { MsgSpawn, MsgDespawn } from '@esengine/network-protocols';
|
||||
import { NetworkIdentity } from '../components/NetworkIdentity';
|
||||
import { NetworkTransform } from '../components/NetworkTransform';
|
||||
import type { NetworkService } from '../services/NetworkService';
|
||||
import type { NetworkSyncSystem } from './NetworkSyncSystem';
|
||||
|
||||
/**
|
||||
* 预制体工厂函数类型
|
||||
* Prefab factory function type
|
||||
*/
|
||||
export type PrefabFactory = (scene: Scene, spawn: MsgSpawn) => Entity;
|
||||
|
||||
/**
|
||||
* 网络生成系统
|
||||
* Network spawn system
|
||||
*
|
||||
* 处理网络实体的生成和销毁。
|
||||
* Handles spawning and despawning of networked entities.
|
||||
*/
|
||||
export class NetworkSpawnSystem extends EntitySystem {
|
||||
private _networkService: NetworkService;
|
||||
private _syncSystem: NetworkSyncSystem;
|
||||
private _prefabFactories: Map<string, PrefabFactory> = new Map();
|
||||
|
||||
constructor(networkService: NetworkService, syncSystem: NetworkSyncSystem) {
|
||||
// 不查询任何实体,此系统只响应网络消息
|
||||
// Don't query any entities, this system only responds to network messages
|
||||
super(Matcher.nothing());
|
||||
this._networkService = networkService;
|
||||
this._syncSystem = syncSystem;
|
||||
}
|
||||
|
||||
protected override onInitialize(): void {
|
||||
this._networkService.setCallbacks({
|
||||
onSpawn: this._handleSpawn.bind(this),
|
||||
onDespawn: this._handleDespawn.bind(this)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册预制体工厂
|
||||
* Register prefab factory
|
||||
*/
|
||||
public registerPrefab(prefabType: string, factory: PrefabFactory): void {
|
||||
this._prefabFactories.set(prefabType, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销预制体工厂
|
||||
* Unregister prefab factory
|
||||
*/
|
||||
public unregisterPrefab(prefabType: string): void {
|
||||
this._prefabFactories.delete(prefabType);
|
||||
}
|
||||
|
||||
private _handleSpawn(msg: MsgSpawn): void {
|
||||
if (!this.scene) return;
|
||||
|
||||
const factory = this._prefabFactories.get(msg.prefab);
|
||||
if (!factory) {
|
||||
this.logger.warn(`Unknown prefab: ${msg.prefab}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const entity = factory(this.scene, msg);
|
||||
|
||||
// 添加网络组件
|
||||
// Add network components
|
||||
const identity = entity.addComponent(new NetworkIdentity());
|
||||
identity.netId = msg.netId;
|
||||
identity.ownerId = msg.ownerId;
|
||||
identity.prefabType = msg.prefab;
|
||||
identity.bHasAuthority = msg.ownerId === this._networkService.clientId;
|
||||
identity.bIsLocalPlayer = identity.bHasAuthority;
|
||||
|
||||
const transform = entity.addComponent(new NetworkTransform());
|
||||
transform.setTarget(msg.pos.x, msg.pos.y, msg.rot);
|
||||
transform.snap();
|
||||
|
||||
// 注册到同步系统
|
||||
// Register to sync system
|
||||
this._syncSystem.registerEntity(msg.netId, entity.id);
|
||||
}
|
||||
|
||||
private _handleDespawn(msg: MsgDespawn): void {
|
||||
const entityId = this._syncSystem.getEntityId(msg.netId);
|
||||
if (entityId === undefined) return;
|
||||
|
||||
const entity = this.scene?.findEntityById(entityId);
|
||||
if (entity) {
|
||||
entity.destroy();
|
||||
}
|
||||
|
||||
this._syncSystem.unregisterEntity(msg.netId);
|
||||
}
|
||||
|
||||
protected override onDestroy(): void {
|
||||
this._prefabFactories.clear();
|
||||
}
|
||||
}
|
||||
104
packages/framework/network/src/systems/NetworkSyncSystem.ts
Normal file
104
packages/framework/network/src/systems/NetworkSyncSystem.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { EntitySystem, Matcher, Time, type Entity } from '@esengine/ecs-framework';
|
||||
import type { MsgSync } from '@esengine/network-protocols';
|
||||
import { NetworkIdentity } from '../components/NetworkIdentity';
|
||||
import { NetworkTransform } from '../components/NetworkTransform';
|
||||
import type { NetworkService } from '../services/NetworkService';
|
||||
|
||||
/**
|
||||
* 网络同步系统
|
||||
* Network sync system
|
||||
*
|
||||
* 处理网络实体的状态同步和插值。
|
||||
* Handles state synchronization and interpolation for networked entities.
|
||||
*/
|
||||
export class NetworkSyncSystem extends EntitySystem {
|
||||
private _networkService: NetworkService;
|
||||
private _netIdToEntity: Map<number, number> = new Map();
|
||||
|
||||
constructor(networkService: NetworkService) {
|
||||
super(Matcher.all(NetworkIdentity, NetworkTransform));
|
||||
this._networkService = networkService;
|
||||
}
|
||||
|
||||
protected override onInitialize(): void {
|
||||
this._networkService.setCallbacks({
|
||||
onSync: this._handleSync.bind(this)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理实体列表
|
||||
* Process entities
|
||||
*/
|
||||
protected override process(entities: readonly Entity[]): void {
|
||||
const deltaTime = Time.deltaTime;
|
||||
|
||||
for (const entity of entities) {
|
||||
const transform = this.requireComponent(entity, NetworkTransform);
|
||||
const identity = this.requireComponent(entity, NetworkIdentity);
|
||||
|
||||
// 只有非本地玩家需要插值
|
||||
// Only non-local players need interpolation
|
||||
if (!identity.bHasAuthority && transform.bInterpolate) {
|
||||
this._interpolate(transform, deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册网络实体
|
||||
* Register network entity
|
||||
*/
|
||||
public registerEntity(netId: number, entityId: number): void {
|
||||
this._netIdToEntity.set(netId, entityId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销网络实体
|
||||
* Unregister network entity
|
||||
*/
|
||||
public unregisterEntity(netId: number): void {
|
||||
this._netIdToEntity.delete(netId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据网络 ID 获取实体 ID
|
||||
* Get entity ID by network ID
|
||||
*/
|
||||
public getEntityId(netId: number): number | undefined {
|
||||
return this._netIdToEntity.get(netId);
|
||||
}
|
||||
|
||||
private _handleSync(msg: MsgSync): void {
|
||||
for (const state of msg.entities) {
|
||||
const entityId = this._netIdToEntity.get(state.netId);
|
||||
if (entityId === undefined) continue;
|
||||
|
||||
const entity = this.scene?.findEntityById(entityId);
|
||||
if (!entity) continue;
|
||||
|
||||
const transform = entity.getComponent(NetworkTransform);
|
||||
if (transform && state.pos) {
|
||||
transform.setTarget(state.pos.x, state.pos.y, state.rot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _interpolate(transform: NetworkTransform, deltaTime: number): void {
|
||||
const t = Math.min(1, transform.lerpSpeed * deltaTime);
|
||||
|
||||
transform.currentX += (transform.targetX - transform.currentX) * t;
|
||||
transform.currentY += (transform.targetY - transform.currentY) * t;
|
||||
|
||||
// 角度插值需要处理环绕
|
||||
// Angle interpolation needs to handle wrap-around
|
||||
let angleDiff = transform.targetRotation - transform.currentRotation;
|
||||
while (angleDiff > Math.PI) angleDiff -= Math.PI * 2;
|
||||
while (angleDiff < -Math.PI) angleDiff += Math.PI * 2;
|
||||
transform.currentRotation += angleDiff * t;
|
||||
}
|
||||
|
||||
protected override onDestroy(): void {
|
||||
this._netIdToEntity.clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user