DEADSOFTWARE

net: i love to log it, log it!
[d2df-sdl.git] / src / game / g_netmaster.pas
index 660d0052bd928d29709b03488f482437fe9539f1..e24bed7accc9c70301965d3614103043016e528e 100644 (file)
@@ -68,9 +68,9 @@ type
     NetHostConnected: Boolean;
     NetHostConReqTime: Int64; // to timeout `connect`; -1 means "waiting for shutdown"
     NetUpdatePending: Boolean; // should we send an update after connection completes?
+    lastConnectTime: Int64;
     updateSent: Boolean;
     lastUpdateTime: Int64;
-    addressInited: Boolean;
     // server list request working flags
     srvAnswered: Integer;
     srvAnswer: array of TNetServer;
@@ -88,6 +88,8 @@ type
 
     function setAddress (hostandport: AnsiString): Boolean;
 
+    function isSameAddress (hostandport: AnsiString): Boolean;
+
     function isValid (): Boolean;
     function isAlive (): Boolean; // not disconnected
     function isConnecting (): Boolean; // is connection in progress?
@@ -97,7 +99,7 @@ type
     // but try to call this at least once in 100 msecs
     procedure pulse ();
 
-    procedure disconnect ();
+    procedure disconnect (forced: Boolean);
     function connect (): Boolean;
 
     procedure update ();
@@ -129,13 +131,26 @@ procedure g_Net_Slist_Private ();
 // make this server public
 procedure g_Net_Slist_Public ();
 
-// called on network mode init
-procedure g_Net_Slist_NetworkStarted ();
-// called on network mode shutdown
-procedure g_Net_Slist_NetworkStopped ();
+// called while the server is running
+procedure g_Net_Slist_ServerUpdate ();
+// called when the server is started
+procedure g_Net_Slist_ServerStarted ();
+// called when the server is stopped
+procedure g_Net_Slist_ServerClosed ();
+
+// called when new netword player comes
+procedure g_Net_Slist_ServerPlayerComes ();
+// called when new netword player comes
+procedure g_Net_Slist_ServerPlayerLeaves ();
+// started new map
+procedure g_Net_Slist_ServerMapStarted ();
+// this server renamed (or password mode changed, or other params changed)
+procedure g_Net_Slist_ServerRenamed ();
 
 procedure g_Net_Slist_Pulse (timeout: Integer=0);
 
+procedure g_Net_Slist_ShutdownAll ();
+
 procedure g_Serverlist_GenerateTable (SL: TNetServerList; var ST: TNetServerTable);
 procedure g_Serverlist_Draw (var SL: TNetServerList; var ST: TNetServerTable);
 procedure g_Serverlist_Control (var SL: TNetServerList; var ST: TNetServerTable);
@@ -150,65 +165,312 @@ uses
   g_map, g_game, g_sound, g_gui, g_menu, g_options, g_language, g_basic,
   wadreader, g_system, utils;
 
-// make this server private
-procedure g_Net_Slist_Private ();
+// ////////////////////////////////////////////////////////////////////////// //
+var
+  NetMHost: pENetHost = nil;
+  NetMEvent: ENetEvent;
+  mlist: array of TMasterHost = nil;
+
+  slSelection: Byte = 0;
+  slFetched: Boolean = False;
+  slDirPressed: Boolean = False;
+  slReadUrgent: Boolean = False;
+
+  reportsEnabled: Boolean = true;
+
+
+//==========================================================================
+//
+//  GetTimerMS
+//
+//==========================================================================
+function GetTimerMS (): Int64;
 begin
+  Result := sys_GetTicks() {div 1000};
 end;
 
 
-// make this server public
-procedure g_Net_Slist_Public ();
+//==========================================================================
+//
+//  findByPeer
+//
+//==========================================================================
+function findByPeer (peer: pENetPeer): Integer;
+var
+  f: Integer;
 begin
+  for f := 0 to High(mlist) do if (mlist[f].peer = peer) then begin result := f; exit; end;
+  result := -1;
 end;
 
 
-// called on network mode init
-procedure g_Net_Slist_NetworkStarted ();
+//==========================================================================
+//
+//  ShutdownAll
+//
+//==========================================================================
+procedure g_Net_Slist_ShutdownAll ();
+var
+  f, sres, idx: Integer;
+  stt, ct: Int64;
+  activeCount: Integer = 0;
 begin
+  if (NetMHost = nil) then exit;
+  for f := 0 to High(mlist) do
+  begin
+    if (mlist[f].isAlive()) then
+    begin
+      Inc(activeCount);
+      if (mlist[f].isConnected() and mlist[f].updateSent) then
+      begin
+        writeln('unregistering from ', f);
+        mlist[f].remove();
+      end;
+      //mlist[f].disconnect(false);
+      enet_peer_disconnect_later(mlist[f].peer, 0);
+    end;
+  end;
+  if (activeCount = 0) then exit;
+  stt := GetTimerMS();
+  while (activeCount > 0) do
+  begin
+    ct := GetTimerMS();
+    if (ct < stt) or (ct-stt >= 1500) then break;
+
+    sres := enet_host_service(NetMHost, @NetMEvent, 100);
+    if (sres < 0) then break;
+    if (sres = 0) then continue;
+
+    idx := findByPeer(NetMEvent.peer);
+    if (idx < 0) then
+    begin
+      if (NetMEvent.kind = ENET_EVENT_TYPE_RECEIVE) then enet_packet_destroy(NetMEvent.packet);
+      continue;
+    end;
+
+    if (NetMEvent.kind = ENET_EVENT_TYPE_CONNECT) then
+    begin
+      mlist[idx].connectedEvent();
+      //mlist[idx].disconnect(false);
+      enet_peer_disconnect(mlist[f].peer, 0);
+    end
+    else if (NetMEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
+    begin
+      mlist[idx].disconnectedEvent();
+      Dec(activeCount);
+    end
+    else if (NetMEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
+    begin
+      mlist[idx].receivedEvent(NetMEvent.packet);
+      enet_packet_destroy(NetMEvent.packet);
+    end;
+  end;
+  enet_host_destroy(NetMHost);
+  NetMHost := nil;
 end;
 
-// called on network mode shutdown
-procedure g_Net_Slist_NetworkStopped ();
+
+//==========================================================================
+//
+//  DisconnectAll
+//
+//==========================================================================
+procedure DisconnectAll ();
+var
+  f: Integer;
+begin
+  for f := 0 to High(mlist) do
+  begin
+    if (mlist[f].isAlive()) then mlist[f].disconnect(false);
+  end;
+end;
+
+
+//==========================================================================
+//
+//  ConnectAll
+//
+//==========================================================================
+procedure ConnectAll (sendUpdate: Boolean);
+var
+  f: Integer;
 begin
+  for f := 0 to High(mlist) do
+  begin
+    // force reconnect
+    mlist[f].lastConnectTime := 0;
+    //if (not mlist[f].isAlive()) then continue;
+    // force updating
+    if (sendUpdate) then
+    begin
+      mlist[f].NetUpdatePending := true;
+      mlist[f].lastUpdateTime := 0;
+    end;
+  end;
 end;
 
 
+//==========================================================================
+//
+//  UpdateAll
+//
+//==========================================================================
+procedure UpdateAll (force: Boolean);
 var
-  NetMHost: pENetHost = nil;
-  NetMEvent: ENetEvent;
-  mlist: array of TMasterHost = nil;
+  f: Integer;
+begin
+  for f := 0 to High(mlist) do
+  begin
+    if (not mlist[f].isAlive()) then continue;
+    mlist[f].NetUpdatePending := true;
+    if (force) then mlist[f].lastUpdateTime := 0;
+  end;
+end;
 
-  slSelection: Byte = 0;
-  slFetched: Boolean = False;
-  slDirPressed: Boolean = False;
-  slReadUrgent: Boolean = False;
 
+//**************************************************************************
+//
+// public api
+//
+//**************************************************************************
 
 //==========================================================================
 //
-//  GetTimerMS
+//  g_Net_Slist_Private
+//
+//  make this server private
 //
 //==========================================================================
-function GetTimerMS (): Int64;
+procedure g_Net_Slist_Private ();
 begin
-  Result := sys_GetTicks() {div 1000};
+  DisconnectAll();
+  reportsEnabled := false;
 end;
 
 
 //==========================================================================
 //
-//  findByPeer
+//  g_Net_Slist_Public
+//
+//  make this server public
 //
 //==========================================================================
-function findByPeer (peer: pENetPeer): Integer;
+procedure g_Net_Slist_Public ();
+begin
+  if (not reportsEnabled) then
+  begin
+    reportsEnabled := true;
+    ConnectAll(true);
+  end;
+end;
+
+
+//==========================================================================
+//
+//  g_Net_Slist_ServerUpdate
+//
+//  called while the server is running
+//
+//==========================================================================
+procedure g_Net_Slist_ServerUpdate ();
+begin
+  UpdateAll(false);
+end;
+
+
+// called when the server is started
+procedure g_Net_Slist_ServerStarted ();
+begin
+  reportsEnabled := NetUseMaster;
+  if reportsEnabled and g_Game_IsServer() and g_Game_IsNet() then
+  begin
+    writeln('*** server started; reporting to master...');
+    ConnectAll(true);
+  end;
+end;
+
+
+//==========================================================================
+//
+//  g_Net_Slist_ServerClosed
+//
+//  called when the server is stopped
+//
+//==========================================================================
+procedure g_Net_Slist_ServerClosed ();
 var
   f: Integer;
 begin
-  for f := 0 to High(mlist) do if (mlist[f].peer = peer) then begin result := f; exit; end;
-  result := -1;
+  if reportsEnabled then
+  begin
+    reportsEnabled := false;
+    for f := 0 to High(mlist) do
+    begin
+      if (mlist[f].isConnected()) then mlist[f].remove();
+    end;
+  end;
+  DisconnectAll();
+end;
+
+
+//==========================================================================
+//
+//  g_Net_Slist_ServerPlayerComes
+//
+//  called when new netword player comes
+//
+//==========================================================================
+procedure g_Net_Slist_ServerPlayerComes ();
+begin
+  UpdateAll(true);
+end;
+
+
+//==========================================================================
+//
+//  g_Net_Slist_ServerPlayerLeaves
+//
+//  called when new netword player comes
+//
+//==========================================================================
+procedure g_Net_Slist_ServerPlayerLeaves ();
+begin
+  UpdateAll(true);
+end;
+
+
+//==========================================================================
+//
+//  g_Net_Slist_ServerMapStarted
+//
+//  started new map
+//
+//==========================================================================
+procedure g_Net_Slist_ServerMapStarted ();
+begin
+  UpdateAll(true);
 end;
 
 
+//==========================================================================
+//
+//  g_Net_Slist_ServerRenamed
+//
+//  this server renamed (or password mode changed, or other params changed)
+//
+//==========================================================================
+procedure g_Net_Slist_ServerRenamed ();
+begin
+  UpdateAll(true);
+end;
+
+
+//**************************************************************************
+//
+// TMasterHost
+//
+//**************************************************************************
+
 //==========================================================================
 //
 //  TMasterHost.Create
@@ -220,7 +482,9 @@ begin
   NetHostConnected := false;
   NetHostConReqTime := 0;
   NetUpdatePending := false;
+  lastConnectTime := 0;
   updateSent := false;
+  lastUpdateTime := 0;
   hostName := '';
   hostPort := 25665;
   SetLength(srvAnswer, 0);
@@ -241,7 +505,7 @@ end;
 procedure TMasterHost.clear ();
 begin
   updateSent := false; // do not send 'remove'
-  disconnect();
+  disconnect(true);
   hostName := '';
   hostPort := 25665;
   netmsg.Free();
@@ -253,6 +517,43 @@ begin
 end;
 
 
+//==========================================================================
+//
+//  TMasterHost.isSameAddress
+//
+//==========================================================================
+function TMasterHost.isSameAddress (hostandport: AnsiString): Boolean;
+var
+  cp, pp: Integer;
+  hn: AnsiString;
+begin
+  result := false;
+  if not isValid() then exit;
+  hostandport := Trim(hostandport);
+  if (length(hostandport) = 0) then exit;
+  hn := hostandport;
+  cp := Pos(':', hostandport);
+  if (cp > 0) then
+  begin
+    hn := Copy(hostandport, 1, cp-1);
+    Delete(hostandport, 1, cp);
+    if (length(hostandport) > 0) then
+    begin
+      try
+        pp := StrToInt(hostandport);
+      except
+        pp := -1;
+      end;
+    end;
+  end
+  else
+  begin
+    pp := 25665;
+  end;
+  result := strEquCI1251(hn, hostName) and (hostPort = pp);
+end;
+
+
 //==========================================================================
 //
 //  TMasterHost.setAddress
@@ -269,14 +570,16 @@ begin
   slUrgent := '';
   slReadUrgent := true;
   updateSent := false; // do not send 'remove'
-  disconnect();
-  addressInited := false;
+  disconnect(true);
   hostName := '';
   hostPort := 25665;
+
+  if (not g_Net_IsNetworkAvailable()) then exit;
+
   hostandport := Trim(hostandport);
   if (length(hostandport) > 0) then
   begin
-    hostName := hostandport;
+      hostName := hostandport;
     cp := Pos(':', hostandport);
     if (cp > 0) then
     begin
@@ -295,17 +598,17 @@ begin
   end;
 
   if not isValid() then exit;
-  if (NetInitDone) then
+
+  if (enet_address_set_host(@enetAddr, PChar(Addr(hostName[1]))) <> 0) then
   begin
-    if (enet_address_set_host(@enetAddr, PChar(Addr(hostName[1]))) <> 0) then
-    begin
-      hostName := '';
-      hostPort := 0;
-    end;
-    enetAddr.Port := hostPort;
+    writeln('SHIT!');
+    hostName := '';
+    hostPort := 0;
   end;
+  enetAddr.Port := hostPort;
 
   result := isValid();
+  //writeln('*********************: ', hostandport, ' [', hostName, ':', hostPort, '] ', result);
 end;
 
 
@@ -380,12 +683,7 @@ procedure TMasterHost.disconnectedEvent ();
 begin
   if not isAlive() then exit;
   e_LogWritefln('disconnected from master at [%s:%u]', [hostName, hostPort], TMsgType.Notify);
-  enet_peer_reset(peer);
-  peer := nil;
-  NetHostConnected := False;
-  NetHostConReqTime := 0;
-  NetUpdatePending := false;
-  updateSent := false;
+  disconnect(true);
   //if (spamConsole) then g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_SLIST_DISC]);
 end;
 
@@ -404,16 +702,6 @@ var
   Cnt: Byte;
   f: Integer;
   s: AnsiString;
-  {
-  I, RX: Integer;
-  T: Int64;
-  Sock: ENetSocket;
-  Buf: ENetBuffer;
-  InMsg: TMsg;
-  SvAddr: ENetAddress;
-  FromSL: Boolean;
-  MyVer, Str: AnsiString;
-  }
 begin
   e_LogWritefln('received packed from master at [%s:%u]', [hostName, hostPort], TMsgType.Notify);
   if not msg.Init(pkt^.data, pkt^.dataLength, True) then exit;
@@ -482,13 +770,14 @@ end;
 procedure TMasterHost.pulse ();
 var
   ct: Int64;
+  mrate: Cardinal;
 begin
   if not isAlive() then exit;
   if (NetHostConReqTime = -1) then exit; // waiting for shutdown (disconnect in progress)
+  ct := GetTimerMS();
   // process pending connection timeout
   if (not NetHostConnected) then
   begin
-    ct := GetTimerMS();
     if (ct < NetHostConReqTime) or (ct-NetHostConReqTime >= 3000) then
     begin
       e_LogWritefln('failed to connect to master at [%s:%u]', [hostName, hostPort], TMsgType.Notify);
@@ -499,6 +788,18 @@ begin
     end;
     exit;
   end;
+  // send update, if necessary
+  if (NetUpdatePending) then
+  begin
+    mrate := NetMasterRate;
+         if (mrate < 10000) then mrate := 10000
+    else if (mrate > 1000*60*10) then mrate := 1000*60*10;
+    if (lastUpdateTime = 0) or (ct < lastUpdateTime) or (ct-lastUpdateTime >= mrate) then
+    begin
+      lastUpdateTime := ct;
+      update();
+    end;
+  end;
 end;
 
 
@@ -507,18 +808,28 @@ end;
 //  TMasterHost.disconnect
 //
 //==========================================================================
-procedure TMasterHost.disconnect ();
+procedure TMasterHost.disconnect (forced: Boolean);
 begin
   if not isAlive() then exit;
-  //if (NetMode = NET_SERVER) and isConnected() and updateSent then remove();
 
-  enet_peer_disconnect_later(peer, 0);
-  // main pulse will take care of the rest
+  if (forced) then
+  begin
+    enet_peer_reset(peer);
+    peer := nil;
+    NetHostConReqTime := 0;
+  end
+  else
+  begin
+    enet_peer_disconnect_later(peer, 0);
+    // main pulse will take care of the rest
+    NetHostConReqTime := -1;
+  end;
+
   NetHostConnected := false;
-  NetHostConReqTime := -1;
   NetUpdatePending := false;
-  updateSent := false;
-
+  //updateSent := false;
+  lastUpdateTime := 0;
+  //lastConnectTime := 0;
   //if (spamConsole) then g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_SLIST_DISC]);
 end;
 
@@ -531,28 +842,24 @@ end;
 function TMasterHost.connect (): Boolean;
 begin
   result := false;
-  if not isValid() or (NetHostConReqTime = -1) then exit;
-  if isAlive() then begin result := true; exit; end;
+  if not isValid() then exit;
+  if (NetHostConReqTime = -1) then
+  begin
+    disconnect(true);
+  end
+  else
+  begin
+    if isAlive() then begin result := true; exit; end;
+  end;
 
+  lastConnectTime := GetTimerMS();
   SetLength(srvAnswer, 0);
   srvAnswered := 0;
   NetHostConnected := false;
   NetHostConReqTime := 0;
   NetUpdatePending := false;
   updateSent := false;
-  if (not NetInitDone) then exit;
-
-  if (not addressInited) then
-  begin
-    if (enet_address_set_host(@enetAddr, PChar(Addr(hostName[1]))) <> 0) then
-    begin
-      hostName := '';
-      hostPort := 0;
-      exit;
-    end;
-    enetAddr.Port := hostPort;
-    addressInited := true;
-  end;
+  lastUpdateTime := 0;
 
   peer := enet_host_connect(NetMHost, @enetAddr, NET_MCHANS, 0);
   if (peer = nil) then
@@ -561,7 +868,7 @@ begin
     exit;
   end;
 
-  NetHostConReqTime := GetTimerMS();
+  NetHostConReqTime := lastConnectTime;
   e_LogWritefln('connecting to master at [%s:%u]', [hostName, hostPort], TMsgType.Notify);
 end;
 
@@ -609,19 +916,32 @@ begin
   end;
 
   netmsg.Clear();
-  try
-    netmsg.Write(Byte(NET_MMSG_UPD));
-    netmsg.Write(NetAddr.port);
 
-    writeInfo(netmsg);
+  if reportsEnabled and g_Game_IsServer() and g_Game_IsNet() and NetUseMaster then
+  begin
+    try
+      netmsg.Write(Byte(NET_MMSG_UPD));
+      netmsg.Write(NetAddr.port);
 
-    pkt := enet_packet_create(netmsg.Data, netmsg.CurSize, ENET_PACKET_FLAG_RELIABLE);
-    if assigned(pkt) then
-    begin
-      if (enet_peer_send(peer, NET_MCHAN_UPD, pkt) = 0) then NetUpdatePending := false;
+      writeInfo(netmsg);
+
+      pkt := enet_packet_create(netmsg.Data, netmsg.CurSize, ENET_PACKET_FLAG_RELIABLE);
+      if assigned(pkt) then
+      begin
+        if (enet_peer_send(peer, NET_MCHAN_UPD, pkt) = 0) then
+        begin
+          e_LogWritefln('sent update to master at [%s:%u]', [hostName, hostPort], TMsgType.Notify);
+          NetUpdatePending := false;
+          updateSent := true;
+        end;
+      end;
+    finally
+      netmsg.Clear();
     end;
-  finally
-    netmsg.Clear();
+  end
+  else
+  begin
+    NetUpdatePending := false;
   end;
 end;
 
@@ -636,6 +956,8 @@ var
   pkt: pENetPacket;
 begin
   NetUpdatePending := false;
+  lastUpdateTime := 0;
+  updateSent := false;
   if not isAlive() then exit;
   if not isConnected() then exit;
 
@@ -662,25 +984,19 @@ end;
 //**************************************************************************
 
 procedure g_Net_Slist_Set (IP: AnsiString; Port: Word);
+var
+  f: Integer;
+  sa: AnsiString;
 begin
-  if (length(mlist) = 0) then
-  begin
-    SetLength(mlist, 1);
-    mlist[0].Create(ip+':'+IntToStr(Port));
-  end
-  else
-  begin
-    mlist[0].setAddress(ip+':'+IntToStr(Port));
-  end;
+  if (not g_Net_IsNetworkAvailable()) then exit;
+  IP := Trim(IP);
+  if (length(IP) = 0) or (Port = 0) then exit;
+  sa := IP+':'+IntToStr(Port);
+  for f := 0 to High(mlist) do if (mlist[f].isSameAddress(sa)) then exit;
+  SetLength(mlist, length(mlist)+1);
+  mlist[High(mlist)].Create(sa);
+  mlist[High(mlist)].setAddress(sa);
   e_LogWritefln('Masterserver address set to [%s:%u]', [IP, Port], TMsgType.Notify);
-  {
-  if NetInitDone then
-  begin
-    enet_address_set_host(@NetSlistAddr, PChar(Addr(IP[1])));
-    NetSlistAddr.Port := Port;
-    e_WriteLog('Masterserver address set to ' + IP + ':' + IntToStr(Port), TMsgType.Notify);
-  end;
-  }
 end;
 
 
@@ -694,7 +1010,10 @@ var
   f: Integer;
   sres: Integer;
   idx: Integer;
+  ct: Int64;
 begin
+  if (not g_Net_IsNetworkAvailable()) then exit;
+
   if (length(mlist) = 0) then
   begin
     if (NetMHost <> nil) then
@@ -717,7 +1036,30 @@ begin
     end;
   end;
 
-  for f := 0 to High(mlist) do mlist[f].pulse();
+  ct := GetTimerMS();
+  for f := 0 to High(mlist) do
+  begin
+    if (not mlist[f].isValid()) then continue;
+    if (not mlist[f].isAlive()) then
+    begin
+      if reportsEnabled and g_Game_IsServer() and g_Game_IsNet() and NetUseMaster then
+      begin
+        if (mlist[f].lastConnectTime = 0) or (ct < mlist[f].lastConnectTime) or (ct-mlist[f].lastConnectTime >= 1000*60*5) then
+        begin
+          mlist[f].connect();
+        end;
+      end;
+    end
+    else
+    begin
+      if not reportsEnabled or not g_Game_IsServer() or not g_Game_IsNet() or not NetUseMaster then
+      begin
+        if (mlist[f].isConnected()) and (mlist[f].updateSent) then mlist[f].remove();
+        mlist[f].disconnect(false);
+      end;
+    end;
+    mlist[f].pulse();
+  end;
 
   while true do
   begin
@@ -824,16 +1166,6 @@ var
   FromSL: Boolean;
   MyVer: AnsiString;
 
-  procedure DisconnectAll ();
-  var
-    f: Integer;
-  begin
-    for f := 0 to High(mlist) do
-    begin
-      if (mlist[f].isAlive()) then mlist[f].disconnect();
-    end;
-  end;
-
   procedure ProcessLocal ();
   begin
     I := Length(SL);
@@ -902,6 +1234,12 @@ begin
   result := false;
   SL := nil;
 
+  if (not g_Net_IsNetworkAvailable()) then
+  begin
+    SetLength(SL, 0);
+    exit;
+  end;
+
   g_Net_Slist_Pulse(); // this will create mhost
 
   NetOut.Clear();
@@ -912,38 +1250,6 @@ begin
   NetOut.Write(MyVer);
 
   try
-    aliveCount := 0;
-    for f := 0 to High(mlist) do
-    begin
-      mlist[f].srvAnswered := 0;
-      if (not mlist[f].isValid()) then continue;
-      if (not mlist[f].isConnected()) then mlist[f].connect();
-      if (not mlist[f].isAlive()) then continue;
-      if (mlist[f].isConnected()) then
-      begin
-        pkt := enet_packet_create(NetOut.Data, NetOut.CurSize, Cardinal(ENET_PACKET_FLAG_RELIABLE));
-        if assigned(pkt) then
-        begin
-          if (enet_peer_send(mlist[f].peer, NET_MCHAN_MAIN, pkt) = 0) then
-          begin
-            Inc(aliveCount);
-            mlist[f].srvAnswered := 1;
-          end;
-        end;
-      end
-      else if (mlist[f].isConnecting()) then
-      begin
-        Inc(aliveCount);
-      end;
-    end;
-
-    if (aliveCount = 0) then
-    begin
-      DisconnectAll();
-      CheckLocalServers();
-      exit;
-    end;
-
     e_WriteLog('Fetching serverlist...', TMsgType.Notify);
     g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_SLIST_FETCH]);
 
@@ -956,8 +1262,22 @@ begin
       hasUnanswered := false;
       for f := 0 to High(mlist) do
       begin
+        {
+        e_LogWritefln('  master #%d: [%s:%u] valid=%d; alive=%d; connected=%d; connecting=%d',
+          [f, mlist[f].hostName, mlist[f].hostPort, Integer(mlist[f].isValid()), Integer(mlist[f].isAlive()),
+          Integer(mlist[f].isConnected()), Integer(mlist[f].isConnecting())], TMsgType.Notify);
+        }
         if (not mlist[f].isValid()) then continue;
-        if (mlist[f].isConnected()) then
+        if (not mlist[f].isAlive()) then
+        begin
+          mlist[f].connect();
+          if (mlist[f].isAlive()) then
+          begin
+            hasUnanswered := true;
+            stt := GetTimerMS();
+          end;
+        end
+        else if (mlist[f].isConnected()) then
         begin
           if (mlist[f].srvAnswered = 0) then
           begin
@@ -968,6 +1288,7 @@ begin
               begin
                 hasUnanswered := true;
                 mlist[f].srvAnswered := 1;
+                stt := GetTimerMS();
               end;
             end;
           end
@@ -1398,6 +1719,8 @@ var
   qm: Boolean;
   Srv: TNetServer;
 begin
+  g_Net_Slist_Pulse();
+
   if gConsoleShow or gChatShow then
     Exit;