/*
    Amiga port by Oliver Gantert

    27.04.2000 - fixed some compiler warnings
    28.04.2000 - commented out some functions, no networking on Amiga yet
*/
/* 
 
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>
#include <netinet/tcp.h>
#endif /* WIN32 */
#include <fcntl.h>
#endif /* AMIGA */

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

/*
 *  All sorts of stuff the server does
 */

void DoServer()
/*
 *  I am an ORBIT server
 */
{
  int c;

  /* Look for any new clients */
  CheckForClient();

  /* Loop through clients */
  for (c=0; c<NCLIENTS; c++)
  {
    if (client[c].active) DoThisClient (c);
  }
}

void DoThisClient (int c)
/*
 *  Server handles client c
 */
{
  int i;

  /* Don't bother if this client is not active */
  if (!client[c].active) return;

  /* Don't bother if this client is us */
  if (client[c].is_me) return;

  /* Bump times */
  client[c].timer.idle += deltaT;
  client[c].timer.ping += deltaT;
  for (i=0; i<NCLIENTS; i++) client[c].timer.posn[i] += deltaT;
  client[c].timer.rollcall += deltaT;

  /* If client has been idle too long, goodbye */
  if (client[c].timer.idle > MAXCLIENTIDLE)
  {
    Cprint ("Dropping idle client");
    Log ("DoThisClient: Dropping idle client %d", c);
    DropClient (c);
    return;
  }

  /* See if this client has anything to say to us */
  ReadFromClient (c);

  /* Send anything we have to send to this client */
  SendToClient (c);
}

void SendToClient (int c)
/*
 *  Send anything we have to send to this client
 */
{
  int cc;

  if (!client[c].active) return;

  /* Time to send ping? */
  if (client[c].timer.ping >= PINGINTERVAL)
  {
    client[c].timer.ping = 0.0;
    SendASCIIPacket (client[c].socket,
    "PING %lf\n", absT);
  }

 /* Time to send position reports? */
 for (cc=0; cc<NCLIENTS; cc++)
 {
  /* Not if it's us and we're dead */
  if ( (cc == server.client) &&
       ((state == STATE_DEAD1) || (state == STATE_DEAD2)) ) continue;

  if (client[cc].active && (cc != c))
  {
   if (client[c].timer.posn[cc] > client[c].posninterval)
   {
    /* Report client cc's position to client c */
    SendPosition (c, cc);
    client[c].timer.posn[cc] = 0.0;
   }
  }
 }

 /* Time to send roll call? */
 if (client[c].timer.rollcall >= ROLLCALLINTERVAL) SendRollCall (c);
/**
 if (client[c].timer.posn >= client[c].posninterval)
 {
  client[c].timer.posn = 0.0;
  SendPositions (c);
 }
**/
}

void ReadFromClient (int c)
/*
 *  See if this client has anything to say to us
 */
{
#ifndef AMIGA
 char buf[1024];
 int r, e;

 /* Bail if not there */
 if (!client[c].active) return;

 /* Try to read from this client */
 r = recv (client[c].socket, buf, 1024, 0);

 /* If recv returns zero the other side has gone away */
 if (r == 0)
 {
  Cprint ("Client %d went away", c);
  Log ("ReadFromClient: Client %d went away", c);
  DropClient (c);

  return;
 }

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

 /* We really have data from the client! */
 client[c].timer.idle = 0.0;

 /* Handle the data */
 buf[r] = 0;
 recv_bytes += r;
/*
 Log ("ReadFromClient: Client %d data:", c);
 LogWrite (buf, r);
*/
 /* Handle packet from Client */
 ClientData (c, buf, r);
#endif /* AMIGA */
}

void ClientData (int c, 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++) ClientByte (c, buf[i]);
#else
 j = client[c].ptr;

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

  /* Got a packet? */
  if (ch == 0)
  {
   ClientPacket (c, client[c].pkt);
   j = 0;
  }
 }

 client[c].ptr = j;
#endif
}

void ClientByte (int c, char b)
/*
 *  Process one byte from server
 */
{
 switch (client[c].state)
 {
 case NETSTATE_MAGIC:
  if (b == NET_MAGIC)
  {
   client[c].state = NETSTATE_SIZE;
  }
  break;

 case NETSTATE_SIZE:
  client[c].remain = 0xff & b;
  client[c].ptr = 0;
  client[c].state = NETSTATE_PACKET;
  if (client[c].remain == 0) client[c].state = NETSTATE_MAGIC;
  break;

 case NETSTATE_PACKET:
  client[c].pkt[client[c].ptr++] = b;
  client[c].remain--;
  if (client[c].remain == 0)
  {
   client[c].pkt[client[c].ptr] = 0;
   ClientPacket (c, client[c].pkt);
   client[c].state = NETSTATE_MAGIC;
  }
  break;

 default:
  Log ("ClientByte: PANIC! No such state: %d", client[c].state);
  break;
 }
}

void CheckForClient()
/*
 *  Look for a new network client
 */
{
#ifndef AMIGA
 int c, e, i, len;
 SOCKET a;
 int one = 1;
 struct sockaddr_in sin;
#ifdef WIN32 
 unsigned long longone = 1; 
#endif 

 len = sizeof (sin);

 /* Ask for any outstanding connections */
 a = accept (server.listening_socket, (struct sockaddr *) &sin, &len);

#ifndef WIN32
 if (a < 0)
 {
  /* If the accept failed, it better be because the socket
     is non-blocking or something went wrong */
  if (errno == EWOULDBLOCK)
  {
   /* No problem, just no new connection */
   return;
  }
  else
  {
   /* Uh oh */
   Log ("CheckForClient: accept() error: %d", errno);
   return;
  }
 }
#else
 if (a == SOCKET_ERROR)
 {
  e = WSAGetLastError();
  if (e == WSAEWOULDBLOCK)
  {
   /* All is well, no new client */
   return;
  }
  else
  {
   /* Uh oh */
   Log ("CheckForClient: accept() error: %d", e);
   return;
  }
 }
#endif
 else
 {
  /* Got one! */
  c = FindClient();
  if (c < 0)
  {
   Cprint ("Too many clients!");
   Log ("CheckForClient: Too many clients");
#ifndef WIN32
   close (a);
#else
   closesocket (a);
#endif
  }
  else
  { 
   /* Make this socket non-blocking too */ 
#ifdef WIN32 
   ioctlsocket (a, FIONBIO, &longone); 
#else 
   fcntl (a, F_SETFL, O_NONBLOCK); 
#endif 

   /* Save client IP address */
   strcpy (client[c].ip, inet_ntoa(sin.sin_addr));

   Log ("CheckForClient: Client %d [%s] connected", c, client[c].ip);
   Cprint ("Client %d [%s] connected", c, client[c].ip);

   /* Disable the Nagle algorithm */
   setsockopt (a, IPPROTO_TCP, TCP_NODELAY, (char *) &one,
    sizeof(one));

   /* Set up new client */
   client[c].active = 1;
   client[c].socket = a;
   client[c].is_me = 0;
   client[c].ping = 0.0;
   client[c].posninterval = POSNINTERVALMEDIUM;
   client[c].frags = 0;

   client[c].target = InitClientTarget();

   client[c].timer.idle = 0.0;
   client[c].timer.ping = PINGINTERVAL;
   for (i=0; i<NCLIENTS; i++) client[c].timer.posn[i] = 0.0;
   client[c].timer.rollcall = 0.0;

   client[c].pkt[0] = 0;
   client[c].ptr = 0;

   /* Send welcome message to new client */
   SendASCIIPacket (client[c].socket, "WELC %d\n", c);
   SendASCIIPacket (client[c].socket,
       "MESG Welcome, client %d, to the ORBIT server.\n", c);

   /* Send current roll call */
   SendRollCall (c);
  }
 }
#endif /* AMIGA */
}

void ClientPacket (int c, char *pkt)
/*
 *  Handle packet from this client
 */
{
 char cmd[128];

/* Log ("ClientPacket: %s", pkt); */ 
 
 /* High bit of first byte means this is a binary packet */ 
 if (0x80 & pkt[0]) 
 { 
  ClientBinaryPacket (c, pkt); 
  return; 
 } 

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

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

 /* Dispatch */
 if (!strcasecmp (cmd, "posn"))
 {
  ClientPosition (c, pkt, 0);
  return;
 }
 else if (!strcasecmp (cmd, "posu"))
 {
  ClientPosition (c, pkt, 1);
  return;
 }
 else if (!strcasecmp (cmd, "fire"))
 {
  ClientFire (c, pkt);
  return;
 }
 else if (!strcasecmp (cmd, "ping"))
 {
  ClientPing (c, pkt);
  return;
 }
 else if (!strcasecmp (cmd, "chat"))
 {
  ClientChat (c, pkt);
  return;
 }
 else if (!strcasecmp (cmd, "name"))
 {
  ClientName (c, pkt);
  return;
 }
 else if (!strcasecmp (cmd, "modl"))
 {
  ClientModel (c, pkt);
  return;
 }
 else if (!strcasecmp (cmd, "vers"))
 {
  ClientVersion (c, pkt);
  return;
 }
 else
 {
  Log ("ClientPacket: Unrecongized command: %s", cmd);
  return;
 }
} 
 
void ClientBinaryPacket (int c, unsigned char *pkt) 
/* 
 *  Binary packet from client c 
 */ 
{ 
 /* Dispatch */ 
 switch (pkt[0]) 
 { 
 case PKT_POSN: 
  ClientBinaryPosition (c, &pkt[1], 0); 
  break; 
 
 case PKT_POSU: 
  ClientBinaryPosition (c, &pkt[1], 1); 
  break; 
 
 case PKT_PING: 
  ClientBinaryPing (c, &pkt[1]); 
  break; 
 
 default: 
  Log ("ClientBinaryPacket: Unrecognized packet type: 0x%x", pkt[0]); 
  break; 
 } 
}

void ClientPing (int c, char *pkt) 
/* 
 *  Handle ping packet from client 
 */ 
{ 
 double t; 
 
 /* Extract time from packet */ 
 if (1 == sscanf (pkt, "%*s %lf", &t)) 
 { 
  /* Compute ping round-trip time in milliseconds */ 
  client[c].ping = 1000.0 * (absT - t); 
 
  /* Use ping time to determine how fast we send position 
     reports to this client */ 
  if (client[c].ping <= PINGFAST) 
  { 
   client[c].posninterval = POSNINTERVALSMALL; 
  } 
  else if (client[c].ping <= PINGSLOW) 
  { 
   client[c].posninterval = POSNINTERVALMEDIUM; 
  } 
  else 
  { 
   client[c].posninterval = POSNINTERVALLARGE; 
  } 
 } 
 else 
 { 
  Log ("ClientPing: Malformed ping packet: %s", pkt); 
 } 
} 
 
void ClientBinaryPing (int c, char *pkt) 
/* 
 *  Handle binary ping packet from client 
 */ 
{ 
 double t; 
 
 /* Extract time from packet */ 
 DecodeBinaryPacket (pkt, "F", &t); 
 
 /* Compute ping round-trip time in milliseconds */ 
 client[c].ping = 1000.0 * (absT - t); 
 
 /* Use ping time to determine how fast we send position 
    reports to this client */ 
 if (client[c].ping <= PINGFAST) 
 { 
  client[c].posninterval = POSNINTERVALSMALL; 
 } 
 else if (client[c].ping <= PINGSLOW) 
 { 
  client[c].posninterval = POSNINTERVALMEDIUM; 
 } 
 else 
 { 
  client[c].posninterval = POSNINTERVALLARGE; 
 } 
} 
 
void ShowClients ()
/*
 *  Give list of clients
 */
{
 int c;
 char buf[4096], buf2[256];

 if (!am_server && !am_client) return;

 /* Init message string */
 strcpy (buf, "Active clients:\\\\");

 /* Add info for each active client */
 for (c=0; c<NCLIENTS; c++)
 {
  if (client[c].active)
  {
   if (am_server)
   { 
    if (c == server.client) 
    { 
     sprintf (buf2, "%d) %s, idle %.0lf, ping %.0lf, frags %d\\", 
      c, target[client[c].target].name, 
      client[c].timer.idle, client[c].ping, 
      client[c].frags); 
    } 
    else 
    {
     sprintf (buf2, "%d) %s [%s], idle %.0lf, ping %.0lf, frags %d\\",
      c, target[client[c].target].name,
      client[c].ip, client[c].timer.idle, client[c].ping,
      client[c].frags); 
    }
   }
   else if (am_client)
   {
    sprintf (buf2, "%d) %s, frags %d\\",
     c, target[client[c].target].name,
     client[c].frags);
   }

   strcat (buf, buf2);
  }
 }

 /* Give message */
 Mprint (buf);
}

void ClientName (int c, char *pkt)
/*
 *  Set client name
 */
{
 int cc;

 if ( (pkt[4] == 0) || (pkt[5] == 0) ) return;

 strncpy (target[client[c].target].name, &pkt[5], 16);
 target[client[c].target].name[16] = 0;

 /* Tell us */
 Cprint ("%s is here", target[client[c].target].name);

 /* Tell everyone else */
 for (cc=0; cc<NCLIENTS; cc++)
 {
  if (client[cc].active && (cc != server.client) && (c != cc) )
  {
   SendASCIIPacket (client[cc].socket, "HELO %d %s",
    c, target[client[c].target].name);
  }
 }
}

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

 Log ("ClientModel: %s", pkt);

 /* Parse out model name */
 if (1 != sscanf (pkt, "%*s %s", buf))
 {
  Log ("ClientModel: Malformed MODL packet: %s", pkt);
  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
 {
  /* Use default model */
  m = LoadModel ("light2.tri");
  target[client[c].target].model = m;
  target[client[c].target].list = model[m].list;
 }
}

void ClientPosition (int c, char *pkt, int urgent) 
/* 
 *  Client c is telling us where it is 
 */ 
{ 
 int t, cc; 
 
 /* Get index of client's target */ 
 t = client[c].target; 
 
 /* Make unhidden */ 
 target[t].hidden = 0; 
 
 /* Parse packet */ 
 if (18 != sscanf (pkt, "%*s %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", 
  &target[t].pos[0], &target[t].pos[1], &target[t].pos[2], 
  &target[t].vel[0], &target[t].vel[1], &target[t].vel[2], 
  &target[t].view[0], &target[t].view[1], &target[t].view[2], 
  &target[t].up[0], &target[t].up[1], &target[t].up[2], 
  &target[t].move_up, &target[t].move_down, 
  &target[t].move_right, &target[t].move_left, 
  &target[t].move_pitchright, &target[t].move_pitchleft)) 
 { 
  Log ("ClientPosition: Malformed POSN packet: %s", pkt); 
 } 
 
 /* Set target's right vector */ 
 Crossp (target[t].right, target[t].up, target[t].view); 
 
 /* If it's urgent, we must tell other clients */ 
 if (urgent) 
 { 
  for (cc=0; cc<NCLIENTS; cc++) 
  { 
   if (client[cc].active && 
       (cc != c) && 
       (cc != server.client) ) 
   { 
    client[cc].timer.posn[c] =  
     client[cc].posninterval; 
   } 
  } 
 } 
} 
 
void ClientBinaryPosition (int c, unsigned char *pkt, int urgent) 
/* 
 *  Client c is telling us where it is 
 */ 
{ 
 int t, cc; 
 
 /* Get index of client's target */ 
 t = client[c].target; 
 
 /* Make unhidden */ 
 target[t].hidden = 0; 
 
 /* Parse packet */ 
 DecodeBinaryPacket (pkt, "VVvvffffff", target[t].pos, target[t].vel, 
  target[t].view, target[t].up, 
  &target[t].move_up, &target[t].move_down, 
  &target[t].move_right, &target[t].move_left, 
  &target[t].move_pitchright, &target[t].move_pitchleft); 
 
 Normalize (target[t].up); 
 Normalize (target[t].view); 
 
 /* Set target's right vector */ 
 Crossp (target[t].right, target[t].up, target[t].view); 
 
 /* If it's urgent, we must tell other clients */ 
 if (urgent) 
 { 
  for (cc=0; cc<NCLIENTS; cc++) 
  { 
   if (client[cc].active && 
       (cc != c) && 
       (cc != server.client) ) 
   { 
    client[cc].timer.posn[c] =  
     client[cc].posninterval; 
   } 
  } 
 } 
} 
 
void ClientVersion (int c, char *pkt)
/*
 *  Client c is telling us what version it is running
 */
{
 char buf[64];

 /* Extract version from packet */
 if (1 != sscanf (pkt, "%*s %s", buf))
 {
  Log ("ClientVersion: Malformed VERS packet: %s", pkt);
  return;
 }

 /* Check version number */
 if (strcmp (buf, VERSION))
 {
  /* Not the same, notify client */
  SendASCIIPacket (client[c].socket,
   "MESG Server is version %s, client is version %s",
   VERSION, buf);
  Log ("ClientVersion: Incompatible client version: %s", buf);
  Cprint ("WARNING:  Client %d is using incompatible version: %s",
   c, buf);
 }
}

void ClientFire (int c, char *pkt)
/*
 *  Client c has sent a fire packet
 */
{
 int t, w;

 /* Get client's target */
 t = client[c].target;

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

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

 /* Tell everyone about it */
 NetClientFires (c, w);
}

void SendPositions (int clnt)
/*
 *  THIS IS NOT CALLED!
 *
 *  Send a position report to client c
 */
{
 int c, t1, t2;
 double v[3], d;

 t1 = client[clnt].target;

 /* Loop through clients */
 for (c=0; c<NCLIENTS; c++)
 {
  if (client[c].active)
  {
   /* Don't tell client about itself */
   if (c == clnt) continue;
   if (c == server.client) continue;

   t2 = client[c].target;

   /* Figure out how far away these clients are */
   Vsub (v, target[t2].pos, target[t1].pos);
   d = Mag2 (v);

   /* If far away, send short position packet */
   if (d > TARG_MAXRANGE2)
   {
    SendPositionShort (clnt, c);
   }
   else /* Send long packet */
   {
    SendPositionLong (clnt, c);
   }
  }
 }
}

void SendPosition (int clnt, int c)
/*
 *  Report c's position to clnt
 */
{
 int t1, t2;
 double v[3], d;

 if ((!client[clnt].active) || (!client[c].active)) return;

 t1 = client[clnt].target;
 t2 = client[c].target;

 /* Determine distance */
 Vsub (v, target[t2].pos, target[t1].pos);
 d = Mag2 (v);

 if (d > TARG_MAXRANGE2)
 {
  SendPositionShort (clnt, c);
 }
 else
 {
  SendPositionLong (clnt, c);
 }
}

void SendPositionShort (int clnt, int c)
/*
 *  Send short position of client c to client clnt
 */
{
 int t;

 t = client[c].target;

#ifdef BINARYPACKETS
 SendBinaryPacket (client[clnt].socket, "ccVV", PKT_POSS, c,
  target[t].pos, target[t].vel);
#else
 SendASCIIPacket (client[clnt].socket,
  "POSS %d %lf %lf %lf %lf %lf %lf", c,
  target[t].pos[0], target[t].pos[1], target[t].pos[2],
  target[t].vel[0], target[t].vel[1], target[t].vel[2]);
#endif
}

void SendPositionLong (int clnt, int c)
/*
 *  Send long position of client c to client clnt
 */
{
 int t;

 t = client[c].target;

#ifdef BINARYPACKETS
 SendBinaryPacket (client[clnt].socket, "ccVVvvffffff", PKT_POSL, c,
  target[t].pos, target[t].vel, target[t].view, target[t].up,
  target[t].move_up, target[t].move_down,
  target[t].move_right, target[t].move_left,
  target[t].move_pitchright, target[t].move_pitchleft);
#else
 SendASCIIPacket (client[clnt].socket,
  "POSL %d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", c,
  target[t].pos[0], target[t].pos[1], target[t].pos[2],
  target[t].vel[0], target[t].vel[1], target[t].vel[2],
  target[t].view[0], target[t].view[1], target[t].view[2],
  target[t].up[0], target[t].up[1], target[t].up[2],
  target[t].move_up, target[t].move_down,
  target[t].move_right, target[t].move_left,
  target[t].move_pitchright, target[t].move_pitchleft);
#endif
}

void SendPlanets (int c)
/*
 *  Send planet positions to client
 */
{
 int p;

 for (p=0; p<NPLANETS; p++)
 { 
#ifdef BINARYPACKETS 
  SendBinaryPacket (client[c].socket, "ccF", PKT_PLAN, p, planet[p].theta); 
#else
  SendASCIIPacket (client[c].socket, "PLAN %d %lf", p, planet[p].theta); 
#endif
 }

 SendASCIIPacket (client[c].socket, "RPLN");
}

void SendRollCall (int c)
/*
 *  Send roll call to client c
 */
{
 int cc, t;

 /* Reset timer */
 client[c].timer.rollcall = 0.0;

 for (cc=0; cc<NCLIENTS; cc++)
 {
  /* Send name if client active */
  if (client[cc].active)
  {
   t = client[cc].target;

   if (t >= 0)
   {
    /* Send name */
    SendASCIIPacket (client[c].socket,
     "NAME %d %s %d", cc, target[t].name, client[cc].frags);

    /* Send model */
    if (cc != server.client)
    {
     SendASCIIPacket (client[c].socket,
      "MODL %d %s", cc, model[target[t].model].name);
    }
    else
    {
     SendASCIIPacket (client[c].socket,
      "MODL %d %s", cc, player.model);
    }
   }
  }
  else
  { 
#ifndef BINARYPACKETS
   /* Show unused */
   SendASCIIPacket (client[c].socket,
    "VCNT %d", cc); 
#endif
  }
 } 
 
#ifdef BINARYPACKETS 
 /* Send vacant slots to client */ 
 SendBinaryVacant (c); 
#endif

 /* Send flags while we're here */
 SendFlags (c); 
 
 /* Oh and what the heck, let's send planet positions too */ 
 SendPlanets (c);
} 
 
void SendBinaryVacant (int c)
/* 
 *  Send binary vacancy report to client c 
 */ 
{ 
 int cc; 
 unsigned int w, bit, i1, i2; 
 
 w = 0; 
 bit = 1; 
 
 /* Make bitmask */ 
 for (cc=0; cc<NCLIENTS; cc++) 
 { 
  if (client[cc].active) w += bit; 
  bit = bit * 2; 
 } 
 
 /* Split into bytes */ 
 i1 = w % 256; 
 i2 = w / 256; 
 
 /* Send the packet */ 
 SendBinaryPacket (client[c].socket, "ccc", PKT_VCNT, i1, i2); 
}

void ClientChat (int c, char *pkt)
/*
 *  Client sent chat message
 */
{
 int cc;

 /* Sanity */
 if ( (pkt[4] == 0) || (pkt[5] == 0) || (strlen(pkt) > TEXTSIZE) ) return;

 /* Show the message to us */
 Cprint ("%s: %s", target[client[c].target].name, &pkt[5]);
 if (sound) PlayAudio (SOUND_COMM);

 /* Send to other clients */
 for (cc=0; cc<NCLIENTS; cc++)
 {
  if (client[cc].active && (cc != server.client) && (cc != c) )
  {
   SendASCIIPacket (client[cc].socket, "CMSG %s: %s",
    target[client[c].target].name, &pkt[5]);
  }
 }
}

void SendFlags()
/*
 *  Send gravity, etc., flags to clients
 */
{
 int c, f;

 /* Set flags */
 f = 0;
 if (gravity) f |= FLAG_GRAVITY;
 if (player.flightmodel == FLIGHT_ARCADE) f |= FLAG_FLIGHTMODEL;
 if (fullstop) f |= FLAG_FULLSTOP; 
 if (realdistances) f |= FLAG_REALDISTANCES; 
 if (orbit) f |= FLAG_ORBIT;

 /* Send em */
 for (c=0; c<NCLIENTS; c++)
 {
  if (client[c].active && (c != server.client) )
  {
   SendASCIIPacket (client[c].socket, "FLAG %d", f);
  }
 }
}

void DoDrop ()
/*
 *  Server admin wants to drop a client
 */
{
 int c;

 if (!am_server) return;

 c = atoi (text.buf);

 /* Check and drop if okay */
 if ( (c < 0) || (c >= NCLIENTS) || (!client[c].active) ||
  client[c].is_me)
 {
  Mprint ("No such client: %d", c);
  return;
 }

 DropClient (c);
 Cprint ("Dropped client %d", c);
}
