/*
    Amiga port by Oliver Gantert

    27.04.2000 - fixed a lot of compiler warnings
*/
/*

ORBIT, a freeware space combat simulator
Copyright (C) 1999  Steve Belczyk <steve1@genesis.nred.ma.us>

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 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "orbit.h"

#ifndef AMIGA
#ifndef WIN32
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#endif /* WIN32 */
#include <fcntl.h>
#endif /* AMIGA */

#include <errno.h>
#include <limits.h>
#include <stdarg.h>

/*
 *  All sorts of stuff a client needs
 */

int FindClient()
/*
 *  Find an unused client
 */
{
  int c;

  for (c=0; c<NCLIENTS; c++)
  {
    if (!client[c].active)
    {
      client[c].active = 1;
      return c;
    }
  }

  /* Ran out */
  return (-1);
}

void DoClient()
/*
 *  Handle ORBIT client duties
 */
{
#ifndef AMIGA
  char buf[1024];
  int r, e;

  /* Bump timers */
  clientme.timer.server += deltaT;
  clientme.timer.pos += deltaT;

  /* If server has been idle too long, goodbye */
  if (clientme.timer.server > MAXSERVERIDLE)
  {
    Mprint ("Server is not responding");
    Log ("DoClient: Dropping idle server");
    #ifndef WIN32
    close (clientme.socket);
    #else
    closesocket (clientme.socket);
    #endif
    am_client = 0;
    return;
  }

  /* Time to report position? */
  if (clientme.timer.pos >= CLIENTPOSINTERVAL)
  {
    ReportPosition();
    clientme.timer.pos = 0.0;
  }

  /* Try to read from the server */
  r = recv (clientme.socket, buf, 1024, 0);

  /* If recv returns zero the other side has gone away */
  if (r == 0)
  {
    Mprint ("Dropped by server");
    Log ("DoClient: Dropped by server");
    #ifndef WIN32
    close (clientme.socket);
    #else
    closesocket (clientme.socket);
    #endif
    am_client = 0;

    InitNetwork();
    InitTargets();

    return;
  }

  /* If we get an error it better be EWOULDBLOCK */
  #ifndef WIN32
  if (r < 0)
  {
    if (errno == EWOULDBLOCK)
    {
      /* All is well, no data from server */
      return;
    }
    else
    {
      /* Uh oh */
      Log ("DoClient: recv() error: %d", errno);
      return;
    }
  }
  #else
  if (r == SOCKET_ERROR)
  {
    e = WSAGetLastError();
    if (e == WSAEWOULDBLOCK)
    {
      /* All is well, no data from server */
      return;
    }
    else
    {
      /* Uh oh */
      Log ("DoClient: recv() error: %d", e);
      return;
    }
  }
  #endif

  /* We really have data from the server! */
  clientme.timer.server = 0.0;

  /* Handle the data */
  buf[r] = 0;
  recv_bytes += r;
  /* Log ("DoClient: Server data: %s", buf); */

  ServerData (buf, r);
#endif /* AMIGA */
}

void ServerData (char *buf, int n)
/*
 *  Separate incoming data into packets
 */
{
  int i;
  #ifndef BINARYPACKETS
  int ch, j;
  #endif

  #ifdef BINARYPACKETS
  for (i=0; i<n; i++) ServerByte (buf[i]);

  #else
  j = clientme.ptr;

  for (i=0; i<n; i++)
  {
    ch = clientme.pkt[j] = buf[i];
    j = (j + 1) % 1024;

    /* Got a packet? */
    if (ch == 0)
    {
      ServerPacket (clientme.pkt);
      j = 0;
    }
  }

  clientme.ptr = j;
  #endif
}

void ServerByte (char c)
/*
 *  Process one byte from server
 */
{
  switch (clientme.state)
  {
    case NETSTATE_MAGIC:
    if (c == NET_MAGIC)
    {
      clientme.state = NETSTATE_SIZE;
    }
    break;

    case NETSTATE_SIZE:
    clientme.remain = 0xff & c;
    clientme.ptr = 0;
    clientme.state = NETSTATE_PACKET;
    if (clientme.remain == 0) clientme.state = NETSTATE_MAGIC;
    break;

    case NETSTATE_PACKET:
    clientme.pkt[clientme.ptr++] = c;
    clientme.remain--;
    if (clientme.remain == 0)
    {
      clientme.pkt[clientme.ptr] = 0;
      ServerPacket (clientme.pkt);
      clientme.state = NETSTATE_MAGIC;
    }
    break;

    default:
    Log ("ServerByte: PANIC! No such state: %d", clientme.state);
    break;
  }
}

void ServerPacket (char *pkt)
/*
 *  Handle a packet from the server
 */
{
  char cmd[128];

  /* If high bit of first byte is set, this is a binary packet */
  if (0x80 & pkt[0])
  {
    ServerBinaryPacket (pkt);
    return;
  }

  /* Sanity check on packet */
  if ( (strlen(pkt) == 0) ||
  (pkt[0] == ' ') ||
  (pkt[0] == '\r') ||
  (pkt[0] == '\n') ||
  (pkt[0] == '\t') )
  {
    Log ("ServerPacket: Insane packet: %s", pkt);
    return;
  }

  /* Extract command part */
  sscanf (pkt, "%s", cmd);

  /* Dispatch */
  if (!strcasecmp (cmd, "poss"))
  {
    ServerPositionShort (pkt);
    return;
  }
  else if (!strcasecmp (cmd, "posl"))
  {
    ServerPositionLong (pkt);
    return;
  }
  else if (!strcasecmp (cmd, "ping"))
  {
    ServerPing (pkt);
    return;
  }
  else if (!strcasecmp (cmd, "fire"))
  {
    ServerFire (pkt);
    return;
  }
  else if (!strcasecmp (cmd, "mhit"))
  {
    ServerMHit (pkt);
    return;
  }
  else if (!strcasecmp (cmd, "mdie"))
  {
    ServerMDie (pkt);
    return;
  }
  else if (!strcasecmp (cmd, "mesg"))
  {
    ServerMessage (pkt);
    return;
  }
  else if (!strcasecmp (cmd, "name"))
  {
    ServerName (pkt);
    return;
  }
  else if (!strcasecmp (cmd, "vcnt"))
  {
    ServerVacant (pkt);
    return;
  }
  else if (!strcasecmp (cmd, "modl"))
  {
    ServerModel (pkt);
    return;
  }
  else if (!strcasecmp (cmd, "flag"))
  {
    ServerFlag (pkt);
    return;
  }
  else if (!strcasecmp (cmd, "crat"))
  {
    ServerCrater (pkt);
    return;
  }
  else if (!strcasecmp (cmd, "cmsg"))
  {
    ServerCMSG (pkt);
    return;
  }
  else if (!strcasecmp (cmd, "helo"))
  {
    ServerHelo (pkt);
    return;
  }
  else if (!strcasecmp (cmd, "gbye"))
  {
    ServerGbye (pkt);
    return;
  }
  else if (!strcasecmp (cmd, "welc"))
  {
    ServerWelcome (pkt);
    return;
  }
  else if (!strcasecmp (cmd, "plan"))
  {
    ServerPlanet (pkt);
    return;
  }
  else if (!strcasecmp (cmd, "rpln"))
  {
    ServerResetPlanets (pkt);
    return;
  }
  else
  {
    Log ("ServerPacket: Unrecongized command: %s", cmd);
    return;
  }
}

void ServerPing (char *pkt)
/*
 *  Handle ping packet from server
 */
{
  /* Just send it right back */
  SendASCIIPacket (clientme.socket, pkt);
}

void ServerBinaryPing (char *pkt)
/*
 *  Handle binary ping packet
 */
{
  double t;
  
  /* Decode */
  DecodeBinaryPacket (pkt, "F", &t);
  
  /* Echo */
  SendBinaryPacket (clientme.socket, "cF", PKT_PING, t);
}

void ServerMessage (char *pkt)
/*
 *  Text message from server
 */
{
  /* Print it to the screen */
  if ( (pkt[4] != 0) && (pkt[5] != 0) )
  {
    Mprint (&pkt[5]);
  }
}

void ServerWelcome (char *pkt)
/*
 *  Server says hello and gives us our client number
 */
{
  int i, t;

  /* Extract client number */
  if (1 != sscanf (pkt, "%*s %d", &i)) return;

  /* Set up our client */
  Log ("ServerWelcome: I am client %d", i);
  clientme.client = i;

  /* Set up client and target */
  client[i].active = 1;
  client[i].is_me = 1;
  client[i].frags = 0;
  t = client[i].target = InitClientTarget();
  strcpy (target[t].name, player.name);
  target[t].hidden = target[t].invisible = 1;
}

void ReportPosition()
/*
 *  Report my position
 */
{
  #ifdef BINARYPACKETS
  int c;
  #else
  char cmd[5];
  #endif

  /* Not if we're dead */
  if ( (state == STATE_DEAD1) || (state == STATE_DEAD2) ) return;

  /* Urgent? */
  if (clientme.urgent)
  {
    #ifdef BINARYPACKETS
    c = PKT_POSU;
    #else
    strcpy (cmd, "POSU");
    #endif
    clientme.urgent = 0;
  }
  else
  {
    #ifdef BINARYPACKETS
    c = PKT_POSN;
    #else
    strcpy (cmd, "POSN");
    #endif
  }
  
  #ifdef BINARYPACKETS
  SendBinaryPacket (clientme.socket, "cVVvvffffff", c,
  player.pos, player.vel, player.view, player.up,
  player.move_up, player.move_down,
  player.move_right, player.move_left,
  player.move_pitchright, player.move_pitchleft);
  #else
  SendASCIIPacket (clientme.socket,
  "POSN %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf\n",
     player.pos[0], player.pos[1], player.pos[2],
     player.vel[0], player.vel[1], player.vel[2],
     player.view[0], player.view[1], player.view[2],
     player.up[0], player.up[1], player.up[2],
  player.move_up, player.move_down,
  player.move_right, player.move_left,
  player.move_pitchright, player.move_pitchleft);
#endif
}

void ServerPositionShort (char *pkt)
/*
 *  The server is sending a client's short position report
 */
{
 int c;
 double pos[3], vel[3];

 /* Parse packet */
 if (7 != sscanf (pkt, "%*s %d %lf %lf %lf %lf %lf %lf", &c,
  &player.pos[0], &player.pos[1], &player.pos[2],
  &player.vel[0], &player.vel[1], &player.vel[2]))
 {
  Log ("ServerPositionShort: Malformed POSS packet: %s", pkt);
  return;
 }

 PositionShort (c, pos, vel);
}

void PositionShort (int c, double *pos, double *vel)
/*
 *  Process short position report
 */
{
 int t;

 /* Sanity */
 if ((c<0) || (c>=NCLIENTS)) return;

 /* Mark client active */
 client[c].active = 1;

 /* Is it for me? */
 if (client[c].is_me)
 {
  Vset (player.pos, pos);
  Vset (player.vel, vel);
 }
 else
 {
  /* Not for me, see if we have target for this client */
  if ((-1) == (t = client[c].target))
  {
   t = client[c].target = InitClientTarget();
  }

  /* Make target unhidden */
  target[t].hidden = 0;

  /* Set position */
  Vset (target[t].pos, pos);
  Vset (target[t].vel, vel);
 }
}

void ServerPositionLong (char *pkt)
/*
 *  The server is sending a client's long position report
 */
{
 int c;
 double pos[3], vel[3], view[3], up[3], move_up, move_down, move_left;
 double move_right, move_pitchright, move_pitchleft;

 /* Parse packet */
 if (19 != sscanf (pkt, "%*s %d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf",
  &c,
  &pos[0], &pos[1], &pos[2], &vel[0], &vel[1], &vel[2],
  &view[0], &view[1], &view[2], &up[0], &up[1], &up[2],
  &move_up, &move_down, &move_right, &move_left,
  &move_pitchright, &move_pitchleft))
 {
  Log ("ServerPositionLong: Malformed POSL packet: %s", pkt);
  return;
 }

 PositionLong (c, pos, vel, view, up, move_up, move_down, move_right,
  move_left, move_pitchright, move_pitchleft);
}

void PositionLong ( int c, double *pos, double *vel, double *view, double *up,
                    double move_up, double move_down, double move_right, double move_left,
                    double move_pitchright, double move_pitchleft )
/*
 *  Process a long position report
 */
{
 int t;

 /* Sanity */
 if ((c<0) || (c>=NCLIENTS)) return;

 /* Mark client active */
 client[c].active = 1;

 /* Is it for me? */
 if (client[c].is_me)
 {
  Vset (player.pos, pos);
  Vset (player.vel, vel);
  Vset (player.view, view);
  Vset (player.up, up);
  player.move_up = move_up;
  player.move_down = move_down;
  player.move_left = move_left;
  player.move_right = move_right;
  player.move_pitchright = move_pitchright;
  player.move_pitchleft = move_pitchleft;
  Crossp (player.right, player.up, player.view);
 }
 else
 {
  /* Not for me, see if we have target for this client */
  if ((-1) == (t = client[c].target))
  {
   t = client[c].target = InitClientTarget();
  }

  /* Make target unhidden */
  target[t].hidden = 0;

  /* Set position */
  Vset (target[t].pos, pos);
  Vset (target[t].vel, vel);
  Vset (target[t].view, view);
  Vset (target[t].up, up);
  target[t].move_up = move_up;
  target[t].move_down = move_down;
  target[t].move_left = move_left;
  target[t].move_right = move_right;
  target[t].move_pitchright = move_pitchright;
  target[t].move_pitchleft = move_pitchleft;

  Normalize (target[t].up);
  Normalize (target[t].view);

  Crossp (target[t].right, target[t].up, target[t].view);
 }
}

void ServerMHit (char *pkt)
/*
 *  We were hit by a missile
 */
{
 double yield;

 /* Parse packet */
 if (1 != sscanf (pkt, "%*s %lf", &yield))
 {
  Log ("ServerMHit: Malformed MHIT packet: %s", pkt);
  return;
 }

 /* Flash screen red */
 palette_flash = 2;

 /* Damage shields */
 player.shields -= yield;
 if (player.shields < 0.0) player.shields = 0.0;
}

void ServerMDie (char *pkt)
/*
 *  Someone was killed.  Hope it wasn't me!
 */
{
 int ckiller, cvictim;
 double v[3], d;

 /* Parse packet */
 if (2 != sscanf (pkt, "%*s %d %d", &cvictim, &ckiller))
 {
  Log ("ServerMDie: Malformed MDIE packet: %s", pkt);
  return;
 }

 /* Adjust frags */
 client[ckiller].frags++;

 /* Tell me about it */
 Cprint ("%s was killed by %s",
  target[client[cvictim].target].name,
  target[client[ckiller].target].name);

 /* Was it me? */
 if (cvictim == clientme.client)
 {
  Cprint ("YOU were killed!");
  NetPlayerDies();
 }
 else
 {
  /* Make target hidden */
  target[client[cvictim].target].hidden = 1;

  /* Make a boom if close enough */
  Vsub (v, player.pos, target[client[cvictim].target].pos);
  d = Mag2 (v);
  if (d <= TARG_MAXRANGE2) Boom (target[client[cvictim].target].pos, 1.0);
 }

 CheckLock();
}

void ServerCrater (char *pkt)
/*
 *  Someone hit a planet.  Hope it wasn't me!
 */
{
 int c, p;
 double v[3], d;

 /* Parse packet */
 if (2 != sscanf (pkt, "%*s %d %d", &c, &p))
 {
  Log ("ServerMDie: Malformed CRAT packet: %s", pkt);
  return;
 }

 /* Adjust frags */
 client[c].frags--;

 /* Tell me about it */
 Cprint ("%s cratered on %s",
  target[client[c].target].name, planet[p].name);

 /* Was it me? */
 if (c == clientme.client)
 {
  NetPlayerDies();
 }
 else
 {
  /* Make target hidden */
  target[client[c].target].hidden = 1;

  /* Make a boom if close enough */
  Vsub (v, player.pos, target[client[c].target].pos);
  d = Mag2 (v);
  if (d <= TARG_MAXRANGE2) Boom (target[client[c].target].pos, 1.0);
 }

 CheckLock();
}

void ServerFire (char *pkt)
/*
 *  Someone nearby has fired a missile
 */
{
 int c, t, w;

 /* Parse packet */
 if (2 != sscanf (pkt, "%*s %d %d", &c, &w))
 {
  Log ("ServerFire: Malformed FIRE packet: %s", pkt);
  return;
 }

 /* Get target for this client */
 t = client[c].target;

 /* Fire the missile! */
 FireMissile (target[t].pos, target[t].vel, target[t].view,
  0, w, t);
}

void ServerPlanet (char *pkt)
/*
 *  Server sends the position of a planet
 */
{
 int p;
 double theta;

 /* Parse packet */
 if (2 != sscanf (pkt, "%*s %d %lf", &p, &theta))
 {
  Log ("ServerPlanet: Malformed PLAN packet: %s", pkt);
  return;
 }

 if ((p >= 0) && (p < NPLANETS)) planet[p].theta = theta;
}

void ServerBinaryPlanet (char *pkt)
/*
 *  Server sends the position of a planet in binary
 */
{
 int p;
 double theta;

 /* Parse packet */
 DecodeBinaryPacket (pkt, "cF", &p, &theta);

 /* Set planet angle */
 if ((p >= 0) && (p < NPLANETS)) planet[p].theta = theta;
}

void ServerResetPlanets (char *pkt)
/*
 *  Server done sending planet positions
 */
{
 PositionPlanets();
}

void ServerName (char *pkt)
/*
 *  Server sends us the name of an active client
 */
{
 int c, t, f;
 char buf[256];

 /* Parse packet */
 if (3 != sscanf (pkt, "%*s %d %s %d", &c, buf, &f))
 {
  Log ("ServerName: Malformed NAME packet: %s", pkt);
  return;
 }

 /* Sanity check */
 if ((c<0) || (c>=NCLIENTS)) return;

/* Log ("ServerName: NAME %d %s %d", c, buf, f); */

 /* Don't bother if it's us */
 if (c == clientme.client)
 {
  client[c].frags = f;
  return;
 }

 /* Show client is active */
 client[c].active = 1;

 /* Set up a target if we didn't know about this one */
 if ((-1) == (t = client[c].target))
 {
  t = InitClientTarget();
  client[c].target = t;
 }

 /* Set name and frags */
 strcpy (target[t].name, buf);
 client[c].frags = f;
}

void ServerHelo (char *pkt)
/*
 *  A new client has joined the game
 */
{
 int c, t;
 char buf[256];

 /* Parse packet */
 if (2 != sscanf (pkt, "%*s %d %s", &c, buf))
 {
  Log ("ServerName: Malformed HELO packet: %s", pkt);
  return;
 }

 /* Sanity check */
 if ((c<0) || (c>=NCLIENTS)) return;

 /* Tell us */
 Cprint ("%s is here", buf);
 Log ("ServerHelo: HELO %d %s", c, buf);

 /* Don't bother if it's us */
 if (c == clientme.client) return;

 /* Set up a target if we didn't know about this one */
 if ((-1) == (t = client[c].target))
 {
  t = InitClientTarget();
  client[c].target = t;
 }

 /* Set name */
 strcpy (target[t].name, buf);
}

void ServerVacant (char *pkt)
/*
 *  Server tells us about an unused client slot
 */
{
 int c;

 /* Parse packet */
 if (1 != sscanf (pkt, "%*s %d", &c))
 {
  Log ("ServerVacant: Malformed VCNT packet: %s", pkt);
  return;
 }

 if ((c<=0) || (c>=NCLIENTS)) return;

 Log ("ServerVacant: VCNT %d", c);

 /* Don't bother if it's us (this should never happen!) */
 if (c == clientme.client)
 {
  Log ("ServerVacant: PANIC! Server says I am vacant!");
  return;
 }

 /* Destroy any associated target */
 if ((-1) != client[c].target) DestroyTarget (client[c].target);
 client[c].active = 0;
 client[c].target = (-1);
}

void ServerGbye (char *pkt)
/*
 *  Someone has left the game
 */
{
 int c;
 char buf[256];

 /* Parse packet */
 if (2 != sscanf (pkt, "%*s %d %s", &c, buf))
 {
  Log ("ServerVacant: Malformed GBYE packet: %s", pkt);
  return;
 }

 if ((c<=0) || (c>=NCLIENTS)) return;

 Log ("ServerGbye: GBYE %d", c);

 /* Don't bother if it's us (this should never happen!) */
 if (c == clientme.client)
 {
  Log ("ServerGbye: PANIC! Server says I am gone!");
  return;
 }

 /* Tell us */
 Cprint ("%s is gone", buf);

 /* Destroy any associated target */
 if ((-1) != client[c].target) DestroyTarget (client[c].target);
 client[c].active = 0;
 client[c].target = (-1);
}

void ServerCMSG (char *pkt)
/*
 *  Server has a console message for us
 */
{
 /* Sanity */
 if ( (pkt[4] == 0) || (pkt[5] == 0) ) return;

 /* Show it */
 Cprint ("%s", &pkt[5]);

 /* Give the sound */
 if (sound) PlayAudio (SOUND_COMM);
}

void ServerFlag (char *pkt)
/*
 *  Game flags from server
 */
{
 int f;

 /* Parse packet */
 if (1 != sscanf (pkt, "%*s %d", &f))
 {
  Log ("ServerFlag: Malformed FLAG packet: %s", pkt);
  return;
 }

 /* Set flags */
 gravity = (f & FLAG_GRAVITY) ? 1 : 0;
 player.flightmodel = (f & FLAG_FLIGHTMODEL) ? FLIGHT_ARCADE : FLIGHT_NEWTONIAN;
 fullstop = (f & FLAG_FULLSTOP) ? 1 : 0;
 realdistances = (f & FLAG_REALDISTANCES) ? 1 : 0;
 orbit = (f & FLAG_ORBIT) ? 1 : 0;
}

void DoConnect ()
/*
 *  Sparky has finished typing the server address
 */
{
 BecomeClient (text.buf);
}

void ServerModel (char *pkt)
/*
 *  Set client model
 */
{
 char buf[128];
 int m, c;

 /* Parse packet */
 if (2 != sscanf (pkt, "%*s %d %s", &c, buf))
 {
  Log ("ServerModel: Malformed MODL packet: %s", pkt);
  return;
 }

 /* Sanity */
 if ( (c < 0) || (c >= NCLIENTS) || (!client[c].active) || (client[c].is_me) ) return;

 /* Try to load model */
 m = LoadModel (buf);

 /* Success? */
 if (m != (-1))
 {
  target[client[c].target].model = m;
  target[client[c].target].list = model[m].list;
 }
 else
 {
  Log ("ServerModel: LoadModel (%s) failed", buf);
  /* Use default model */
  m = LoadModel ("light2.tri");
  target[client[c].target].model = m;
  target[client[c].target].list = model[m].list;
 }
}

void ServerBinaryPacket (unsigned char *pkt)
/*
 *  Handle binary packet from server
 */
{
 /* Dispatch */
 switch (pkt[0])
 {
 case PKT_POSS:
  BinaryPositionShort (&pkt[1]);
  break;

 case PKT_POSL:
  BinaryPositionLong (&pkt[1]);
  break;

 case PKT_PLAN:
  ServerBinaryPlanet (&pkt[1]);
  break;

 case PKT_VCNT:
  ServerBinaryVacant (&pkt[1]);
  break;

 case PKT_PING:
  ServerBinaryPing (&pkt[1]);
  break;

 default:
  Log ("ServerBinaryPacket: Unrecognized packet type: 0x%x",
   pkt[0]);
  break;
 }
}

void BinaryPositionShort (unsigned char *pkt)
/*
 *  Binary short position report
 */
{
 int c;
 double pos[3], vel[3];

 DecodeBinaryPacket (pkt, "cVV", &c, pos, vel);
 PositionShort (c, pos, vel);
}

void BinaryPositionLong (unsigned char *pkt)
/*
 *  Binary long position report
 */
{
 int c;
 double pos[3], vel[3], view[3], up[3];
 double move_up, move_down;
 double move_right, move_left, move_pitchright, move_pitchleft;

 DecodeBinaryPacket (pkt, "cVVvvffffff", &c, pos, vel, view, up,
  &move_up, &move_down, &move_right, &move_left,
  &move_pitchright, &move_pitchleft);

 PositionLong (c, pos, vel, view, up,
  move_up, move_down, move_right, move_left,
  move_pitchright, move_pitchleft);
}

void ServerBinaryVacant (char *pkt)
/*
 *  Server tells us which clients are in use
 */
{
 int c;
 unsigned int w, bit, i1, i2;

 /* Parse packet */
 DecodeBinaryPacket (pkt, "cc", &i1, &i2);
 w = i1 + 256*i2;

 /* See which bits are set */
 bit = 1;

 for (c=0; c<NCLIENTS; c++)
 {
  if (!(w & bit))
  {
   /* Don't bother if it's us (this should never happen!) */
   if (c == clientme.client)
   {
    Log ("ServerVacant: PANIC! Server says I am vacant!");
    return;
   }

   /* Destroy any associated target */
   if ((-1) != client[c].target) DestroyTarget (client[c].target);
   client[c].active = 0;
   client[c].target = (-1);
  }

  bit = bit * 2;
 }
}
