DEADSOFTWARE

net: implemented (half-assed) download resuming
authorKetmar Dark <ketmar@ketmar.no-ip.org>
Sat, 12 Oct 2019 01:47:26 +0000 (04:47 +0300)
committerKetmar Dark <ketmar@ketmar.no-ip.org>
Sat, 12 Oct 2019 15:54:12 +0000 (18:54 +0300)
src/game/g_net.pas
src/game/g_res_downloader.pas
src/shared/utils.pas

index 251e06bb213071c07a958e9d2f0a4741b3b4cedd..db8db654355b63265729be6574a70106b1068872 100644 (file)
@@ -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);
index ae645a1196677443c505ee10f75dcd0e16f63193..b2844c651539202b622fdcdea8e46fd063b60652 100644 (file)
@@ -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;
index a854ca45bc922f155465ed98a32daad0710ced8c..215e60a8115bfb5373da9329867f78804a001139 100644 (file)
@@ -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