]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/gtrack.cpp
set proper invalid value for game packet type
[taylor/freespace2.git] / src / network / gtrack.cpp
1 /*
2  * Copyright (C) Volition, Inc. 2005.  All rights reserved.
3  * 
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on the 
6  * source.
7  *
8 */
9
10
11 //Game Tracker client code
12 /*
13
14   InitGameTracker(int game_type); //D3 or Freespace
15          
16   //Call periodically so we can send our update, and make sure it is received
17   IdleGameTracker();
18   
19   StartTrackerGame(void *buffer)  //Call with a freespace_net_game_data or d3_net_game_data structure
20   //Call with a freespace_net_game_data or d3_net_game_data structure
21   //Updates our memory so when it is time, we send the latest game data
22   UpdateGameData(gamedata);
23
24
25   //Call to signify end of game
26
27   RequestGameList();//Sends a GNT_GAMELIST_REQ packet to the server.
28
29   game_list * GetGameList();//returns a pointer to a game_list struct
30
31 */
32
33
34 #ifdef PLAT_UNIX
35 #include <arpa/inet.h>
36 #include <netinet/in.h>
37 #include <errno.h>
38 #include <netdb.h>
39 #endif
40
41 #include "pstypes.h"
42 #include "timer.h"
43 #include "multi.h"
44 #include "multi_pxo.h"
45 #include "gtrack.h"
46 #include "ptrack.h"
47
48
49 // check structs for size compatibility
50 SDL_COMPILE_TIME_ASSERT(game_packet_header, sizeof(game_packet_header) == 529);
51 SDL_COMPILE_TIME_ASSERT(freespace2_net_game_data, sizeof(freespace2_net_game_data) == 120);
52 SDL_COMPILE_TIME_ASSERT(game_list, sizeof(game_list) == 384);
53 SDL_COMPILE_TIME_ASSERT(filter_game_list_struct, sizeof(filter_game_list_struct) == 40);
54
55 //Variables
56 // SOCKET gamesock;
57 struct sockaddr_in      gtrackaddr;
58
59 game_list GameBuffer[MAX_GAME_BUFFERS];
60 int GameType;//d3 or fs
61
62 unsigned int LastTrackerUpdate;
63 unsigned int LastSentToTracker;
64 unsigned int TrackerAckdUs;
65 unsigned int TrackerGameIsRunning;
66
67 game_packet_header TrackerGameData;
68 game_packet_header GameListReq;
69 game_packet_header TrackAckPacket;
70 game_packet_header GameOverPacket;
71
72 #ifdef MAKE_FS1
73 freespace_net_game_data         *FreeSpaceTrackerGameData;
74 #else
75 freespace2_net_game_data        *FreeSpace2TrackerGameData;
76 #endif
77
78 //Start New 7-9-98
79 unsigned int LastGameOverPacket;
80 unsigned int FirstGameOverPacket;
81
82 int SendingGameOver;
83 //End New 7-9-98
84
85
86 static int SerializeGamePacket(const game_packet_header *gph, ubyte *data)
87 {
88         int packet_size = 0;
89
90         PXO_ADD_UINT(gph->len);
91         PXO_ADD_DATA(gph->game_type);
92         PXO_ADD_DATA(gph->junk); // not used, basically just padding for compatibility
93         PXO_ADD_INT(gph->type);
94         PXO_ADD_UINT(gph->sig);
95
96         switch (gph->type) {
97                 // these have no other data
98                 case GNT_CLIENT_ACK:
99                 case GNT_GAMEOVER:
100                         break;
101
102                 // this one may or may not have extra data
103                 case GNT_GAMELIST_REQ: {
104                         if (gph->len > GAME_HEADER_ONLY_SIZE) {
105                                 SDL_assert(gph->len == (GAME_HEADER_ONLY_SIZE+sizeof(filter_game_list_struct)));
106
107                                 filter_game_list_struct *filter = (filter_game_list_struct *)&gph->data;
108
109                                 PXO_ADD_INT(filter->rank);
110                                 PXO_ADD_DATA(filter->channel);
111                         }
112
113                         break;
114                 }
115
116                 case GNT_GAMEUPDATE: {
117                         freespace2_net_game_data *game_data = (freespace2_net_game_data *)&gph->data;
118
119                         PXO_ADD_DATA(game_data->game_name);
120                         PXO_ADD_INT(game_data->difficulty);
121                         PXO_ADD_INT(game_data->type);
122                         PXO_ADD_INT(game_data->state);
123                         PXO_ADD_INT(game_data->max_players);
124                         PXO_ADD_INT(game_data->current_num_players);
125                         PXO_ADD_DATA(game_data->mission_name);
126                         PXO_ADD_DATA(game_data->channel);
127
128                         break;
129                 }
130
131                 case GNT_GAME_COUNT_REQ: {
132                         char channel[CHANNEL_LEN];
133
134                         memcpy(channel, gph->data, sizeof(channel));
135
136                         PXO_ADD_DATA(channel);
137
138                         break;
139                 }
140
141                 // we shouldn't be sending any other packet types
142                 default:
143                         Int3();
144                         break;
145         }
146
147         SDL_assert(packet_size >= (int)GAME_HEADER_ONLY_SIZE);
148         SDL_assert(packet_size == (int)gph->len);
149
150         return packet_size;
151 }
152
153 static void DeserializeGamePacket(const ubyte *data, const int data_size, game_packet_header *gph)
154 {
155         int offset = 0;
156         int i;
157
158         memset(gph, 0, sizeof(game_packet_header));
159
160         // make sure we received a complete base packet
161         if (data_size < (int)GAME_HEADER_ONLY_SIZE) {
162                 gph->len = 0;
163                 gph->type = 255;        // invalid = 0xff
164
165                 return;
166         }
167
168         PXO_GET_UINT(gph->len);
169         PXO_GET_DATA(gph->game_type);
170         PXO_GET_DATA(gph->junk); // not used, basically just padding for compatibility
171         PXO_GET_INT(gph->type);
172         PXO_GET_UINT(gph->sig);
173
174         // sanity check data size to make sure we reveived all of the expected packet
175         // (not exactly sure what -1 is for, but that's how it is later)
176         if ((int)gph->len-1 > data_size) {
177                 gph->len = 0;
178                 gph->type = -1;
179
180                 return;
181         }
182
183         switch (gph->type) {
184                 case GNT_SERVER_ACK:
185                         break;
186
187                 case GNT_GAMELIST_DATA: {
188                         game_list *games = (game_list *)&gph->data;
189
190                         PXO_GET_DATA(games->game_type);
191
192                         for (i = 0; i < MAX_GAME_LISTS_PER_PACKET; i++) {
193                                 PXO_GET_DATA(games->game_name[i]);
194                         }
195
196                         PXO_GET_DATA(games->pad);       // padded bytes for alignment
197
198                         for (i = 0; i < MAX_GAME_LISTS_PER_PACKET; i++) {
199                                 PXO_GET_UINT(games->game_server[i]);
200                         }
201
202                         for (i = 0; i < MAX_GAME_LISTS_PER_PACKET; i++) {
203                                 PXO_GET_USHORT(games->port[i]);
204                         }
205
206                         break;
207                 }
208
209                 case GNT_GAME_COUNT_DATA: {
210                         int n_users = 0;
211                         char channel[512];
212
213                         PXO_GET_INT(n_users);
214
215                         SDL_strlcpy(channel, (char *)(data+offset), SDL_arraysize(channel));
216                         offset += strlen(channel);
217
218                         memcpy(gph->data, &n_users, sizeof(int));
219                         memcpy(gph->data+sizeof(int), channel, strlen(channel));
220
221                         break;
222                 }
223
224                 default:
225                         break;
226         }
227
228         //SDL_assert(offset == data_size);
229 }
230
231
232 int InitGameTrackerClient(int gametype)
233 {
234         struct sockaddr_in sockaddr;
235         in_addr_t iaddr;
236
237         GameType = gametype;
238         LastTrackerUpdate = 0;
239         switch(gametype)
240         {
241         case GT_FREESPACE:
242 #ifndef MAKE_FS1
243                 Int3();
244                 return 0;
245 #else
246                 TrackerGameData.len = GAME_HEADER_ONLY_SIZE+sizeof(freespace_net_game_data);
247 #endif
248                 break;
249
250         case GT_FREESPACE2:
251 #ifdef MAKE_FS1
252                 Int3();
253                 return 0;
254 #else
255                 TrackerGameData.len = GAME_HEADER_ONLY_SIZE+sizeof(freespace2_net_game_data);
256 #endif
257                 break;
258
259         default:
260                 Int3();
261                 return 0;
262         }
263         TrackerGameData.game_type = (unsigned char)gametype;    //1==freespace (GT_FREESPACE), 2==D3, 3==tuberacer, etc.
264         TrackerGameData.type = GNT_GAMEUPDATE;  //Used to specify what to do ie. Add a new net game (GNT_GAMESTARTED), remove a net game (game over), etc.
265
266 #ifdef MAKE_FS1
267         FreeSpaceTrackerGameData = (freespace_net_game_data *)&TrackerGameData.data;
268 #else
269         FreeSpace2TrackerGameData = (freespace2_net_game_data *)&TrackerGameData.data;
270 #endif
271         
272         GameListReq.game_type = (unsigned char)gametype;
273         GameListReq.type = GNT_GAMELIST_REQ;
274         GameListReq.len = GAME_HEADER_ONLY_SIZE;
275
276         TrackAckPacket.game_type = (unsigned char)gametype;
277         TrackAckPacket.len = GAME_HEADER_ONLY_SIZE;
278         TrackAckPacket.type = GNT_CLIENT_ACK;
279
280         GameOverPacket.game_type = (unsigned char)gametype;
281         GameOverPacket.len = GAME_HEADER_ONLY_SIZE;
282         GameOverPacket.type = GNT_GAMEOVER;
283
284         // gamesock = socket(AF_INET,SOCK_DGRAM,0);
285         
286         /*
287         if ( gamesock == INVALID_SOCKET )
288         {
289                 printf("Unable to open a socket.\n");
290                 return 0;
291         }
292         */
293         
294         memset( &sockaddr, 0, sizeof(struct sockaddr_in) );
295         sockaddr.sin_family = AF_INET; 
296         sockaddr.sin_addr.s_addr = INADDR_ANY; 
297         sockaddr.sin_port = 0;//htons(GAMEPORT);
298         
299         /*
300         if (SOCKET_ERROR==bind(gamesock, (struct sockaddr*)&sockaddr, sizeof (sockaddr)))
301         {       
302                 printf("Unable to bind a socket.\n");
303                 printf("WSAGetLastError() returned %d.\n",WSAGetLastError());
304                 return 0;
305         }
306         */
307                 
308         iaddr = inet_addr ( Multi_options_g.game_tracker_ip ); 
309         if ( iaddr == INADDR_NONE ) {
310                 // first try and resolve by name
311                 struct hostent *he;
312                 he = gethostbyname( Multi_options_g.game_tracker_ip );
313                 if(!he)
314                 {               
315                         return 0;
316                         /*
317                         // try and resolve by address           
318                         unsigned int n_order = inet_addr(Multi_game_tracker_ip_address);
319                         he = gethostbyaddr((char*)&n_order,4,PF_INET);          
320
321                         if(!he){
322                                 return 0;
323                         }
324                         */
325                 }
326
327                 iaddr = ((in_addr *)(he->h_addr))->s_addr;
328         }
329
330         // This would be a good place to resolve the IP based on a domain name
331         gtrackaddr.sin_addr.s_addr = iaddr;
332         gtrackaddr.sin_family = AF_INET; 
333         gtrackaddr.sin_port = htons( GAMEPORT );
334
335         //Start New 7-9-98
336         SendingGameOver = 0;
337         //End New 7-9-98
338
339         return 1;
340 }
341
342 void IdleGameTracker()
343 {
344         fd_set read_fds;                   
345         struct timeval timeout;
346         ubyte packet_data[sizeof(game_packet_header)];
347         int packet_length = 0;
348
349         PSNET_TOP_LAYER_PROCESS();
350         
351         timeout.tv_sec=0;            
352         timeout.tv_usec=0;
353         if((TrackerGameIsRunning) && ((timer_get_seconds()-LastTrackerUpdate)>TRACKER_UPDATE_INTERVAL) && !SendingGameOver)
354         {
355                 //Time to update the tracker again
356                 packet_length = SerializeGamePacket(&TrackerGameData, packet_data);
357                 SENDTO(Unreliable_socket, (char *)&packet_data, packet_length, 0, (struct sockaddr *)&gtrackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
358                 TrackerAckdUs = 0;
359                 LastTrackerUpdate = timer_get_seconds();
360         }
361         else if((TrackerGameIsRunning)&&(!TrackerAckdUs)&&((timer_get_milliseconds()-LastSentToTracker)>TRACKER_RESEND_TIME))
362         {
363                 //We still haven't been acked by the last packet and it's time to resend.
364                 packet_length = SerializeGamePacket(&TrackerGameData, packet_data);
365                 SENDTO(Unreliable_socket, (char *)&packet_data, packet_length, 0, (struct sockaddr *)&gtrackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
366                 TrackerAckdUs = 0;
367                 LastTrackerUpdate = timer_get_seconds();
368                 LastSentToTracker = timer_get_milliseconds();
369         }
370
371         //Start New 7-9-98
372         if(SendingGameOver){
373                 if((timer_get_milliseconds()-LastGameOverPacket)>TRACKER_RESEND_TIME){
374                         //resend
375                         packet_length = SerializeGamePacket(&GameOverPacket, packet_data);
376                         LastGameOverPacket = timer_get_milliseconds();
377                         SENDTO(Unreliable_socket, (char *)&packet_data, packet_length, 0, (struct sockaddr *)&gtrackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
378                 } 
379                 /*
380                 else if((timer_get_milliseconds()-FirstGameOverPacket)>NET_ACK_TIMEOUT) {
381                         //Giving up, it timed out.
382                         SendingGameOver = 2;
383                 }
384                 */
385         }
386         //End New 7-9-98
387
388         //Check for incoming
389                 
390         FD_ZERO(&read_fds);
391         FD_SET(Unreliable_socket, &read_fds);    
392
393 #ifndef PLAT_UNIX
394         if(SELECT(0,&read_fds,NULL,NULL,&timeout, PSNET_TYPE_GAME_TRACKER))
395 #else
396         if(SELECT(Unreliable_socket+1,&read_fds,NULL,NULL,&timeout, PSNET_TYPE_GAME_TRACKER))
397 #endif
398         {
399                 unsigned int bytesin;
400                 int addrsize;
401                 struct sockaddr_in fromaddr;
402
403                 game_packet_header inpacket;
404                 addrsize = sizeof(struct sockaddr_in);
405
406                 bytesin = RECVFROM(Unreliable_socket, (char *)&packet_data, sizeof(game_packet_header), 0, (struct sockaddr *)&fromaddr, &addrsize, PSNET_TYPE_GAME_TRACKER);
407                 DeserializeGamePacket(packet_data, bytesin, &inpacket);
408                 if((int)bytesin==-1)
409                 {
410                         int wserr=WSAGetLastError();
411                         mprintf(("RECVFROM() failure. WSAGetLastError() returned %d\n",wserr));
412                         
413                 }
414
415                 // subtract one from the header
416                 inpacket.len--;
417
418                 //Check to make sure the packets ok
419                 if(bytesin==inpacket.len)
420                 {
421                         switch(inpacket.type)
422                         {
423                         case GNT_SERVER_ACK:
424                                 //The server got our packet so we can stop sending now
425                                 TrackerAckdUs = 1;                              
426                                 
427                                 // 7/13/98 -- because of the FreeSpace iterative frame process -- set this value to 0, instead
428                                 // of to 2 (as it originally was) since we call SendGameOver() only once.  Once we get the ack
429                                 // from the server, we can assume that we are done.
430                                 // need to mark this as 0
431                                 SendingGameOver = 0;                                                    
432                                 break;
433                         case GNT_GAMELIST_DATA:
434                                 int i;
435                                 //Woohoo! Game data! put it in the buffer (if one's free)
436                                 for(i=0;i<MAX_GAME_BUFFERS;i++)
437                                 {
438                                         if(GameBuffer[i].game_type==GT_UNUSED)
439                                         {
440                                                 memcpy(&GameBuffer[i],&inpacket.data,sizeof(game_list));
441                                                 i=MAX_GAME_BUFFERS+1;
442                                         }
443                                 }
444                                 break;
445
446                         case GNT_GAME_COUNT_DATA:
447                                 //Here, inpacket.data contains the following structure
448                                 //struct {
449                                 //      int numusers;
450                                 //      char channel[];//Null terminated
451                                 //      }
452                                 //You can add whatever code, or callback, etc. you need to deal with this data
453
454                                 // let the PXO screen know about this data
455                                 int num_servers;
456                                 char channel[512];
457
458                                 // get the user count
459                                 memcpy(&num_servers,inpacket.data,sizeof(int));
460
461                                 // copy the channel name
462                                 SDL_strlcpy(channel, inpacket.data+sizeof(int), SDL_arraysize(channel));
463
464                                 // send it to the PXO screen                            
465                                 multi_pxo_channel_count_update(channel,num_servers);
466                                 break;
467                         }
468                         AckPacket(inpacket.sig);                        
469                 }
470         }
471 }
472
473 void UpdateGameData(void *buffer)
474 {
475         SendingGameOver = 0;
476
477         switch(GameType){
478         case GT_FREESPACE:
479 #ifndef MAKE_FS1
480                 Int3();
481 #else
482                 memcpy(FreeSpaceTrackerGameData,buffer,sizeof(freespace_net_game_data));
483 #endif
484                 break;
485
486         case GT_FREESPACE2:
487 #ifdef MAKE_FS1
488                 Int3();
489 #else
490                 memcpy(FreeSpace2TrackerGameData,buffer,sizeof(freespace2_net_game_data));
491 #endif
492                 break;
493
494         default:
495                 Int3();
496                 break;
497         }
498 }
499
500 game_list * GetGameList()
501 {
502         static game_list gl;
503         for(int i=0;i<MAX_GAME_BUFFERS;i++)
504         {
505                 if(GameBuffer[i].game_type!=GT_UNUSED)
506                 {
507                         memcpy(&gl,&GameBuffer[i],sizeof(game_list));
508                         GameBuffer[i].game_type = GT_UNUSED;
509                         return &gl;
510                 }
511         }
512         return NULL;
513 }
514
515 void RequestGameList()
516 {
517         ubyte packet_data[sizeof(game_packet_header)];
518         int packet_length = 0;
519
520         GameListReq.len = GAME_HEADER_ONLY_SIZE;
521
522         packet_length = SerializeGamePacket(&GameListReq, packet_data);
523         SENDTO(Unreliable_socket, (char *)&packet_data, packet_length, 0, (struct sockaddr *)&gtrackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
524 }
525
526 void RequestGameListWithFilter(void *filter)
527 {
528         ubyte packet_data[sizeof(game_packet_header)];
529         int packet_length = 0;
530
531         memcpy(&GameListReq.data,filter,sizeof(filter_game_list_struct));
532         GameListReq.len = GAME_HEADER_ONLY_SIZE+sizeof(filter_game_list_struct);
533
534         packet_length = SerializeGamePacket(&GameListReq, packet_data);
535         SENDTO(Unreliable_socket, (char *)&packet_data, packet_length, 0, (struct sockaddr *)&gtrackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
536 }
537
538
539 /* REPLACED BELOW
540 void SendGameOver()
541 {
542         TrackerGameIsRunning = 0;
543         sendto(gamesock,(const char *)&GameOverPacket,GameOverPacket.len,0,(struct sockaddr *)&gtrackaddr,sizeof(struct sockaddr_in));
544 }
545 */
546
547 //Start New 7-9-98
548 int SendGameOver()
549 {
550         ubyte packet_data[sizeof(game_packet_header)];
551         int packet_length = 0;
552
553         if(SendingGameOver==2) 
554         {
555                 SendingGameOver = 0;    
556                 return 1;
557         }
558         if(SendingGameOver==1) 
559         {
560                 //Wait until it's sent.
561                 IdleGameTracker();
562                 return 0;
563         }
564         if(SendingGameOver==0)
565         {
566                 LastGameOverPacket = timer_get_milliseconds();
567                 FirstGameOverPacket = timer_get_milliseconds();
568                 SendingGameOver = 1;
569                 TrackerGameIsRunning = 0;
570
571                 packet_length = SerializeGamePacket(&GameOverPacket, packet_data);
572                 SENDTO(Unreliable_socket, (char *)&packet_data, packet_length, 0, (struct sockaddr *)&gtrackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
573
574                 return 0;
575         }
576         return 0;
577 }
578 //End New 7-9-98
579
580 void AckPacket(int sig)
581 {
582         ubyte packet_data[sizeof(game_packet_header)];
583         int packet_length = 0;
584
585         TrackAckPacket.sig = sig;
586
587         packet_length = SerializeGamePacket(&TrackAckPacket, packet_data);
588         SENDTO(Unreliable_socket, (char *)&packet_data, packet_length, 0, (struct sockaddr *)&gtrackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
589 }
590
591 void StartTrackerGame(void *buffer)
592 {
593         SendingGameOver = 0;
594
595         switch(GameType){
596         case GT_FREESPACE:
597 #ifndef MAKE_FS1
598                 Int3();
599 #else
600                 memcpy(FreeSpaceTrackerGameData,buffer,sizeof(freespace_net_game_data));
601 #endif
602                 break;
603
604         case GT_FREESPACE2:
605 #ifdef MAKE_FS1
606                 Int3();
607 #else
608                 memcpy(FreeSpace2TrackerGameData,buffer,sizeof(freespace2_net_game_data));
609 #endif
610                 break;
611
612         default:
613                 Int3();
614                 break;
615         }
616         TrackerGameIsRunning = 1;
617         LastTrackerUpdate = 0;  
618 }
619
620 //A new function
621 void RequestGameCountWithFilter(void *filter) 
622 {
623         game_packet_header GameCountReq;
624         ubyte packet_data[sizeof(game_packet_header)];
625         int packet_length = 0;
626
627 #ifdef MAKE_FS1
628         GameCountReq.game_type = GT_FREESPACE;
629 #else
630         GameCountReq.game_type = GT_FREESPACE2;
631 #endif
632         GameCountReq.type = GNT_GAME_COUNT_REQ;
633         GameCountReq.len = GAME_HEADER_ONLY_SIZE+sizeof(filter_game_list_struct);       
634         memcpy(&GameCountReq.data, ((filter_game_list_struct*)filter)->channel, sizeof(filter_game_list_struct) - 4);
635
636         packet_length = SerializeGamePacket(&GameCountReq, packet_data);
637         SENDTO(Unreliable_socket, (char *)&packet_data, packet_length, 0, (struct sockaddr *)&gtrackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
638 }