csterm-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/GameRunner.d.ts +54 -0
- package/dist/GameRunner.js +998 -0
- package/dist/LockstepRunner.d.ts +39 -0
- package/dist/LockstepRunner.js +276 -0
- package/dist/Room.d.ts +43 -0
- package/dist/Room.js +498 -0
- package/dist/RoomManager.d.ts +29 -0
- package/dist/RoomManager.js +277 -0
- package/dist/VoiceRelay.d.ts +62 -0
- package/dist/VoiceRelay.js +167 -0
- package/dist/hub/HubServer.d.ts +37 -0
- package/dist/hub/HubServer.js +266 -0
- package/dist/hub/PoolRegistry.d.ts +35 -0
- package/dist/hub/PoolRegistry.js +185 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +175 -0
- package/dist/pool/GameServer.d.ts +29 -0
- package/dist/pool/GameServer.js +185 -0
- package/dist/pool/PoolClient.d.ts +48 -0
- package/dist/pool/PoolClient.js +204 -0
- package/dist/protocol.d.ts +433 -0
- package/dist/protocol.js +59 -0
- package/dist/test/HeadlessClient.d.ts +44 -0
- package/dist/test/HeadlessClient.js +196 -0
- package/dist/test/testTeamSync.d.ts +2 -0
- package/dist/test/testTeamSync.js +82 -0
- package/dist/types.d.ts +164 -0
- package/dist/types.js +139 -0
- package/package.json +50 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { RoomManager } from '../RoomManager.js';
|
|
2
|
+
import { ServerConfig } from '../types.js';
|
|
3
|
+
export interface GameServerConfig extends ServerConfig {
|
|
4
|
+
serverName: string;
|
|
5
|
+
publicEndpoint: string;
|
|
6
|
+
hubUrl?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class GameServer {
|
|
9
|
+
private config;
|
|
10
|
+
private wss;
|
|
11
|
+
private httpServer;
|
|
12
|
+
private roomManager;
|
|
13
|
+
private poolClient;
|
|
14
|
+
private socketToClientId;
|
|
15
|
+
constructor(config?: Partial<GameServerConfig>);
|
|
16
|
+
start(): void;
|
|
17
|
+
stop(): void;
|
|
18
|
+
private handleConnection;
|
|
19
|
+
private connectToHub;
|
|
20
|
+
private calculateLoad;
|
|
21
|
+
getRoomManager(): RoomManager;
|
|
22
|
+
getStats(): {
|
|
23
|
+
clients: number;
|
|
24
|
+
rooms: number;
|
|
25
|
+
hubConnected: boolean;
|
|
26
|
+
};
|
|
27
|
+
isHubConnected(): boolean;
|
|
28
|
+
private printBanner;
|
|
29
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// Game Server - Handles actual game rooms and client connections
|
|
2
|
+
import { WebSocketServer } from 'ws';
|
|
3
|
+
import { createServer } from 'http';
|
|
4
|
+
import { RoomManager } from '../RoomManager.js';
|
|
5
|
+
import { PoolClient } from './PoolClient.js';
|
|
6
|
+
import { parseClientMessage } from '../protocol.js';
|
|
7
|
+
import { DEFAULT_SERVER_CONFIG } from '../types.js';
|
|
8
|
+
export class GameServer {
|
|
9
|
+
config;
|
|
10
|
+
wss = null;
|
|
11
|
+
httpServer = null;
|
|
12
|
+
roomManager;
|
|
13
|
+
poolClient = null;
|
|
14
|
+
socketToClientId = new WeakMap();
|
|
15
|
+
constructor(config = {}) {
|
|
16
|
+
this.config = {
|
|
17
|
+
...DEFAULT_SERVER_CONFIG,
|
|
18
|
+
serverName: 'CS-CLI Server',
|
|
19
|
+
publicEndpoint: `ws://localhost:${config.port || DEFAULT_SERVER_CONFIG.port}`,
|
|
20
|
+
...config,
|
|
21
|
+
};
|
|
22
|
+
this.roomManager = new RoomManager(this.config);
|
|
23
|
+
// Set up room event handlers for hub notifications
|
|
24
|
+
this.roomManager.onRoomCreated = (room) => {
|
|
25
|
+
this.poolClient?.notifyRoomCreated(room);
|
|
26
|
+
};
|
|
27
|
+
this.roomManager.onRoomClosed = (roomId) => {
|
|
28
|
+
this.poolClient?.notifyRoomClosed(roomId);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
// Start the game server
|
|
32
|
+
start() {
|
|
33
|
+
this.httpServer = createServer();
|
|
34
|
+
this.wss = new WebSocketServer({ server: this.httpServer });
|
|
35
|
+
this.wss.on('connection', (socket, request) => {
|
|
36
|
+
this.handleConnection(socket, request);
|
|
37
|
+
});
|
|
38
|
+
this.wss.on('error', (error) => {
|
|
39
|
+
console.error('[GameServer] WebSocket server error:', error);
|
|
40
|
+
});
|
|
41
|
+
this.httpServer.listen(this.config.port, () => {
|
|
42
|
+
this.printBanner();
|
|
43
|
+
});
|
|
44
|
+
// Connect to hub if configured
|
|
45
|
+
if (this.config.hubUrl) {
|
|
46
|
+
this.connectToHub();
|
|
47
|
+
}
|
|
48
|
+
// Stats logging
|
|
49
|
+
setInterval(() => {
|
|
50
|
+
const stats = this.roomManager.getStats();
|
|
51
|
+
if (stats.clients > 0 || stats.rooms > 0) {
|
|
52
|
+
console.log(`[GameServer] Stats: ${stats.clients} clients, ${stats.rooms} rooms`);
|
|
53
|
+
}
|
|
54
|
+
}, 60000);
|
|
55
|
+
}
|
|
56
|
+
// Stop the game server
|
|
57
|
+
stop() {
|
|
58
|
+
console.log('[GameServer] Shutting down...');
|
|
59
|
+
// Disconnect from hub
|
|
60
|
+
this.poolClient?.disconnect();
|
|
61
|
+
// Close all client connections
|
|
62
|
+
if (this.wss) {
|
|
63
|
+
this.wss.clients.forEach((socket) => {
|
|
64
|
+
socket.close(1001, 'Server shutting down');
|
|
65
|
+
});
|
|
66
|
+
this.wss.close();
|
|
67
|
+
this.wss = null;
|
|
68
|
+
}
|
|
69
|
+
// Stop room manager
|
|
70
|
+
this.roomManager.shutdown();
|
|
71
|
+
// Close HTTP server
|
|
72
|
+
if (this.httpServer) {
|
|
73
|
+
this.httpServer.close();
|
|
74
|
+
this.httpServer = null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Handle new client connection
|
|
78
|
+
handleConnection(socket, request) {
|
|
79
|
+
const clientIp = request.socket.remoteAddress || 'unknown';
|
|
80
|
+
const clientId = this.roomManager.addClient(socket);
|
|
81
|
+
this.socketToClientId.set(socket, clientId);
|
|
82
|
+
console.log(`[GameServer] New connection from ${clientIp} (${clientId})`);
|
|
83
|
+
socket.on('message', (data, isBinary) => {
|
|
84
|
+
try {
|
|
85
|
+
// Check for binary voice frames (first byte 0x01)
|
|
86
|
+
if (isBinary || (data.length > 0 && data[0] === 0x01)) {
|
|
87
|
+
// Route to room's binary handler
|
|
88
|
+
this.roomManager.handleBinaryData(clientId, data);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const message = parseClientMessage(data.toString());
|
|
92
|
+
if (message) {
|
|
93
|
+
this.roomManager.handleMessage(clientId, message);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
console.warn(`[GameServer] Invalid message from ${clientId}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
console.error(`[GameServer] Error processing message from ${clientId}:`, error);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
socket.on('close', (code) => {
|
|
104
|
+
console.log(`[GameServer] Connection closed: ${clientId} (code: ${code})`);
|
|
105
|
+
this.roomManager.removeClient(clientId);
|
|
106
|
+
});
|
|
107
|
+
socket.on('error', (error) => {
|
|
108
|
+
console.error(`[GameServer] Socket error for ${clientId}:`, error.message);
|
|
109
|
+
});
|
|
110
|
+
// Send initial room list
|
|
111
|
+
socket.send(JSON.stringify({
|
|
112
|
+
type: 'room_list',
|
|
113
|
+
rooms: this.roomManager.listRooms(),
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
// Connect to hub as a pool server
|
|
117
|
+
connectToHub() {
|
|
118
|
+
if (!this.config.hubUrl)
|
|
119
|
+
return;
|
|
120
|
+
console.log(`[GameServer] Connecting to hub at ${this.config.hubUrl}`);
|
|
121
|
+
this.poolClient = new PoolClient({
|
|
122
|
+
hubUrl: this.config.hubUrl,
|
|
123
|
+
serverName: this.config.serverName,
|
|
124
|
+
endpoint: this.config.publicEndpoint,
|
|
125
|
+
maxRooms: this.config.maxRooms,
|
|
126
|
+
}, {
|
|
127
|
+
onStatusChange: (status) => {
|
|
128
|
+
console.log(`[GameServer] Hub connection status: ${status}`);
|
|
129
|
+
},
|
|
130
|
+
onRegistered: (poolId) => {
|
|
131
|
+
console.log(`[GameServer] Registered with hub as ${poolId}`);
|
|
132
|
+
},
|
|
133
|
+
onRejected: (reason) => {
|
|
134
|
+
console.error(`[GameServer] Hub rejected registration: ${reason}`);
|
|
135
|
+
},
|
|
136
|
+
onError: (error) => {
|
|
137
|
+
console.error(`[GameServer] Hub connection error:`, error.message);
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
// Set up state callbacks
|
|
141
|
+
this.poolClient.setStateCallbacks(() => this.roomManager.listRooms(), () => this.roomManager.getStats().clients, () => this.calculateLoad());
|
|
142
|
+
this.poolClient.connect();
|
|
143
|
+
}
|
|
144
|
+
// Calculate server load (0-100)
|
|
145
|
+
calculateLoad() {
|
|
146
|
+
const stats = this.roomManager.getStats();
|
|
147
|
+
const roomLoad = (stats.rooms / this.config.maxRooms) * 100;
|
|
148
|
+
return Math.min(100, Math.round(roomLoad));
|
|
149
|
+
}
|
|
150
|
+
// Get room manager for external access
|
|
151
|
+
getRoomManager() {
|
|
152
|
+
return this.roomManager;
|
|
153
|
+
}
|
|
154
|
+
// Get stats
|
|
155
|
+
getStats() {
|
|
156
|
+
const stats = this.roomManager.getStats();
|
|
157
|
+
return {
|
|
158
|
+
...stats,
|
|
159
|
+
hubConnected: this.poolClient?.isRegistered() ?? false,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
// Check if connected to hub
|
|
163
|
+
isHubConnected() {
|
|
164
|
+
return this.poolClient?.isRegistered() ?? false;
|
|
165
|
+
}
|
|
166
|
+
// Print startup banner
|
|
167
|
+
printBanner() {
|
|
168
|
+
const hubStatus = this.config.hubUrl ? `Connecting to ${this.config.hubUrl}` : 'Standalone';
|
|
169
|
+
console.log(`
|
|
170
|
+
╔═══════════════════════════════════════════════════╗
|
|
171
|
+
║ ║
|
|
172
|
+
║ CS-CLI Game Server ║
|
|
173
|
+
║ ║
|
|
174
|
+
║ Name: ${this.config.serverName.padEnd(40)}║
|
|
175
|
+
║ Port: ${this.config.port.toString().padEnd(40)}║
|
|
176
|
+
║ Endpoint: ${this.config.publicEndpoint.substring(0, 36).padEnd(36)}║
|
|
177
|
+
║ Max Rooms: ${this.config.maxRooms.toString().padEnd(36)}║
|
|
178
|
+
║ Hub: ${hubStatus.substring(0, 41).padEnd(41)}║
|
|
179
|
+
║ ║
|
|
180
|
+
║ Server is running... ║
|
|
181
|
+
║ ║
|
|
182
|
+
╚═══════════════════════════════════════════════════╝
|
|
183
|
+
`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { RoomInfo } from '../protocol.js';
|
|
2
|
+
export interface PoolClientConfig {
|
|
3
|
+
hubUrl: string;
|
|
4
|
+
serverName: string;
|
|
5
|
+
endpoint: string;
|
|
6
|
+
maxRooms: number;
|
|
7
|
+
reconnectInterval?: number;
|
|
8
|
+
heartbeatInterval?: number;
|
|
9
|
+
}
|
|
10
|
+
export type PoolClientStatus = 'disconnected' | 'connecting' | 'connected' | 'registered';
|
|
11
|
+
export interface PoolClientEvents {
|
|
12
|
+
onStatusChange?: (status: PoolClientStatus) => void;
|
|
13
|
+
onRegistered?: (poolId: string) => void;
|
|
14
|
+
onRejected?: (reason: string) => void;
|
|
15
|
+
onError?: (error: Error) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare class PoolClient {
|
|
18
|
+
private config;
|
|
19
|
+
private events;
|
|
20
|
+
private ws;
|
|
21
|
+
private status;
|
|
22
|
+
private poolId;
|
|
23
|
+
private reconnectTimer;
|
|
24
|
+
private heartbeatTimer;
|
|
25
|
+
private getRoomsCallback;
|
|
26
|
+
private getPlayerCountCallback;
|
|
27
|
+
private getLoadCallback;
|
|
28
|
+
constructor(config: PoolClientConfig, events?: PoolClientEvents);
|
|
29
|
+
setStateCallbacks(getRooms: () => RoomInfo[], getPlayerCount: () => number, getLoad: () => number): void;
|
|
30
|
+
connect(): void;
|
|
31
|
+
disconnect(): void;
|
|
32
|
+
notifyRoomCreated(room: RoomInfo): void;
|
|
33
|
+
notifyRoomClosed(roomId: string): void;
|
|
34
|
+
getStatus(): PoolClientStatus;
|
|
35
|
+
getPoolId(): string | null;
|
|
36
|
+
isRegistered(): boolean;
|
|
37
|
+
private handleOpen;
|
|
38
|
+
private handleMessage;
|
|
39
|
+
private handleClose;
|
|
40
|
+
private handleError;
|
|
41
|
+
private send;
|
|
42
|
+
private startHeartbeat;
|
|
43
|
+
private stopHeartbeat;
|
|
44
|
+
private sendHeartbeat;
|
|
45
|
+
private scheduleReconnect;
|
|
46
|
+
private stopReconnect;
|
|
47
|
+
private setStatus;
|
|
48
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// Pool Client - Connects a game server to the central hub
|
|
2
|
+
import WebSocket from 'ws';
|
|
3
|
+
import { serializePoolToHubMessage, } from '../protocol.js';
|
|
4
|
+
export class PoolClient {
|
|
5
|
+
config;
|
|
6
|
+
events;
|
|
7
|
+
ws = null;
|
|
8
|
+
status = 'disconnected';
|
|
9
|
+
poolId = null;
|
|
10
|
+
reconnectTimer = null;
|
|
11
|
+
heartbeatTimer = null;
|
|
12
|
+
getRoomsCallback = null;
|
|
13
|
+
getPlayerCountCallback = null;
|
|
14
|
+
getLoadCallback = null;
|
|
15
|
+
constructor(config, events = {}) {
|
|
16
|
+
this.config = {
|
|
17
|
+
reconnectInterval: 5000,
|
|
18
|
+
heartbeatInterval: 10000,
|
|
19
|
+
...config,
|
|
20
|
+
};
|
|
21
|
+
this.events = events;
|
|
22
|
+
}
|
|
23
|
+
// Set callbacks for getting current state
|
|
24
|
+
setStateCallbacks(getRooms, getPlayerCount, getLoad) {
|
|
25
|
+
this.getRoomsCallback = getRooms;
|
|
26
|
+
this.getPlayerCountCallback = getPlayerCount;
|
|
27
|
+
this.getLoadCallback = getLoad;
|
|
28
|
+
}
|
|
29
|
+
// Connect to the hub
|
|
30
|
+
connect() {
|
|
31
|
+
if (this.ws) {
|
|
32
|
+
this.ws.close();
|
|
33
|
+
}
|
|
34
|
+
this.setStatus('connecting');
|
|
35
|
+
console.log(`[PoolClient] Connecting to hub at ${this.config.hubUrl}`);
|
|
36
|
+
this.ws = new WebSocket(this.config.hubUrl);
|
|
37
|
+
this.ws.on('open', () => {
|
|
38
|
+
this.handleOpen();
|
|
39
|
+
});
|
|
40
|
+
this.ws.on('message', (data) => {
|
|
41
|
+
this.handleMessage(data.toString());
|
|
42
|
+
});
|
|
43
|
+
this.ws.on('close', () => {
|
|
44
|
+
this.handleClose();
|
|
45
|
+
});
|
|
46
|
+
this.ws.on('error', (error) => {
|
|
47
|
+
this.handleError(error);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
// Disconnect from the hub
|
|
51
|
+
disconnect() {
|
|
52
|
+
this.stopHeartbeat();
|
|
53
|
+
this.stopReconnect();
|
|
54
|
+
if (this.ws) {
|
|
55
|
+
// Send unregister message
|
|
56
|
+
if (this.status === 'registered') {
|
|
57
|
+
this.send({ type: 'pool_unregister' });
|
|
58
|
+
}
|
|
59
|
+
this.ws.close();
|
|
60
|
+
this.ws = null;
|
|
61
|
+
}
|
|
62
|
+
this.poolId = null;
|
|
63
|
+
this.setStatus('disconnected');
|
|
64
|
+
}
|
|
65
|
+
// Notify hub of a new room
|
|
66
|
+
notifyRoomCreated(room) {
|
|
67
|
+
if (this.status === 'registered') {
|
|
68
|
+
this.send({
|
|
69
|
+
type: 'pool_room_created',
|
|
70
|
+
room,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Notify hub of a closed room
|
|
75
|
+
notifyRoomClosed(roomId) {
|
|
76
|
+
if (this.status === 'registered') {
|
|
77
|
+
this.send({
|
|
78
|
+
type: 'pool_room_closed',
|
|
79
|
+
roomId,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Get current status
|
|
84
|
+
getStatus() {
|
|
85
|
+
return this.status;
|
|
86
|
+
}
|
|
87
|
+
// Get assigned pool ID
|
|
88
|
+
getPoolId() {
|
|
89
|
+
return this.poolId;
|
|
90
|
+
}
|
|
91
|
+
// Check if connected and registered
|
|
92
|
+
isRegistered() {
|
|
93
|
+
return this.status === 'registered';
|
|
94
|
+
}
|
|
95
|
+
// Handle WebSocket open
|
|
96
|
+
handleOpen() {
|
|
97
|
+
console.log(`[PoolClient] Connected to hub, registering...`);
|
|
98
|
+
this.setStatus('connected');
|
|
99
|
+
// Send registration
|
|
100
|
+
this.send({
|
|
101
|
+
type: 'pool_register',
|
|
102
|
+
serverName: this.config.serverName,
|
|
103
|
+
endpoint: this.config.endpoint,
|
|
104
|
+
maxRooms: this.config.maxRooms,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
// Handle incoming message
|
|
108
|
+
handleMessage(data) {
|
|
109
|
+
try {
|
|
110
|
+
const msg = JSON.parse(data);
|
|
111
|
+
switch (msg.type) {
|
|
112
|
+
case 'pool_accepted':
|
|
113
|
+
this.poolId = msg.poolId;
|
|
114
|
+
this.setStatus('registered');
|
|
115
|
+
this.startHeartbeat();
|
|
116
|
+
console.log(`[PoolClient] Registered with hub as ${msg.poolId}`);
|
|
117
|
+
this.events.onRegistered?.(msg.poolId);
|
|
118
|
+
break;
|
|
119
|
+
case 'pool_rejected':
|
|
120
|
+
console.error(`[PoolClient] Registration rejected: ${msg.reason}`);
|
|
121
|
+
this.events.onRejected?.(msg.reason);
|
|
122
|
+
this.ws?.close();
|
|
123
|
+
break;
|
|
124
|
+
case 'pool_ping':
|
|
125
|
+
// Hub is checking if we're alive - respond with heartbeat
|
|
126
|
+
this.sendHeartbeat();
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
console.error(`[PoolClient] Error parsing message:`, error);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Handle WebSocket close
|
|
135
|
+
handleClose() {
|
|
136
|
+
console.log(`[PoolClient] Disconnected from hub`);
|
|
137
|
+
this.stopHeartbeat();
|
|
138
|
+
this.ws = null;
|
|
139
|
+
this.poolId = null;
|
|
140
|
+
if (this.status !== 'disconnected') {
|
|
141
|
+
this.setStatus('disconnected');
|
|
142
|
+
this.scheduleReconnect();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Handle WebSocket error
|
|
146
|
+
handleError(error) {
|
|
147
|
+
console.error(`[PoolClient] WebSocket error:`, error.message);
|
|
148
|
+
this.events.onError?.(error);
|
|
149
|
+
}
|
|
150
|
+
// Send a message to the hub
|
|
151
|
+
send(msg) {
|
|
152
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
153
|
+
this.ws.send(serializePoolToHubMessage(msg));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Start heartbeat timer
|
|
157
|
+
startHeartbeat() {
|
|
158
|
+
this.stopHeartbeat();
|
|
159
|
+
this.heartbeatTimer = setInterval(() => {
|
|
160
|
+
this.sendHeartbeat();
|
|
161
|
+
}, this.config.heartbeatInterval);
|
|
162
|
+
}
|
|
163
|
+
// Stop heartbeat timer
|
|
164
|
+
stopHeartbeat() {
|
|
165
|
+
if (this.heartbeatTimer) {
|
|
166
|
+
clearInterval(this.heartbeatTimer);
|
|
167
|
+
this.heartbeatTimer = null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Send heartbeat with current state
|
|
171
|
+
sendHeartbeat() {
|
|
172
|
+
const rooms = this.getRoomsCallback?.() ?? [];
|
|
173
|
+
const playerCount = this.getPlayerCountCallback?.() ?? 0;
|
|
174
|
+
const load = this.getLoadCallback?.() ?? 0;
|
|
175
|
+
this.send({
|
|
176
|
+
type: 'pool_heartbeat',
|
|
177
|
+
rooms,
|
|
178
|
+
playerCount,
|
|
179
|
+
load,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
// Schedule reconnection
|
|
183
|
+
scheduleReconnect() {
|
|
184
|
+
this.stopReconnect();
|
|
185
|
+
console.log(`[PoolClient] Reconnecting in ${this.config.reconnectInterval}ms...`);
|
|
186
|
+
this.reconnectTimer = setTimeout(() => {
|
|
187
|
+
this.connect();
|
|
188
|
+
}, this.config.reconnectInterval);
|
|
189
|
+
}
|
|
190
|
+
// Stop reconnection timer
|
|
191
|
+
stopReconnect() {
|
|
192
|
+
if (this.reconnectTimer) {
|
|
193
|
+
clearTimeout(this.reconnectTimer);
|
|
194
|
+
this.reconnectTimer = null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Update status and notify
|
|
198
|
+
setStatus(status) {
|
|
199
|
+
if (this.status !== status) {
|
|
200
|
+
this.status = status;
|
|
201
|
+
this.events.onStatusChange?.(status);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|