feat(network): 基于 TSRPC 的网络同步模块 (#318)
- network-protocols: 共享协议包,使用 TSRPC CLI 生成完整类型验证 - network: 浏览器客户端,提供 NetworkPlugin、NetworkService 和同步系统 - network-server: Node.js 服务端,提供 GameServer 和房间管理
This commit is contained in:
104
packages/network/src/systems/NetworkSyncSystem.ts
Normal file
104
packages/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