void NetInterface::checkTimeouts()
{
   U32 time = Platform::getVirtualMilliseconds();
   if(time > mLastTimeoutCheckTime + TimeoutCheckInterval)
   {
      for(U32 i = 0; i < mPendingConnections.size();)
      {
         NetConnection *pending = mPendingConnections[i];

         if(pending->getConnectionState() == NetConnection::AwaitingChallengeResponse &&
            time > pending->mConnectLastSendTime + ChallengeRetryTime)
         {
            if(pending->mConnectSendCount > ChallengeRetryCount)
            {
               pending->onConnectTimedOut();
               removePendingConnection(pending);
               pending->deleteObject();
               continue;
            }
            else
               sendConnectChallengeRequest(pending);
         }
         else if(pending->getConnectionState() == NetConnection::AwaitingConnectResponse &&
            time > pending->mConnectLastSendTime + ConnectRetryTime)
         {
            if(pending->mConnectSendCount > ConnectRetryCount)
            {
               pending->onConnectTimedOut();
               removePendingConnection(pending);
               pending->deleteObject();
               continue;
            }
            else
               sendConnectRequest(pending);
         }
         i++;
      }
      mLastTimeoutCheckTime = time;
      NetConnection *walk = NetConnection::getConnectionList();

      while(walk)
      {
         NetConnection *next = walk->getNext();
         if(walk->checkTimeout(time))
         {
            // this baddie timed out
            walk->onTimedOut();
            walk->deleteObject();
         }
         walk = next;
      }
   }
}
void NetInterface::handleConnectReject(const NetAddress *address, BitStream *stream)
{
   U32 connectSequence;
   stream->read(&connectSequence);
   NetConnection *conn = findPendingConnection(address, connectSequence);
   if(!conn || (conn->getConnectionState() != NetConnection::AwaitingChallengeResponse &&
                conn->getConnectionState() != NetConnection::AwaitingConnectResponse))
      return;
   removePendingConnection(conn);
   char reason[256];
   stream->readString(reason);
   conn->onConnectionRejected(reason);
   conn->deleteObject();
}
void NetInterface::handleDisconnect(const NetAddress *address, BitStream *stream)
{
   NetConnection *conn = NetConnection::lookup(address);
   if(!conn)
      return;

   U32 connectSequence;
   char reason[256];

   stream->read(&connectSequence);
   stream->readString(reason);

   if(conn->getSequence() != connectSequence)
      return;

   conn->onDisconnect(reason);
   conn->deleteObject();
}
void NetInterface::handleConnectAccept(const NetAddress *address, BitStream *stream)
{
   U32 connectSequence;
   stream->read(&connectSequence);
   NetConnection *conn = findPendingConnection(address, connectSequence);
   if(!conn || conn->getConnectionState() != NetConnection::AwaitingConnectResponse)
      return;
   const char *errorString = NULL;
   if(!conn->readConnectAccept(stream, &errorString))
   {
      conn->handleStartupError(errorString);
      removePendingConnection(conn);
      conn->deleteObject();
      return;
   }

   removePendingConnection(conn); // remove from the pending connection list
   conn->setNetworkConnection(true);
   conn->onConnectionEstablished(true); // notify the connection that it has been established
   conn->setEstablished(); // installs the connection in the connection table, and causes pings/timeouts to happen
   conn->setConnectSequence(connectSequence);
}
void NetInterface::handleConnectRequest(const NetAddress *address, BitStream *stream)
{
   if(!mAllowConnections)
      return;
   Con::printf("Got Connect Request");
   U32 connectSequence;
   stream->read(&connectSequence);

   // see if the connection is in the main connection table:

   NetConnection *connect = NetConnection::lookup(address);
   if(connect && connect->getSequence() == connectSequence)
   {
      sendConnectAccept(connect);
      return;
   }
   U32 addressDigest[4];
   U32 computedAddressDigest[4];

   stream->read(&addressDigest[0]);
   stream->read(&addressDigest[1]);
   stream->read(&addressDigest[2]);
   stream->read(&addressDigest[3]);

   computeNetMD5(address, connectSequence, computedAddressDigest);
   if(addressDigest[0] != computedAddressDigest[0] ||
      addressDigest[1] != computedAddressDigest[1] ||
      addressDigest[2] != computedAddressDigest[2] ||
      addressDigest[3] != computedAddressDigest[3])
      return; // bogus connection attempt

   if(connect)
   {
      if(connect->getSequence() > connectSequence)
         return; // the existing connection should be kept - the incoming request is stale.
      else
         connect->deleteObject(); // disconnect this one, and allow the new one to be created.
   }

   char connectionClass[255];
   stream->readString(connectionClass);

   ConsoleObject *co = ConsoleObject::create(connectionClass);
   NetConnection *conn = dynamic_cast<NetConnection *>(co);
   if(!conn || !conn->canRemoteCreate())
   {
      delete co;
      return;
   }
   conn->registerObject();
   conn->setNetAddress(address);
   conn->setNetworkConnection(true);
   conn->setSequence(connectSequence);

   const char *errorString = NULL;
   if(!conn->readConnectRequest(stream, &errorString))
   {
      sendConnectReject(conn, errorString);
      conn->deleteObject();
      return;
   }
   conn->setNetworkConnection(true);
   conn->onConnectionEstablished(false);
   conn->setEstablished();
   conn->setConnectSequence(connectSequence);
   sendConnectAccept(conn);
}