@@ -3,47 +3,33 @@ import { promisify } from "util";
33import { Request , Response } from "express" ;
44import { Injectable , Logger } from "@nestjs/common" ;
55
6- type Fragment = {
7- data ?: Buffer ;
8- gipped : boolean ;
9- [ key : string ] : any ;
10- } ;
11-
126@Injectable ( )
137export 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