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