@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.
- package/dist/client.d.ts +51 -0
- package/dist/client.js +100 -0
- package/dist/dedicated.d.ts +72 -0
- package/dist/dedicated.js +1104 -0
- package/dist/index.cjs +1586 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1543 -0
- package/dist/net/nodeWsDriver.d.ts +16 -0
- package/dist/net/nodeWsDriver.js +122 -0
- package/dist/protocol/player.d.ts +2 -0
- package/dist/protocol/player.js +1 -0
- package/dist/protocol/write.d.ts +7 -0
- package/dist/protocol/write.js +167 -0
- package/dist/protocol.d.ts +17 -0
- package/dist/protocol.js +71 -0
- package/dist/server.d.ts +50 -0
- package/dist/server.js +12 -0
- package/dist/transport.d.ts +7 -0
- package/dist/transport.js +1 -0
- package/dist/transports/websocket.d.ts +11 -0
- package/dist/transports/websocket.js +38 -0
- package/package.json +35 -0
- package/src/client.ts +173 -0
- package/src/dedicated.ts +1295 -0
- package/src/index.ts +8 -0
- package/src/net/nodeWsDriver.ts +129 -0
- package/src/protocol/player.ts +2 -0
- package/src/protocol/write.ts +185 -0
- package/src/protocol.ts +91 -0
- package/src/server.ts +76 -0
- package/src/transport.ts +8 -0
- package/src/transports/websocket.ts +42 -0
- package/test.bsp +0 -0
- package/tests/client.test.ts +20 -0
- package/tests/connection_flow.test.ts +93 -0
- package/tests/dedicated.test.ts +211 -0
- package/tests/dedicated_trace.test.ts +117 -0
- package/tests/integration/configstring_sync.test.ts +235 -0
- package/tests/lag.test.ts +144 -0
- package/tests/protocol/player.test.ts +88 -0
- package/tests/protocol/write.test.ts +107 -0
- package/tests/protocol.test.ts +102 -0
- package/tests/server-state.test.ts +17 -0
- package/tests/server.test.ts +99 -0
- package/tests/unit/dedicated_timeout.test.ts +142 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
- 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
|
+
}
|