quake2ts 0.0.561 → 0.0.563
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/package.json +1 -1
- package/packages/client/dist/browser/index.global.js +15 -15
- package/packages/client/dist/browser/index.global.js.map +1 -1
- package/packages/client/dist/cjs/index.cjs +273 -1
- package/packages/client/dist/cjs/index.cjs.map +1 -1
- package/packages/client/dist/esm/index.js +273 -1
- package/packages/client/dist/esm/index.js.map +1 -1
- package/packages/client/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/client/dist/types/net/connection.d.ts +2 -0
- package/packages/client/dist/types/net/connection.d.ts.map +1 -1
- package/packages/server/dist/client.d.ts +51 -0
- package/packages/server/dist/client.js +100 -0
- package/packages/server/dist/dedicated.d.ts +69 -0
- package/packages/server/dist/dedicated.js +1013 -0
- package/packages/server/dist/index.cjs +27 -2
- package/packages/server/dist/index.d.ts +7 -161
- package/packages/server/dist/index.js +26 -2
- package/packages/server/dist/net/nodeWsDriver.d.ts +16 -0
- package/packages/server/dist/net/nodeWsDriver.js +122 -0
- package/packages/server/dist/protocol/player.d.ts +23 -0
- package/packages/server/dist/protocol/player.js +137 -0
- package/packages/server/dist/protocol/write.d.ts +7 -0
- package/packages/server/dist/protocol/write.js +167 -0
- package/packages/server/dist/protocol.d.ts +17 -0
- package/packages/server/dist/protocol.js +71 -0
- package/packages/server/dist/server.d.ts +50 -0
- package/packages/server/dist/server.js +12 -0
- package/packages/server/dist/server.test.d.ts +1 -0
- package/packages/server/dist/server.test.js +69 -0
- package/packages/server/dist/transport.d.ts +7 -0
- package/packages/server/dist/transport.js +1 -0
- package/packages/server/dist/transports/websocket.d.ts +11 -0
- package/packages/server/dist/transports/websocket.js +38 -0
- package/packages/test-utils/dist/index.cjs +1610 -1188
- package/packages/test-utils/dist/index.cjs.map +1 -1
- package/packages/test-utils/dist/index.d.cts +326 -132
- package/packages/test-utils/dist/index.d.ts +326 -132
- package/packages/test-utils/dist/index.js +1596 -1189
- package/packages/test-utils/dist/index.js.map +1 -1
- package/packages/server/dist/index.d.cts +0 -161
|
@@ -33,6 +33,7 @@ __export(index_exports, {
|
|
|
33
33
|
ClientMessageParser: () => ClientMessageParser,
|
|
34
34
|
ClientState: () => ClientState,
|
|
35
35
|
DedicatedServer: () => DedicatedServer,
|
|
36
|
+
ServerState: () => ServerState,
|
|
36
37
|
WebSocketNetDriver: () => WebSocketNetDriver,
|
|
37
38
|
createClient: () => createClient,
|
|
38
39
|
createServer: () => createServer
|
|
@@ -331,6 +332,19 @@ var import_shared4 = require("@quake2ts/shared");
|
|
|
331
332
|
var import_engine = require("@quake2ts/engine");
|
|
332
333
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
333
334
|
var import_game2 = require("@quake2ts/game");
|
|
335
|
+
|
|
336
|
+
// src/server.ts
|
|
337
|
+
var ServerState = /* @__PURE__ */ ((ServerState2) => {
|
|
338
|
+
ServerState2[ServerState2["Dead"] = 0] = "Dead";
|
|
339
|
+
ServerState2[ServerState2["Loading"] = 1] = "Loading";
|
|
340
|
+
ServerState2[ServerState2["Game"] = 2] = "Game";
|
|
341
|
+
ServerState2[ServerState2["Cinematic"] = 3] = "Cinematic";
|
|
342
|
+
ServerState2[ServerState2["Demo"] = 4] = "Demo";
|
|
343
|
+
ServerState2[ServerState2["Pic"] = 5] = "Pic";
|
|
344
|
+
return ServerState2;
|
|
345
|
+
})(ServerState || {});
|
|
346
|
+
|
|
347
|
+
// src/dedicated.ts
|
|
334
348
|
var import_shared5 = require("@quake2ts/shared");
|
|
335
349
|
|
|
336
350
|
// src/protocol/player.ts
|
|
@@ -1003,7 +1017,11 @@ var DedicatedServer = class {
|
|
|
1003
1017
|
}
|
|
1004
1018
|
onClientMessage(client, data) {
|
|
1005
1019
|
const buffer = data.byteOffset === 0 && data.byteLength === data.buffer.byteLength ? data.buffer : data.slice().buffer;
|
|
1006
|
-
|
|
1020
|
+
if (buffer instanceof ArrayBuffer) {
|
|
1021
|
+
client.messageQueue.push(new Uint8Array(buffer));
|
|
1022
|
+
} else {
|
|
1023
|
+
client.messageQueue.push(new Uint8Array(buffer));
|
|
1024
|
+
}
|
|
1007
1025
|
}
|
|
1008
1026
|
onClientDisconnect(client) {
|
|
1009
1027
|
console.log(`Client ${client.index} disconnected`);
|
|
@@ -1229,7 +1247,13 @@ var DedicatedServer = class {
|
|
|
1229
1247
|
if (data.length === 0) {
|
|
1230
1248
|
continue;
|
|
1231
1249
|
}
|
|
1232
|
-
|
|
1250
|
+
let buffer;
|
|
1251
|
+
if (data.buffer instanceof ArrayBuffer) {
|
|
1252
|
+
buffer = data.buffer;
|
|
1253
|
+
} else {
|
|
1254
|
+
buffer = new Uint8Array(data).buffer;
|
|
1255
|
+
}
|
|
1256
|
+
const reader = new import_shared4.BinaryStream(buffer);
|
|
1233
1257
|
const parser = new ClientMessageParser(reader, {
|
|
1234
1258
|
onMove: (checksum, lastFrame, cmd) => this.handleMove(client, cmd, checksum, lastFrame),
|
|
1235
1259
|
onUserInfo: (info) => this.handleUserInfo(client, info),
|
|
@@ -1573,6 +1597,7 @@ function createServer(options = {}) {
|
|
|
1573
1597
|
ClientMessageParser,
|
|
1574
1598
|
ClientState,
|
|
1575
1599
|
DedicatedServer,
|
|
1600
|
+
ServerState,
|
|
1576
1601
|
WebSocketNetDriver,
|
|
1577
1602
|
createClient,
|
|
1578
1603
|
createServer
|
|
@@ -1,161 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
private closeCallback;
|
|
9
|
-
private errorCallback;
|
|
10
|
-
connect(url: string): Promise<void>;
|
|
11
|
-
attach(socket: WebSocket): void;
|
|
12
|
-
disconnect(): void;
|
|
13
|
-
send(data: Uint8Array): void;
|
|
14
|
-
onMessage(callback: (data: Uint8Array) => void): void;
|
|
15
|
-
onClose(callback: () => void): void;
|
|
16
|
-
onError(callback: (error: Error) => void): void;
|
|
17
|
-
isConnected(): boolean;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
interface NetworkTransport {
|
|
21
|
-
listen(port: number): Promise<void>;
|
|
22
|
-
close(): void;
|
|
23
|
-
onConnection(callback: (driver: NetDriver, info?: any) => void): void;
|
|
24
|
-
onError(callback: (error: Error) => void): void;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface ClientInfo {
|
|
28
|
-
id: number;
|
|
29
|
-
name: string;
|
|
30
|
-
ping: number;
|
|
31
|
-
address: string;
|
|
32
|
-
}
|
|
33
|
-
interface ServerOptions {
|
|
34
|
-
mapName?: string;
|
|
35
|
-
maxPlayers?: number;
|
|
36
|
-
deathmatch?: boolean;
|
|
37
|
-
port?: number;
|
|
38
|
-
transport?: NetworkTransport;
|
|
39
|
-
}
|
|
40
|
-
declare class DedicatedServer implements GameEngine {
|
|
41
|
-
private transport;
|
|
42
|
-
private svs;
|
|
43
|
-
private sv;
|
|
44
|
-
private game;
|
|
45
|
-
private frameTimeout;
|
|
46
|
-
private entityIndex;
|
|
47
|
-
private history;
|
|
48
|
-
private backup;
|
|
49
|
-
onClientConnected?: (clientId: number, name: string) => void;
|
|
50
|
-
onClientDisconnected?: (clientId: number) => void;
|
|
51
|
-
onServerError?: (error: Error) => void;
|
|
52
|
-
private options;
|
|
53
|
-
constructor(optionsOrPort?: ServerOptions | number);
|
|
54
|
-
setTransport(transport: NetworkTransport): void;
|
|
55
|
-
startServer(mapName?: string): Promise<void>;
|
|
56
|
-
stopServer(): void;
|
|
57
|
-
kickPlayer(clientId: number): void;
|
|
58
|
-
changeMap(mapName: string): Promise<void>;
|
|
59
|
-
getConnectedClients(): ClientInfo[];
|
|
60
|
-
private start;
|
|
61
|
-
private loadMap;
|
|
62
|
-
private initGame;
|
|
63
|
-
private populateBaselines;
|
|
64
|
-
private entityToState;
|
|
65
|
-
stop(): void;
|
|
66
|
-
private handleConnection;
|
|
67
|
-
private onClientMessage;
|
|
68
|
-
private onClientDisconnect;
|
|
69
|
-
private dropClient;
|
|
70
|
-
private handleMove;
|
|
71
|
-
private handleUserInfo;
|
|
72
|
-
private handleStringCmd;
|
|
73
|
-
private handleStatus;
|
|
74
|
-
private handleGetChallenge;
|
|
75
|
-
private handleConnect;
|
|
76
|
-
private handleBegin;
|
|
77
|
-
private spawnClient;
|
|
78
|
-
private sendServerData;
|
|
79
|
-
private SV_SetConfigString;
|
|
80
|
-
private SV_WriteConfigString;
|
|
81
|
-
private SV_ReadPackets;
|
|
82
|
-
private runFrame;
|
|
83
|
-
private SV_SendClientMessages;
|
|
84
|
-
private SV_SendClientFrame;
|
|
85
|
-
trace(start: any, end: any): any;
|
|
86
|
-
multicast(origin: any, type: MulticastType, event: ServerCommand, ...args: any[]): void;
|
|
87
|
-
unicast(ent: Entity, reliable: boolean, event: ServerCommand, ...args: any[]): void;
|
|
88
|
-
configstring(index: number, value: string): void;
|
|
89
|
-
private recordHistory;
|
|
90
|
-
setLagCompensation(active: boolean, client?: Entity, lagMs?: number): void;
|
|
91
|
-
}
|
|
92
|
-
declare function createServer(options?: ServerOptions): DedicatedServer;
|
|
93
|
-
|
|
94
|
-
declare enum ClientState {
|
|
95
|
-
Free = 0,
|
|
96
|
-
Zombie = 1,// client has been disconnected, but don't reuse connection for a couple seconds
|
|
97
|
-
Connected = 2,// has been assigned to a client_t, but not in game yet
|
|
98
|
-
Spawned = 3,// client is fully in game
|
|
99
|
-
Active = 4
|
|
100
|
-
}
|
|
101
|
-
interface ClientFrame {
|
|
102
|
-
areaBytes: number;
|
|
103
|
-
areaBits: Uint8Array;
|
|
104
|
-
playerState: PlayerState;
|
|
105
|
-
numEntities: number;
|
|
106
|
-
firstEntity: number;
|
|
107
|
-
sentTime: number;
|
|
108
|
-
entities: EntityState[];
|
|
109
|
-
packetCRC: number;
|
|
110
|
-
}
|
|
111
|
-
interface Client {
|
|
112
|
-
index: number;
|
|
113
|
-
state: ClientState;
|
|
114
|
-
net: NetDriver;
|
|
115
|
-
netchan: NetChan;
|
|
116
|
-
userInfo: string;
|
|
117
|
-
lastFrame: number;
|
|
118
|
-
lastCmd: UserCommand;
|
|
119
|
-
commandMsec: number;
|
|
120
|
-
frameLatency: number[];
|
|
121
|
-
ping: number;
|
|
122
|
-
messageSize: number[];
|
|
123
|
-
rate: number;
|
|
124
|
-
suppressCount: number;
|
|
125
|
-
edict: Entity | null;
|
|
126
|
-
name: string;
|
|
127
|
-
messageLevel: number;
|
|
128
|
-
datagram: Uint8Array;
|
|
129
|
-
frames: ClientFrame[];
|
|
130
|
-
download?: Uint8Array;
|
|
131
|
-
downloadSize: number;
|
|
132
|
-
downloadCount: number;
|
|
133
|
-
lastMessage: number;
|
|
134
|
-
lastConnect: number;
|
|
135
|
-
challenge: number;
|
|
136
|
-
messageQueue: Uint8Array[];
|
|
137
|
-
lastPacketEntities: number[];
|
|
138
|
-
commandQueue: UserCommand[];
|
|
139
|
-
lastCommandTime: number;
|
|
140
|
-
commandCount: number;
|
|
141
|
-
}
|
|
142
|
-
declare function createClient(index: number, net: NetDriver): Client;
|
|
143
|
-
|
|
144
|
-
interface ClientMessageHandler {
|
|
145
|
-
onMove(checksum: number, lastFrame: number, userCmd: UserCommand): void;
|
|
146
|
-
onUserInfo(info: string): void;
|
|
147
|
-
onStringCmd(cmd: string): void;
|
|
148
|
-
onNop(): void;
|
|
149
|
-
onBad(): void;
|
|
150
|
-
}
|
|
151
|
-
declare class ClientMessageParser {
|
|
152
|
-
private stream;
|
|
153
|
-
private handler;
|
|
154
|
-
constructor(stream: BinaryStream, handler: ClientMessageHandler);
|
|
155
|
-
parseMessage(): void;
|
|
156
|
-
private parseMove;
|
|
157
|
-
private parseUserInfo;
|
|
158
|
-
private parseStringCmd;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export { type Client, type ClientFrame, type ClientInfo, type ClientMessageHandler, ClientMessageParser, ClientState, DedicatedServer, type NetworkTransport, type ServerOptions, WebSocketNetDriver, createClient, createServer };
|
|
1
|
+
export * from './net/nodeWsDriver.js';
|
|
2
|
+
export * from './dedicated.js';
|
|
3
|
+
export * from './client.js';
|
|
4
|
+
export * from './protocol.js';
|
|
5
|
+
export { ServerOptions, createServer } from './dedicated.js';
|
|
6
|
+
export { NetworkTransport } from './transport.js';
|
|
7
|
+
export * from './server.js';
|
|
@@ -290,6 +290,19 @@ import { BinaryWriter as BinaryWriter2, ServerCommand as ServerCommand2, BinaryS
|
|
|
290
290
|
import { parseBsp } from "@quake2ts/engine";
|
|
291
291
|
import fs from "fs/promises";
|
|
292
292
|
import { createPlayerInventory, createPlayerWeaponStates } from "@quake2ts/game";
|
|
293
|
+
|
|
294
|
+
// src/server.ts
|
|
295
|
+
var ServerState = /* @__PURE__ */ ((ServerState2) => {
|
|
296
|
+
ServerState2[ServerState2["Dead"] = 0] = "Dead";
|
|
297
|
+
ServerState2[ServerState2["Loading"] = 1] = "Loading";
|
|
298
|
+
ServerState2[ServerState2["Game"] = 2] = "Game";
|
|
299
|
+
ServerState2[ServerState2["Cinematic"] = 3] = "Cinematic";
|
|
300
|
+
ServerState2[ServerState2["Demo"] = 4] = "Demo";
|
|
301
|
+
ServerState2[ServerState2["Pic"] = 5] = "Pic";
|
|
302
|
+
return ServerState2;
|
|
303
|
+
})(ServerState || {});
|
|
304
|
+
|
|
305
|
+
// src/dedicated.ts
|
|
293
306
|
import { writeDeltaEntity, writeRemoveEntity } from "@quake2ts/shared";
|
|
294
307
|
|
|
295
308
|
// src/protocol/player.ts
|
|
@@ -962,7 +975,11 @@ var DedicatedServer = class {
|
|
|
962
975
|
}
|
|
963
976
|
onClientMessage(client, data) {
|
|
964
977
|
const buffer = data.byteOffset === 0 && data.byteLength === data.buffer.byteLength ? data.buffer : data.slice().buffer;
|
|
965
|
-
|
|
978
|
+
if (buffer instanceof ArrayBuffer) {
|
|
979
|
+
client.messageQueue.push(new Uint8Array(buffer));
|
|
980
|
+
} else {
|
|
981
|
+
client.messageQueue.push(new Uint8Array(buffer));
|
|
982
|
+
}
|
|
966
983
|
}
|
|
967
984
|
onClientDisconnect(client) {
|
|
968
985
|
console.log(`Client ${client.index} disconnected`);
|
|
@@ -1188,7 +1205,13 @@ var DedicatedServer = class {
|
|
|
1188
1205
|
if (data.length === 0) {
|
|
1189
1206
|
continue;
|
|
1190
1207
|
}
|
|
1191
|
-
|
|
1208
|
+
let buffer;
|
|
1209
|
+
if (data.buffer instanceof ArrayBuffer) {
|
|
1210
|
+
buffer = data.buffer;
|
|
1211
|
+
} else {
|
|
1212
|
+
buffer = new Uint8Array(data).buffer;
|
|
1213
|
+
}
|
|
1214
|
+
const reader = new BinaryStream2(buffer);
|
|
1192
1215
|
const parser = new ClientMessageParser(reader, {
|
|
1193
1216
|
onMove: (checksum, lastFrame, cmd) => this.handleMove(client, cmd, checksum, lastFrame),
|
|
1194
1217
|
onUserInfo: (info) => this.handleUserInfo(client, info),
|
|
@@ -1531,6 +1554,7 @@ export {
|
|
|
1531
1554
|
ClientMessageParser,
|
|
1532
1555
|
ClientState,
|
|
1533
1556
|
DedicatedServer,
|
|
1557
|
+
ServerState,
|
|
1534
1558
|
WebSocketNetDriver,
|
|
1535
1559
|
createClient,
|
|
1536
1560
|
createServer
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { NetDriver } from '@quake2ts/shared';
|
|
2
|
+
import WebSocket from 'ws';
|
|
3
|
+
export declare class WebSocketNetDriver implements NetDriver {
|
|
4
|
+
private socket;
|
|
5
|
+
private messageCallback;
|
|
6
|
+
private closeCallback;
|
|
7
|
+
private errorCallback;
|
|
8
|
+
connect(url: string): Promise<void>;
|
|
9
|
+
attach(socket: WebSocket): void;
|
|
10
|
+
disconnect(): void;
|
|
11
|
+
send(data: Uint8Array): void;
|
|
12
|
+
onMessage(callback: (data: Uint8Array) => void): void;
|
|
13
|
+
onClose(callback: () => void): void;
|
|
14
|
+
onError(callback: (error: Error) => void): void;
|
|
15
|
+
isConnected(): boolean;
|
|
16
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import WebSocket from 'ws';
|
|
2
|
+
export class WebSocketNetDriver {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.socket = null;
|
|
5
|
+
this.messageCallback = null;
|
|
6
|
+
this.closeCallback = null;
|
|
7
|
+
this.errorCallback = null;
|
|
8
|
+
}
|
|
9
|
+
async connect(url) {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
try {
|
|
12
|
+
this.socket = new WebSocket(url);
|
|
13
|
+
this.socket.binaryType = 'arraybuffer';
|
|
14
|
+
this.socket.onopen = () => {
|
|
15
|
+
resolve();
|
|
16
|
+
};
|
|
17
|
+
this.socket.onerror = (event) => {
|
|
18
|
+
const error = new Error('WebSocket connection error ' + event.message);
|
|
19
|
+
if (this.errorCallback) {
|
|
20
|
+
this.errorCallback(error);
|
|
21
|
+
}
|
|
22
|
+
reject(error);
|
|
23
|
+
};
|
|
24
|
+
this.socket.onclose = () => {
|
|
25
|
+
if (this.closeCallback) {
|
|
26
|
+
this.closeCallback();
|
|
27
|
+
}
|
|
28
|
+
this.socket = null;
|
|
29
|
+
};
|
|
30
|
+
this.socket.onmessage = (event) => {
|
|
31
|
+
if (this.messageCallback) {
|
|
32
|
+
if (event.data instanceof ArrayBuffer) {
|
|
33
|
+
this.messageCallback(new Uint8Array(event.data));
|
|
34
|
+
}
|
|
35
|
+
else if (Buffer.isBuffer(event.data)) {
|
|
36
|
+
// ws in Node might return Buffer
|
|
37
|
+
this.messageCallback(new Uint8Array(event.data));
|
|
38
|
+
}
|
|
39
|
+
else if (Array.isArray(event.data)) {
|
|
40
|
+
// Buffer[]
|
|
41
|
+
const totalLength = event.data.reduce((acc, buf) => acc + buf.length, 0);
|
|
42
|
+
const result = new Uint8Array(totalLength);
|
|
43
|
+
let offset = 0;
|
|
44
|
+
for (const buf of event.data) {
|
|
45
|
+
result.set(buf, offset);
|
|
46
|
+
offset += buf.length;
|
|
47
|
+
}
|
|
48
|
+
this.messageCallback(result);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
console.warn('Received non-binary message from server', typeof event.data);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
reject(e);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
// Method to attach an existing socket (server-side incoming connection)
|
|
62
|
+
attach(socket) {
|
|
63
|
+
this.socket = socket;
|
|
64
|
+
this.socket.binaryType = 'arraybuffer';
|
|
65
|
+
this.socket.onclose = () => {
|
|
66
|
+
if (this.closeCallback)
|
|
67
|
+
this.closeCallback();
|
|
68
|
+
this.socket = null;
|
|
69
|
+
};
|
|
70
|
+
this.socket.onerror = (event) => {
|
|
71
|
+
if (this.errorCallback)
|
|
72
|
+
this.errorCallback(new Error(event.message));
|
|
73
|
+
};
|
|
74
|
+
this.socket.onmessage = (event) => {
|
|
75
|
+
if (this.messageCallback) {
|
|
76
|
+
if (event.data instanceof ArrayBuffer) {
|
|
77
|
+
this.messageCallback(new Uint8Array(event.data));
|
|
78
|
+
}
|
|
79
|
+
else if (Buffer.isBuffer(event.data)) {
|
|
80
|
+
this.messageCallback(new Uint8Array(event.data));
|
|
81
|
+
}
|
|
82
|
+
else if (Array.isArray(event.data)) { // ws specific
|
|
83
|
+
// handle fragmentation if necessary, usually it's Buffer[]
|
|
84
|
+
const totalLength = event.data.reduce((acc, buf) => acc + buf.length, 0);
|
|
85
|
+
const result = new Uint8Array(totalLength);
|
|
86
|
+
let offset = 0;
|
|
87
|
+
for (const buf of event.data) {
|
|
88
|
+
result.set(buf, offset);
|
|
89
|
+
offset += buf.length;
|
|
90
|
+
}
|
|
91
|
+
this.messageCallback(result);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
disconnect() {
|
|
97
|
+
if (this.socket) {
|
|
98
|
+
this.socket.close();
|
|
99
|
+
this.socket = null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
send(data) {
|
|
103
|
+
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
|
104
|
+
this.socket.send(data);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
console.warn('Attempted to send data on closed or connecting socket');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
onMessage(callback) {
|
|
111
|
+
this.messageCallback = callback;
|
|
112
|
+
}
|
|
113
|
+
onClose(callback) {
|
|
114
|
+
this.closeCallback = callback;
|
|
115
|
+
}
|
|
116
|
+
onError(callback) {
|
|
117
|
+
this.errorCallback = callback;
|
|
118
|
+
}
|
|
119
|
+
isConnected() {
|
|
120
|
+
return this.socket !== null && this.socket.readyState === WebSocket.OPEN;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { BinaryWriter, Vec3 } from '@quake2ts/shared';
|
|
2
|
+
export interface ProtocolPlayerState {
|
|
3
|
+
pm_type: number;
|
|
4
|
+
origin: Vec3;
|
|
5
|
+
velocity: Vec3;
|
|
6
|
+
pm_time: number;
|
|
7
|
+
pm_flags: number;
|
|
8
|
+
gravity: number;
|
|
9
|
+
delta_angles: Vec3;
|
|
10
|
+
viewoffset: Vec3;
|
|
11
|
+
viewangles: Vec3;
|
|
12
|
+
kick_angles: Vec3;
|
|
13
|
+
gun_index: number;
|
|
14
|
+
gun_frame: number;
|
|
15
|
+
gun_offset: Vec3;
|
|
16
|
+
gun_angles: Vec3;
|
|
17
|
+
blend: number[];
|
|
18
|
+
fov: number;
|
|
19
|
+
rdflags: number;
|
|
20
|
+
stats: number[];
|
|
21
|
+
watertype: number;
|
|
22
|
+
}
|
|
23
|
+
export declare function writePlayerState(writer: BinaryWriter, ps: ProtocolPlayerState): void;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Bitflags matching demo/parser.ts
|
|
2
|
+
const PS_M_TYPE = (1 << 0);
|
|
3
|
+
const PS_M_ORIGIN = (1 << 1);
|
|
4
|
+
const PS_M_VELOCITY = (1 << 2);
|
|
5
|
+
const PS_M_TIME = (1 << 3);
|
|
6
|
+
const PS_M_FLAGS = (1 << 4);
|
|
7
|
+
const PS_M_GRAVITY = (1 << 5);
|
|
8
|
+
const PS_M_DELTA_ANGLES = (1 << 6);
|
|
9
|
+
const PS_VIEWOFFSET = (1 << 7);
|
|
10
|
+
const PS_VIEWANGLES = (1 << 8);
|
|
11
|
+
const PS_KICKANGLES = (1 << 9);
|
|
12
|
+
const PS_BLEND = (1 << 10);
|
|
13
|
+
const PS_FOV = (1 << 11);
|
|
14
|
+
const PS_WEAPONINDEX = (1 << 12);
|
|
15
|
+
const PS_WEAPONFRAME = (1 << 13);
|
|
16
|
+
const PS_RDFLAGS = (1 << 14);
|
|
17
|
+
const PS_WATERTYPE = (1 << 15);
|
|
18
|
+
export function writePlayerState(writer, ps) {
|
|
19
|
+
// Determine mask
|
|
20
|
+
let mask = 0;
|
|
21
|
+
if (ps.pm_type !== 0)
|
|
22
|
+
mask |= PS_M_TYPE;
|
|
23
|
+
if (ps.origin.x !== 0 || ps.origin.y !== 0 || ps.origin.z !== 0)
|
|
24
|
+
mask |= PS_M_ORIGIN;
|
|
25
|
+
if (ps.velocity.x !== 0 || ps.velocity.y !== 0 || ps.velocity.z !== 0)
|
|
26
|
+
mask |= PS_M_VELOCITY;
|
|
27
|
+
if (ps.pm_time !== 0)
|
|
28
|
+
mask |= PS_M_TIME;
|
|
29
|
+
if (ps.pm_flags !== 0)
|
|
30
|
+
mask |= PS_M_FLAGS;
|
|
31
|
+
if (ps.gravity !== 0)
|
|
32
|
+
mask |= PS_M_GRAVITY;
|
|
33
|
+
if (ps.delta_angles.x !== 0 || ps.delta_angles.y !== 0 || ps.delta_angles.z !== 0)
|
|
34
|
+
mask |= PS_M_DELTA_ANGLES;
|
|
35
|
+
if (ps.viewoffset.x !== 0 || ps.viewoffset.y !== 0 || ps.viewoffset.z !== 0)
|
|
36
|
+
mask |= PS_VIEWOFFSET;
|
|
37
|
+
if (ps.viewangles.x !== 0 || ps.viewangles.y !== 0 || ps.viewangles.z !== 0)
|
|
38
|
+
mask |= PS_VIEWANGLES;
|
|
39
|
+
if (ps.kick_angles.x !== 0 || ps.kick_angles.y !== 0 || ps.kick_angles.z !== 0)
|
|
40
|
+
mask |= PS_KICKANGLES;
|
|
41
|
+
if (ps.gun_index !== 0)
|
|
42
|
+
mask |= PS_WEAPONINDEX;
|
|
43
|
+
// Weapon frame includes offset/angles
|
|
44
|
+
if (ps.gun_frame !== 0 ||
|
|
45
|
+
ps.gun_offset.x !== 0 || ps.gun_offset.y !== 0 || ps.gun_offset.z !== 0 ||
|
|
46
|
+
ps.gun_angles.x !== 0 || ps.gun_angles.y !== 0 || ps.gun_angles.z !== 0) {
|
|
47
|
+
mask |= PS_WEAPONFRAME;
|
|
48
|
+
}
|
|
49
|
+
if (ps.blend && (ps.blend[0] !== 0 || ps.blend[1] !== 0 || ps.blend[2] !== 0 || ps.blend[3] !== 0)) {
|
|
50
|
+
mask |= PS_BLEND;
|
|
51
|
+
}
|
|
52
|
+
if (ps.fov !== 0)
|
|
53
|
+
mask |= PS_FOV;
|
|
54
|
+
if (ps.rdflags !== 0)
|
|
55
|
+
mask |= PS_RDFLAGS;
|
|
56
|
+
if (ps.watertype !== 0)
|
|
57
|
+
mask |= PS_WATERTYPE;
|
|
58
|
+
// Stats mask calculation
|
|
59
|
+
let statsMask = 0;
|
|
60
|
+
// Only support first 32 stats for now
|
|
61
|
+
for (let i = 0; i < 32; i++) {
|
|
62
|
+
if (ps.stats[i] && ps.stats[i] !== 0) {
|
|
63
|
+
statsMask |= (1 << i);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Write header
|
|
67
|
+
writer.writeShort(mask);
|
|
68
|
+
// Write fields
|
|
69
|
+
if (mask & PS_M_TYPE)
|
|
70
|
+
writer.writeByte(ps.pm_type);
|
|
71
|
+
if (mask & PS_M_ORIGIN) {
|
|
72
|
+
writer.writeShort(Math.round(ps.origin.x * 8));
|
|
73
|
+
writer.writeShort(Math.round(ps.origin.y * 8));
|
|
74
|
+
writer.writeShort(Math.round(ps.origin.z * 8));
|
|
75
|
+
}
|
|
76
|
+
if (mask & PS_M_VELOCITY) {
|
|
77
|
+
writer.writeShort(Math.round(ps.velocity.x * 8));
|
|
78
|
+
writer.writeShort(Math.round(ps.velocity.y * 8));
|
|
79
|
+
writer.writeShort(Math.round(ps.velocity.z * 8));
|
|
80
|
+
}
|
|
81
|
+
if (mask & PS_M_TIME)
|
|
82
|
+
writer.writeByte(ps.pm_time);
|
|
83
|
+
if (mask & PS_M_FLAGS)
|
|
84
|
+
writer.writeByte(ps.pm_flags);
|
|
85
|
+
if (mask & PS_M_GRAVITY)
|
|
86
|
+
writer.writeShort(ps.gravity);
|
|
87
|
+
if (mask & PS_M_DELTA_ANGLES) {
|
|
88
|
+
writer.writeShort(Math.round(ps.delta_angles.x * (32768 / 180)));
|
|
89
|
+
writer.writeShort(Math.round(ps.delta_angles.y * (32768 / 180)));
|
|
90
|
+
writer.writeShort(Math.round(ps.delta_angles.z * (32768 / 180)));
|
|
91
|
+
}
|
|
92
|
+
if (mask & PS_VIEWOFFSET) {
|
|
93
|
+
writer.writeChar(Math.round(ps.viewoffset.x * 4));
|
|
94
|
+
writer.writeChar(Math.round(ps.viewoffset.y * 4));
|
|
95
|
+
writer.writeChar(Math.round(ps.viewoffset.z * 4));
|
|
96
|
+
}
|
|
97
|
+
if (mask & PS_VIEWANGLES) {
|
|
98
|
+
writer.writeAngle16(ps.viewangles.x);
|
|
99
|
+
writer.writeAngle16(ps.viewangles.y);
|
|
100
|
+
writer.writeAngle16(ps.viewangles.z);
|
|
101
|
+
}
|
|
102
|
+
if (mask & PS_KICKANGLES) {
|
|
103
|
+
writer.writeChar(Math.round(ps.kick_angles.x * 4));
|
|
104
|
+
writer.writeChar(Math.round(ps.kick_angles.y * 4));
|
|
105
|
+
writer.writeChar(Math.round(ps.kick_angles.z * 4));
|
|
106
|
+
}
|
|
107
|
+
if (mask & PS_WEAPONINDEX)
|
|
108
|
+
writer.writeByte(ps.gun_index);
|
|
109
|
+
if (mask & PS_WEAPONFRAME) {
|
|
110
|
+
writer.writeByte(ps.gun_frame);
|
|
111
|
+
writer.writeChar(Math.round(ps.gun_offset.x * 4));
|
|
112
|
+
writer.writeChar(Math.round(ps.gun_offset.y * 4));
|
|
113
|
+
writer.writeChar(Math.round(ps.gun_offset.z * 4));
|
|
114
|
+
writer.writeChar(Math.round(ps.gun_angles.x * 4));
|
|
115
|
+
writer.writeChar(Math.round(ps.gun_angles.y * 4));
|
|
116
|
+
writer.writeChar(Math.round(ps.gun_angles.z * 4));
|
|
117
|
+
}
|
|
118
|
+
if (mask & PS_BLEND) {
|
|
119
|
+
writer.writeByte(Math.round(ps.blend[0]));
|
|
120
|
+
writer.writeByte(Math.round(ps.blend[1]));
|
|
121
|
+
writer.writeByte(Math.round(ps.blend[2]));
|
|
122
|
+
writer.writeByte(Math.round(ps.blend[3]));
|
|
123
|
+
}
|
|
124
|
+
if (mask & PS_FOV)
|
|
125
|
+
writer.writeByte(ps.fov);
|
|
126
|
+
if (mask & PS_RDFLAGS)
|
|
127
|
+
writer.writeByte(ps.rdflags);
|
|
128
|
+
if (mask & PS_WATERTYPE)
|
|
129
|
+
writer.writeByte(ps.watertype);
|
|
130
|
+
// Write Stats
|
|
131
|
+
writer.writeLong(statsMask);
|
|
132
|
+
for (let i = 0; i < 32; i++) {
|
|
133
|
+
if (statsMask & (1 << i)) {
|
|
134
|
+
writer.writeShort(ps.stats[i]);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { BinaryWriter, ServerCommand } from '@quake2ts/shared';
|
|
2
|
+
/**
|
|
3
|
+
* Writes a server command and its arguments to a BinaryWriter.
|
|
4
|
+
* This handles the serialization of generic arguments passed to multicast/unicast
|
|
5
|
+
* into the specific binary format expected by the protocol.
|
|
6
|
+
*/
|
|
7
|
+
export declare function writeServerCommand(writer: BinaryWriter, event: ServerCommand, ...args: any[]): void;
|