Skip to content

Commit b6f766d

Browse files
committed
wip
1 parent c9aae03 commit b6f766d

2 files changed

Lines changed: 124 additions & 101 deletions

File tree

src/matches/match-relay/match-relay.controller.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ export class MatchRelayController {
3030
@Param("fragment") fragment: string,
3131
@Res() response: Response,
3232
) {
33-
this.matchRelayService.getFragment(response, matchId, parseInt(fragment));
33+
this.matchRelayService.getFragment(
34+
response,
35+
matchId,
36+
parseInt(fragment),
37+
"full",
38+
);
3439
}
3540

3641
@Get(":fragment/delta")
@@ -39,7 +44,12 @@ export class MatchRelayController {
3944
@Param("fragment") fragment: string,
4045
@Res() response: Response,
4146
) {
42-
this.matchRelayService.getFragment(response, matchId, parseInt(fragment));
47+
this.matchRelayService.getFragment(
48+
response,
49+
matchId,
50+
parseInt(fragment),
51+
"delta",
52+
);
4353
}
4454

4555
@Get(":token/:fragment/start")
@@ -57,7 +67,12 @@ export class MatchRelayController {
5767
@Param("fragment") fragment: string,
5868
@Res() response: Response,
5969
) {
60-
this.matchRelayService.getFragment(response, matchId, parseInt(fragment));
70+
this.matchRelayService.getFragment(
71+
response,
72+
matchId,
73+
parseInt(fragment),
74+
"full",
75+
);
6176
}
6277

6378
@Get(":token/:fragment/delta")
@@ -66,7 +81,12 @@ export class MatchRelayController {
6681
@Param("fragment") fragment: string,
6782
@Res() response: Response,
6883
) {
69-
this.matchRelayService.getFragment(response, matchId, parseInt(fragment));
84+
this.matchRelayService.getFragment(
85+
response,
86+
matchId,
87+
parseInt(fragment),
88+
"delta",
89+
);
7090
}
7191

7292
@Post(":token/:fragment/start")

src/matches/match-relay/match-relay.service.ts

Lines changed: 100 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,33 @@ import { promisify } from "util";
33
import { Request, Response } from "express";
44
import { Injectable, Logger } from "@nestjs/common";
55

6-
type Fragment = {
7-
data?: Buffer;
8-
gipped: boolean;
9-
[key: string]: any;
10-
};
11-
126
@Injectable()
137
export class MatchRelayService {
148
private readonly gzip = promisify(zlib.gzip);
159

16-
private readonly broadcasts: {
17-
[key: string]: {
18-
start: number;
19-
fragments: Fragment[];
20-
};
21-
} = {};
10+
private readonly broadcasts: { [key: string]: any[] } = {};
2211

2312
constructor(private readonly logger: Logger) {}
2413

25-
public getStart(response: Response, matchId: string, fragmentIndex: number) {
14+
public getStart(response: Response, matchId: string, fragment: number) {
2615
const broadcast = this.broadcasts[matchId];
2716

28-
console.info(`request`, {
29-
fragmentIndex,
30-
start_fragment: broadcast?.start,
31-
});
32-
if (broadcast?.start == null || broadcast.start != fragmentIndex) {
17+
if (broadcast?.[0] == null || broadcast[0].signup_fragment != fragment) {
3318
return this.relayError(
3419
response,
3520
404,
3621
"Invalid or expired start fragment, please re-sync",
3722
);
3823
}
3924

40-
this.getFragment(response, matchId, fragmentIndex);
25+
this.serveBlob(response, broadcast[0], "start");
4126
}
4227

4328
public getFragment(
4429
response: Response,
4530
matchId: string,
4631
fragmentIndex: number,
32+
field: "start" | "full" | "delta",
4733
) {
4834
const broadcast = this.broadcasts[matchId];
4935
if (!broadcast) {
@@ -56,22 +42,7 @@ export class MatchRelayService {
5642
return;
5743
}
5844

59-
const fragment = broadcast.fragments[fragmentIndex];
60-
61-
if (fragment == null) {
62-
response.writeHead(404, "Field not found");
63-
response.end();
64-
return;
65-
}
66-
const headers: { [key: string]: string } = {
67-
"Content-Type": "application/octet-stream",
68-
};
69-
70-
if (fragment.gipped) {
71-
headers["Content-Encoding"] = "gzip";
72-
}
73-
response.writeHead(200, headers);
74-
response.end(fragment.data);
45+
this.serveBlob(response, broadcast[fragmentIndex], field);
7546
}
7647

7748
public getSyncInfo(
@@ -84,63 +55,77 @@ export class MatchRelayService {
8455
response.setHeader("Expires", new Date(nowMs + 3000).toUTCString());
8556

8657
const broadcast = this.broadcasts[matchId];
87-
if (!broadcast || broadcast.start == null) {
58+
if (!broadcast) {
59+
this.logger.error(`Broadcast not found for matchId ${matchId}`);
8860
this.relayError(
8961
response,
9062
404,
91-
`[${matchId}] broadcast not found or not started`,
63+
`Broadcast not found for matchId ${matchId}`,
9264
);
9365
return;
9466
}
9567

96-
let fragment: Fragment;
97-
let fragmentIndex: number;
68+
const match_field_0 = broadcast[0];
69+
if (match_field_0 == null || match_field_0.start == null) {
70+
response.writeHead(404, "Broadcast has not started yet");
71+
response.end();
72+
return;
73+
}
74+
75+
let fragment: number | null = null;
9876
const fragmentParam = request.query.fragment as string | undefined;
77+
let frag: any = null;
9978

10079
if (fragmentParam == null) {
101-
fragment = broadcast.fragments[broadcast.start];
80+
fragment = Math.max(0, broadcast.length - 8);
81+
82+
if (fragment >= 0 && fragment >= match_field_0.signup_fragment) {
83+
const _fragment = broadcast[fragment];
84+
if (this.isSyncReady(_fragment)) {
85+
frag = _fragment;
86+
}
87+
}
10288
} else {
103-
fragmentIndex = parseInt(fragmentParam);
89+
fragment = parseInt(fragmentParam);
10490

105-
if (fragmentIndex < broadcast.start) {
106-
fragmentIndex = broadcast.start;
91+
if (fragment < match_field_0.signup_fragment) {
92+
fragment = match_field_0.signup_fragment;
10793
}
10894

109-
for (let i = fragmentIndex; i < broadcast.fragments.length; i++) {
110-
const _fragment = broadcast.fragments[i];
95+
for (let i = fragment; i < broadcast.length; i++) {
96+
const _fragment = broadcast[i];
11197
if (this.isSyncReady(_fragment)) {
112-
fragment = _fragment;
98+
frag = _fragment;
99+
fragment = i;
113100
break;
114101
}
115102
}
116103
}
117104

118-
if (!fragment) {
119-
console.info(`fragment not found`, fragmentIndex);
105+
if (!frag) {
120106
response.writeHead(405, "Fragment not found, please check back soon");
121107
response.end();
122108
return;
123109
}
124110

125111
response.writeHead(200, { "Content-Type": "application/json" });
126-
127-
const startFragment = broadcast.fragments[broadcast.start];
112+
if (match_field_0.protocol == null) {
113+
match_field_0.protocol = 5;
114+
}
128115

129116
response.end(
130117
JSON.stringify({
131-
tick: fragment.tick,
132-
endtick: fragment.endtick,
133-
maxtick: this.getMatchBroadcastEndTick(
134-
Object.values(broadcast.fragments),
135-
),
136-
rtdelay: (nowMs - fragment.timestamp) / 1000,
137-
rcvage: (nowMs - startFragment.timestamp) / 1000,
138-
fragment: fragmentIndex || broadcast.start,
139-
signup_fragment: broadcast.start,
140-
tps: startFragment.tps,
141-
keyframe_interval: startFragment.keyframe_interval,
142-
map: startFragment.map,
143-
protocol: startFragment.protocol,
118+
tick: frag.tick,
119+
endtick: frag.endtick,
120+
maxtick: this.getMatchBroadcastEndTick(broadcast),
121+
rtdelay: (nowMs - frag.timestamp) / 1000,
122+
rcvage: (nowMs - (broadcast[broadcast.length - 1]?.timestamp || nowMs)) / 1000,
123+
fragment: fragment,
124+
signup_fragment: match_field_0.signup_fragment,
125+
tps: match_field_0.tps,
126+
keyframe_interval: match_field_0.keyframe_interval,
127+
map: match_field_0.map,
128+
protocol: match_field_0.protocol,
144129
}),
145130
);
146131
}
@@ -150,68 +135,65 @@ export class MatchRelayService {
150135
response: Response,
151136
field: string,
152137
matchId: string,
153-
fragmentIndex: number,
138+
fragment: number,
154139
): void {
155140
if (!this.broadcasts[matchId]) {
156141
this.logger.log(`Creating new match broadcast for matchId ${matchId}`);
157-
this.broadcasts[matchId] = { start: null, fragments: [] };
142+
this.broadcasts[matchId] = [];
158143
}
159144
const broadcast = this.broadcasts[matchId];
160145

161146
if (field == "start") {
162-
if (broadcast.start == null) {
163-
broadcast.start = fragmentIndex;
147+
response.writeHead(200);
148+
149+
if (broadcast[0] == null) {
150+
broadcast[0] = {};
164151
}
165-
}
166152

167-
if (broadcast.start == null) {
168-
response.writeHead(205);
169-
response.end();
170-
return;
153+
broadcast[0].signup_fragment = fragment;
154+
fragment = 0;
155+
} else {
156+
if (broadcast[0] == null || broadcast[0].start == null) {
157+
response.writeHead(205);
158+
response.end();
159+
return;
160+
} else {
161+
response.writeHead(200);
162+
}
163+
if (broadcast[fragment] == null) {
164+
broadcast[fragment] = {};
165+
}
171166
}
172167

173-
response.writeHead(200);
174-
175-
broadcast.fragments[fragmentIndex] = {
176-
gipped: false,
177-
};
178-
179168
Object.entries(request.query).forEach(([key, value]) => {
180169
const strValue = String(value);
181170
const numValue = Number(strValue);
182-
broadcast.fragments[fragmentIndex][key] =
171+
broadcast[fragment][key] =
183172
!isNaN(numValue) && strValue === String(numValue) ? numValue : value;
184173
});
185174

186175
const body: Buffer[] = [];
187-
188176
request.on("data", function (data: Buffer) {
189177
body.push(data);
190178
});
191-
192179
request.on("end", () => {
193180
const totalBuffer = Buffer.concat(body);
194-
195-
broadcast.fragments[fragmentIndex].timestamp = Date.now();
181+
182+
// Send response immediately (like old code)
183+
response.end();
196184

197185
this.gzip(totalBuffer)
198186
.then((compressedBlob: Buffer) => {
199-
broadcast.fragments[fragmentIndex].gipped = true;
200-
broadcast.fragments[fragmentIndex].data = compressedBlob;
201-
202-
if (field === "start") {
203-
broadcast.start = fragmentIndex;
204-
}
187+
broadcast[fragment][field + "_ungzlen"] = totalBuffer.length;
188+
broadcast[fragment][field] = compressedBlob;
189+
broadcast[fragment].timestamp = Date.now();
205190
})
206191
.catch((error: Error) => {
207192
this.logger.error(
208193
`Cannot gzip ${totalBuffer.length} bytes: ${error}`,
209194
);
210-
broadcast.fragments[fragmentIndex].data = totalBuffer;
211-
})
212-
.finally(() => {
213-
console.info(`${fragmentIndex}:${field}`);
214-
response.end();
195+
broadcast[fragment][field] = totalBuffer;
196+
broadcast[fragment].timestamp = Date.now();
215197
});
216198
});
217199
}
@@ -237,13 +219,34 @@ export class MatchRelayService {
237219
);
238220
}
239221

240-
private getMatchBroadcastEndTick(fragments: any[]): number {
241-
for (let i = fragments.length - 1; i >= 0; i--) {
242-
const fragment = fragments[i];
222+
private getMatchBroadcastEndTick(broadcast: any[]): number {
223+
for (let i = broadcast.length - 1; i >= 0; i--) {
224+
const fragment = broadcast[i];
243225
if (fragment?.endtick != null) {
244226
return fragment.endtick;
245227
}
246228
}
247229
return 0;
248230
}
231+
232+
private serveBlob(response: Response, fragmentRec: any, field: string): void {
233+
const blob = fragmentRec?.[field];
234+
235+
if (!blob) {
236+
response.writeHead(404, "Field not found");
237+
response.end();
238+
return;
239+
}
240+
241+
const ungzipped_length = fragmentRec[field + "_ungzlen"];
242+
243+
const headers: { [key: string]: string } = {
244+
"Content-Type": "application/octet-stream",
245+
};
246+
if (ungzipped_length) {
247+
headers["Content-Encoding"] = "gzip";
248+
}
249+
response.writeHead(200, headers);
250+
response.end(blob);
251+
}
249252
}

0 commit comments

Comments
 (0)