@quake2ts/server 0.0.1

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.
Files changed (48) hide show
  1. package/dist/client.d.ts +51 -0
  2. package/dist/client.js +100 -0
  3. package/dist/dedicated.d.ts +72 -0
  4. package/dist/dedicated.js +1104 -0
  5. package/dist/index.cjs +1586 -0
  6. package/dist/index.d.ts +7 -0
  7. package/dist/index.js +1543 -0
  8. package/dist/net/nodeWsDriver.d.ts +16 -0
  9. package/dist/net/nodeWsDriver.js +122 -0
  10. package/dist/protocol/player.d.ts +2 -0
  11. package/dist/protocol/player.js +1 -0
  12. package/dist/protocol/write.d.ts +7 -0
  13. package/dist/protocol/write.js +167 -0
  14. package/dist/protocol.d.ts +17 -0
  15. package/dist/protocol.js +71 -0
  16. package/dist/server.d.ts +50 -0
  17. package/dist/server.js +12 -0
  18. package/dist/transport.d.ts +7 -0
  19. package/dist/transport.js +1 -0
  20. package/dist/transports/websocket.d.ts +11 -0
  21. package/dist/transports/websocket.js +38 -0
  22. package/package.json +35 -0
  23. package/src/client.ts +173 -0
  24. package/src/dedicated.ts +1295 -0
  25. package/src/index.ts +8 -0
  26. package/src/net/nodeWsDriver.ts +129 -0
  27. package/src/protocol/player.ts +2 -0
  28. package/src/protocol/write.ts +185 -0
  29. package/src/protocol.ts +91 -0
  30. package/src/server.ts +76 -0
  31. package/src/transport.ts +8 -0
  32. package/src/transports/websocket.ts +42 -0
  33. package/test.bsp +0 -0
  34. package/tests/client.test.ts +20 -0
  35. package/tests/connection_flow.test.ts +93 -0
  36. package/tests/dedicated.test.ts +211 -0
  37. package/tests/dedicated_trace.test.ts +117 -0
  38. package/tests/integration/configstring_sync.test.ts +235 -0
  39. package/tests/lag.test.ts +144 -0
  40. package/tests/protocol/player.test.ts +88 -0
  41. package/tests/protocol/write.test.ts +107 -0
  42. package/tests/protocol.test.ts +102 -0
  43. package/tests/server-state.test.ts +17 -0
  44. package/tests/server.test.ts +99 -0
  45. package/tests/unit/dedicated_timeout.test.ts +142 -0
  46. package/tsconfig.json +9 -0
  47. package/tsconfig.tsbuildinfo +1 -0
  48. package/vitest.config.ts +40 -0
package/src/client.ts ADDED
@@ -0,0 +1,173 @@
1
+ import { NetDriver, UserCommand, PlayerState, UPDATE_BACKUP, MAX_CONFIGSTRINGS, EntityState, NetChan } from '@quake2ts/shared';
2
+ import { Entity } from '@quake2ts/game';
3
+
4
+ export enum ClientState {
5
+ Free = 0,
6
+ Zombie = 1, // client has been disconnected, but don't reuse connection for a couple seconds
7
+ Connected = 2, // has been assigned to a client_t, but not in game yet
8
+ Spawned = 3, // client is fully in game
9
+ Active = 4 // In game (added to match existing code usage)
10
+ }
11
+
12
+ export interface ClientFrame {
13
+ areaBytes: number;
14
+ areaBits: Uint8Array;
15
+ playerState: PlayerState;
16
+ numEntities: number;
17
+ firstEntity: number; // into the circular sv_packet_entities[]
18
+ sentTime: number; // for ping calculations
19
+ entities: EntityState[];
20
+ packetCRC: number; // CRC of the packet sending this frame
21
+ }
22
+
23
+ export interface Client {
24
+ index: number; // Client index (0 to maxClients - 1)
25
+ state: ClientState;
26
+ net: NetDriver;
27
+ netchan: NetChan; // Network channel for reliable communication
28
+ userInfo: string;
29
+
30
+ lastFrame: number; // for delta compression
31
+ lastCmd: UserCommand;
32
+
33
+ commandMsec: number; // For anti-cheat/speed control
34
+
35
+ frameLatency: number[]; // [LATENCY_COUNTS]
36
+ ping: number;
37
+
38
+ messageSize: number[]; // [RATE_MESSAGES]
39
+ rate: number;
40
+ suppressCount: number;
41
+
42
+ edict: Entity | null; // The player entity
43
+ name: string;
44
+ messageLevel: number;
45
+
46
+ // The datagram is written to by sound calls, prints, temp ents, etc.
47
+ datagram: Uint8Array; // This needs to be a growable buffer really
48
+
49
+ frames: ClientFrame[]; // [UPDATE_BACKUP]
50
+
51
+ // Download state
52
+ download?: Uint8Array;
53
+ downloadSize: number;
54
+ downloadCount: number;
55
+
56
+ lastMessage: number; // sv.framenum when packet was last received
57
+ lastConnect: number;
58
+
59
+ challenge: number;
60
+
61
+ messageQueue: Uint8Array[]; // Queue for incoming packets
62
+ lastPacketEntities: number[]; // List of entity numbers sent in the last packet
63
+
64
+ // Rate limiting
65
+ commandQueue: UserCommand[];
66
+ lastCommandTime: number;
67
+ commandCount: number;
68
+ }
69
+
70
+ export function createClient(index: number, net: NetDriver): Client {
71
+ // Initialize frames array
72
+ const frames: ClientFrame[] = [];
73
+ for (let i = 0; i < UPDATE_BACKUP; i++) {
74
+ frames.push({
75
+ areaBytes: 0,
76
+ areaBits: new Uint8Array(0), // Size depends on map areas
77
+ playerState: createEmptyPlayerState(),
78
+ numEntities: 0,
79
+ firstEntity: 0,
80
+ sentTime: 0,
81
+ entities: [],
82
+ packetCRC: 0
83
+ });
84
+ }
85
+
86
+ const netchan = new NetChan();
87
+ // Initialize qport - normally we would get this from userinfo or handshake
88
+ netchan.setup(Math.floor(Math.random() * 65536));
89
+
90
+ return {
91
+ index,
92
+ state: ClientState.Connected,
93
+ net,
94
+ netchan,
95
+ userInfo: '',
96
+
97
+ lastFrame: 0,
98
+ lastCmd: createEmptyUserCommand(),
99
+
100
+ commandMsec: 0,
101
+ frameLatency: [],
102
+ ping: 0,
103
+ messageSize: [],
104
+ rate: 25000, // Default rate
105
+ suppressCount: 0,
106
+
107
+ edict: null,
108
+ name: `Player ${index}`,
109
+ messageLevel: 0,
110
+
111
+ datagram: new Uint8Array(0),
112
+
113
+ frames,
114
+
115
+ downloadSize: 0,
116
+ downloadCount: 0,
117
+
118
+ lastMessage: 0,
119
+ lastConnect: Date.now(),
120
+ challenge: 0,
121
+
122
+ messageQueue: [],
123
+ lastPacketEntities: [],
124
+
125
+ commandQueue: [],
126
+ lastCommandTime: 0,
127
+ commandCount: 0
128
+ };
129
+ }
130
+
131
+ function createEmptyUserCommand(): UserCommand {
132
+ return {
133
+ msec: 0,
134
+ buttons: 0,
135
+ angles: { x: 0, y: 0, z: 0 },
136
+ forwardmove: 0,
137
+ sidemove: 0,
138
+ upmove: 0,
139
+ sequence: 0,
140
+ lightlevel: 0,
141
+ impulse: 0
142
+ };
143
+ }
144
+
145
+ function createEmptyPlayerState(): PlayerState {
146
+ return {
147
+ origin: { x: 0, y: 0, z: 0 },
148
+ velocity: { x: 0, y: 0, z: 0 },
149
+ viewAngles: { x: 0, y: 0, z: 0 },
150
+ onGround: false,
151
+ waterLevel: 0,
152
+ watertype: 0,
153
+ mins: { x: -16, y: -16, z: -24 },
154
+ maxs: { x: 16, y: 16, z: 32 },
155
+ damageAlpha: 0,
156
+ damageIndicators: [],
157
+ blend: [0, 0, 0, 0],
158
+ // Stubs for new fields
159
+ stats: [],
160
+ kick_angles: { x: 0, y: 0, z: 0 },
161
+ kick_origin: { x: 0, y: 0, z: 0 },
162
+ gunoffset: { x: 0, y: 0, z: 0 },
163
+ gunangles: { x: 0, y: 0, z: 0 },
164
+ gunindex: 0,
165
+ pm_type: 0,
166
+ pm_time: 0,
167
+ pm_flags: 0,
168
+ gun_frame: 0,
169
+ rdflags: 0,
170
+ fov: 90,
171
+ renderfx: 0
172
+ };
173
+ }