
/***********************************************************
*                      K O U L E S                         *
*----------------------------------------------------------*
*  C1995 JAHUSOFT                                          *
*        Jan Hubicka                                       *
*        Dukelskych Bojovniku 1944                         *
*        390 03 Tabor                                      *
*        Czech Republic                                    *
*        Phone: 0041-361-32613                             *
*        eMail: hubicka@limax.paru.cas.cz                  *
*----------------------------------------------------------*
*   Copyright(c)1995,1996 by Jan Hubicka.See README for    *
*                    licence details.                      *
*----------------------------------------------------------*
*  client.c network client routines                        *
***********************************************************/
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "socklib.h"
#include "net.h"
#include "client.h"
#include "koules.h"
#ifdef NETSUPPORT
#define error printf
#ifdef SOUND
extern int      ssound;
#endif
extern void     process_keys ();
extern int      fadedout;
extern int      nplayers;
extern int      explosion (int x, int y, int type, int letter, int n);
extern int      creators_points (int radius, int x, int y, int color);
extern void     setnplayers (int n);
extern int      read_objects (unsigned char *buffer, int n);
extern void     effect (int nos);
extern void     cmenu2 (unsigned char *message, int size);
extern void     client_loop2 (int draw);
char            servername[256];
static int      socket_c;
static int      sock;
static int      nodata;
static int      quited = 0;
static int      port;
static int      falied;
static int      allframes, dispframes, realdisplayed;
static int      rframe;
static long     rpos;
static long     rcount = 1;
static long     rrcount = 0;
static long     timeout = 1;
static int      nodisplayco = 0;
static char     hostname[256];
#define BUFFERSIZE 65536
static unsigned char buffer[BUFFERSIZE];
struct c_table
  {
    void            (*func) (unsigned char *message, int size);
  };
static unsigned char obuffer[BUFFERSIZE];
static unsigned char ibuffer[BUFFERSIZE];
static unsigned char rbuffer[BUFFERSIZE];
static int      rposition;
static int      obufferpos;
#define WAITTIME 5
void
                csendbegining (unsigned char *message, int size);

void
csetrrcount (long scount)
{
  int             size, pos = 0, count;
  if (scount <= rrcount)
    return;			/*Old repeated message */
  do
    {
      GETLONG ((rbuffer + pos), count);
      GETSHORT ((rbuffer + pos) + 4, size);
      pos += size;
    }
  while (count < scount);
  memmove (rbuffer, rbuffer + pos, rposition - pos);
  rposition -= pos;
  rrcount = scount;
}

void
cflushreliable ()
{
  if (rposition)
    {
      PUTHEAD (SRELIABLE) memcpy (buffer + HEADSIZE, rbuffer, rposition);
      csendbegining (buffer, rposition + HEADSIZE);
    }
}

void
csendbuffer ()
{
  int             result;
  if (timeout)
    {
      timeout--;
      if (!timeout)
	{
	  cflushreliable ();
	  timeout = WAITTIME;
	}
    }
  if (!obufferpos)
    return;
  PUTLONG ((obuffer), rpos);
  falied = 0;
  GetSocketError (sock);
  /*if (!(rand()%2)) *//*Emulate internet :)))))) */
  if ((result = DgramSend (sock, servername, port,
			   (char *) obuffer, obufferpos + 4)) <= 0)
    {
      if (errno == EAGAIN || errno == EWOULDBLOCK || result >= 0)
	{
	  GetSocketError (sock);
	  falied = 1;
	  if (timeout == WAITTIME)
	    timeout = 1;
	}
      else
	{
	  perror ("Can't send message to server\n");
	  SocketClose (socket_c);
	  SocketClose (sock);
	  exit(-1);
	}
    }
  obufferpos = 0;
}
void
csendbegining (unsigned char *message, int size)	/*send at more reliable beggining of packet */
{
  if (obufferpos + size + 6 > BUFFERSIZE)
    csendbuffer ();

  memmove (obuffer + size + 6, obuffer + 4, obufferpos);
  PUTSHORT ((obuffer + 4), size + 2);
  memcpy (obuffer + 6, message, size);
  obufferpos += size + 2;
}
void
csendreliable (unsigned char *message, int size)
{
  if (size == 0)
    return;
  if (rposition + size + 6 > BUFFERSIZE)
    {
      printf ("Connection to server broken too long! Reliable buffer owerfllow\n"
	      "Game aborted\n");
      exit (-1);
    }
  PUTLONG ((rbuffer + rposition), rcount);
  rcount++;
  PUTSHORT ((rbuffer + rposition + 4), (size + 6));
  memcpy (rbuffer + rposition + 6, message, size);
  rposition += size + 6;
  timeout = 1;
}

void
csend (unsigned char *message, int size)
{
  if (obufferpos + size + 2 + 4 > BUFFERSIZE)
    csendbuffer ();
  PUTSHORT ((obuffer + obufferpos + 4), size + 2);
  memcpy (obuffer + obufferpos + 6, message, size);
  obufferpos += size + 2;
}
void
register_players ()
{
  PUTHEAD (SREGISTER);
  PUTCHAR ((buffer + 1), nplayers);
  csendreliable (buffer, HEADSIZE + 1);
  gamemode = WAIT;
}
void
start_game ()
{
  PUTHEAD (SSTART);
  csendreliable (buffer, HEADSIZE);
  gamemode = WAIT;
#ifdef SOUND
  sound = ssound;
#endif
}
void
CQuit (char *text)
{
  static int      n;
  if (quited)
    return;
  quited = 1;
  PUTHEAD (SQUIT);
  strcpy ((char *) (buffer + HEADSIZE), text);
  printf ("Terminating game.\nReason:%s\n", text);
  for (n = 0; n < 5; n++)
    {				/*repeat quiting messages to make sure that
				   all clients received it */
      csend (buffer, strlen (text) + HEADSIZE + 1);
      csendbuffer ();
    }
  SocketClose (sock);
  quited = 1;
  /*exit(0); */
  uninitialize ();
  exit (0);
}

void
GetPos (void)
{
#if 0
  int             x = /*(dispframes + realdisplayed + 1) / 2 */ dispframes / 4;
#else
  int             x = realdisplayed + 5;
#endif
  PUTHEAD (SPOS);
  PUTCHAR (buffer + HEADSIZE, x);
  csendreliable (buffer, HEADSIZE + 1);
}
void
SendKeys (int player, int c)
{
  PUTHEAD (SKEY);
  PUTCHAR (buffer + HEADSIZE, c);
  PUTCHAR (buffer + HEADSIZE + 1, player);
  csendreliable (buffer, HEADSIZE + 2);
}
void
SendRotation (int player, int c)
{
  PUTHEAD (SROT);
  PUTCHAR (buffer + HEADSIZE, c);
  PUTCHAR (buffer + HEADSIZE + 1, player);
  csendreliable (buffer, HEADSIZE + 2);
}
void
SendMouse (int player, int x, int y, int buttons)
{
  PUTHEAD (SMOUSE);
  PUTSHORT (buffer + HEADSIZE, x);
  PUTSHORT (buffer + HEADSIZE + 2, y);
  PUTCHAR (buffer + HEADSIZE + 4, buttons);
  PUTCHAR (buffer + HEADSIZE + 5, player);
  csendreliable (buffer, HEADSIZE + 6);
}
void
SendJoystick (int player, int x, int y, int buttons)
{
  PUTHEAD (SJOYSTICK);
  PUTSHORT (buffer + HEADSIZE, x);
  PUTSHORT (buffer + HEADSIZE + 2, y);
  PUTCHAR (buffer + HEADSIZE + 4, buttons);
  PUTCHAR (buffer + HEADSIZE + 5, player);
  csendreliable (buffer, HEADSIZE + 6);
}
void
SendControls ()
{
  static struct control c[5];
  int             i;
  for (i = 0; i < 5; i++)
    {
      if (control[i] && memcmp ((char *) &controls[i], (char *) &c[i], sizeof (controls[i])))
	{
	  memcpy ((char *) &c[i], (char *) &controls[i], sizeof (controls[i]));
	  switch (c[i].type)
	    {
	    case C_KEYBOARD:
	      SendKeys (i, c[i].mask);
	      break;
	    case C_RKEYBOARD:
	      SendRotation (i, c[i].mask);
	      break;
	    case C_MOUSE:
	      SendMouse (i, c[i].mx, c[i].my, c[i].mask);
	      break;
	    case C_JOYSTICK1:
	      SendMouse (i, c[i].jx, c[i].jy, c[i].mask);
	      break;
	    }
	}
    }
}
void
Ready (void)
{
  PUTHEAD (SREADY);
  csendreliable (buffer, HEADSIZE);
}
static void
print (unsigned char *message, int size)
{
  printf ("Server's message:%s\n", (char *) message);
}
static void
quit (unsigned char *message, int size)
{
  printf ("Uninitializing:%s\n", (char *) message);
  SocketClose (sock);
  quited = 1;
  exit (0);
}
static void
cexplosion (unsigned char *message, int size)
{
  int             x, y, type, letter, n;
  GETSHORT (message, x);
  GETSHORT ((message + 2), y);
  GETCHAR ((message + 4), type);
  GETCHAR ((message + 5), letter);
  GETCHAR ((message + 6), n);
  explosion (x, y, type, letter, n);
}
static void
coutro (unsigned char *message, int size)
{
  outro2 ();
  CQuit ("End of game\nBye");
}
static void
ccreatord_points (unsigned char *message, int size)
{
  int             x, y, radius, color;
  GETSHORT (message, x);
  GETSHORT ((message + 2), y);
  GETSHORT ((message + 4), radius);
  GETCHAR ((message + 6), color);
  creators_points (radius, x, y, color);
}
static void
csound (unsigned char *message, int size)
{
  int             s;
  GETCHAR (message, s);
  Effect (s, next);
}

static void
cplayers (unsigned char *message, int size)
{
  int             s;
  GETCHAR (message, s);
  setnplayers (s);
}
static void
creg (unsigned char *message, int size)
{
  int             s;
  for (s = 0; s < MAXROCKETS; s++)
    if (message[s])
      control[s] = 1;
    else
      control[s] = 0;
}
static void
cpos (unsigned char *message, int size)
{
  read_objects ((unsigned char *) message, nodata);
  rframe = 1;
  nodata = 0;
}
static void
clevel (unsigned char *message, int size)
{
  int             nos;
  GETCHAR (message, lastlevel);
  GETCHAR (message + 1, nos);
  GETCHAR (message + 2, gameplan);
  gamemode = GAME;
#ifdef SOUND
  sound = ssound;
#endif
  gameplan_init ();
  effect (nos);
  nodisplayco = 10;
  Ready ();
}
static void
cgame (unsigned char *message, int size)
{
  gamemode = GAME;
  Ready ();
}
void            creliable (unsigned char *message, int size);
static struct c_table ctable[] =
{
  {print},
  {quit},
  {cpos},
  {cexplosion},
  {ccreatord_points},
  {csound},
  {cplayers},
  {creg},
  {cmenu2},
  {clevel},
  {cgame},
  {creliable},
  {coutro}
};
void
creliable (unsigned char *message, int size)
{
  int             pos = 0;
  while (pos < size)
    {
      long            rpos1;
      int             size;
      GETLONG ((message + pos), rpos1);
      if (rpos1 > rpos + 1)
	{
	  printf ("Fatal error in transfering reliable messages!\n"
		  "Game may become VERY unstable..please contact author \n");
	  return;
	}
      GETSHORT ((message + pos + 4), size);
      if (rpos1 <= rpos)
	{
	  pos += size;
	  continue;
	}
      rpos = rpos1;
      ctable[message[pos + 6]].func ((message + pos + 7), size - 7);
      pos += size;
    }
}
void
process_message (void)
{
  int             bytes, pos = 4;
  long            scount;
  if (!SocketReadable (sock))
    return;
  GetSocketError (sock);
  if ((bytes = DgramReceiveAny (sock, (char *) ibuffer, BUFFERSIZE)) <= 0)
    {
      if (errno == EAGAIN || errno == EWOULDBLOCK || bytes >= 0)
	{
	  GetSocketError (sock);
	  return;
	}
      CQuit ("Can't receive server's message\n");
      SocketClose (sock);
    }
  GETLONG (ibuffer, scount);
  csetrrcount (scount);
  while (pos + 2 < bytes)
    {
      int             size;
      GETSHORT (ibuffer + pos, size);
      if (pos + size <= bytes)
	{
	  ctable[ibuffer[pos + 2]].func ((ibuffer + pos + 3), size - 3);
	}
      pos += size;
    }

}
#define VERSION 0
int
init_client (void)
{
  long            bytes, pport;
  int             gx, gy;
  int             retries = 0;
  GetLocalHostName (hostname, sizeof hostname);
/*Open server socket */
  printf ("Opening server socket\n");
  if ((socket_c = CreateDgramSocket (0)) == -1)
    {
      error ("Could not create connection socket");
      SocketClose (socket_c);
      exit (-1);
    }
/*Create connection message */
  strcpy ((char *) buffer, "Koules");
  PUTCHAR (buffer + 7, VERSION);
  PUTSHORT (buffer + 8, GAMEWIDTH);
  PUTSHORT (buffer + 10, GAMEHEIGHT);
  PUTLONG (buffer + 12, (long) getpid ());
  printf ("Asking server at %s port %i\n", servername, initport);
#if 0
  if (DgramConnect (socket_c, servername, initport) == -1)
    {
      perror ("Connection error");
      error ("Can't connect to server %s on port %d", servername, port);
      close (socket_c);
      return -1;
    }
#endif
again:
  GetSocketError (socket_c);
  errno = 0;
  do
    {
      if (retries)
	sleep (1);
      retries++;
      if (retries > 100)
	printf (" Connection timet out\n"), exit (-1);
      if (errno)
	{
	  perror ("Can't receive reply");
	  exit (-1);
	}
      if (DgramSend (socket_c, servername, initport,
		     (char *) buffer, INITPACKETSIZE) == -1)
	{
	  SocketClose (socket_c);
	  error ("Can't send message to server\n");
	  exit (-1);
	}
      if (errno)
	{
	  perror ("Can't send message to server");
	  /*exit (-1); */
	  GetSocketError (socket_c);
	  errno = 0;
	}
      SetTimeout (1, 10 * 1000);
    }
  while (!(SocketReadable (socket_c)));
  bytes = DgramReceiveAny (socket_c, (char *) buffer, BUFFERSIZE);
  if (bytes != REPLYSIZE)
    goto again;
  SocketClose (socket_c);
  GETLONG (buffer, port);
  if (port == 0)
    {
      printf ("Server refused me! (too many players or incompatible screen size)\n"
	      "Try -W server's option is you are using 320x200 clients..\n");
      exit (-1);
    }
  GETSHORT ((buffer + 4), gx);
  GETSHORT ((buffer + 6), gy);

  printf ("YYYYAAAAAAA Server replied.\n"
	  "Opening port %i\n", port);
  if ((sock = CreateDgramSocket (0)) == -1)
    {
      error ("Can't create datagram socket");
      return -1;
    }
  SetTimeout (1, 10 * 1000);

  /*if (DgramConnect (sock, servername, port) == -1)
     {
     error ("Can't connect to server %s on port %d", servername, port);
     close (sock);
     return -1;
     } */
  if (SetSocketNonBlocking (sock, 1) == -1)
    {
      error ("Can't make socket non-blocking");
      return -1;
    }
  printf ("Sending initialization message\n");
  pport = GetPortNum (sock);
  if (gx / DIV > GAMEWIDTH || gy / DIV > GAMEHEIGHT)
    {
      printf ("Server's gamepool too large\n"
	      "Try -W on server's command line\n");

      PUTLONG (buffer, 0);
      if (DgramSend (sock, servername, port,
		     (char *) buffer, 4) == -1)
	close (sock);
      exit (-1);
    }
  else
    GAMEWIDTH = gx, GAMEHEIGHT = gy;
  PUTHEAD (SINIT);
  PUTLONG (buffer + HEADSIZE, pport);
  if (SetSocketReceiveBufferSize (sock, NETBUFFER) == -1)
    {
      error ("Can't set socket buffer size");
      return -1;
    }
  if (SetSocketSendBufferSize (sock, NETBUFFER) == -1)
    {
      error ("Can't set socket buffer size");
      return -1;
    }
  GetSocketError (sock);
  csendreliable (buffer, 4 + HEADSIZE);
  csendbuffer ();
  csendbuffer ();
  csendbuffer ();
  printf ("Starting game...\n");
  return (0);
}
void
client_loop (void)
{
  long            VfTime = 0;
  long            VendSleep = 0;
  struct timeval  VlastClk;
  struct timeval  VnewClk;
  int             wait = 0;
#if 0
  int             drawed = 0;
  int             display = 1;
#endif
  client = 1;

  load_rc ();
  init_menu ();

  Ready ();
#ifdef XSUPPORT
  nopause = 1;
#endif
  gamemode = MENU;
  gettimeofday (&VlastClk, NULL);
  gettimeofday (&VnewClk, NULL);
  VendSleep = VlastClk.tv_usec;
  VfTime = 1000000 / 25;
  dispframes = 25 * 4;
  realdisplayed = 25;
  GetPos ();
  dispframes = 0;
  realdisplayed = 0;
  while (1)
    {
      if (nodisplayco)
	nodisplayco--;
      SetTimeout (0, 0);
      nodata++;
      while (SocketReadable (sock))
	process_message ();
      gettimeofday (&VnewClk, NULL);
      if (VnewClk.tv_usec < VendSleep)
	VendSleep -= 1000000;
      wait = (VfTime - VnewClk.tv_usec + VendSleep);
      allframes++;
      if (fadedout)
	rframe = 1;
      process_keys ();
      if ((wait > 0 || tbreak))
	{			/*display=0; */
	  csendbuffer ();
	  realdisplayed++;
	  client_loop2 (rframe && (!nodisplayco));
	  rframe = 0;
	}
      else
	{
	  csendbuffer ();
	  client_loop2 (0);
	}
      if (allframes == 25 || tbreak)
	{
	  /*printf ("Displayed frames:%i really:%i\n", dispframes / 4, realdisplayed); */
	  if (tbreak)
	    realdisplayed = 25;
	  GetPos ();
	  allframes = 0;
	  dispframes = 0;
	  realdisplayed = 0;
	}
      SendControls ();
#ifndef HAVEUSLEEP
      do
	{			/*my emulation of usleep isn't reiable on HP-UX machines
				   when sockets are comming :( */
#endif
	  gettimeofday (&VnewClk, NULL);
	  if (VnewClk.tv_usec < VendSleep)
	    VendSleep -= 1000000;
	  wait = (VfTime - VnewClk.tv_usec + VendSleep);
	  if (tbreak)
	    wait = VfTime;
	  if (wait > 0)
	    usleep (wait);
#ifndef HAVEUSLEEP
	}
      while (wait > 10 && !tbreak);
#endif
      VendSleep = VnewClk.tv_usec + wait;
      gettimeofday (&VlastClk, NULL);
      if (tbreak)
	VendSleep = VlastClk.tv_usec;
      tbreak = 0;
    }
}
#endif
