a2ccbcd76931f037166184e93ecfe9c2444fca64
[zeRace] / server.c
1 /* zeRace dedicated server */
2
3 #include <SDL_net.h>
4 #include "network.h"
5 #include "car.h"
6 #include "tracklist.h"
7
8 /* each client has his data */
9 struct _clients
10 {
11   char pseudo[MAXLINELENGTH];
12   int connected;
13   int lasttime;
14   struct _car car;
15   IPaddress address;
16   int dx,dy;
17 } clients [MAX_CLIENTS];
18
19 /* UDP stuff */
20 UDPsocket udpsock;
21 UDPpacket *packet;
22
23 struct _tracklist *tracklist;
24 SDL_Surface *fun;
25 SDL_Surface *cars[256];
26
27
28 /* return the id of a connected address */
29 int lookup(IPaddress address)
30 {
31   int i;
32   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;
33   return -1;
34 }
35
36
37 /* announce the server on internet */
38 void announce(char *name,int clients)
39 {
40   IPaddress ip;
41   TCPsocket tcpsock;
42   char *temp;
43   char *msg1=
44     "POST /zerace/announce.php HTTP/1.0\n"
45     "Host: royale.zerezo.com\n"
46     "User-Agent: zeRace dedicated server " VERSION "\n"
47     "Content-Type: application/x-www-form-urlencoded\n"
48     "Content-Length: 99999\n"
49     "\n"
50     "version=" VERSION
51     "&port=" PORT
52     "&name=";
53   char *msg2="&clients=";
54   int len,result;
55   
56   printf("announcing server... ");
57   fflush(stdout);
58
59   if (SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
60   {
61     fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
62     return;
63   }
64   
65   tcpsock=SDLNet_TCP_Open(&ip);
66   if (!tcpsock)
67   {
68     fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
69     return;
70   }
71
72   temp=(char *)malloc(strlen(msg1)+strlen(name)+strlen(msg2)+10);
73   sprintf(temp,"%s%s%s%d\n",msg1,name,msg2,clients);
74   
75   len=strlen(temp);
76   result=SDLNet_TCP_Send(tcpsock,temp,len);
77   if (result<len)
78     fprintf(stderr,"SDLNet_TCP_Send: %s\n", SDLNet_GetError());
79   else
80     printf("done\n");
81   
82   free(temp);
83   SDLNet_TCP_Close(tcpsock);
84 }
85
86
87 /* load the car sprite and rotate it for every angles */
88 void zeRace_generate_cars()
89 {
90   int j;
91   SDL_Surface *car;
92   char temp[20]="sprites/carX.png";
93   temp[11]='A';
94   /* load the car sprite */
95   car=IMG_Load(temp);
96   /* and rotate it for all available angles */
97   for (j=0;j<256;j++)
98   {
99     float x,y;
100     float tcos,tsin;
101     if ((cars[j]=SDL_CreateRGBSurface(SDL_SWSURFACE,30,30,32,RMASK,GMASK,BMASK,AMASK))==NULL)
102     {
103       fprintf(stderr,"CreateRGBSurface failed: %s\n",SDL_GetError());
104     };
105     tcos=cos(2*M_PI*j/256);
106     tsin=sin(2*M_PI*j/256);
107     for (x=0;x<cars[j]->w;x++) for (y=0;y<cars[j]->h;y++)
108     {
109       int x2,y2;
110       x2=(x-cars[j]->w/2.0)*tcos+(y-cars[j]->h/2.0)*tsin+car->w/2.0;
111       y2=(x-cars[j]->w/2.0)*tsin-(y-cars[j]->h/2.0)*tcos+car->h/2.0;
112       if (x2>0 && x2<car->w && y2>0 && y2<car->h)
113         putpixel(cars[j],x,y,getpixel(car,x2,y2));
114     }
115   }
116   SDL_FreeSurface(car);
117 }
118
119
120 /* main program */
121 int main(int argc,char *argv[])
122 {
123   int id,i,j,time=0,nb;
124   char *tmp;
125   unsigned char ip[4];
126   int nb_laps,network_speed,pub,col;
127   
128   if (argc!=6)
129   {
130     fprintf(stderr,
131       "usage: %s 'server_name' nb_laps network_speed (public|private) (col|nocol)\n"
132       "  server_name : the name of the server\n"
133       "  nb_laps : the number of laps to complete for each race\n"
134       "  network_speed : frequency of network messages (1 for fast network, 10 for slow network...)\n"
135       "  private : this server will not be listed in the 'internet games'\n"
136       "  col : the server will compute collisions between cars\n"
137       ,argv[0]
138     );
139     exit(1);
140   }
141   printf("server_name : %s\n",argv[1]);
142   printf("nb_laps : %d\n",nb_laps=atoi(argv[2]));
143   printf("network_speed : %d\n",network_speed=atoi(argv[3]));
144   printf("public : %d\n",pub=strcmp("private",argv[4]));
145   printf("col : %d\n",col=strcmp("nocol",argv[5]));
146   
147   if (!zeRace_get_tracks(&tracklist)) exit(1);
148
149   if (SDL_Init(0)==-1)
150   {
151     fprintf(stderr,"SDL_Init: %s\n",SDL_GetError());
152     exit(1);
153   };
154   if (SDLNet_Init()==-1)
155   {
156     fprintf(stderr,"SDLNet_Init: %s\n",SDLNet_GetError());
157     exit(2);
158   }
159   
160   udpsock=SDLNet_UDP_Open(atoi(PORT));
161   if (!udpsock)
162   {
163     fprintf(stderr,"SDLNet_UDP_Open: %s\n",SDLNet_GetError());
164     exit(2);
165   }
166
167   packet=SDLNet_AllocPacket(1024);
168   if (!packet)
169   {
170     fprintf(stderr,"SDLNet_AllocPacket: %s\n",SDLNet_GetError());
171     exit(2);
172   }
173   
174   zeRace_generate_cars();
175   
176   for (;;)
177   {
178     /* announce the server on internet if wanted */
179     nb=0;
180     for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected) nb++;
181     if (pub) announce(argv[1],nb);
182     
183     /* load new track */
184     printf("loading track \"%s\"\n",tracklist->name);
185     fun=IMG_Load(tracklist->function);
186     
187     /* reset clients variables */
188     for (i=0;i<MAX_CLIENTS;i++)
189     {
190       clients[i].lasttime=time;
191       clients[i].car.x=tracklist->x;
192       clients[i].car.y=tracklist->y;
193       clients[i].car.w=30;
194       clients[i].car.h=30;
195       clients[i].car.angle=tracklist->a*2*M_PI/360;
196       clients[i].car.speed=0;
197       clients[i].car.lap=0;
198       clients[i].car.lastcheck=0;
199     }
200     
201     /* tell the clients */
202     tmp=packet->data;
203     strcpy(tmp,"track");
204     tmp+=strlen(tmp)+1;
205     *tmp++=1; /* startup countdown */
206     strcpy(tmp,tracklist->name);
207     tmp+=strlen(tmp)+1;
208     SDLNet_Write32(time,tmp);
209     tmp+=4;
210     SDLNet_Write32(network_speed,tmp);
211     tmp+=4;
212     packet->len=(void *)tmp-(void *)packet->data;
213     for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected)
214     {
215       packet->address=clients[i].address;
216       SDLNet_UDP_Send(udpsock,-1,packet);
217     }
218     
219     /* wait for everybody startup */
220     for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected) break;
221     /* 5000ms for countdown, and 500ms for loading time... */
222     if (i!=MAX_CLIENTS) SDL_Delay(5500);
223     
224     printf("go\n");
225     
226     /* main race loop */
227     for (;;)
228     {
229       int finish=0;
230       
231       /* read all available packets */
232       while (SDLNet_UDP_Recv(udpsock,packet))
233       {
234         /* return the local id based on the address */
235         id=lookup(packet->address);
236         
237         /* look for type of message */
238         tmp=packet->data;
239         
240         /* new connection ? */
241         if (strcmp(tmp,"connect")==0)
242         {
243           /* allready connected ? */
244           if (id!=-1 && clients[id].connected)
245           {
246             /* should not happen */
247             printf("client %d allready connected\n",id);
248           }
249           else
250           for (i=0;i<MAX_CLIENTS;i++) if (!clients[i].connected)
251           {
252             clients[i].address=packet->address;
253             clients[i].connected=1;
254             clients[i].lasttime=time;
255             memset(&clients[i].car,0,sizeof(struct _car));
256             tmp+=strlen(tmp)+1;
257             strcpy(clients[i].pseudo,tmp);
258             tmp+=strlen(tmp)+1;
259             clients[i].car.color=SDLNet_Read16(tmp);
260             clients[i].car.x=tracklist->x;
261             clients[i].car.y=tracklist->y;
262             clients[i].car.w=30;
263             clients[i].car.h=30;
264             clients[i].car.angle=tracklist->a*2*M_PI/360;
265             clients[i].car.speed=0;
266             clients[i].car.lap=0;
267             clients[i].car.lastcheck=0;
268             memcpy(ip,&packet->address.host,4);
269             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);
270             tmp=packet->data;
271             strcpy(tmp,"track");
272             tmp+=strlen(tmp)+1;
273             *tmp++=0; /* no startup countdown */
274             strcpy(tmp,tracklist->name);
275             tmp+=strlen(tmp)+1;
276             SDLNet_Write32(time,tmp);
277             tmp+=4;
278             SDLNet_Write32(network_speed,tmp);
279             tmp+=4;
280             packet->len=(void *)tmp-(void *)packet->data;
281             SDLNet_UDP_Send(udpsock,-1,packet);
282             break;
283           }
284         }
285         else
286         
287         /* disconnection ? */
288         if (strcmp(tmp,"disconnect")==0 && id!=-1)
289         {
290           clients[id].connected=0;
291           printf("client %d disconnected\n",id);
292         }
293         else
294         
295         /* keys message ? */
296         if (strcmp(tmp,"keys")==0)
297         {
298           if (id==-1 || !clients[id].connected)
299           {
300             /* should not happen */
301             printf("discarded \"keys\" message\n");
302           }
303           else
304           {
305             int temp,x,y,x2,y2;
306             tmp+=strlen(tmp)+1;
307             temp=SDLNet_Read32(tmp);
308             tmp+=4;
309             x=SDLNet_Read32(tmp);
310             tmp+=4;
311             y=SDLNet_Read32(tmp);
312             tmp+=4;
313             if (clients[id].lasttime<=temp)
314             {
315               tmp+=temp-clients[id].lasttime;
316               while (*tmp)
317               {
318                 move_car(&clients[id].car,*tmp-'A',fun);
319                 clients[id].lasttime++;
320                 tmp++;
321               }
322               /* check that the server and the client are still synchronized */
323               x2=clients[id].car.x;
324               y2=clients[id].car.y;
325               if (x!=x2 || y!=y2)
326               {
327                 int round;
328                 /* this should not happen with a perfect network protocol :) */
329                 printf("client %d unsync at %d\n",id,time);
330                 /* instead of dropping the client, we send him a "dumb" collision to resync him */
331                 tmp=packet->data;
332                 strcpy(tmp,"collision");
333                 tmp+=strlen(tmp)+1;
334                 SDLNet_Write32(time,tmp);
335                 tmp+=4;
336                 round=(clients[id].car.x+100)*65536;
337                 clients[id].car.x=(float)round/65536-100;
338                 SDLNet_Write32(round,tmp);
339                 tmp+=4;
340                 round=(clients[id].car.y+100)*65536;
341                 clients[id].car.y=(float)round/65536-100;
342                 SDLNet_Write32(round,tmp);
343                 tmp+=4;
344                 round=(clients[id].car.speed+100)*65536;
345                 clients[id].car.speed=(float)round/65536-100;
346                 SDLNet_Write32(round,tmp);
347                 tmp+=4;
348                 round=(clients[id].car.angle+100)*65536;
349                 clients[id].car.angle=(float)round/65536-100;
350                 SDLNet_Write32(round,tmp);
351                 tmp+=4;
352                 packet->len=(void *)tmp-(void *)packet->data;
353                 packet->address=clients[id].address;
354                 SDLNet_UDP_Send(udpsock,-1,packet);
355                 clients[id].lasttime=time;
356               }
357             }
358           }
359         }
360       }
361       
362       /* check for timeouts */
363       for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected && clients[i].lasttime+MAX_LAG<time)
364       {
365         printf("client %d timeout at %d\n",i,time);
366         packet->address=clients[i].address;
367         strcpy(packet->data,"disconnected");
368         SDLNet_UDP_Send(udpsock,-1,packet);
369         clients[i].connected=0;
370       }
371       
372       /* should we check for collisions ? */
373       if (col)
374       {
375         /* check for collisions */
376         for (i=0;i<MAX_CLIENTS;i++) clients[i].dx=clients[i].dy=0;
377         for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected)
378           for (j=i+1;j<MAX_CLIENTS;j++) if (clients[j].connected)
379             if (i!=j)
380               if (abs(clients[i].car.x-clients[j].car.x)<30 && abs(clients[i].car.y-clients[j].car.y)<30)
381               {
382                 int x1,y1,x2,y2;
383                 for (x1=0;x1<30;x1++)
384                   for (y1=0;y1<30;y1++)
385                     if (getpixel(cars[(unsigned char)(256*clients[i].car.angle/2.0/M_PI)%256],x1,y1)!=0)
386                     {
387                       x2=x1+clients[i].car.x-clients[j].car.x;
388                       y2=y1+clients[i].car.y-clients[j].car.y;
389                       if (x2>0 && x2<30 && y2>0 && y2<30)
390                       {
391                         if (getpixel(cars[(unsigned char)(256*clients[j].car.angle/2.0/M_PI)%256],x2,y2)!=0)
392                         {
393                           if (x1<30/2) { clients[i].dx++; clients[j].dx--; } else { clients[i].dx--; clients[j].dx++; }
394                           if (y1<30/2) { clients[i].dy++; clients[j].dy--; } else { clients[i].dy--; clients[j].dy++; }
395                         }
396                       }
397                     }
398               }
399         
400         /* now compute the collisions */
401         for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected) if (clients[i].dx || clients[i].dy)
402         {
403           Uint32 c;
404           Uint8 g,t;
405           int dx=clients[i].dx;
406           int dy=clients[i].dy;
407           /* do not jump to much */
408           while (abs(dx)>5) dx/=2;
409           while (abs(dy)>5) dy/=2;
410           /* get the pixel color under the center of car in the function map */
411           c=getpixel(fun,clients[i].car.x+dx,clients[i].car.y+dy);
412           /* green layer (road quality) */
413           SDL_GetRGB(c,fun->format,&t,&g,&t);
414           /* if the destination is not a wall and not outside of the track */
415           if (g!=0 && clients[i].car.x>cars[0]->w && clients[i].car.x<fun->w-cars[0]->w && clients[i].car.y>cars[0]->h && clients[i].car.y<fun->h-cars[0]->h)
416           {
417             int round;
418             clients[i].car.x+=dx;
419             clients[i].car.y+=dy;
420             tmp=packet->data;
421             strcpy(tmp,"collision");
422             tmp+=strlen(tmp)+1;
423             SDLNet_Write32(time,tmp);
424             tmp+=4;
425             round=(clients[i].car.x+100)*65536;
426             clients[i].car.x=(float)round/65536-100;
427             SDLNet_Write32(round,tmp);
428             tmp+=4;
429             round=(clients[i].car.y+100)*65536;
430             clients[i].car.y=(float)round/65536-100;
431             SDLNet_Write32(round,tmp);
432             tmp+=4;
433             round=(clients[i].car.speed+100)*65536;
434             clients[i].car.speed=(float)round/65536-100;
435             SDLNet_Write32(round,tmp);
436             tmp+=4;
437             round=(clients[i].car.angle+100)*65536;
438             clients[i].car.angle=(float)round/65536-100;
439             SDLNet_Write32(round,tmp);
440             tmp+=4;
441             packet->len=(void *)tmp-(void *)packet->data;
442             packet->address=clients[i].address;
443             SDLNet_UDP_Send(udpsock,-1,packet);
444             clients[i].lasttime=time;
445           }
446         }
447       }
448       
449       /* send update to clients */
450       if (time%network_speed==0) for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected)
451       {
452         tmp=packet->data;
453         strcpy(tmp,"positions");
454         tmp+=strlen(tmp)+1;
455         SDLNet_Write32(time,tmp);
456         tmp+=4; /* for server time */
457         tmp+=4; /* for client time */
458         tmp+=2; /* for number of cars */
459         nb=0;
460         for (j=0;j<MAX_CLIENTS;j++) if (j!=i && clients[j].connected)
461         {
462           SDLNet_Write16(clients[j].car.x,tmp);
463           tmp+=2;
464           SDLNet_Write16(clients[j].car.y,tmp);
465           tmp+=2;
466           SDLNet_Write16(clients[j].car.angle*1000,tmp);
467           tmp+=2;
468           SDLNet_Write16(clients[j].car.color,tmp);
469           tmp+=2;
470           SDLNet_Write16(clients[j].car.lights_brake,tmp);
471           tmp+=2;
472           SDLNet_Write16(clients[j].car.lights_backwards,tmp);
473           tmp+=2;
474           SDLNet_Write16(clients[j].car.lights_warning,tmp);
475           tmp+=2;
476           nb++;
477         }
478         SDLNet_Write16(nb,packet->data+strlen("positions")+1+4+4);
479         SDLNet_Write32(clients[i].lasttime,packet->data+strlen("positions")+1+4);
480         packet->len=(void *)tmp-(void *)packet->data;
481         packet->address=clients[i].address;
482         SDLNet_UDP_Send(udpsock,-1,packet);
483       }
484       
485       /* did someone finish the track ? */
486       for (i=0;i<MAX_CLIENTS;i++) { if (clients[i].car.lapflag==1) printf("client %d : %d laps\n",i,clients[i].car.lap); clients[i].car.lapflag=0; }
487       for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected && clients[i].car.lap==nb_laps) finish=1;
488       if (finish) break;
489         
490       /* wait like clients */
491       SDL_Delay(7);
492       time++;
493     }
494     
495     /* send the top 10 screen */
496     tmp=packet->data;
497     strcpy(tmp,"finish");
498     tmp+=strlen(tmp)+1;
499     tmp+=2; /* space for number */
500     nb=0;
501     for (i=0;i<10;i++)
502     {
503       int best_sc=-1,best_id;
504       for (j=0;j<MAX_CLIENTS;j++) if (clients[j].connected && clients[j].car.lap*32+clients[j].car.lastcheck>best_sc)
505       {
506         best_sc=clients[j].car.lap*32+clients[j].car.lastcheck;
507         best_id=j;
508       }
509       if (best_sc!=-1)
510       {
511         sprintf(tmp,"%s : %d",clients[best_id].pseudo,best_sc);
512         tmp+=strlen(tmp)+1;
513         SDLNet_Write16(clients[best_id].car.color,tmp);
514         tmp+=2;
515         clients[best_id].car.lap=-1;
516         nb++;
517         printf("top %d : %s - %d\n",nb,clients[best_id].pseudo,best_sc);
518       }
519     }
520     SDLNet_Write16(nb,packet->data+strlen("finish")+1);
521     packet->len=(void *)tmp-(void *)packet->data;
522     for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected)
523     {
524       packet->address=clients[i].address;
525       SDLNet_UDP_Send(udpsock,-1,packet);
526     }
527     SDL_Delay(5000);
528     
529     SDL_FreeSurface(fun);
530     tracklist=tracklist->next;
531   }
532
533   return 0;
534 }