AREXPORT void ArServerClient::processPacket(ArNetPacket *packet, bool tcp)
{
  std::string str;
  struct sockaddr_in sin;
  unsigned int clientUdpPort;
  ArNetPacket retPacket;

  //printf("Command number %d\n", packet->getCommand());
  // if we're in intro mode and received back the intro
  if (myState == STATE_SENT_INTRO && 
      packet->getCommand() == ArClientCommands::INTRODUCTION)
  {
    char user[512];
    unsigned char password[16];
    clientUdpPort = packet->bufToUByte2();
    packet->bufToStr(user, sizeof(user));
    packet->bufToData((char *)password, 16);

    if (myRejecting != 0)
    {
      retPacket.empty();
      retPacket.setCommand(ArServerCommands::REJECTED);
      retPacket.byte2ToBuf(myRejecting);
      retPacket.strToBuf(myRejectingString.c_str());
      sendPacketTcp(&retPacket);
      if (myRejecting == 2)
	ArLog::log(ArLog::Normal, 
   "%sRejected connection from %s since we're using a central server at %s",
		   myLogPrefix.c_str(), getIPString(), 
		   myRejectingString.c_str());
      internalSwitchState(STATE_REJECTED);
      return;
    }

    // if user info is NULL we're not checking passwords
    if (myUserInfo != NULL && 
	!myUserInfo->matchUserPassword(user, password, myPasswordKey.c_str(),
				       myServerKey.c_str(),
				       myLogPasswordFailureVerbosely))
    {
      retPacket.empty();
      retPacket.setCommand(ArServerCommands::REJECTED);
      retPacket.byte2ToBuf(1);
      retPacket.strToBuf("");
      sendPacketTcp(&retPacket);
      ArLog::log(ArLog::Normal, "%sRejected user '%s' or password from %s",
		 myLogPrefix.c_str(), user, getIPString());
      internalSwitchState(STATE_REJECTED);
      return;
    }
    if (myUserInfo != NULL)
      myGroups = myUserInfo->getUsersGroups(user);
    else
      myGroups.clear();
    sin.sin_family = AF_INET;
    sin.sin_addr = *myTcpSocket.inAddr();
    sin.sin_port = ArSocket::hostToNetOrder(clientUdpPort);
    if (myUserInfo != NULL)
      ArLog::log(ArLog::Normal, 
		 "%sClient connected from %s with user %s", 
		 myLogPrefix.c_str(), getIPString(), user);
    else
      ArLog::log(ArLog::Normal, 
		 "%sClient connected from %s", myLogPrefix.c_str(), 
		 getIPString());

    setUdpAddress(&sin);
    // send that we've connected
    retPacket.empty();
    retPacket.setCommand(ArServerCommands::CONNECTED);
    sendPacketTcp(&retPacket);
    // note that we're connected
    internalSwitchState(STATE_CONNECTED);

    // send them the list
    sendListPacket();
    // send the udp introduction if we're using udp
    if (!myTcpOnly)
    {
      retPacket.empty();
      retPacket.setCommand(ArServerCommands::UDP_INTRODUCTION);
      retPacket.byte4ToBuf(myIntroKey);
      sendPacketUdp(&retPacket);
    }
  }
  // if we aren't in intro mode and got an intro somethings wrong
  else if (packet->getCommand() == ArClientCommands::INTRODUCTION)
  {
    ArLog::log(ArLog::Terse, 
	       "%sReceived introduction when not in intro mode",
	       myLogPrefix.c_str());
    return;
  }
  // if we got this over tcp then they only want tcp
  else if (packet->getCommand() == ArClientCommands::UDP_INTRODUCTION)
  {
    if (!myTcpOnly)
    {
      ArLog::log(ArLog::Normal, "%sGot UDP introduction over tcp, assuming client only wants tcp data.", myLogPrefix.c_str());
      myTcpOnly = true;
    }
    return;
  }
  // if we're connected and got a udp confirmation
  else if ((myState == STATE_CONNECTED || 
	    myState == STATE_SENT_INTRO) && 
	   packet->getCommand() == ArClientCommands::UDP_CONFIRMATION)
  {
    myUdpConfirmedTo = true;
    ArLog::log(myVerboseLogLevel, 
	       "%s: udp connection to client confirmed.", myLogPrefix.c_str());
    return;
  }
  // if we're not connected (or close) and got a confirmation
  else if (packet->getCommand() == ArClientCommands::UDP_CONFIRMATION)
  {
    ArLog::log(ArLog::Normal, 
	       "%sReceived udp confirmation when not connected.",
	       myLogPrefix.c_str());
    return;
  }
  else if (packet->getCommand() == ArClientCommands::TCP_ONLY)
  {
    ArLog::log(myVerboseLogLevel, "%sClient only wants tcp data.", myLogPrefix.c_str());
    myTcpOnly = true;
    return;
  }
  else if (packet->getCommand() == ArClientCommands::SHUTDOWN)
  {
    ArLog::log(ArLog::Normal, "%sClient from %s has disconnected.",
	       myLogPrefix.c_str(), getIPString());
    internalSwitchState(STATE_DISCONNECTED);
    return;
  }
  // if we're connected its a request, then set all that up
  else if (myState == STATE_CONNECTED && 
	   packet->getCommand() == ArClientCommands::REQUEST)
  {
    std::list<ArServerClientData *>::iterator it;
    ArServerClientData *data;
    ArServerData *serverData;
    unsigned int command;
    long mSec;
    // see which one they requested
    command = packet->bufToUByte2();
    mSec = packet->bufToByte4();

    // first we see if we already have this one
    for (it = myRequested.begin(); it != myRequested.end(); ++it)
    {
      data = (*it);
      serverData = data->getServerData();
      if (serverData->getCommand() == command)
      {
	trackPacketReceived(packet, command);
	data->setMSec(mSec);
	data->setPacket(packet);
	data->getPacket()->setCommand(command);
	serverData->callRequestChangedFunctor();
	ArLog::log(myVerboseLogLevel, 
	   "%sRevised request for command %s to %d mSec with new argument", 
		   myLogPrefix.c_str(), 
		   findCommandName(serverData->getCommand()), mSec);
	return;
      }
    }
    // we didn't have it, so make a new one
    std::map<unsigned int, ArServerData *>::iterator sdit;
    if ((sdit = myDataMap->find(command)) == myDataMap->end())
    {
      ArLog::log(ArLog::Terse, 
  	      "%sGot request for command %d which doesn't exist", 
		 myLogPrefix.c_str(), command);
      return;
    }
    serverData = (*sdit).second;
    if (serverData == NULL) 
    {
      ArLog::log(ArLog::Terse, 
		 "%sprocessPackets request handler has NULL serverData", 
		 myLogPrefix.c_str());
    }
    if (myUserInfo != NULL && 
	serverData->getCommandGroup() != NULL &&
	serverData->getCommandGroup()[0] != '\0' &&
	myGroups.count(serverData->getCommandGroup()) == 0 &&
	myGroups.count("all") == 0)
    {
      ArLog::log(ArLog::Normal, 
		 "%s%s tried to request command '%s' but it doesn't have access to that command", 
		 myLogPrefix.c_str(), getIPString(), 
		 serverData->getName());
      return;
    }
    trackPacketReceived(packet, command);
    data = new ArServerClientData(serverData, mSec, packet);
    data->getPacket()->setCommand(command);
    ArLog::log(myVerboseLogLevel, 
	       "%sadded request for command %s every %d mSec", 
	       myLogPrefix.c_str(), serverData->getName(), mSec);
    if (mSec == 0)
      ArLog::log(ArLog::Normal, "%sClient from %s requested command %s every at 0 msec", myLogPrefix.c_str(), 
		 getIPString(), serverData->getName());
    myRequested.push_front(data);
    serverData->callRequestChangedFunctor();
    pushCommand(command);
    pushForceTcpFlag(false);
    if (serverData->getFunctor() != NULL)
      serverData->getFunctor()->invoke(this, data->getPacket());
    popCommand();
    popForceTcpFlag();
    return;
  }
  // if we got a request when we're not connected
  else if (packet->getCommand() == ArClientCommands::REQUEST)
  {
    ArLog::log(ArLog::Normal, 
	       "Got a request while not connected.",
	       myLogPrefix.c_str());
    return;
  }
  // if we're connected its a requestStop, then set all that up
  else if (myState == STATE_CONNECTED && 
	   packet->getCommand() == ArClientCommands::REQUESTSTOP)
  {
    std::list<ArServerClientData *>::iterator it;
    ArServerClientData *data;
    ArServerData *serverData;
    unsigned int command;
    // see which one they requested
    command = packet->bufToUByte2();

    // first we see if we have this one
    for (it = myRequested.begin(); it != myRequested.end(); ++it)
    {
      data = (*it);
      serverData = data->getServerData();
      // we have a match, so set the new params then return
      if (data->getServerData()->getCommand() == command)
      {
	trackPacketReceived(packet, command);
	myRequested.erase(it);
	ArLog::log(myVerboseLogLevel, "%sStopped request for command %s", 
		   myLogPrefix.c_str(), 
		   findCommandName(serverData->getCommand()));
	delete data;
	serverData->callRequestChangedFunctor();
	return;
      }
    }
    // if we don't have it... that means that it wasn't here
    
    // find out what to call it
    std::map<unsigned int, ArServerData *>::iterator sdit;
    if ((sdit = myDataMap->find(command)) == myDataMap->end())
    {
      ArLog::log(ArLog::Terse, 
		 "%sGot a requeststop for command %d which doesn't exist", 
		 myLogPrefix.c_str(), command);
      return;
    }
    trackPacketReceived(packet, command);
    serverData = (*sdit).second;
    if (serverData == NULL) 
      ArLog::log(ArLog::Terse, 
		 "%srequeststop handler has NULL serverData on back command %d", 
		 myLogPrefix.c_str(), command);
    else
      ArLog::log(ArLog::Normal, "%s: Got a stop request for command %s that isn't requested", myLogPrefix.c_str(), serverData->getName());
    return;
  }
  // if we got a requestStop when we're not connected
  else if (packet->getCommand() == ArClientCommands::REQUESTSTOP)
  {
    ArLog::log(ArLog::Normal, 
	       "%sGot a requeststop while not connected.", 
	       myLogPrefix.c_str());
    return;
  }
  // if we're connected and its a command to execute just once
  else if (myState == STATE_CONNECTED)
  {
    unsigned int command;
    std::map<unsigned int, ArServerData *>::iterator it;
    ArServerData *serverData;

    command = packet->getCommand();
    if ((it = myDataMap->find(command)) == myDataMap->end())
    {
      ArLog::log(ArLog::Terse, 
  	      "%sArServerClient got request for command %d which doesn't exist", 
		 myLogPrefix.c_str(), command);
      return;
    }
    serverData = (*it).second;
    if (myUserInfo != NULL && 
	serverData->getCommandGroup() != NULL &&
	serverData->getCommandGroup()[0] != '\0' &&
	myGroups.count(serverData->getCommandGroup()) == 0 &&
	myGroups.count("all") == 0)
    {
      ArLog::log(ArLog::Normal, 
		 "%s%s tried to request command '%s' once but it doesn't have access to that command", myLogPrefix.c_str(),
		 getIPString(), 
		 serverData->getName());
      return;
    }
    trackPacketReceived(packet, command);

    // copy it out and return if its an idle packet
    if (myAllowIdlePackets && serverData->isIdlePacket())
    {
      myHaveIdlePackets = true;
      if (command <= 255)
	ArLog::log(myVerboseLogLevel, "%sStoring idle command %d", 
		   myLogPrefix.c_str(), command);
      else
	ArLog::log(myVerboseLogLevel, "%sStoring idle command %s", 
		   myLogPrefix.c_str(), serverData->getName());
      myIdlePacketsMutex.lock();
      ArNetPacket *idlePacket = new ArNetPacket(packet->getLength() + 5);
      idlePacket->duplicatePacket(packet);
      myIdlePackets.push_back(idlePacket);
      myIdlePacketsMutex.unlock();
      return;
    }
    // If its a slow or an idle packet (and we're not allowing the
    // idle behavior) and we allow slow packets then copy it
    else if (myAllowSlowPackets && (serverData->isSlowPacket() || 
				    serverData->isIdlePacket()))
    {
      myHaveSlowPackets = true;
      if (command <= 255)
	ArLog::log(myVerboseLogLevel, "%sStoring slow command %d", 
		   myLogPrefix.c_str(), command);
      else
	ArLog::log(myVerboseLogLevel, "%sStoring slow command %s", 
		   myLogPrefix.c_str(), serverData->getName());
      mySlowPacketsMutex.lock();
      ArNetPacket *slowPacket = new ArNetPacket(packet->getLength() + 5);
      slowPacket->duplicatePacket(packet);
      mySlowPackets.push_back(slowPacket);
      mySlowPacketsMutex.unlock();
      return;
    }


    if (command <= 255)
      ArLog::log(myVerboseLogLevel, "%sGot command %s", 
		 myLogPrefix.c_str(), serverData->getName());
    else
      ArLog::log(ArLog::Verbose, "%sGot command %s", 
		 myLogPrefix.c_str(), serverData->getName());
    pushCommand(command);
    pushForceTcpFlag(tcp);
    if (serverData->getFunctor() != NULL)
      serverData->getFunctor()->invoke(this, packet);
    if (serverData->getRequestOnceFunctor() != NULL)
      serverData->getRequestOnceFunctor()->invoke(this, packet);
    popCommand();
    popForceTcpFlag();
    return;
  }
  else
  {
    ArLog::log(ArLog::Terse, 
	       "%sRogue packet command %s in state %d", myLogPrefix.c_str(),
	       findCommandName(packet->getCommand()), myState);
  }
}