DEADSOFTWARE

net: game: better hash management; calculate resource hashes only once, send all...
[d2df-sdl.git] / src / game / g_game.pas
index 21f25cb1a7806c22898c998af38ebc3a63204a9a..4e2382628b74a8faa5a2198439b96e5695ae8bc8 100644 (file)
@@ -2,8 +2,7 @@
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * the Free Software Foundation, version 3 of the License ONLY.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -102,7 +101,7 @@ procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
 procedure g_Game_Restart();
 procedure g_Game_RestartLevel();
 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
-procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
+procedure g_Game_ClientWAD(NewWAD: String; const WHash: TMD5Digest);
 procedure g_Game_SaveOptions();
 function  g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
 procedure g_Game_ChangeMap(const MapPath: String);
@@ -211,7 +210,6 @@ const
   ANNOUNCE_ALL    = 3;
 
   CONFIG_FILENAME = 'Doom2DF.cfg';
-  LOG_FILENAME = 'Doom2DF.log';
 
   TEST_MAP_NAME = '$$$_TEST_$$$';
 
@@ -1343,8 +1341,10 @@ begin
     g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD+':MUSIC\ROUNDMUS', True, True);
     g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD+':MUSIC\ENDMUS', True);
 
+{$IFNDEF HEADLESS}
     g_Game_SetLoadingText(_lc[I_LOAD_MENUS], 0, False);
     g_Menu_Init();
+{$ENDIF}
 
     gMusic := TMusic.Create();
     gMusic.SetByName('MUSIC_MENU');
@@ -2130,7 +2130,7 @@ begin
         begin
           if (NetMHost = nil) or (NetMPeer = nil) then
           begin
-            if not g_Net_Slist_Connect then g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
+            g_Net_Slist_Connect(false); // non-blocking connection to the master
           end;
 
           g_Net_Slist_Update;
@@ -2175,7 +2175,9 @@ begin
       //e_WriteLog('Read language file', MSG_NOTIFY);
       //g_Language_Load(DataDir + gLanguage + '.txt');
       g_Language_Set(gLanguage);
+{$IFNDEF HEADLESS}
       g_Menu_Reset();
+{$ENDIF}
       gLanguageChange := False;
     end;
   end;
@@ -3509,7 +3511,7 @@ end;
 
 procedure DrawPlayer(p: TPlayer);
 var
-  px, py, a, b, c, d: Integer;
+  px, py, a, b, c, d, i: Integer;
   //R: TRect;
 begin
   if (p = nil) or (p.FDummy) then
@@ -3664,8 +3666,21 @@ begin
 
   renderMapInternal(-c, -d, true);
 
-  if (gGameSettings.GameMode <> GM_SINGLE) and gPlayerIndicator then
-    p.DrawIndicator();
+  if (gGameSettings.GameMode <> GM_SINGLE) and (gPlayerIndicator > 0) then
+    case gPlayerIndicator of
+      1:
+        p.DrawIndicator(_RGB(255, 255, 255));
+
+      2:
+        for i := 0 to High(gPlayers) do
+          if gPlayers[i] <> nil then
+            if gPlayers[i] = p then p.DrawIndicator(_RGB(255, 255, 255))
+            else if (gPlayers[i].Team = p.Team) and (gPlayers[i].Team <> TEAM_NONE) then
+              if gPlayerIndicatorStyle = 1 then
+                gPlayers[i].DrawIndicator(_RGB(192, 192, 192))
+              else gPlayers[i].DrawIndicator(gPlayers[i].GetColor);
+    end;
+
   if p.FSpectator then
     e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
                          p.GameY + PLAYER_RECT_CY - 4,
@@ -3804,15 +3819,15 @@ begin
       if plView1 <> nil then
       begin
         gHearPoint1.Active := True;
-        gHearPoint1.Coords.X := plView1.GameX;
-        gHearPoint1.Coords.Y := plView1.GameY;
+        gHearPoint1.Coords.X := plView1.GameX + PLAYER_RECT.Width;
+        gHearPoint1.Coords.Y := plView1.GameY + PLAYER_RECT.Height DIV 2;
       end else
         gHearPoint1.Active := False;
       if plView2 <> nil then
       begin
         gHearPoint2.Active := True;
-        gHearPoint2.Coords.X := plView2.GameX;
-        gHearPoint2.Coords.Y := plView2.GameY;
+        gHearPoint2.Coords.X := plView2.GameX + PLAYER_RECT.Width;
+        gHearPoint2.Coords.Y := plView2.GameY + PLAYER_RECT.Height DIV 2;
       end else
         gHearPoint2.Active := False;
 
@@ -4038,7 +4053,9 @@ begin
     g_ActiveWindow.Draw();
   end;
 
+{$IFNDEF HEADLESS}
   g_Console_Draw();
+{$ENDIF}
 
   if g_debug_Sounds and gGameOn then
   begin
@@ -4074,7 +4091,9 @@ begin
   g_PlayerModel_FreeData();
   g_Texture_DeleteAll();
   g_Frames_DeleteAll();
+{$IFNDEF HEADLESS}
   //g_Menu_Free(); //k8: this segfaults after resolution change; who cares?
+{$ENDIF}
 
   if NetInitDone then g_Net_Free;
 
@@ -4606,14 +4625,24 @@ begin
   OuterLoop := True;
   while OuterLoop do
   begin
-    while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
+    while (enet_host_service(NetHost, @NetEvent, 50) > 0) do
     begin
       if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
       begin
+        if (NetEvent.channelID = NET_CHAN_DOWNLOAD_EX) then
+        begin
+          // ignore all download packets, they're processed by separate code
+          enet_packet_destroy(NetEvent.packet);
+          continue;
+        end;
         Ptr := NetEvent.packet^.data;
         if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
+        begin
+          enet_packet_destroy(NetEvent.packet);
           continue;
+        end;
 
+        InMsg.ReadLongWord(); // skip size
         MID := InMsg.ReadByte();
 
         if (MID = NET_MSG_INFO) and (State = 0) then
@@ -4634,11 +4663,11 @@ begin
           gGameSettings.Options := InMsg.ReadLongWord();
           T := InMsg.ReadLongWord();
 
-          newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
-          if newResPath = '' then
+          //newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
+          //if newResPath = '' then
           begin
-            g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
-            newResPath := g_Res_DownloadWAD(WadName);
+            //g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
+            newResPath := g_Res_DownloadMapWAD(WadName, gWADHash);
             if newResPath = '' then
             begin
               g_FatalError(_lc[I_NET_ERR_HASH]);
@@ -4646,6 +4675,7 @@ begin
               NetState := NET_STATE_NONE;
               Exit;
             end;
+            e_LogWritefln('using downloaded map wad [%s] for [%s]`', [newResPath, WadName], TMsgType.Notify);
           end;
           newResPath := ExtractRelativePath(MapsDir, newResPath);
 
@@ -4701,8 +4731,7 @@ begin
 
     ProcessLoading(true);
 
-    if e_KeyPressed(IK_SPACE) or e_KeyPressed(IK_ESCAPE) or e_KeyPressed(VK_ESCAPE) or
-       e_KeyPressed(JOY0_JUMP) or e_KeyPressed(JOY1_JUMP) or e_KeyPressed(JOY2_JUMP) or e_KeyPressed(JOY3_JUMP) then
+    if g_Net_UserRequestExit() then
     begin
       State := 0;
       break;
@@ -4765,6 +4794,7 @@ function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath:
 var
   NewWAD, ResName: String;
   I: Integer;
+  nws: AnsiString;
 begin
   g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
   g_Player_RemoveAllCorpses();
@@ -4788,15 +4818,25 @@ begin
     ResName := g_ExtractFileName(Map);
     if g_Game_IsServer then
     begin
-      gWADHash := MD5File(MapsDir + NewWAD);
-      g_Game_LoadWAD(NewWAD);
+      nws := findDiskWad(MapsDir+NewWAD);
+      if (length(nws) = 0) then
+      begin
+        ResName := '';
+      end
+      else
+      begin
+        if (g_Game_IsNet) then gWADHash := MD5File(nws);
+        //writeln('********: nws=', nws, ' : Map=', Map, ' : nw=', NewWAD, ' : resname=', ResName);
+        g_Game_LoadWAD(NewWAD);
+      end;
     end else
       // hash received in MC_RECV_GameEvent -> NET_EV_MAPSTART
       g_Game_ClientWAD(NewWAD, gWADHash);
   end else
     ResName := Map;
 
-  Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
+  //writeln('********: gsw=', gGameSettings.WAD, '; rn=', ResName);
+  Result := (ResName <> '') and g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
   if Result then
     begin
       g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
@@ -4868,9 +4908,10 @@ begin
     if NetUseMaster then
     begin
       if (NetMHost = nil) or (NetMPeer = nil) then
-        if not g_Net_Slist_Connect then
-          g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
-
+      begin
+        // let the connection be blocking here, why not?
+        g_Net_Slist_Connect();
+      end;
       g_Net_Slist_Update;
     end;
 
@@ -4968,19 +5009,33 @@ begin
   gNextMap := Map;
 end;
 
-procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
+procedure g_Game_ClientWAD(NewWAD: String; const WHash: TMD5Digest);
 var
-  gWAD: String;
+  gWAD, xwad: String;
 begin
-  if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
-    Exit;
-  if not g_Game_IsClient then
+  if not g_Game_IsClient then Exit;
+  //e_LogWritefln('*** g_Game_ClientWAD: `%s`', [NewWAD]);
+
+  gWAD := g_Res_DownloadMapWAD(ExtractFileName(NewWAD), WHash);
+  if gWAD = '' then
+  begin
+    g_Game_Free();
+    g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
     Exit;
+  end;
+
+  xwad := ExtractRelativePath(MapsDir, gWAD);
+  e_LogWritefln('using downloaded client map wad [%s] for [%s]`', [xwad, NewWAD], TMsgType.Notify);
+  NewWAD := xwad;
+  g_Game_LoadWAD(NewWAD);
+
+  {
+  if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then Exit;
   gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
   if gWAD = '' then
   begin
     g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
-    gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
+    gWAD := g_Res_DownloadMapWAD(ExtractFileName(NewWAD), WHash);
     if gWAD = '' then
     begin
       g_Game_Free();
@@ -4990,6 +5045,7 @@ begin
   end;
   NewWAD := ExtractRelativePath(MapsDir, gWAD);
   g_Game_LoadWAD(NewWAD);
+  }
 end;
 
 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
@@ -5481,9 +5537,7 @@ begin
       if g_Game_IsServer and g_Game_IsNet then
         if NetUseMaster then
         begin
-          if NetMPeer = nil then
-            if not g_Net_Slist_Connect() then
-              g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
+          if NetMPeer = nil then g_Net_Slist_Connect();
           g_Net_Slist_Update();
         end
         else
@@ -7202,27 +7256,14 @@ begin
     e_StopChannels();
 end;
 
-procedure g_Game_UpdateTriggerSounds();
-var
-  i: Integer;
+procedure g_Game_UpdateTriggerSounds;
+  var i: Integer;
 begin
   if gTriggers <> nil then
     for i := 0 to High(gTriggers) do
       with gTriggers[i] do
-        if (TriggerType = TRIGGER_SOUND) and
-           (Sound <> nil) and
-           (tgcLocal) and
-           Sound.IsPlaying() then
-        begin
-          if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
-             ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
-          begin
-            Sound.SetPan(0.5 - tgcPan/255.0);
-            Sound.SetVolume(tgcVolume/255.0);
-          end
-          else
-            Sound.SetCoords(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0);
-        end;
+        if (TriggerType = TRIGGER_SOUND) and (Sound <> nil) and tgcLocal and Sound.IsPlaying() then
+          Sound.SetCoordsRect(X, Y, Width, Height, tgcVolume / 255.0)
 end;
 
 function g_Game_IsWatchedPlayer(UID: Word): Boolean;