@@ -3,18 +3,41 @@ import { promisify } from "util";
33import { Request , Response } from "express" ;
44import { Injectable , Logger } from "@nestjs/common" ;
55
6+ type Fragment = {
7+ start ?: Buffer ;
8+ full ?: Buffer ;
9+ delta ?: Buffer ;
10+ start_ungzlen ?: number ;
11+ full_ungzlen ?: number ;
12+ delta_ungzlen ?: number ;
13+ tick ?: number ;
14+ endtick ?: number ;
15+ timestamp ?: number ;
16+ signup_fragment ?: number ;
17+ tps ?: number ;
18+ keyframe_interval ?: number ;
19+ map ?: string ;
20+ protocol ?: number ;
21+ [ key : string ] : any ;
22+ } ;
23+
24+ type Broadcast = Fragment [ ] ;
25+
626@Injectable ( )
727export class MatchRelayService {
828 private readonly gzip = promisify ( zlib . gzip ) ;
929
10- private readonly broadcasts : { [ key : string ] : any [ ] } = { } ;
30+ private readonly broadcasts : { [ key : string ] : Broadcast } = { } ;
1131
1232 constructor ( private readonly logger : Logger ) { }
1333
14- public getStart ( response : Response , matchId : string , fragment : number ) {
34+ public getStart ( response : Response , matchId : string , fragmentIndex : number ) {
1535 const broadcast = this . broadcasts [ matchId ] ;
1636
17- if ( broadcast ?. [ 0 ] == null || broadcast [ 0 ] . signup_fragment != fragment ) {
37+ if (
38+ broadcast ?. [ 0 ] == null ||
39+ broadcast [ 0 ] . signup_fragment != fragmentIndex
40+ ) {
1841 return this . relayError (
1942 response ,
2043 404 ,
@@ -29,7 +52,7 @@ export class MatchRelayService {
2952 response : Response ,
3053 matchId : string ,
3154 fragmentIndex : number ,
32- field : "start" | "full" | "delta" ,
55+ field : string ,
3356 ) {
3457 const broadcast = this . broadcasts [ matchId ] ;
3558 if ( ! broadcast ) {
@@ -66,37 +89,41 @@ export class MatchRelayService {
6689 }
6790
6891 const match_field_0 = broadcast [ 0 ] ;
92+ // Check if start fragment exists at index 0 (start is a Buffer, so check if it exists)
6993 if ( match_field_0 == null || match_field_0 . start == null ) {
7094 response . writeHead ( 404 , "Broadcast has not started yet" ) ;
7195 response . end ( ) ;
7296 return ;
7397 }
7498
75- let fragment : number | null = null ;
99+ let fragmentIndex : number | null = null ;
76100 const fragmentParam = request . query . fragment as string | undefined ;
77- let frag : any = null ;
101+ let frag : Fragment | null = null ;
78102
79103 if ( fragmentParam == null ) {
80- fragment = Math . max ( 0 , broadcast . length - 8 ) ;
104+ fragmentIndex = Math . max ( 0 , broadcast . length - 8 ) ;
81105
82- if ( fragment >= 0 && fragment >= match_field_0 . signup_fragment ) {
83- const _fragment = broadcast [ fragment ] ;
106+ if (
107+ fragmentIndex >= 0 &&
108+ fragmentIndex >= match_field_0 . signup_fragment
109+ ) {
110+ const _fragment = broadcast [ fragmentIndex ] ;
84111 if ( this . isSyncReady ( _fragment ) ) {
85112 frag = _fragment ;
86113 }
87114 }
88115 } else {
89- fragment = parseInt ( fragmentParam ) ;
116+ fragmentIndex = parseInt ( fragmentParam ) ;
90117
91- if ( fragment < match_field_0 . signup_fragment ) {
92- fragment = match_field_0 . signup_fragment ;
118+ if ( fragmentIndex < match_field_0 . signup_fragment ) {
119+ fragmentIndex = match_field_0 . signup_fragment ;
93120 }
94121
95- for ( let i = fragment ; i < broadcast . length ; i ++ ) {
122+ for ( let i = fragmentIndex ; i < broadcast . length ; i ++ ) {
96123 const _fragment = broadcast [ i ] ;
97124 if ( this . isSyncReady ( _fragment ) ) {
98125 frag = _fragment ;
99- fragment = i ;
126+ fragmentIndex = i ;
100127 break ;
101128 }
102129 }
@@ -119,8 +146,10 @@ export class MatchRelayService {
119146 endtick : frag . endtick ,
120147 maxtick : this . getMatchBroadcastEndTick ( broadcast ) ,
121148 rtdelay : ( nowMs - frag . timestamp ) / 1000 ,
122- rcvage : ( nowMs - ( broadcast [ broadcast . length - 1 ] ?. timestamp || nowMs ) ) / 1000 ,
123- fragment : fragment ,
149+ rcvage :
150+ ( nowMs - ( broadcast [ broadcast . length - 1 ] ?. timestamp || nowMs ) ) /
151+ 1000 ,
152+ fragment : fragmentIndex ,
124153 signup_fragment : match_field_0 . signup_fragment ,
125154 tps : match_field_0 . tps ,
126155 keyframe_interval : match_field_0 . keyframe_interval ,
@@ -135,7 +164,7 @@ export class MatchRelayService {
135164 response : Response ,
136165 field : string ,
137166 matchId : string ,
138- fragment : number ,
167+ fragmentIndex : number ,
139168 ) : void {
140169 if ( ! this . broadcasts [ matchId ] ) {
141170 this . logger . log ( `Creating new match broadcast for matchId ${ matchId } ` ) ;
@@ -150,25 +179,29 @@ export class MatchRelayService {
150179 broadcast [ 0 ] = { } ;
151180 }
152181
153- broadcast [ 0 ] . signup_fragment = fragment ;
154- fragment = 0 ;
182+ // Start fragments are always stored at index 0
183+ // Store the original fragment number in signup_fragment
184+ broadcast [ 0 ] . signup_fragment = fragmentIndex ;
185+ fragmentIndex = 0 ;
155186 } else {
187+ // For non-start fields, ensure start fragment exists at index 0
188+ // Start fragment is always at index 0, check if the start Buffer exists
156189 if ( broadcast [ 0 ] == null || broadcast [ 0 ] . start == null ) {
157190 response . writeHead ( 205 ) ;
158191 response . end ( ) ;
159192 return ;
160193 } else {
161194 response . writeHead ( 200 ) ;
162195 }
163- if ( broadcast [ fragment ] == null ) {
164- broadcast [ fragment ] = { } ;
196+ if ( broadcast [ fragmentIndex ] == null ) {
197+ broadcast [ fragmentIndex ] = { } ;
165198 }
166199 }
167200
168201 Object . entries ( request . query ) . forEach ( ( [ key , value ] ) => {
169202 const strValue = String ( value ) ;
170203 const numValue = Number ( strValue ) ;
171- broadcast [ fragment ] [ key ] =
204+ broadcast [ fragmentIndex ] [ key ] =
172205 ! isNaN ( numValue ) && strValue === String ( numValue ) ? numValue : value ;
173206 } ) ;
174207
@@ -178,22 +211,22 @@ export class MatchRelayService {
178211 } ) ;
179212 request . on ( "end" , ( ) => {
180213 const totalBuffer = Buffer . concat ( body ) ;
181-
214+
182215 // Send response immediately (like old code)
183216 response . end ( ) ;
184217
185218 this . gzip ( totalBuffer )
186219 . then ( ( compressedBlob : Buffer ) => {
187- broadcast [ fragment ] [ field + "_ungzlen" ] = totalBuffer . length ;
188- broadcast [ fragment ] [ field ] = compressedBlob ;
189- broadcast [ fragment ] . timestamp = Date . now ( ) ;
220+ broadcast [ fragmentIndex ] [ field + "_ungzlen" ] = totalBuffer . length ;
221+ broadcast [ fragmentIndex ] [ field ] = compressedBlob ;
222+ broadcast [ fragmentIndex ] . timestamp = Date . now ( ) ;
190223 } )
191224 . catch ( ( error : Error ) => {
192225 this . logger . error (
193226 `Cannot gzip ${ totalBuffer . length } bytes: ${ error } ` ,
194227 ) ;
195- broadcast [ fragment ] [ field ] = totalBuffer ;
196- broadcast [ fragment ] . timestamp = Date . now ( ) ;
228+ broadcast [ fragmentIndex ] [ field ] = totalBuffer ;
229+ broadcast [ fragmentIndex ] . timestamp = Date . now ( ) ;
197230 } ) ;
198231 } ) ;
199232 }
@@ -207,19 +240,18 @@ export class MatchRelayService {
207240 response . end ( ) ;
208241 }
209242
210- private isSyncReady ( fragment : any ) : boolean {
243+ private isSyncReady ( fragment : Fragment | undefined ) : boolean {
211244 return (
212245 fragment != null &&
213- typeof fragment === "object" &&
214246 fragment . full != null &&
215247 fragment . delta != null &&
216248 fragment . tick != null &&
217249 fragment . endtick != null &&
218- fragment . timestamp
250+ fragment . timestamp != null
219251 ) ;
220252 }
221253
222- private getMatchBroadcastEndTick ( broadcast : any [ ] ) : number {
254+ private getMatchBroadcastEndTick ( broadcast : Broadcast ) : number {
223255 for ( let i = broadcast . length - 1 ; i >= 0 ; i -- ) {
224256 const fragment = broadcast [ i ] ;
225257 if ( fragment ?. endtick != null ) {
@@ -229,7 +261,11 @@ export class MatchRelayService {
229261 return 0 ;
230262 }
231263
232- private serveBlob ( response : Response , fragmentRec : any , field : string ) : void {
264+ private serveBlob (
265+ response : Response ,
266+ fragmentRec : Fragment | undefined ,
267+ field : string ,
268+ ) : void {
233269 const blob = fragmentRec ?. [ field ] ;
234270
235271 if ( ! blob ) {
0 commit comments