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.
@@ -0,0 +1,433 @@
1
+ export type TeamId = 'T' | 'CT' | 'SPECTATOR';
2
+ export type GameModeType = 'deathmatch' | 'competitive';
3
+ export type GamePhase = 'pre_match' | 'warmup' | 'freeze' | 'live' | 'round_end' | 'halftime' | 'match_end';
4
+ export type WeaponType = 'knife' | 'pistol' | 'rifle' | 'shotgun' | 'sniper';
5
+ export type BotDifficulty = 'easy' | 'medium' | 'hard';
6
+ export interface Vec3 {
7
+ x: number;
8
+ y: number;
9
+ z: number;
10
+ }
11
+ export interface RoomConfig {
12
+ name: string;
13
+ map: string;
14
+ mode: GameModeType;
15
+ maxPlayers: number;
16
+ botCount: number;
17
+ botDifficulty: BotDifficulty;
18
+ isPrivate: boolean;
19
+ password?: string;
20
+ }
21
+ export interface RoomInfo {
22
+ id: string;
23
+ name: string;
24
+ map: string;
25
+ mode: GameModeType;
26
+ playerCount: number;
27
+ maxPlayers: number;
28
+ botCount: number;
29
+ isPrivate: boolean;
30
+ phase: GamePhase;
31
+ hostId: string;
32
+ }
33
+ export interface PlayerInput {
34
+ forward: number;
35
+ strafe: number;
36
+ yaw: number;
37
+ pitch: number;
38
+ jump: boolean;
39
+ crouch: boolean;
40
+ }
41
+ export interface PlayerSnapshot {
42
+ id: string;
43
+ name: string;
44
+ position: Vec3;
45
+ yaw: number;
46
+ pitch: number;
47
+ health: number;
48
+ armor: number;
49
+ team: TeamId;
50
+ isAlive: boolean;
51
+ currentWeapon: WeaponType;
52
+ money: number;
53
+ kills: number;
54
+ deaths: number;
55
+ }
56
+ export interface BotSnapshot {
57
+ id: string;
58
+ name: string;
59
+ position: Vec3;
60
+ yaw: number;
61
+ pitch: number;
62
+ health: number;
63
+ armor: number;
64
+ team: TeamId;
65
+ isAlive: boolean;
66
+ currentWeapon: WeaponType;
67
+ kills: number;
68
+ deaths: number;
69
+ }
70
+ export interface DroppedWeaponSnapshot {
71
+ id: string;
72
+ weaponType: WeaponType;
73
+ position: Vec3;
74
+ }
75
+ export interface GameStateSnapshot {
76
+ tick: number;
77
+ timestamp: number;
78
+ phase: GamePhase;
79
+ roundTime: number;
80
+ freezeTime: number;
81
+ players: PlayerSnapshot[];
82
+ bots: BotSnapshot[];
83
+ droppedWeapons: DroppedWeaponSnapshot[];
84
+ tScore: number;
85
+ ctScore: number;
86
+ roundNumber: number;
87
+ }
88
+ export interface KillEvent {
89
+ killerId: string;
90
+ killerName: string;
91
+ victimId: string;
92
+ victimName: string;
93
+ weapon: WeaponType;
94
+ headshot: boolean;
95
+ }
96
+ export interface HitEvent {
97
+ attackerId: string;
98
+ victimId: string;
99
+ damage: number;
100
+ headshot: boolean;
101
+ }
102
+ export interface FireEvent {
103
+ playerId: string;
104
+ origin: Vec3;
105
+ direction: Vec3;
106
+ weapon: WeaponType;
107
+ }
108
+ export interface ListRoomsMessage {
109
+ type: 'list_rooms';
110
+ }
111
+ export interface CreateRoomMessage {
112
+ type: 'create_room';
113
+ config: RoomConfig;
114
+ }
115
+ export interface JoinRoomMessage {
116
+ type: 'join_room';
117
+ roomId: string;
118
+ playerName: string;
119
+ password?: string;
120
+ }
121
+ export interface LeaveRoomMessage {
122
+ type: 'leave_room';
123
+ }
124
+ export interface PlayerInputMessage {
125
+ type: 'input';
126
+ input: PlayerInput;
127
+ sequence: number;
128
+ }
129
+ export interface FireMessage {
130
+ type: 'fire';
131
+ }
132
+ export interface ReloadMessage {
133
+ type: 'reload';
134
+ }
135
+ export interface BuyWeaponMessage {
136
+ type: 'buy_weapon';
137
+ weaponName: string;
138
+ }
139
+ export interface PickupWeaponMessage {
140
+ type: 'pickup_weapon';
141
+ weaponId: string;
142
+ }
143
+ export interface DropWeaponMessage {
144
+ type: 'drop_weapon';
145
+ }
146
+ export interface SelectWeaponMessage {
147
+ type: 'select_weapon';
148
+ slot: number;
149
+ }
150
+ export interface ChatMessage {
151
+ type: 'chat';
152
+ message: string;
153
+ teamOnly: boolean;
154
+ }
155
+ export interface ReadyMessage {
156
+ type: 'ready';
157
+ }
158
+ export interface StartGameMessage {
159
+ type: 'start_game';
160
+ }
161
+ export interface ChangeTeamMessage {
162
+ type: 'change_team';
163
+ team: TeamId;
164
+ }
165
+ export type ClientMessage = ListRoomsMessage | CreateRoomMessage | JoinRoomMessage | LeaveRoomMessage | PlayerInputMessage | FireMessage | ReloadMessage | BuyWeaponMessage | PickupWeaponMessage | DropWeaponMessage | SelectWeaponMessage | ChatMessage | ReadyMessage | StartGameMessage | ChangeTeamMessage | LockstepInputMessage | LockstepSyncMessage;
166
+ export interface RoomListMessage {
167
+ type: 'room_list';
168
+ rooms: RoomInfo[];
169
+ }
170
+ export interface RoomJoinedMessage {
171
+ type: 'room_joined';
172
+ roomId: string;
173
+ playerId: string;
174
+ room: RoomInfo;
175
+ }
176
+ export interface RoomErrorMessage {
177
+ type: 'room_error';
178
+ error: string;
179
+ }
180
+ export interface PlayerJoinedMessage {
181
+ type: 'player_joined';
182
+ playerId: string;
183
+ playerName: string;
184
+ }
185
+ export interface PlayerLeftMessage {
186
+ type: 'player_left';
187
+ playerId: string;
188
+ playerName: string;
189
+ }
190
+ export interface GameStateMessage {
191
+ type: 'game_state';
192
+ state: GameStateSnapshot;
193
+ }
194
+ export interface PhaseChangeMessage {
195
+ type: 'phase_change';
196
+ phase: GamePhase;
197
+ roundNumber: number;
198
+ tScore: number;
199
+ ctScore: number;
200
+ }
201
+ export interface FireEventMessage {
202
+ type: 'fire_event';
203
+ event: FireEvent;
204
+ }
205
+ export interface HitEventMessage {
206
+ type: 'hit_event';
207
+ event: HitEvent;
208
+ }
209
+ export interface KillEventMessage {
210
+ type: 'kill_event';
211
+ event: KillEvent;
212
+ }
213
+ export interface SpawnEventMessage {
214
+ type: 'spawn_event';
215
+ entityId: string;
216
+ entityType: 'player' | 'bot';
217
+ position: Vec3;
218
+ team: TeamId;
219
+ }
220
+ export interface WeaponDroppedMessage {
221
+ type: 'weapon_dropped';
222
+ weaponId: string;
223
+ weaponType: WeaponType;
224
+ position: Vec3;
225
+ }
226
+ export interface WeaponPickedUpMessage {
227
+ type: 'weapon_picked_up';
228
+ weaponId: string;
229
+ playerId: string;
230
+ }
231
+ export interface ChatReceivedMessage {
232
+ type: 'chat_received';
233
+ senderId: string;
234
+ senderName: string;
235
+ message: string;
236
+ teamOnly: boolean;
237
+ }
238
+ export interface PlayerReadyMessage {
239
+ type: 'player_ready';
240
+ playerId: string;
241
+ ready: boolean;
242
+ }
243
+ export interface PlayerTeamChangedMessage {
244
+ type: 'player_team_changed';
245
+ playerId: string;
246
+ team: TeamId;
247
+ }
248
+ export interface GameStartingMessage {
249
+ type: 'game_starting';
250
+ countdown: number;
251
+ }
252
+ export interface AssignedTeamMessage {
253
+ type: 'assigned_team';
254
+ team: TeamId;
255
+ }
256
+ export interface InputAckMessage {
257
+ type: 'input_ack';
258
+ sequence: number;
259
+ position: Vec3;
260
+ }
261
+ export interface LockstepInputMessage {
262
+ type: 'lockstep_input';
263
+ tick: number;
264
+ input: PlayerInput;
265
+ actions: LockstepAction[];
266
+ position: Vec3;
267
+ yaw: number;
268
+ pitch: number;
269
+ health: number;
270
+ isAlive: boolean;
271
+ }
272
+ export interface LockstepAction {
273
+ type: 'fire' | 'reload' | 'buy' | 'drop' | 'pickup' | 'select_weapon' | 'hit' | 'death';
274
+ data?: any;
275
+ }
276
+ export interface LockstepTickMessage {
277
+ type: 'lockstep_tick';
278
+ tick: number;
279
+ inputs: Array<{
280
+ playerId: string;
281
+ input: PlayerInput;
282
+ actions: LockstepAction[];
283
+ position: Vec3;
284
+ yaw: number;
285
+ pitch: number;
286
+ health: number;
287
+ isAlive: boolean;
288
+ }>;
289
+ }
290
+ export interface LockstepStartMessage {
291
+ type: 'lockstep_start';
292
+ tick: number;
293
+ players: Array<{
294
+ id: string;
295
+ name: string;
296
+ team: TeamId;
297
+ spawnPosition: Vec3;
298
+ spawnYaw: number;
299
+ }>;
300
+ mapData: {
301
+ bounds: {
302
+ min: Vec3;
303
+ max: Vec3;
304
+ };
305
+ colliders: Array<{
306
+ min: Vec3;
307
+ max: Vec3;
308
+ }>;
309
+ spawnPoints: Array<{
310
+ position: Vec3;
311
+ angle: number;
312
+ team: string;
313
+ }>;
314
+ };
315
+ }
316
+ export interface LockstepSyncMessage {
317
+ type: 'lockstep_sync';
318
+ tick: number;
319
+ hash: string;
320
+ }
321
+ export interface LockstepDesyncMessage {
322
+ type: 'lockstep_desync';
323
+ tick: number;
324
+ expectedHash: string;
325
+ receivedHash: string;
326
+ playerId: string;
327
+ }
328
+ export type ServerMessage = RoomListMessage | RoomJoinedMessage | RoomErrorMessage | PlayerJoinedMessage | PlayerLeftMessage | GameStateMessage | PhaseChangeMessage | FireEventMessage | HitEventMessage | KillEventMessage | SpawnEventMessage | WeaponDroppedMessage | WeaponPickedUpMessage | ChatReceivedMessage | PlayerReadyMessage | PlayerTeamChangedMessage | GameStartingMessage | AssignedTeamMessage | InputAckMessage | LockstepTickMessage | LockstepStartMessage | LockstepDesyncMessage;
329
+ export interface PoolServerInfo {
330
+ id: string;
331
+ name: string;
332
+ endpoint: string;
333
+ maxRooms: number;
334
+ currentRooms: number;
335
+ playerCount: number;
336
+ load: number;
337
+ lastHeartbeat: number;
338
+ rooms: RoomInfo[];
339
+ }
340
+ export interface AggregatedRoomInfo extends RoomInfo {
341
+ poolId: string;
342
+ poolName: string;
343
+ poolEndpoint: string;
344
+ }
345
+ export interface PoolRegisterMessage {
346
+ type: 'pool_register';
347
+ serverName: string;
348
+ endpoint: string;
349
+ maxRooms: number;
350
+ }
351
+ export interface PoolHeartbeatMessage {
352
+ type: 'pool_heartbeat';
353
+ rooms: RoomInfo[];
354
+ playerCount: number;
355
+ load: number;
356
+ }
357
+ export interface PoolRoomCreatedMessage {
358
+ type: 'pool_room_created';
359
+ room: RoomInfo;
360
+ }
361
+ export interface PoolRoomClosedMessage {
362
+ type: 'pool_room_closed';
363
+ roomId: string;
364
+ }
365
+ export interface PoolUnregisterMessage {
366
+ type: 'pool_unregister';
367
+ }
368
+ export type PoolToHubMessage = PoolRegisterMessage | PoolHeartbeatMessage | PoolRoomCreatedMessage | PoolRoomClosedMessage | PoolUnregisterMessage;
369
+ export interface PoolAcceptedMessage {
370
+ type: 'pool_accepted';
371
+ poolId: string;
372
+ }
373
+ export interface PoolRejectedMessage {
374
+ type: 'pool_rejected';
375
+ reason: string;
376
+ }
377
+ export interface PoolPingMessage {
378
+ type: 'pool_ping';
379
+ }
380
+ export type HubToPoolMessage = PoolAcceptedMessage | PoolRejectedMessage | PoolPingMessage;
381
+ export interface HubListRoomsMessage {
382
+ type: 'hub_list_rooms';
383
+ }
384
+ export interface HubGetEndpointMessage {
385
+ type: 'hub_get_endpoint';
386
+ roomId: string;
387
+ }
388
+ export interface HubCreateRoomMessage {
389
+ type: 'hub_create_room';
390
+ config: RoomConfig;
391
+ preferredPool?: string;
392
+ }
393
+ export type ClientToHubMessage = HubListRoomsMessage | HubGetEndpointMessage | HubCreateRoomMessage;
394
+ export interface HubRoomListMessage {
395
+ type: 'hub_room_list';
396
+ rooms: AggregatedRoomInfo[];
397
+ pools: {
398
+ id: string;
399
+ name: string;
400
+ playerCount: number;
401
+ }[];
402
+ }
403
+ export interface HubRoomEndpointMessage {
404
+ type: 'hub_room_endpoint';
405
+ roomId: string;
406
+ endpoint: string;
407
+ token: string;
408
+ }
409
+ export interface HubRoomNotFoundMessage {
410
+ type: 'hub_room_not_found';
411
+ roomId: string;
412
+ }
413
+ export interface HubRoomCreatedMessage {
414
+ type: 'hub_room_created';
415
+ roomId: string;
416
+ endpoint: string;
417
+ token: string;
418
+ }
419
+ export interface HubErrorMessage {
420
+ type: 'hub_error';
421
+ error: string;
422
+ }
423
+ export type HubToClientMessage = HubRoomListMessage | HubRoomEndpointMessage | HubRoomNotFoundMessage | HubRoomCreatedMessage | HubErrorMessage;
424
+ export declare function isClientMessage(msg: unknown): msg is ClientMessage;
425
+ export declare function parseClientMessage(data: string): ClientMessage | null;
426
+ export declare function serializeServerMessage(msg: ServerMessage): string;
427
+ export declare function isPoolToHubMessage(msg: unknown): msg is PoolToHubMessage;
428
+ export declare function parsePoolToHubMessage(data: string): PoolToHubMessage | null;
429
+ export declare function isClientToHubMessage(msg: unknown): msg is ClientToHubMessage;
430
+ export declare function parseClientToHubMessage(data: string): ClientToHubMessage | null;
431
+ export declare function serializeHubToPoolMessage(msg: HubToPoolMessage): string;
432
+ export declare function serializeHubToClientMessage(msg: HubToClientMessage): string;
433
+ export declare function serializePoolToHubMessage(msg: PoolToHubMessage): string;
@@ -0,0 +1,59 @@
1
+ // Network protocol message type definitions for CS-CLI multiplayer
2
+ // ============ Helpers ============
3
+ export function isClientMessage(msg) {
4
+ return typeof msg === 'object' && msg !== null && 'type' in msg;
5
+ }
6
+ export function parseClientMessage(data) {
7
+ try {
8
+ const msg = JSON.parse(data);
9
+ if (isClientMessage(msg)) {
10
+ return msg;
11
+ }
12
+ return null;
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ }
18
+ export function serializeServerMessage(msg) {
19
+ return JSON.stringify(msg);
20
+ }
21
+ export function isPoolToHubMessage(msg) {
22
+ return typeof msg === 'object' && msg !== null && 'type' in msg;
23
+ }
24
+ export function parsePoolToHubMessage(data) {
25
+ try {
26
+ const msg = JSON.parse(data);
27
+ if (isPoolToHubMessage(msg)) {
28
+ return msg;
29
+ }
30
+ return null;
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ export function isClientToHubMessage(msg) {
37
+ return typeof msg === 'object' && msg !== null && 'type' in msg;
38
+ }
39
+ export function parseClientToHubMessage(data) {
40
+ try {
41
+ const msg = JSON.parse(data);
42
+ if (isClientToHubMessage(msg)) {
43
+ return msg;
44
+ }
45
+ return null;
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ }
51
+ export function serializeHubToPoolMessage(msg) {
52
+ return JSON.stringify(msg);
53
+ }
54
+ export function serializeHubToClientMessage(msg) {
55
+ return JSON.stringify(msg);
56
+ }
57
+ export function serializePoolToHubMessage(msg) {
58
+ return JSON.stringify(msg);
59
+ }
@@ -0,0 +1,44 @@
1
+ import { ServerMessage, RoomConfig, RoomInfo, TeamId, GamePhase } from '../protocol.js';
2
+ interface HeadlessClientCallbacks {
3
+ onConnect?: () => void;
4
+ onDisconnect?: (reason: string) => void;
5
+ onMessage?: (message: ServerMessage) => void;
6
+ onRoomList?: (rooms: RoomInfo[]) => void;
7
+ onRoomJoined?: (roomId: string, playerId: string, room: RoomInfo) => void;
8
+ onPlayerJoined?: (playerId: string, playerName: string) => void;
9
+ onPlayerTeamChanged?: (playerId: string, team: TeamId) => void;
10
+ onPhaseChange?: (phase: GamePhase, round: number, tScore: number, ctScore: number) => void;
11
+ }
12
+ export declare class HeadlessClient {
13
+ private socket;
14
+ private callbacks;
15
+ private playerId;
16
+ private playerName;
17
+ private roomId;
18
+ private team;
19
+ private messageLog;
20
+ constructor(playerName?: string);
21
+ connect(serverUrl?: string): Promise<void>;
22
+ disconnect(): void;
23
+ setCallbacks(callbacks: HeadlessClientCallbacks): void;
24
+ listRooms(): void;
25
+ createRoom(config: Partial<RoomConfig>): void;
26
+ joinRoom(roomId: string, password?: string): void;
27
+ leaveRoom(): void;
28
+ setReady(): void;
29
+ startGame(): void;
30
+ changeTeam(team: 'T' | 'CT'): void;
31
+ sendInput(forward: number, strafe: number, yaw: number, pitch: number, jump?: boolean): void;
32
+ sendFire(): void;
33
+ getPlayerId(): string | null;
34
+ getRoomId(): string | null;
35
+ getTeam(): TeamId | null;
36
+ getMessageLog(): ServerMessage[];
37
+ clearMessageLog(): void;
38
+ waitForMessage(type: string, timeout?: number): Promise<ServerMessage>;
39
+ private send;
40
+ private handleMessage;
41
+ private dispatchMessage;
42
+ }
43
+ export declare function delay(ms: number): Promise<void>;
44
+ export {};