77
88#define PLUGIN_VERSION " manual"
99
10+ #define TIMEOUT_PERIOD 90 // How long until we consider a requested file to be timed out
11+
1012enum struct FileEnum
1113{
1214 int Client ;
@@ -28,12 +30,14 @@ Address EngineAddress;
2830// Sending
2931int TransferID ;
3032ArrayList SendListing ;
31- bool InQuery [MAXPLAYERS +1 ];
3233Handle SendingTimer [MAXPLAYERS +1 ];
3334char CurrentlySending [MAXPLAYERS +1 ][PLATFORM_MAX_PATH ];
3435
3536// Requesting
3637ArrayList RequestListing ;
38+ Handle RequestingTimer [MAXPLAYERS +1 ];
39+ int CurrentlyRequesting [MAXPLAYERS +1 ] = {- 1 , ...};
40+ int StartedRequestingAt [MAXPLAYERS +1 ];
3741
3842methodmap CNetChan
3943{
@@ -48,7 +52,8 @@ methodmap CNetChan
4852 }
4953 public int RequestFile (const char [] filename )
5054 {
51- return SDKCall (SDKRequestFile , this , filename );
55+ int id = SDKCall (SDKRequestFile , this , filename );
56+ return id ;
5257 }
5358 public bool IsFileInWaitingList (const char [] filename )
5459 {
@@ -268,8 +273,30 @@ public void OnClientDisconnect_Post(int client)
268273
269274 delete SendingTimer [client ];
270275 CurrentlySending [client ][0 ] = 0 ;
276+
277+ delete RequestingTimer [client ];
278+ CurrentlyRequesting [client ] = - 1 ;
279+ }
280+
281+ public void OnNotifyPluginUnloaded (Handle plugin )
282+ {
283+ int match = - 1 ;
284+ while ((match = SendListing .FindValue (plugin , FileEnum ::Plugin )) != - 1 )
285+ {
286+ SendListing .Erase (match );
287+ }
288+
289+ match = - 1 ;
290+ while ((match = RequestListing .FindValue (plugin , FileEnum ::Plugin )) != - 1 )
291+ {
292+ RequestListing .Erase (match );
293+ }
271294}
272295
296+ /*
297+ Requesting Files
298+ */
299+
273300public MRESReturn OnFileReceived (Address address , DHookParam param )
274301{
275302 int id = param .Get (2 );
@@ -282,8 +309,15 @@ public MRESReturn OnFileReceived(Address address, DHookParam param)
282309 RequestListing .GetArray (i , info );
283310 if (info .Id == id && chan == CNetChan (info .Client ))
284311 {
312+ if (CurrentlyRequesting [info .Client ] == id )
313+ CurrentlyRequesting [info .Client ] = - 1 ;
314+
285315 RequestListing .Erase (i );
286316 CallRequestFileFinish (info , true );
317+
318+ if (RequestingTimer [info .Client ])
319+ TriggerTimer (RequestingTimer [info .Client ]);
320+
287321 break ;
288322 }
289323 }
@@ -302,14 +336,106 @@ public MRESReturn OnFileDenied(Address address, DHookParam param)
302336 RequestListing .GetArray (i , info );
303337 if (info .Id == id && chan == CNetChan (info .Client ))
304338 {
339+ if (CurrentlyRequesting [info .Client ] == id )
340+ CurrentlyRequesting [info .Client ] = - 1 ;
341+
305342 RequestListing .Erase (i );
306343 CallRequestFileFinish (info , false );
344+
345+ if (RequestingTimer [info .Client ])
346+ TriggerTimer (RequestingTimer [info .Client ]);
347+
307348 break ;
308349 }
309350 }
310351 return MRES_Ignored ;
311352}
312353
354+ void StartRequestingClient (int client )
355+ {
356+ if (! RequestingTimer [client ])
357+ {
358+ RequestingTimer [client ] = CreateTimer (1.0 , Timer_RequestingClient , client , TIMER_REPEAT );
359+ TriggerTimer (RequestingTimer [client ]);
360+ }
361+ }
362+
363+ public Action Timer_RequestingClient (Handle timer , int client )
364+ {
365+ CNetChan chan = CNetChan (client );
366+ if (! chan )
367+ return Plugin_Continue ;
368+
369+ static FileEnum info ;
370+
371+ if (CurrentlyRequesting [client ] != - 1 )
372+ {
373+ // Client still giving this file
374+ if ((StartedRequestingAt [client ] + TIMEOUT_PERIOD ) > GetTime ())
375+ return Plugin_Continue ;
376+
377+ // We timed out, this can be a case of the client getting stuck.
378+ // Request a different file to unstuck the client then try again.
379+ DeleteFile (" download/scripts/cheatcodes.txt" );
380+
381+ info .Client = client ;
382+ strcopy (info .Filename , sizeof (info .Filename ), " scripts/cheatcodes.txt" );
383+ info .Func = INVALID_FUNCTION ;
384+ info .Id = chan .RequestFile (info .Filename );
385+ RequestListing .PushArray (info );
386+
387+ CurrentlyRequesting [client ] = info .Id ;
388+ StartedRequestingAt [client ] += 10 ;
389+ return Plugin_Continue ;
390+ }
391+
392+ int length = RequestListing .Length ;
393+ for (int i ; i < length ; i ++ )
394+ {
395+ RequestListing .GetArray (i , info );
396+ if (info .Client == client )
397+ {
398+ info .Id = chan .RequestFile (info .Filename );
399+ RequestListing .SetArray (i , info );
400+
401+ CurrentlyRequesting [client ] = info .Id ;
402+ StartedRequestingAt [client ] = GetTime ();
403+ return Plugin_Continue ;
404+ }
405+ }
406+
407+ // No more files to send
408+ RequestingTimer [client ] = null ;
409+ return Plugin_Stop ;
410+ }
411+
412+ static void CallRequestFileFinish (const FileEnum info , bool success )
413+ {
414+ if (info .Func && info .Func != INVALID_FUNCTION )
415+ {
416+ Call_StartFunction (info .Plugin , info .Func );
417+ Call_PushCell (info .Client );
418+ Call_PushString (info .Filename );
419+ Call_PushCell (info .Id );
420+ Call_PushCell (success );
421+ Call_PushCell (info .Data );
422+ Call_Finish ();
423+ }
424+ }
425+
426+ /*
427+ Sending Files
428+ */
429+
430+ void StartSendingClient (int client )
431+ {
432+ if (! SendingTimer [client ])
433+ {
434+ SendingTimer [client ] = CreateTimer (0.1 , Timer_SendingClient , client , TIMER_REPEAT );
435+ TriggerTimer (SendingTimer [client ]);
436+ }
437+ }
438+
313439public Action Timer_SendingClient (Handle timer , int client )
314440{
315441 CNetChan chan = CNetChan (client );
@@ -320,22 +446,26 @@ public Action Timer_SendingClient(Handle timer, int client)
320446 {
321447 // Client still downloading this file
322448 if (chan .IsFileInWaitingList (CurrentlySending [client ]))
449+ {
323450 return Plugin_Continue ;
324-
325- // We finished this file
326- int length = SendListing .Length ;
327- for (int i ; i < length ; i ++ )
451+ }
452+ else
328453 {
329- static FileEnum info ;
330- SendListing .GetArray ( i , info ) ;
331- if ( info . Client == client && StrEqual ( info . Filename , CurrentlySending [ client ], false ) )
454+ // We finished this file
455+ int length = SendListing .Length ;
456+ for ( int i ; i < length ; i ++ )
332457 {
333- SendListing .Erase (i );
334- CallSentFileFinish (info , true );
335- break ;
458+ static FileEnum info ;
459+ SendListing .GetArray (i , info );
460+ if (info .Client == client && StrEqual (info .Filename , CurrentlySending [client ], false ))
461+ {
462+ SendListing .Erase (i );
463+ CallSentFileFinish (info , true );
464+ break ;
465+ }
336466 }
337467 }
338-
468+
339469 CurrentlySending [client ][0 ] = 0 ;
340470 }
341471
@@ -379,51 +509,16 @@ static void CallSentFileFinish(const FileEnum info, bool success)
379509 }
380510}
381511
382- static void CallRequestFileFinish (const FileEnum info , bool success )
383- {
384- if (info .Func && info .Func != INVALID_FUNCTION )
385- {
386- Call_StartFunction (info .Plugin , info .Func );
387- Call_PushCell (info .Client );
388- Call_PushString (info .Filename );
389- Call_PushCell (info .Id );
390- Call_PushCell (success );
391- Call_PushCell (info .Data );
392- Call_Finish ();
393- }
394- }
512+ /*
513+ Natives
514+ */
395515
396516void StartNative ()
397517{
398518 if (! SendListing )
399519 ThrowNativeError (SP_ERROR_NATIVE , " Please wait until OnAllPluginsLoaded" );
400520}
401521
402- void StartSendingClient (int client )
403- {
404- // Clients need sv_allowupload in order for this to work, sorry CSGO fans
405- if (! InQuery [client ] && QueryClientConVar (client , " sv_allowupload" , QueryCallback ) != QUERYCOOKIE_FAILED )
406- InQuery [client ] = true ;
407- }
408-
409- public void QueryCallback (QueryCookie cookie , int client , ConVarQueryResult result , const char [] cvarName , const char [] cvarValue , any value )
410- {
411- if (! SendingTimer [client ] && IsClientInGame (client ))
412- {
413- if (result == ConVarQuery_Okay && StringToInt (cvarValue ))
414- {
415- SendingTimer [client ] = CreateTimer (0.5 , Timer_SendingClient , client , TIMER_REPEAT );
416- Timer_SendingClient (null , client );
417- }
418- else
419- {
420- PrintToChat (client , " [SM] The server is trying to send you a file, enable sv_allowupload to allow this process" );
421- }
422- }
423-
424- InQuery [client ] = false ;
425- }
426-
427522int GetNativeClient (int param )
428523{
429524 int client = GetNativeCell (param );
@@ -486,13 +581,10 @@ public any Native_RequestFile(Handle plugin, int params)
486581 info .Func = GetNativeFunction (3 );
487582 info .Data = GetNativeCell (4 );
488583
489- CNetChan chan = CNetChan (info .Client );
490- if (! chan )
491- ThrowError (" Native_RequestFile: Client address is invalid" );
492-
493- info .Id = chan .RequestFile (info .Filename );
494584 RequestListing .PushArray (info );
495- return info .Id ;
585+
586+ StartRequestingClient (info .Client );
587+ return CurrentlyRequesting [info .Client ];
496588}
497589
498590public any Native_IsFileInWaitingList (Handle plugin , int params )
0 commit comments