version 0.4
[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 } clients [MAX_CLIENTS];
17
18 /* UDP stuff */
19 UDPsocket udpsock;
20 UDPpacket *packet;
21
22 struct _tracklist *tracklist;
23 SDL_Surface *fun;
24
25
26 /* return the id of a connected address */
27 int lookup(IPaddress address)
28 {
29   int i;
30   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;
31   return -1;
32 }
33
34
35 /* announce the server on internet */
36 void announce(char *name,int clients)
37 {
38   IPaddress ip;
39   TCPsocket tcpsock;
40   char *temp;
41   char *msg1=
42     "POST /zerace/announce.php HTTP/1.0\n"
43     "Host: royale.zerezo.com\n"
44     "User-Agent: zeRace dedicated server " VERSION "\n"
45     "Content-Type: application/x-www-form-urlencoded\n"
46     "Content-Length: 99999\n"
47     "\n"
48     "version=" VERSION
49     "&port=" PORT
50     "&name=";
51   char *msg2="&clients=";
52   int len,result;
53   
54   printf("announcing server... ");
55   fflush(stdout);
56
57   if (SDLNet_ResolveHost(&ip,"royale.zerezo.com",80)==-1)
58   {
59     fprintf(stderr,"SDLNet_ResolveHost: %s\n",SDLNet_GetError());
60     return;
61   }
62   
63   tcpsock=SDLNet_TCP_Open(&ip);
64   if (!tcpsock)
65   {
66     fprintf(stderr,"SDLNet_TCP_Open: %s\n",SDLNet_GetError());
67     return;
68   }
69
70   temp=(char *)malloc(strlen(msg1)+strlen(name)+strlen(msg2)+10);
71   sprintf(temp,"%s%s%s%d\n",msg1,name,msg2,clients);
72   
73   len=strlen(temp);
74   result=SDLNet_TCP_Send(tcpsock,temp,len);
75   if (result<len)
76     fprintf(stderr,"SDLNet_TCP_Send: %s\n", SDLNet_GetError());
77   else
78     printf("done\n");
79   
80   free(temp);
81   SDLNet_TCP_Close(tcpsock);
82 }
83
84
85 /* main program */
86 int main(int argc,char *argv[])
87 {
88   int id,i,j,time=0,nb;
89   char *tmp;
90   unsigned char ip[4];
91   int nb_laps,network_speed,pub;
92   
93   if (argc!=5)
94   {
95     fprintf(stderr,
96       "usage: %s 'server_name' nb_laps network_speed (public|private)\n"
97       "  server_name : the name of the server\n"
98       "  nb_laps : the number of laps to complete for each race\n"
99       "  network_speed : frequency of network messages (1 for fast network, 10 for slow network...)\n"
100       "  private : this server will not be listed in the 'internet games'\n"
101       ,argv[0]
102     );
103     exit(1);
104   }
105   printf("server_name : %s\n",argv[1]);
106   printf("nb_laps : %d\n",nb_laps=atoi(argv[2]));
107   printf("network_speed : %d\n",network_speed=atoi(argv[3]));
108   printf("public : %d\n",pub=strcmp("private",argv[4]));
109   
110   if (!zeRace_get_tracks(&tracklist)) exit(1);
111
112   if (SDL_Init(0)==-1)
113   {
114     fprintf(stderr,"SDL_Init: %s\n",SDL_GetError());
115     exit(1);
116   };
117   if (SDLNet_Init()==-1)
118   {
119     fprintf(stderr,"SDLNet_Init: %s\n",SDLNet_GetError());
120     exit(2);
121   }
122   
123   udpsock=SDLNet_UDP_Open(atoi(PORT));
124   if (!udpsock)
125   {
126     fprintf(stderr,"SDLNet_UDP_Open: %s\n",SDLNet_GetError());
127     exit(2);
128   }
129
130   packet=SDLNet_AllocPacket(1024);
131   if (!packet)
132   {
133     fprintf(stderr,"SDLNet_AllocPacket: %s\n",SDLNet_GetError());
134     exit(2);
135   }
136
137   for (;;)
138   {
139     /* announce the server on internet if wanted */
140     nb=0;
141     for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected) nb++;
142     if (pub) announce(argv[1],nb);
143     
144     /* load new track */
145     printf("loading track \"%s\"\n",tracklist->name);
146     fun=IMG_Load(tracklist->function);
147     
148     /* reset clients variables */
149     for (i=0;i<MAX_CLIENTS;i++)
150     {
151       clients[i].lasttime=time;
152       clients[i].car.x=tracklist->x;
153       clients[i].car.y=tracklist->y;
154       clients[i].car.w=30;
155       clients[i].car.h=30;
156       clients[i].car.angle=tracklist->a*2*M_PI/360;
157       clients[i].car.speed=0;
158       clients[i].car.lap=0;
159       clients[i].car.lastcheck=0;
160     }
161     
162     /* tell the clients */
163     tmp=packet->data;
164     strcpy(tmp,"track");
165     tmp+=strlen(tmp)+1;
166     *tmp++=1; /* startup countdown */
167     strcpy(tmp,tracklist->name);
168     tmp+=strlen(tmp)+1;
169     SDLNet_Write32(time,tmp);
170     tmp+=4;
171     SDLNet_Write32(network_speed,tmp);
172     tmp+=4;
173     packet->len=(void *)tmp-(void *)packet->data;
174     for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected)
175     {
176       packet->address=clients[i].address;
177       SDLNet_UDP_Send(udpsock,-1,packet);
178     }
179     
180     /* wait for everybody startup */
181     for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected) break;
182     /* 5000ms for countdown, and 500ms for loading time... */
183     if (i!=MAX_CLIENTS) SDL_Delay(5500);
184     
185     printf("go\n");
186     
187     /* main race loop */
188     for (;;)
189     {
190       int finish=0;
191       
192       /* read all available packets */
193       while (SDLNet_UDP_Recv(udpsock,packet))
194       {
195         /* return the local id based on the address */
196         id=lookup(packet->address);
197         
198         /* look for type of message */
199         tmp=packet->data;
200         /*printf("%s\n",tmp);*/
201         
202         /* new connection ? */
203         if (strcmp(tmp,"connect")==0)
204         {
205           /* allready connected ? */
206           if (id!=-1 && clients[id].connected)
207           {
208             /* should not happen */
209             printf("client %d allready connected\n",id);
210           }
211           else
212           for (i=0;i<MAX_CLIENTS;i++) if (!clients[i].connected)
213           {
214             clients[i].address=packet->address;
215             clients[i].connected=1;
216             clients[i].lasttime=time;
217             memset(&clients[i].car,0,sizeof(struct _car));
218             tmp+=strlen(tmp)+1;
219             strcpy(clients[i].pseudo,tmp);
220             tmp+=strlen(tmp)+1;
221             clients[i].car.color=SDLNet_Read16(tmp);
222             clients[i].car.x=tracklist->x;
223             clients[i].car.y=tracklist->y;
224             clients[i].car.w=30;
225             clients[i].car.h=30;
226             clients[i].car.angle=tracklist->a*2*M_PI/360;
227             clients[i].car.speed=0;
228             clients[i].car.lap=0;
229             clients[i].car.lastcheck=0;
230             memcpy(ip,&packet->address.host,4);
231             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);
232             tmp=packet->data;
233             strcpy(tmp,"track");
234             tmp+=strlen(tmp)+1;
235             *tmp++=0; /* no startup countdown */
236             strcpy(tmp,tracklist->name);
237             tmp+=strlen(tmp)+1;
238             SDLNet_Write32(time,tmp);
239             tmp+=4;
240             SDLNet_Write32(network_speed,tmp);
241             tmp+=4;
242             packet->len=(void *)tmp-(void *)packet->data;
243             SDLNet_UDP_Send(udpsock,-1,packet);
244             break;
245           }
246         }
247         else
248         
249         /* disconnection ? */
250         if (strcmp(tmp,"disconnect")==0 && id!=-1)
251         {
252           clients[id].connected=0;
253           printf("client %d disconnected\n",id);
254         }
255         else
256         
257         /* keys message ? */
258         if (strcmp(tmp,"keys")==0)
259         {
260           if (id==-1 || !clients[id].connected)
261           {
262             /* should not happen */
263             printf("discarded \"keys\" message\n");
264           }
265           else
266           {
267             int temp;
268             tmp+=strlen(tmp)+1;
269             temp=SDLNet_Read32(tmp);
270             tmp+=4;
271             if (clients[id].lasttime<=temp)
272             {
273               tmp+=temp-clients[id].lasttime;
274               /* printf("servertime = %d lasttime = %d temp = %d strlen(tmp) = %d\n",time,clients[id].lasttime,temp,strlen(tmp)); */
275               /*printf("keys = %s\n",tmp);*/
276               while (*tmp)
277               {
278                 move_car(&clients[id].car,*tmp-'A',fun);
279                 /*printf("%d = %f\n",id,clients[id].car.angle);*/
280                 clients[id].lasttime++;
281                 tmp++;
282               }
283             }
284           }
285         }
286       }
287       
288       /* check for timeouts */
289       for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected && clients[i].lasttime+MAX_LAG<time)
290       {
291         printf("client %d timeout at %d\n",i,time);
292         packet->address=clients[i].address;
293         strcpy(packet->data,"disconnected");
294         SDLNet_UDP_Send(udpsock,-1,packet);
295         clients[i].connected=0;
296       }
297       
298       /* send update to clients */
299       if (time%network_speed==0) for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected)
300       {
301         tmp=packet->data;
302         strcpy(tmp,"positions");
303         tmp+=strlen(tmp)+1;
304         SDLNet_Write32(time,tmp);
305         tmp+=4; /* for server time */
306         tmp+=4; /* for client time */
307         tmp+=2; /* for number of cars */
308         nb=0;
309         for (j=0;j<MAX_CLIENTS;j++) if (j!=i && clients[j].connected)
310         {
311           SDLNet_Write16(clients[j].car.x,tmp);
312           tmp+=2;
313           SDLNet_Write16(clients[j].car.y,tmp);
314           tmp+=2;
315           SDLNet_Write16(clients[j].car.angle*1000,tmp);
316           tmp+=2;
317           SDLNet_Write16(clients[j].car.color,tmp);
318           tmp+=2;
319           nb++;
320         }
321         SDLNet_Write16(nb,packet->data+strlen("positions")+1+4+4);
322         SDLNet_Write32(clients[i].lasttime,packet->data+strlen("positions")+1+4);
323         packet->len=(void *)tmp-(void *)packet->data;
324         packet->address=clients[i].address;
325         SDLNet_UDP_Send(udpsock,-1,packet);
326       }
327       
328       /* did someone finish the track ? */
329       for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected && clients[i].car.lap==nb_laps) finish=1;
330       if (finish) break;
331         
332       /* wait like clients */
333       SDL_Delay(7);
334       time++;
335     }
336     
337     /* send the top 10 screen */
338     tmp=packet->data;
339     strcpy(tmp,"finish");
340     tmp+=strlen(tmp)+1;
341     tmp+=2; /* space for number */
342     nb=0;
343     for (i=0;i<10;i++)
344     {
345       int best_sc=-1,best_id;
346       for (j=0;j<MAX_CLIENTS;j++) if (clients[j].connected && clients[j].car.lap*32+clients[j].car.lastcheck>best_sc)
347       {
348         best_sc=clients[j].car.lap*32+clients[j].car.lastcheck;
349         best_id=j;
350       }
351       if (best_sc!=-1)
352       {
353         sprintf(tmp,"%s : %d",clients[best_id].pseudo,best_sc);
354         tmp+=strlen(tmp)+1;
355         SDLNet_Write16(clients[best_id].car.color,tmp);
356         tmp+=2;
357         clients[best_id].car.lap=-1;
358         nb++;
359         printf("top %d : %s - %d\n",nb,clients[best_id].pseudo,best_sc);
360       }
361     }
362     SDLNet_Write16(nb,packet->data+strlen("finish")+1);
363     packet->len=(void *)tmp-(void *)packet->data;
364     for (i=0;i<MAX_CLIENTS;i++) if (clients[i].connected)
365     {
366       packet->address=clients[i].address;
367       SDLNet_UDP_Send(udpsock,-1,packet);
368     }
369     SDL_Delay(5000);
370     
371     SDL_FreeSurface(fun);
372     tracklist=tracklist->next;
373   }
374
375   return 0;
376 }