From 651f063582ce4ebc8eb1b36025d7553675005995 Mon Sep 17 00:00:00 2001 From: Ketmar Dark Date: Sat, 12 Oct 2019 04:47:26 +0300 Subject: [PATCH] net: implemented (half-assed) download resuming --- src/game/g_net.pas | 14 ++++++- src/game/g_res_downloader.pas | 71 ++++++++++++++++++++++++++++++++++- src/shared/utils.pas | 26 +++++++++++++ 3 files changed, 108 insertions(+), 3 deletions(-) diff --git a/src/game/g_net.pas b/src/game/g_net.pas index 251e06b..db8db65 100644 --- a/src/game/g_net.pas +++ b/src/game/g_net.pas @@ -90,6 +90,7 @@ type lastAckTime: Int64; // msecs; if not "in progress", we're waiting for the first ack inProgress: Boolean; diskBuffer: PChar; // of `chunkSize` bytes + resumed: Boolean; end; TNetClient = record @@ -1885,17 +1886,28 @@ var chunk: Integer; csize: Integer; buf: PChar = nil; + resumed: Boolean; //stx: Int64; begin + tf.resumed := false; + e_LogWritefln('file `%s`, size=%d (%d)', [tf.diskName, Integer(strm.size), tf.size], TMsgType.Notify); + // check if we should resume downloading + resumed := (strm.size > tf.chunkSize) and (strm.size < tf.size); // send request trans_omsg.Clear(); trans_omsg.Write(Byte(NTF_CLIENT_START)); - trans_omsg.Write(LongInt(0)); + if resumed then chunk := strm.size div tf.chunkSize else chunk := 0; + trans_omsg.Write(LongInt(chunk)); if not ftransSendClientMsg(trans_omsg) then begin result := -1; exit; end; + strm.Seek(chunk*tf.chunkSize, soFromBeginning); chunkTotal := (tf.size+tf.chunkSize-1) div tf.chunkSize; e_LogWritefln('receiving file `%s` (%d chunks)', [tf.diskName, chunkTotal], TMsgType.Notify); g_Game_SetLoadingText('downloading "'+ExtractFileName(tf.diskName)+'"', chunkTotal, False); + tf.resumed := resumed; + + if (chunk > 0) then g_Game_StepLoading(chunk); + nextChunk := chunk; // wait for reply data FillChar(ev, SizeOf(ev), 0); diff --git a/src/game/g_res_downloader.pas b/src/game/g_res_downloader.pas index ae645a1..b2844c6 100644 --- a/src/game/g_res_downloader.pas +++ b/src/game/g_res_downloader.pas @@ -161,6 +161,7 @@ var strm: TStream; fname: AnsiString; wadname: AnsiString; + md5: TMD5Digest; begin //SetLength(mapData.ExternalResources, 0); result := ''; @@ -199,7 +200,7 @@ begin end; fname := GameDir+'/maps/downloads/'+FileName; try - strm := createDiskFile(fname); + strm := openDiskFileRW(fname); except e_WriteLog('cannot create map file `'+FileName+'`', TMsgType.Fatal); result := ''; @@ -221,6 +222,39 @@ begin result := ''; exit; end; + // if it was resumed, check md5 and initiate full download if necessary + if tf.resumed then + begin + md5 := MD5File(fname); + // sorry for pasta, i am asshole + if not MD5Match(md5, tf.hash) then + begin + e_LogWritefln('resuming failed; downloading map `%s` from scratch...', [fname]); + try + DeleteFile(fname); + strm := createDiskFile(fname); + except + e_WriteLog('cannot create map file `'+fname+'`', TMsgType.Fatal); + result := ''; + exit; + end; + try + res := g_Net_ReceiveResourceFile(-1{map}, tf, strm); + except + e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal); + strm.Free; + result := ''; + exit; + end; + strm.Free; + if (res <> 0) then + begin + e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal); + result := ''; + exit; + end; + end; + end; result := fname; end; @@ -245,7 +279,7 @@ begin fname := GameDir+'/wads/downloads/'+tf.diskName; e_LogWritefln('downloading resource `%s` to `%s`...', [tf.diskName, fname]); try - strm := createDiskFile(fname); + strm := openDiskFileRW(fname); except e_WriteLog('cannot create resource file `'+fname+'`', TMsgType.Fatal); result := ''; @@ -266,6 +300,39 @@ begin result := ''; exit; end; + // if it was resumed, check md5 and initiate full download if necessary + if tf.resumed then + begin + md5 := MD5File(fname); + // sorry for pasta, i am asshole + if not MD5Match(md5, tf.hash) then + begin + e_LogWritefln('resuming failed; downloading resource `%s` to `%s` from scratch...', [tf.diskName, fname]); + try + DeleteFile(fname); + strm := createDiskFile(fname); + except + e_WriteLog('cannot create resource file `'+fname+'`', TMsgType.Fatal); + result := ''; + exit; + end; + try + res := g_Net_ReceiveResourceFile(f, tf, strm); + except + e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal); + strm.Free; + result := ''; + exit; + end; + strm.Free; + if (res <> 0) then + begin + e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal); + result := ''; + exit; + end; + end; + end; g_Res_PutReplacementWad(tf.diskName, fname); end; end; diff --git a/src/shared/utils.pas b/src/shared/utils.pas index a854ca4..215e60a 100644 --- a/src/shared/utils.pas +++ b/src/shared/utils.pas @@ -110,6 +110,8 @@ function isWadNamesEqu (wna, wnb: AnsiString): Boolean; // they throws function openDiskFileRO (pathname: AnsiString): TStream; function createDiskFile (pathname: AnsiString): TStream; +// creates file if necessary +function openDiskFileRW (pathname: AnsiString): TStream; // little endian procedure writeSign (st: TStream; const sign: AnsiString); @@ -1217,6 +1219,30 @@ begin end; +function openDiskFileRW (pathname: AnsiString): TStream; +var + path: AnsiString; + oldname: AnsiString; +begin + //writeln('*** TRYING R/W FILE "', pathname, '"'); + path := ExtractFilePath(pathname); + if length(path) > 0 then + begin + if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"'); + end; + oldname := pathname; + if findFileCI(oldname) then + begin + //writeln('*** found old file "', oldname, '"'); + result := TFileStream.Create(oldname, fmOpenReadWrite or fmShareDenyWrite); + end + else + begin + result := TFileStream.Create(path+ExtractFileName(pathname), fmCreate); + end; +end; + + procedure writeIntegerLE (st: TStream; vp: Pointer; size: Integer); {$IFDEF ENDIAN_LITTLE} begin -- 2.29.2