2025-08-10 12:35:39 +08:00
|
|
|
/**
|
|
|
|
|
* 网络库基础功能测试
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import 'reflect-metadata';
|
|
|
|
|
import { NetworkManager } from '../src/NetworkManager';
|
|
|
|
|
import { NetworkIdentity } from '../src/NetworkIdentity';
|
|
|
|
|
import { NetworkBehaviour } from '../src/NetworkBehaviour';
|
|
|
|
|
import { SyncVar } from '../src/decorators/SyncVar';
|
|
|
|
|
import { ClientRpc } from '../src/decorators/ClientRpc';
|
|
|
|
|
import { Command } from '../src/decorators/Command';
|
2025-08-11 11:11:56 +08:00
|
|
|
import { NetworkRegistry } from '../src/Core/NetworkRegistry';
|
|
|
|
|
import { SyncVarManager } from '../src/Core/SyncVarManager';
|
|
|
|
|
import { RpcManager } from '../src/Core/RpcManager';
|
2025-08-10 12:35:39 +08:00
|
|
|
|
|
|
|
|
// 测试用的玩家组件
|
|
|
|
|
class TestPlayerComponent extends NetworkBehaviour {
|
|
|
|
|
@SyncVar({ onChanged: 'onHealthChanged' })
|
|
|
|
|
public health: number = 100;
|
|
|
|
|
|
|
|
|
|
@SyncVar()
|
|
|
|
|
public playerName: string = 'Player';
|
|
|
|
|
|
|
|
|
|
public lastHealthChangeValue: number = 0;
|
|
|
|
|
|
|
|
|
|
@ClientRpc()
|
|
|
|
|
public showDamageEffect(damage: number, position: { x: number; y: number }): void {
|
|
|
|
|
console.log(`显示伤害特效: ${damage} at (${position.x}, ${position.y})`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Command()
|
|
|
|
|
public movePlayer(direction: { x: number; y: number }): void {
|
|
|
|
|
console.log(`移动玩家: (${direction.x}, ${direction.y})`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private onHealthChanged(oldValue: number, newValue: number): void {
|
|
|
|
|
this.lastHealthChangeValue = newValue;
|
|
|
|
|
console.log(`生命值变化: ${oldValue} -> ${newValue}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 模拟实体类
|
|
|
|
|
class MockEntity {
|
|
|
|
|
private components: any[] = [];
|
|
|
|
|
public name: string = 'TestEntity';
|
|
|
|
|
|
|
|
|
|
public addComponent(component: any): void {
|
|
|
|
|
this.components.push(component);
|
|
|
|
|
component.entity = this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getComponent(componentType: any): any {
|
|
|
|
|
return this.components.find(c => c instanceof componentType);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getComponents(): any[] {
|
|
|
|
|
return this.components;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe('网络库基础功能测试', () => {
|
|
|
|
|
let networkManager: NetworkManager;
|
|
|
|
|
let entity: MockEntity;
|
|
|
|
|
let networkIdentity: NetworkIdentity;
|
|
|
|
|
let playerComponent: TestPlayerComponent;
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
// 重置单例
|
|
|
|
|
(NetworkManager as any)._instance = null;
|
|
|
|
|
NetworkRegistry.instance.reset();
|
|
|
|
|
SyncVarManager.instance.clearPendingChanges();
|
|
|
|
|
RpcManager.instance.clearPendingCalls();
|
|
|
|
|
|
|
|
|
|
// 创建网络管理器
|
|
|
|
|
networkManager = new NetworkManager();
|
|
|
|
|
|
|
|
|
|
// 创建测试实体
|
|
|
|
|
entity = new MockEntity();
|
|
|
|
|
networkIdentity = new NetworkIdentity();
|
|
|
|
|
playerComponent = new TestPlayerComponent();
|
|
|
|
|
|
|
|
|
|
entity.addComponent(networkIdentity);
|
|
|
|
|
entity.addComponent(playerComponent);
|
|
|
|
|
|
|
|
|
|
// 手动调用组件初始化以注册网络行为
|
|
|
|
|
playerComponent.start();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
|
networkManager?.destroy();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('网络身份管理', () => {
|
|
|
|
|
test('网络对象注册和查找', () => {
|
|
|
|
|
const networkId = networkManager.registerNetworkObject(entity);
|
|
|
|
|
|
|
|
|
|
expect(networkId).toBeGreaterThan(0);
|
|
|
|
|
expect(networkIdentity.networkId).toBe(networkId);
|
|
|
|
|
|
|
|
|
|
const foundIdentity = NetworkRegistry.instance.find(networkId);
|
|
|
|
|
expect(foundIdentity).toBe(networkIdentity);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('网络权威设置', () => {
|
|
|
|
|
networkManager.registerNetworkObject(entity);
|
|
|
|
|
|
|
|
|
|
expect(networkIdentity.hasAuthority).toBe(false);
|
|
|
|
|
expect(playerComponent.hasAuthority).toBe(false);
|
|
|
|
|
|
|
|
|
|
networkIdentity.setAuthority(true, 1);
|
|
|
|
|
expect(networkIdentity.hasAuthority).toBe(true);
|
|
|
|
|
expect(networkIdentity.ownerId).toBe(1);
|
|
|
|
|
expect(playerComponent.hasAuthority).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('本地玩家设置', () => {
|
|
|
|
|
networkManager.registerNetworkObject(entity);
|
|
|
|
|
|
|
|
|
|
expect(networkIdentity.isLocalPlayer).toBe(false);
|
|
|
|
|
expect(playerComponent.isLocalPlayer).toBe(false);
|
|
|
|
|
|
|
|
|
|
NetworkRegistry.instance.setLocalPlayer(networkIdentity);
|
|
|
|
|
expect(networkIdentity.isLocalPlayer).toBe(true);
|
|
|
|
|
expect(networkIdentity.hasAuthority).toBe(true);
|
|
|
|
|
expect(playerComponent.isLocalPlayer).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('SyncVar 同步变量', () => {
|
|
|
|
|
test('SyncVar 属性同步', () => {
|
|
|
|
|
networkManager.registerNetworkObject(entity);
|
|
|
|
|
networkIdentity.setAuthority(true, 1);
|
|
|
|
|
|
|
|
|
|
// 修改同步变量
|
|
|
|
|
playerComponent.health = 80;
|
|
|
|
|
playerComponent.playerName = 'TestPlayer';
|
|
|
|
|
|
|
|
|
|
// 检查待同步消息
|
|
|
|
|
const messages = SyncVarManager.instance.getPendingMessages();
|
|
|
|
|
expect(messages.length).toBeGreaterThanOrEqual(2);
|
|
|
|
|
|
|
|
|
|
// 验证消息内容
|
2025-08-11 11:11:56 +08:00
|
|
|
const healthMessage = messages.find((m: any) => m.propertyName === 'health');
|
2025-08-10 12:35:39 +08:00
|
|
|
expect(healthMessage).toBeDefined();
|
|
|
|
|
expect(healthMessage?.value).toBe(80);
|
|
|
|
|
|
2025-08-11 11:11:56 +08:00
|
|
|
const nameMessage = messages.find((m: any) => m.propertyName === 'playerName');
|
2025-08-10 12:35:39 +08:00
|
|
|
expect(nameMessage).toBeDefined();
|
|
|
|
|
expect(nameMessage?.value).toBe('TestPlayer');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('SyncVar 变化回调', () => {
|
|
|
|
|
networkManager.registerNetworkObject(entity);
|
|
|
|
|
networkIdentity.setAuthority(true, 1);
|
|
|
|
|
|
|
|
|
|
expect(playerComponent.lastHealthChangeValue).toBe(0);
|
|
|
|
|
|
|
|
|
|
playerComponent.health = 75;
|
|
|
|
|
expect(playerComponent.lastHealthChangeValue).toBe(75);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('权威验证', () => {
|
|
|
|
|
networkManager.registerNetworkObject(entity);
|
|
|
|
|
|
|
|
|
|
// 没有权威时不应该能修改
|
|
|
|
|
expect(networkIdentity.hasAuthority).toBe(false);
|
|
|
|
|
|
|
|
|
|
const originalHealth = playerComponent.health;
|
|
|
|
|
playerComponent.health = 50;
|
|
|
|
|
|
|
|
|
|
// 检查是否有待同步消息(没有权威应该没有)
|
|
|
|
|
const messages = SyncVarManager.instance.getPendingMessages();
|
|
|
|
|
expect(messages.length).toBe(0);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('RPC 远程过程调用', () => {
|
|
|
|
|
test('RPC 方法注册', () => {
|
|
|
|
|
networkManager.registerNetworkObject(entity);
|
|
|
|
|
|
|
|
|
|
const stats = RpcManager.instance.getStats();
|
|
|
|
|
expect(stats.registeredComponents).toBeGreaterThan(0);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('RPC 消息生成', () => {
|
|
|
|
|
networkManager.registerNetworkObject(entity);
|
|
|
|
|
|
|
|
|
|
// 模拟服务端调用ClientRpc
|
|
|
|
|
if (NetworkManager.isServer) {
|
|
|
|
|
playerComponent.showDamageEffect(25, { x: 100, y: 200 });
|
|
|
|
|
|
|
|
|
|
const rpcMessages = RpcManager.instance.getPendingRpcMessages();
|
2025-08-11 11:11:56 +08:00
|
|
|
const damageMessage = rpcMessages.find((m: any) => m.methodName === 'showDamageEffect');
|
2025-08-10 12:35:39 +08:00
|
|
|
expect(damageMessage).toBeDefined();
|
|
|
|
|
expect(damageMessage?.isClientRpc).toBe(true);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('网络管理器状态', () => {
|
|
|
|
|
test('网络端类型判断', () => {
|
|
|
|
|
// 默认应该是客户端
|
|
|
|
|
expect(NetworkManager.isClient).toBe(true);
|
|
|
|
|
expect(NetworkManager.isServer).toBe(false);
|
|
|
|
|
expect(NetworkManager.isConnected).toBe(false);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('连接状态管理', () => {
|
|
|
|
|
expect(networkManager.getConnectionState()).toBe('disconnected');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('网络统计信息', () => {
|
|
|
|
|
const stats = networkManager.getStats();
|
|
|
|
|
expect(stats).toHaveProperty('connectionCount');
|
|
|
|
|
expect(stats).toHaveProperty('messagesSent');
|
|
|
|
|
expect(stats).toHaveProperty('messagesReceived');
|
|
|
|
|
expect(stats.connectionCount).toBe(0);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('网络注册表管理', () => {
|
|
|
|
|
test('多个网络对象管理', () => {
|
|
|
|
|
// 创建多个实体
|
|
|
|
|
const entity2 = new MockEntity();
|
|
|
|
|
const networkIdentity2 = new NetworkIdentity();
|
|
|
|
|
entity2.addComponent(networkIdentity2);
|
|
|
|
|
const playerComponent2 = new TestPlayerComponent();
|
|
|
|
|
entity2.addComponent(playerComponent2);
|
|
|
|
|
playerComponent2.start();
|
|
|
|
|
|
|
|
|
|
const networkId1 = networkManager.registerNetworkObject(entity);
|
|
|
|
|
const networkId2 = networkManager.registerNetworkObject(entity2);
|
|
|
|
|
|
|
|
|
|
expect(networkId1).not.toBe(networkId2);
|
|
|
|
|
expect(NetworkRegistry.instance.getAllNetworkObjects().length).toBe(2);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('网络对象注销', () => {
|
|
|
|
|
const networkId = networkManager.registerNetworkObject(entity);
|
|
|
|
|
|
|
|
|
|
expect(NetworkRegistry.instance.exists(networkId)).toBe(true);
|
|
|
|
|
|
|
|
|
|
NetworkRegistry.instance.unregister(networkId);
|
|
|
|
|
|
|
|
|
|
expect(NetworkRegistry.instance.exists(networkId)).toBe(false);
|
|
|
|
|
expect(NetworkRegistry.instance.find(networkId)).toBeNull();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('按所有者查找对象', () => {
|
|
|
|
|
const networkId = networkManager.registerNetworkObject(entity);
|
|
|
|
|
networkIdentity.setAuthority(true, 123);
|
|
|
|
|
|
|
|
|
|
const ownedObjects = NetworkRegistry.instance.getObjectsByOwner(123);
|
|
|
|
|
expect(ownedObjects.length).toBe(1);
|
|
|
|
|
expect(ownedObjects[0]).toBe(networkIdentity);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|