quake2ts 0.0.288 → 0.0.289
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 +13 -13
- package/packages/client/dist/browser/index.global.js.map +1 -1
- package/packages/client/dist/cjs/index.cjs +554 -2
- package/packages/client/dist/cjs/index.cjs.map +1 -1
- package/packages/client/dist/esm/index.js +554 -2
- package/packages/client/dist/esm/index.js.map +1 -1
- package/packages/client/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/engine/dist/browser/index.global.js +16 -16
- package/packages/engine/dist/browser/index.global.js.map +1 -1
- package/packages/engine/dist/cjs/index.cjs +327 -0
- package/packages/engine/dist/cjs/index.cjs.map +1 -1
- package/packages/engine/dist/esm/index.js +327 -0
- package/packages/engine/dist/esm/index.js.map +1 -1
- package/packages/engine/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/game/dist/browser/index.global.js +3 -3
- package/packages/game/dist/browser/index.global.js.map +1 -1
- package/packages/game/dist/cjs/index.cjs +492 -1
- package/packages/game/dist/cjs/index.cjs.map +1 -1
- package/packages/game/dist/esm/index.js +492 -1
- package/packages/game/dist/esm/index.js.map +1 -1
- package/packages/game/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/server/dist/index.cjs +54 -11
- package/packages/server/dist/index.d.cts +6 -1
- package/packages/server/dist/index.d.ts +6 -1
- package/packages/server/dist/index.js +56 -13
- package/packages/shared/dist/browser/index.global.js +1 -1
- package/packages/shared/dist/browser/index.global.js.map +1 -1
- package/packages/shared/dist/cjs/index.cjs +509 -0
- package/packages/shared/dist/cjs/index.cjs.map +1 -1
- package/packages/shared/dist/esm/index.js +507 -0
- package/packages/shared/dist/esm/index.js.map +1 -1
- package/packages/shared/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/shared/dist/types/net/index.d.ts +1 -0
- package/packages/shared/dist/types/net/index.d.ts.map +1 -1
- package/packages/shared/dist/types/protocol/crc.d.ts +5 -0
- package/packages/shared/dist/types/protocol/crc.d.ts.map +1 -0
- package/packages/shared/dist/types/protocol/index.d.ts +1 -0
- package/packages/shared/dist/types/protocol/index.d.ts.map +1 -1
- package/packages/tools/dist/tsconfig.tsbuildinfo +1 -1
|
@@ -175,13 +175,17 @@ function createClient(index, net) {
|
|
|
175
175
|
numEntities: 0,
|
|
176
176
|
firstEntity: 0,
|
|
177
177
|
sentTime: 0,
|
|
178
|
-
entities: []
|
|
178
|
+
entities: [],
|
|
179
|
+
packetCRC: 0
|
|
179
180
|
});
|
|
180
181
|
}
|
|
182
|
+
const netchan = new import_shared.NetChan();
|
|
183
|
+
netchan.setup(Math.floor(Math.random() * 65536));
|
|
181
184
|
return {
|
|
182
185
|
index,
|
|
183
186
|
state: 2 /* Connected */,
|
|
184
187
|
net,
|
|
188
|
+
netchan,
|
|
185
189
|
userInfo: "",
|
|
186
190
|
lastFrame: 0,
|
|
187
191
|
lastCmd: createEmptyUserCommand(),
|
|
@@ -203,7 +207,10 @@ function createClient(index, net) {
|
|
|
203
207
|
lastConnect: Date.now(),
|
|
204
208
|
challenge: 0,
|
|
205
209
|
messageQueue: [],
|
|
206
|
-
lastPacketEntities: []
|
|
210
|
+
lastPacketEntities: [],
|
|
211
|
+
commandQueue: [],
|
|
212
|
+
lastCommandTime: 0,
|
|
213
|
+
commandCount: 0
|
|
207
214
|
};
|
|
208
215
|
}
|
|
209
216
|
function createEmptyUserCommand() {
|
|
@@ -1007,9 +1014,17 @@ var DedicatedServer = class {
|
|
|
1007
1014
|
client.net.disconnect();
|
|
1008
1015
|
}
|
|
1009
1016
|
}
|
|
1010
|
-
handleMove(client, cmd) {
|
|
1017
|
+
handleMove(client, cmd, checksum, lastFrame) {
|
|
1018
|
+
if (lastFrame > 0 && lastFrame <= client.lastFrame && lastFrame > client.lastFrame - import_shared4.UPDATE_BACKUP) {
|
|
1019
|
+
const frameIdx = lastFrame % import_shared4.UPDATE_BACKUP;
|
|
1020
|
+
const frame = client.frames[frameIdx];
|
|
1021
|
+
if (frame.packetCRC !== checksum) {
|
|
1022
|
+
console.warn(`Client ${client.index} checksum mismatch for frame ${lastFrame}: expected ${frame.packetCRC}, got ${checksum}`);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1011
1025
|
client.lastCmd = cmd;
|
|
1012
1026
|
client.lastMessage = this.sv.frame;
|
|
1027
|
+
client.commandCount++;
|
|
1013
1028
|
}
|
|
1014
1029
|
handleUserInfo(client, info) {
|
|
1015
1030
|
client.userInfo = info;
|
|
@@ -1091,12 +1106,20 @@ var DedicatedServer = class {
|
|
|
1091
1106
|
SV_SetConfigString(index, value) {
|
|
1092
1107
|
if (index < 0 || index >= import_shared4.MAX_CONFIGSTRINGS) return;
|
|
1093
1108
|
this.sv.configStrings[index] = value;
|
|
1094
|
-
const writer = new import_shared4.BinaryWriter();
|
|
1095
|
-
this.SV_WriteConfigString(writer, index, value);
|
|
1096
|
-
const data = writer.getData();
|
|
1097
1109
|
for (const client of this.svs.clients) {
|
|
1098
1110
|
if (client && client.state >= 2 /* Connected */) {
|
|
1099
|
-
client.
|
|
1111
|
+
if (client.netchan) {
|
|
1112
|
+
try {
|
|
1113
|
+
client.netchan.writeReliableByte(import_shared4.ServerCommand.configstring);
|
|
1114
|
+
client.netchan.writeReliableShort(index);
|
|
1115
|
+
client.netchan.writeReliableString(value);
|
|
1116
|
+
const packet = client.netchan.transmit();
|
|
1117
|
+
client.net.send(packet);
|
|
1118
|
+
} catch (e) {
|
|
1119
|
+
console.warn(`Client ${client.index} reliable buffer overflow`);
|
|
1120
|
+
this.dropClient(client);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1100
1123
|
}
|
|
1101
1124
|
}
|
|
1102
1125
|
}
|
|
@@ -1109,11 +1132,18 @@ var DedicatedServer = class {
|
|
|
1109
1132
|
for (const client of this.svs.clients) {
|
|
1110
1133
|
if (!client || client.state === 0 /* Free */) continue;
|
|
1111
1134
|
while (client.messageQueue.length > 0) {
|
|
1112
|
-
const
|
|
1113
|
-
if (!
|
|
1135
|
+
const rawData = client.messageQueue.shift();
|
|
1136
|
+
if (!rawData) continue;
|
|
1137
|
+
const data = client.netchan.process(rawData);
|
|
1138
|
+
if (!data) {
|
|
1139
|
+
continue;
|
|
1140
|
+
}
|
|
1141
|
+
if (data.length === 0) {
|
|
1142
|
+
continue;
|
|
1143
|
+
}
|
|
1114
1144
|
const reader = new import_shared4.BinaryStream(data.buffer);
|
|
1115
1145
|
const parser = new ClientMessageParser(reader, {
|
|
1116
|
-
onMove: (checksum, lastFrame, cmd) => this.handleMove(client, cmd),
|
|
1146
|
+
onMove: (checksum, lastFrame, cmd) => this.handleMove(client, cmd, checksum, lastFrame),
|
|
1117
1147
|
onUserInfo: (info) => this.handleUserInfo(client, info),
|
|
1118
1148
|
onStringCmd: (cmd) => this.handleStringCmd(client, cmd),
|
|
1119
1149
|
onNop: () => {
|
|
@@ -1147,6 +1177,16 @@ var DedicatedServer = class {
|
|
|
1147
1177
|
}
|
|
1148
1178
|
}
|
|
1149
1179
|
if (client && client.state === 4 /* Active */ && client.edict) {
|
|
1180
|
+
const now = Date.now();
|
|
1181
|
+
if (now - client.lastCommandTime >= 1e3) {
|
|
1182
|
+
client.lastCommandTime = now;
|
|
1183
|
+
client.commandCount = 0;
|
|
1184
|
+
}
|
|
1185
|
+
if (client.commandCount > 200) {
|
|
1186
|
+
console.warn(`Client ${client.index} kicked for command flooding`);
|
|
1187
|
+
this.dropClient(client);
|
|
1188
|
+
continue;
|
|
1189
|
+
}
|
|
1150
1190
|
this.game.clientThink(client.edict, client.lastCmd);
|
|
1151
1191
|
}
|
|
1152
1192
|
}
|
|
@@ -1242,7 +1282,10 @@ var DedicatedServer = class {
|
|
|
1242
1282
|
}
|
|
1243
1283
|
}
|
|
1244
1284
|
writer.writeShort(0);
|
|
1245
|
-
|
|
1285
|
+
const frameData = writer.getData();
|
|
1286
|
+
currentFrame.packetCRC = (0, import_shared4.crc8)(frameData);
|
|
1287
|
+
const packet = client.netchan.transmit(frameData);
|
|
1288
|
+
client.net.send(packet);
|
|
1246
1289
|
client.lastFrame = this.sv.frame;
|
|
1247
1290
|
client.lastPacketEntities = currentEntityIds;
|
|
1248
1291
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NetDriver, ServerCommand, PlayerState, EntityState, UserCommand, BinaryStream } from '@quake2ts/shared';
|
|
1
|
+
import { NetDriver, ServerCommand, PlayerState, EntityState, NetChan, UserCommand, BinaryStream } from '@quake2ts/shared';
|
|
2
2
|
import WebSocket from 'ws';
|
|
3
3
|
import { GameEngine, MulticastType, Entity } from '@quake2ts/game';
|
|
4
4
|
|
|
@@ -68,11 +68,13 @@ interface ClientFrame {
|
|
|
68
68
|
firstEntity: number;
|
|
69
69
|
sentTime: number;
|
|
70
70
|
entities: EntityState[];
|
|
71
|
+
packetCRC: number;
|
|
71
72
|
}
|
|
72
73
|
interface Client {
|
|
73
74
|
index: number;
|
|
74
75
|
state: ClientState;
|
|
75
76
|
net: NetDriver;
|
|
77
|
+
netchan: NetChan;
|
|
76
78
|
userInfo: string;
|
|
77
79
|
lastFrame: number;
|
|
78
80
|
lastCmd: UserCommand;
|
|
@@ -95,6 +97,9 @@ interface Client {
|
|
|
95
97
|
challenge: number;
|
|
96
98
|
messageQueue: Uint8Array[];
|
|
97
99
|
lastPacketEntities: number[];
|
|
100
|
+
commandQueue: UserCommand[];
|
|
101
|
+
lastCommandTime: number;
|
|
102
|
+
commandCount: number;
|
|
98
103
|
}
|
|
99
104
|
declare function createClient(index: number, net: NetDriver): Client;
|
|
100
105
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NetDriver, ServerCommand, PlayerState, EntityState, UserCommand, BinaryStream } from '@quake2ts/shared';
|
|
1
|
+
import { NetDriver, ServerCommand, PlayerState, EntityState, NetChan, UserCommand, BinaryStream } from '@quake2ts/shared';
|
|
2
2
|
import WebSocket from 'ws';
|
|
3
3
|
import { GameEngine, MulticastType, Entity } from '@quake2ts/game';
|
|
4
4
|
|
|
@@ -68,11 +68,13 @@ interface ClientFrame {
|
|
|
68
68
|
firstEntity: number;
|
|
69
69
|
sentTime: number;
|
|
70
70
|
entities: EntityState[];
|
|
71
|
+
packetCRC: number;
|
|
71
72
|
}
|
|
72
73
|
interface Client {
|
|
73
74
|
index: number;
|
|
74
75
|
state: ClientState;
|
|
75
76
|
net: NetDriver;
|
|
77
|
+
netchan: NetChan;
|
|
76
78
|
userInfo: string;
|
|
77
79
|
lastFrame: number;
|
|
78
80
|
lastCmd: UserCommand;
|
|
@@ -95,6 +97,9 @@ interface Client {
|
|
|
95
97
|
challenge: number;
|
|
96
98
|
messageQueue: Uint8Array[];
|
|
97
99
|
lastPacketEntities: number[];
|
|
100
|
+
commandQueue: UserCommand[];
|
|
101
|
+
lastCommandTime: number;
|
|
102
|
+
commandCount: number;
|
|
98
103
|
}
|
|
99
104
|
declare function createClient(index: number, net: NetDriver): Client;
|
|
100
105
|
|
|
@@ -115,7 +115,7 @@ import { WebSocketServer } from "ws";
|
|
|
115
115
|
import { createGame, MulticastType, Solid } from "@quake2ts/game";
|
|
116
116
|
|
|
117
117
|
// src/client.ts
|
|
118
|
-
import { UPDATE_BACKUP } from "@quake2ts/shared";
|
|
118
|
+
import { UPDATE_BACKUP, NetChan } from "@quake2ts/shared";
|
|
119
119
|
var ClientState = /* @__PURE__ */ ((ClientState2) => {
|
|
120
120
|
ClientState2[ClientState2["Free"] = 0] = "Free";
|
|
121
121
|
ClientState2[ClientState2["Zombie"] = 1] = "Zombie";
|
|
@@ -135,13 +135,17 @@ function createClient(index, net) {
|
|
|
135
135
|
numEntities: 0,
|
|
136
136
|
firstEntity: 0,
|
|
137
137
|
sentTime: 0,
|
|
138
|
-
entities: []
|
|
138
|
+
entities: [],
|
|
139
|
+
packetCRC: 0
|
|
139
140
|
});
|
|
140
141
|
}
|
|
142
|
+
const netchan = new NetChan();
|
|
143
|
+
netchan.setup(Math.floor(Math.random() * 65536));
|
|
141
144
|
return {
|
|
142
145
|
index,
|
|
143
146
|
state: 2 /* Connected */,
|
|
144
147
|
net,
|
|
148
|
+
netchan,
|
|
145
149
|
userInfo: "",
|
|
146
150
|
lastFrame: 0,
|
|
147
151
|
lastCmd: createEmptyUserCommand(),
|
|
@@ -163,7 +167,10 @@ function createClient(index, net) {
|
|
|
163
167
|
lastConnect: Date.now(),
|
|
164
168
|
challenge: 0,
|
|
165
169
|
messageQueue: [],
|
|
166
|
-
lastPacketEntities: []
|
|
170
|
+
lastPacketEntities: [],
|
|
171
|
+
commandQueue: [],
|
|
172
|
+
lastCommandTime: 0,
|
|
173
|
+
commandCount: 0
|
|
167
174
|
};
|
|
168
175
|
}
|
|
169
176
|
function createEmptyUserCommand() {
|
|
@@ -271,7 +278,7 @@ var ClientMessageParser = class {
|
|
|
271
278
|
};
|
|
272
279
|
|
|
273
280
|
// src/dedicated.ts
|
|
274
|
-
import { BinaryWriter as BinaryWriter2, ServerCommand as ServerCommand2, BinaryStream as BinaryStream2, traceBox, UPDATE_BACKUP as UPDATE_BACKUP2, MAX_CONFIGSTRINGS as MAX_CONFIGSTRINGS2, MAX_EDICTS, CollisionEntityIndex, inPVS, inPHS } from "@quake2ts/shared";
|
|
281
|
+
import { BinaryWriter as BinaryWriter2, ServerCommand as ServerCommand2, BinaryStream as BinaryStream2, traceBox, UPDATE_BACKUP as UPDATE_BACKUP2, MAX_CONFIGSTRINGS as MAX_CONFIGSTRINGS2, MAX_EDICTS, CollisionEntityIndex, inPVS, inPHS, crc8 } from "@quake2ts/shared";
|
|
275
282
|
import { parseBsp } from "@quake2ts/engine";
|
|
276
283
|
import fs from "fs/promises";
|
|
277
284
|
import { createPlayerInventory, createPlayerWeaponStates } from "@quake2ts/game";
|
|
@@ -967,9 +974,17 @@ var DedicatedServer = class {
|
|
|
967
974
|
client.net.disconnect();
|
|
968
975
|
}
|
|
969
976
|
}
|
|
970
|
-
handleMove(client, cmd) {
|
|
977
|
+
handleMove(client, cmd, checksum, lastFrame) {
|
|
978
|
+
if (lastFrame > 0 && lastFrame <= client.lastFrame && lastFrame > client.lastFrame - UPDATE_BACKUP2) {
|
|
979
|
+
const frameIdx = lastFrame % UPDATE_BACKUP2;
|
|
980
|
+
const frame = client.frames[frameIdx];
|
|
981
|
+
if (frame.packetCRC !== checksum) {
|
|
982
|
+
console.warn(`Client ${client.index} checksum mismatch for frame ${lastFrame}: expected ${frame.packetCRC}, got ${checksum}`);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
971
985
|
client.lastCmd = cmd;
|
|
972
986
|
client.lastMessage = this.sv.frame;
|
|
987
|
+
client.commandCount++;
|
|
973
988
|
}
|
|
974
989
|
handleUserInfo(client, info) {
|
|
975
990
|
client.userInfo = info;
|
|
@@ -1051,12 +1066,20 @@ var DedicatedServer = class {
|
|
|
1051
1066
|
SV_SetConfigString(index, value) {
|
|
1052
1067
|
if (index < 0 || index >= MAX_CONFIGSTRINGS2) return;
|
|
1053
1068
|
this.sv.configStrings[index] = value;
|
|
1054
|
-
const writer = new BinaryWriter2();
|
|
1055
|
-
this.SV_WriteConfigString(writer, index, value);
|
|
1056
|
-
const data = writer.getData();
|
|
1057
1069
|
for (const client of this.svs.clients) {
|
|
1058
1070
|
if (client && client.state >= 2 /* Connected */) {
|
|
1059
|
-
client.
|
|
1071
|
+
if (client.netchan) {
|
|
1072
|
+
try {
|
|
1073
|
+
client.netchan.writeReliableByte(ServerCommand2.configstring);
|
|
1074
|
+
client.netchan.writeReliableShort(index);
|
|
1075
|
+
client.netchan.writeReliableString(value);
|
|
1076
|
+
const packet = client.netchan.transmit();
|
|
1077
|
+
client.net.send(packet);
|
|
1078
|
+
} catch (e) {
|
|
1079
|
+
console.warn(`Client ${client.index} reliable buffer overflow`);
|
|
1080
|
+
this.dropClient(client);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1060
1083
|
}
|
|
1061
1084
|
}
|
|
1062
1085
|
}
|
|
@@ -1069,11 +1092,18 @@ var DedicatedServer = class {
|
|
|
1069
1092
|
for (const client of this.svs.clients) {
|
|
1070
1093
|
if (!client || client.state === 0 /* Free */) continue;
|
|
1071
1094
|
while (client.messageQueue.length > 0) {
|
|
1072
|
-
const
|
|
1073
|
-
if (!
|
|
1095
|
+
const rawData = client.messageQueue.shift();
|
|
1096
|
+
if (!rawData) continue;
|
|
1097
|
+
const data = client.netchan.process(rawData);
|
|
1098
|
+
if (!data) {
|
|
1099
|
+
continue;
|
|
1100
|
+
}
|
|
1101
|
+
if (data.length === 0) {
|
|
1102
|
+
continue;
|
|
1103
|
+
}
|
|
1074
1104
|
const reader = new BinaryStream2(data.buffer);
|
|
1075
1105
|
const parser = new ClientMessageParser(reader, {
|
|
1076
|
-
onMove: (checksum, lastFrame, cmd) => this.handleMove(client, cmd),
|
|
1106
|
+
onMove: (checksum, lastFrame, cmd) => this.handleMove(client, cmd, checksum, lastFrame),
|
|
1077
1107
|
onUserInfo: (info) => this.handleUserInfo(client, info),
|
|
1078
1108
|
onStringCmd: (cmd) => this.handleStringCmd(client, cmd),
|
|
1079
1109
|
onNop: () => {
|
|
@@ -1107,6 +1137,16 @@ var DedicatedServer = class {
|
|
|
1107
1137
|
}
|
|
1108
1138
|
}
|
|
1109
1139
|
if (client && client.state === 4 /* Active */ && client.edict) {
|
|
1140
|
+
const now = Date.now();
|
|
1141
|
+
if (now - client.lastCommandTime >= 1e3) {
|
|
1142
|
+
client.lastCommandTime = now;
|
|
1143
|
+
client.commandCount = 0;
|
|
1144
|
+
}
|
|
1145
|
+
if (client.commandCount > 200) {
|
|
1146
|
+
console.warn(`Client ${client.index} kicked for command flooding`);
|
|
1147
|
+
this.dropClient(client);
|
|
1148
|
+
continue;
|
|
1149
|
+
}
|
|
1110
1150
|
this.game.clientThink(client.edict, client.lastCmd);
|
|
1111
1151
|
}
|
|
1112
1152
|
}
|
|
@@ -1202,7 +1242,10 @@ var DedicatedServer = class {
|
|
|
1202
1242
|
}
|
|
1203
1243
|
}
|
|
1204
1244
|
writer.writeShort(0);
|
|
1205
|
-
|
|
1245
|
+
const frameData = writer.getData();
|
|
1246
|
+
currentFrame.packetCRC = crc8(frameData);
|
|
1247
|
+
const packet = client.netchan.transmit(frameData);
|
|
1248
|
+
client.net.send(packet);
|
|
1206
1249
|
client.lastFrame = this.sv.frame;
|
|
1207
1250
|
client.lastPacketEntities = currentEntityIds;
|
|
1208
1251
|
}
|