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