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