version 0.3
[zeRace] / server.c
diff --git a/server.c b/server.c
new file mode 100644 (file)
index 0000000..c6da730
--- /dev/null
+++ b/server.c
@@ -0,0 +1,366 @@
+/* zeRace dedicated server */
+
+#include <SDL_net.h>
+#include "network.h"
+#include "car.h"
+#include "tracklist.h"
+
+/* each client has his data */
+struct _clients
+{
+  char pseudo[MAXLINELENGTH];
+  int connected;
+  int lasttime;
+  struct _car car;
+  IPaddress address;
+} clients [MAX_CLIENTS];
+
+/* UDP stuff */
+UDPsocket udpsock;
+UDPpacket *packet;
+
+struct _tracklist *tracklist;
+SDL_Surface *fun;
+
+
+/* return the id of a connected address */
+int lookup(IPaddress address)
+{
+  int i;
+  for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected && clients[i].address.host==address.host && clients[i].address.port==address.port) return i;
+  return -1;
+}
+
+
+/* announce the server on internet */
+void announce(char *name,int clients)
+{
+  IPaddress ip;
+  TCPsocket tcpsock;
+  char *temp;
+  char *msg1=
+    "POST /zerace/announce.php HTTP/1.0\n"
+    "Host: royale.zerezo.com\n"
+    "User-Agent: zeRace dedicated server " VERSION "\n"
+    "Content-Type: application/x-www-form-urlencoded\n"
+    "Content-Length: 99999\n"
+    "\n"
+    "version=" VERSION
+    "&port=" PORT
+    "&name=";
+  char *msg2="&clients=";
+  int len,result;
+  
+  printf("announcing server... ");
+  fflush(stdout);
+
+  if(SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
+  {
+    fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
+    return;
+  }
+  
+  tcpsock=SDLNet_TCP_Open(&ip);
+  if(!tcpsock)
+  {
+    fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
+    return;
+  }
+
+  temp=(char *)malloc(strlen(msg1)+strlen(name)+strlen(msg2)+10);
+  sprintf(temp,"%s%s%s%d\n",msg1,name,msg2,clients);
+  
+  len=strlen(temp);
+  result=SDLNet_TCP_Send(tcpsock,temp,len);
+  if(result<len)
+    fprintf(stderr,"SDLNet_TCP_Send: %s\n", SDLNet_GetError());
+  else
+    printf("done\n");
+
+  SDLNet_TCP_Close(tcpsock);
+}
+
+
+/* main program */
+int main(int argc,char *argv[])
+{
+  int id,i,j,time=0,nb;
+  char *tmp;
+  unsigned char ip[4];
+  int nb_laps,network_speed,pub;
+  
+  if (argc!=5)
+  {
+    fprintf(stderr,
+      "usage: %s 'server_name' nb_laps network_speed (public|private)\n"
+      "  server_name : the name of the server\n"
+      "  nb_laps : the number of laps to complete for each race\n"
+      "  network_speed : frequency of network messages (1 for fast network, 10 for slow network...)\n"
+      "  private : this server will not be listed in the 'internet games'\n"
+      ,argv[0]
+    );
+    exit(1);
+  }
+  printf("server_name : %s\n",argv[1]);
+  printf("nb_laps : %d\n",nb_laps=atoi(argv[2]));
+  printf("network_speed : %d\n",network_speed=atoi(argv[3]));
+  printf("public : %d\n",pub=strcmp("private",argv[4]));
+  
+  if (!zeRace_get_tracks(&tracklist)) exit(1);
+
+  if (SDL_Init(0)==-1)
+  {
+    fprintf(stderr,"SDL_Init: %s\n",SDL_GetError());
+    exit(1);
+  };
+  if (SDLNet_Init()==-1)
+  {
+    fprintf(stderr,"SDLNet_Init: %s\n",SDLNet_GetError());
+    exit(2);
+  }
+  
+  udpsock=SDLNet_UDP_Open(atoi(PORT));
+  if (!udpsock)
+  {
+    fprintf(stderr,"SDLNet_UDP_Open: %s\n",SDLNet_GetError());
+    exit(2);
+  }
+
+  packet=SDLNet_AllocPacket(1024);
+  if (!packet)
+  {
+    fprintf(stderr,"SDLNet_AllocPacket: %s\n",SDLNet_GetError());
+    exit(2);
+  }
+
+  for (;;)
+  {
+    /* announce the server on internet if wanted */
+    nb=0;
+    for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected) nb++;
+    if (pub) announce(argv[1],nb);
+    
+    /* load new track */
+    printf("loading track \"%s\"\n",tracklist->name);
+    fun=IMG_Load(tracklist->function);
+    
+    /* reset clients variables */
+    for (i=0;i<MAX_CLIENTS;i++)
+    {
+      clients[i].lasttime=time;
+      clients[i].car.x=tracklist->x;
+      clients[i].car.y=tracklist->y;
+      clients[i].car.w=30;
+      clients[i].car.h=30;
+      clients[i].car.angle=tracklist->a*2*M_PI/360;
+      clients[i].car.speed=0;
+      clients[i].car.lap=0;
+      clients[i].car.lastcheck=0;
+    }
+    
+    /* tell the clients */
+    tmp=packet->data;
+    strcpy(tmp,"track");
+    tmp+=strlen(tmp)+1;
+    *tmp++=1; /* startup countdown */
+    strcpy(tmp,tracklist->name);
+    tmp+=strlen(tmp)+1;
+    memcpy(tmp,&time,sizeof(int));
+    tmp+=sizeof(int);
+    memcpy(tmp,&network_speed,sizeof(int));
+    tmp+=sizeof(int);
+    packet->len=(void *)tmp-(void *)packet->data;
+    for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected)
+    {
+      packet->address=clients[i].address;
+      SDLNet_UDP_Send(udpsock,-1,packet);
+    }
+    
+    /* wait for everybody startup */
+    for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected) break;
+    if (i!=MAX_CLIENTS) SDL_Delay(5000);
+    
+    printf("go\n");
+    
+    /* main race loop */
+    for (;;)
+    {
+      int finish=0;
+      
+      /* read all available packets */
+      while (SDLNet_UDP_Recv(udpsock,packet))
+      {
+        /* return the local id based on the address */
+        id=lookup(packet->address);
+        
+        /* look for type of message */
+        tmp=packet->data;
+        /*printf("%s\n",tmp);*/
+        
+        /* new connection ? */
+        if (strcmp(tmp,"connect")==0)
+        {
+          /* allready connected ? */
+          if (id!=-1 && clients[id].connected)
+          {
+            /* should not happen */
+            printf("client %d allready connected\n",id);
+          }
+          else
+          for (i=0;i<MAX_CLIENTS;i++) if (!clients[i].connected)
+          {
+            clients[i].address=packet->address;
+            clients[i].connected=1;
+            clients[i].lasttime=time;
+            memset(&clients[i].car,0,sizeof(struct _car));
+            tmp+=strlen(tmp)+1;
+            strcpy(clients[i].pseudo,tmp);
+            tmp+=strlen(tmp)+1;
+            memcpy(&clients[i].car.color,tmp,sizeof(int));
+            clients[i].car.x=tracklist->x;
+            clients[i].car.y=tracklist->y;
+            clients[i].car.w=30;
+            clients[i].car.h=30;
+            clients[i].car.angle=tracklist->a*2*M_PI/360;
+            clients[i].car.speed=0;
+            clients[i].car.lap=0;
+            clients[i].car.lastcheck=0;
+            memcpy(ip,&packet->address.host,4);
+            printf("client %d connected at %d : %d.%d.%d.%d:%d (pseudo : %s, color : %d)\n",i,time,ip[0],ip[1],ip[2],ip[3],packet->address.port,clients[i].pseudo,clients[i].car.color);
+            tmp=packet->data;
+            strcpy(tmp,"track");
+            tmp+=strlen(tmp)+1;
+            *tmp++=0; /* no startup countdown */
+            strcpy(tmp,tracklist->name);
+            tmp+=strlen(tmp)+1;
+            memcpy(tmp,&time,sizeof(int));
+            tmp+=sizeof(int);
+            memcpy(tmp,&network_speed,sizeof(int));
+            tmp+=sizeof(int);
+            packet->len=(void *)tmp-(void *)packet->data;
+            SDLNet_UDP_Send(udpsock,-1,packet);
+            break;
+          }
+        }
+        else
+        
+        /* disconnection ? */
+        if (strcmp(tmp,"disconnect")==0 && id!=-1)
+        {
+          clients[id].connected=0;
+          printf("client %d disconnected\n",id);
+        }
+        else
+        
+        /* keys message ? */
+        if (strcmp(tmp,"keys")==0)
+        {
+          if (id==-1 || !clients[id].connected)
+          {
+            /* should not happen */
+            printf("discarded \"keys\" message\n");
+          }
+          else
+          {
+            int temp;
+            tmp+=strlen(tmp)+1;
+            memcpy(&temp,tmp,sizeof(int));
+            tmp+=sizeof(int);
+            if (clients[id].lasttime==temp)
+            {
+              /* printf("servertime = %d lasttime = %d temp = %d strlen(tmp) = %d\n",time,clients[id].lasttime,temp,strlen(tmp)); */
+              /*printf("keys = %s\n",tmp);*/
+              while (*tmp)
+              {
+                move_car(&clients[id].car,*tmp-'A',fun);
+                /*printf("%d = %f\n",id,clients[id].car.angle);*/
+                clients[id].lasttime++;
+                tmp++;
+              }
+            }
+          }
+        }
+      }
+      
+      /* check for timeouts */
+      for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected && clients[i].lasttime+MAX_LAG<time)
+      {
+        printf("client %d timeout at %d\n",i,time);
+        packet->address=clients[i].address;
+        strcpy(packet->data,"disconnected");
+        SDLNet_UDP_Send(udpsock,-1,packet);
+        clients[i].connected=0;
+      }
+      
+      /* send update to clients */
+      if (time%network_speed==0) for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected)
+      {
+        tmp=packet->data;
+        strcpy(tmp,"positions");
+        tmp+=strlen(tmp)+1;
+        memcpy(tmp,&time,sizeof(int));
+        tmp+=sizeof(int); /* for server time */
+        tmp+=sizeof(int); /* for client time */
+        tmp+=sizeof(int); /* for number of cars */
+        nb=0;
+        for (j=0;j<MAX_CLIENTS;j++) if (j!=i && clients[j].connected)
+        {
+          memcpy(tmp,&clients[j].car,sizeof(struct _car));
+          tmp+=sizeof(struct _car);
+          nb++;
+        }
+        memcpy(packet->data+strlen("positions")+1+sizeof(int)+sizeof(int),&nb,sizeof(int));
+        packet->len=(void *)tmp-(void *)packet->data;
+        memcpy(packet->data+strlen("positions")+1+sizeof(int),&clients[i].lasttime,sizeof(int));
+        packet->address=clients[i].address;
+        SDLNet_UDP_Send(udpsock,-1,packet);
+      }
+      
+      /* did someone finish the track ? */
+      for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected && clients[i].car.lap==nb_laps) finish=1;
+      if (finish) break;
+        
+      /* wait like clients */
+      SDL_Delay(7);
+      time++;
+    }
+    
+    /* send the top 10 screen */
+    tmp=packet->data;
+    strcpy(tmp,"finish");
+    tmp+=strlen(tmp)+1;
+    tmp+=sizeof(int); /* space for number */
+    nb=0;
+    for (i=0;i<10;i++)
+    {
+      int best_sc=-1,best_id;
+      for (j=0;j<MAX_CLIENTS;j++) if (clients[j].connected && clients[j].car.lap*32+clients[j].car.lastcheck>best_sc)
+      {
+        best_sc=clients[j].car.lap*32+clients[j].car.lastcheck;
+        best_id=j;
+      }
+      if (best_sc!=-1)
+      {
+        sprintf(tmp,"%s : %d",clients[best_id].pseudo,best_sc);
+        tmp+=strlen(tmp)+1;
+        memcpy(tmp,&clients[best_id].car.color,sizeof(int));
+        tmp+=sizeof(int);
+        clients[best_id].car.lap=-1;
+        nb++;
+        printf("top %d : %s - %d\n",nb,clients[best_id].pseudo,best_sc);
+      }
+    }
+    memcpy(packet->data+strlen("finish")+1,&nb,sizeof(int));
+    packet->len=(void *)tmp-(void *)packet->data;
+    for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected)
+    {
+      packet->address=clients[i].address;
+      SDLNet_UDP_Send(udpsock,-1,packet);
+    }
+    SDL_Delay(5000);
+  
+    tracklist=tracklist->next;
+  }
+
+  return 0;
+}