]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/gtrack.cpp
add basic support for getting though firewalls
[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                 case GNT_PEER_PING: {
281                         int ip_address = 0;
282                         ushort port = 0;
283
284                         PXO_GET_INT(ip_address);
285                         PXO_GET_USHORT(port);
286
287                         memcpy(gph->data, &ip_address, sizeof(int));
288                         memcpy(gph->data+sizeof(int), &port, sizeof(ushort));
289
290                         break;
291                 }
292
293                 default:
294                         break;
295         }
296
297         SDL_assert(offset == data_size);
298 }
299
300
301 int InitGameTrackerClient(int gametype)
302 {
303         struct sockaddr_in sockaddr;
304         in_addr_t iaddr;
305
306         GameType = gametype;
307         LastTrackerUpdate = 0;
308         switch(gametype)
309         {
310         case GT_FREESPACE:
311 #ifndef MAKE_FS1
312                 Int3();
313                 return 0;
314 #else
315                 TrackerGameData.len = GAME_HEADER_ONLY_SIZE+sizeof(freespace_net_game_data);
316 #endif
317                 break;
318
319         case GT_FREESPACE2:
320 #ifdef MAKE_FS1
321                 Int3();
322                 return 0;
323 #else
324                 TrackerGameData.len = GAME_HEADER_ONLY_SIZE+sizeof(freespace2_net_game_data);
325 #endif
326                 break;
327
328         default:
329                 Int3();
330                 return 0;
331         }
332         TrackerGameData.game_type = (unsigned char)gametype;    //1==freespace (GT_FREESPACE), 2==D3, 3==tuberacer, etc.
333         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.
334
335 #ifdef MAKE_FS1
336         FreeSpaceTrackerGameData = (freespace_net_game_data *)&TrackerGameData.data;
337 #else
338         FreeSpace2TrackerGameData = (freespace2_net_game_data *)&TrackerGameData.data;
339 #endif
340         
341         GameListReq.game_type = (unsigned char)gametype;
342         GameListReq.type = GNT_GAMELIST_REQ;
343         GameListReq.len = GAME_HEADER_ONLY_SIZE;
344
345         TrackAckPacket.game_type = (unsigned char)gametype;
346         TrackAckPacket.len = GAME_HEADER_ONLY_SIZE;
347         TrackAckPacket.type = GNT_CLIENT_ACK;
348
349         GameOverPacket.game_type = (unsigned char)gametype;
350         GameOverPacket.len = GAME_HEADER_ONLY_SIZE;
351         GameOverPacket.type = GNT_GAMEOVER;
352
353 #ifdef MAKE_FS1
354         gamesock = socket(AF_INET,SOCK_DGRAM,0);
355         
356         if ( gamesock == (SOCKET)INVALID_SOCKET )
357         {
358                 mprintf(("Unable to open a socket.\n"));
359                 return 0;
360         }
361 #endif
362         
363         memset( &sockaddr, 0, sizeof(struct sockaddr_in) );
364         sockaddr.sin_family = AF_INET; 
365         sockaddr.sin_addr.s_addr = INADDR_ANY; 
366         sockaddr.sin_port = 0;//htons(GAMEPORT);
367         
368 #ifdef MAKE_FS1
369         if (SOCKET_ERROR==bind(gamesock, (struct sockaddr*)&sockaddr, sizeof (sockaddr)))
370         {       
371                 mprintf(("Unable to bind a socket.\n"));
372                 mprintf(("WSAGetLastError() returned %d.\n",WSAGetLastError()));
373                 return 0;
374         }
375 #endif
376                 
377         iaddr = inet_addr ( Multi_options_g.game_tracker_ip ); 
378         if ( iaddr == INADDR_NONE ) {
379                 // first try and resolve by name
380                 struct hostent *he;
381                 he = gethostbyname( Multi_options_g.game_tracker_ip );
382                 if(!he)
383                 {               
384                         return 0;
385                         /*
386                         // try and resolve by address           
387                         unsigned int n_order = inet_addr(Multi_game_tracker_ip_address);
388                         he = gethostbyaddr((char*)&n_order,4,PF_INET);          
389
390                         if(!he){
391                                 return 0;
392                         }
393                         */
394                 }
395
396                 iaddr = ((in_addr *)(he->h_addr))->s_addr;
397         }
398
399         // This would be a good place to resolve the IP based on a domain name
400         gtrackaddr.sin_addr.s_addr = iaddr;
401         gtrackaddr.sin_family = AF_INET; 
402         gtrackaddr.sin_port = htons( GAMEPORT );
403
404         //Start New 7-9-98
405         SendingGameOver = 0;
406         //End New 7-9-98
407
408         return 1;
409 }
410
411 void IdleGameTracker()
412 {
413         fd_set read_fds;                   
414         struct timeval timeout;
415         ubyte packet_data[sizeof(game_packet_header)];
416         int packet_length = 0;
417
418 #ifndef MAKE_FS1
419         PSNET_TOP_LAYER_PROCESS();
420 #endif
421         
422         timeout.tv_sec=0;            
423         timeout.tv_usec=0;
424         if((TrackerGameIsRunning) && ((timer_get_seconds()-LastTrackerUpdate)>TRACKER_UPDATE_INTERVAL) && !SendingGameOver)
425         {
426                 //Time to update the tracker again
427                 packet_length = SerializeGamePacket(&TrackerGameData, packet_data);
428                 PXO_SENDTO(GAMESOCK, (char *)&packet_data, packet_length, 0, (struct sockaddr *)&gtrackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
429                 TrackerAckdUs = 0;
430                 LastTrackerUpdate = timer_get_seconds();
431         }
432         else if((TrackerGameIsRunning)&&(!TrackerAckdUs)&&((timer_get_milliseconds()-LastSentToTracker)>TRACKER_RESEND_TIME))
433         {
434                 //We still haven't been acked by the last packet and it's time to resend.
435                 packet_length = SerializeGamePacket(&TrackerGameData, packet_data);
436                 PXO_SENDTO(GAMESOCK, (char *)&packet_data, packet_length, 0, (struct sockaddr *)&gtrackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
437                 TrackerAckdUs = 0;
438                 LastTrackerUpdate = timer_get_seconds();
439                 LastSentToTracker = timer_get_milliseconds();
440         }
441
442         //Start New 7-9-98
443         if(SendingGameOver){
444                 if((timer_get_milliseconds()-LastGameOverPacket)>TRACKER_RESEND_TIME){
445                         //resend
446                         packet_length = SerializeGamePacket(&GameOverPacket, packet_data);
447                         LastGameOverPacket = timer_get_milliseconds();
448                         PXO_SENDTO(GAMESOCK, (char *)&packet_data, packet_length, 0, (struct sockaddr *)&gtrackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
449                 } 
450                 /*
451                 else if((timer_get_milliseconds()-FirstGameOverPacket)>NET_ACK_TIMEOUT) {
452                         //Giving up, it timed out.
453                         SendingGameOver = 2;
454                 }
455                 */
456         }
457         //End New 7-9-98
458
459         //Check for incoming
460                 
461         FD_ZERO(&read_fds);
462         FD_SET(GAMESOCK, &read_fds);
463
464         if(PXO_SELECT(GAMESOCK+1,&read_fds,NULL,NULL,&timeout, PSNET_TYPE_GAME_TRACKER))
465         {
466                 int bytesin;
467                 int addrsize;
468                 struct sockaddr_in fromaddr;
469
470                 game_packet_header inpacket;
471
472                 SDL_zero(inpacket);
473                 addrsize = sizeof(struct sockaddr_in);
474
475                 bytesin = PXO_RECVFROM(GAMESOCK, (char *)&packet_data, sizeof(game_packet_header), 0, (struct sockaddr *)&fromaddr, &addrsize, PSNET_TYPE_GAME_TRACKER);
476
477                 if (bytesin > 0) {
478                         DeserializeGamePacket(packet_data, bytesin, &inpacket);
479
480 #ifndef MAKE_FS1
481                         // subtract one from the header
482                         inpacket.len--;
483 #endif
484 #ifndef NDEBUG
485                 } else {
486                         int wserr=WSAGetLastError();
487                         mprintf(("RECVFROM() failure. WSAGetLastError() returned %d\n",wserr));
488 #endif
489                 }
490
491                 //Check to make sure the packets ok
492                 if ( (bytesin > 0) && (bytesin == (int)inpacket.len) )
493                 {
494                         switch(inpacket.type)
495                         {
496                         case GNT_SERVER_ACK:
497                                 //The server got our packet so we can stop sending now
498                                 TrackerAckdUs = 1;                              
499                                 
500                                 // 7/13/98 -- because of the FreeSpace iterative frame process -- set this value to 0, instead
501                                 // of to 2 (as it originally was) since we call SendGameOver() only once.  Once we get the ack
502                                 // from the server, we can assume that we are done.
503                                 // need to mark this as 0
504                                 SendingGameOver = 0;                                                    
505                                 break;
506                         case GNT_GAMELIST_DATA:
507                                 int i;
508                                 //Woohoo! Game data! put it in the buffer (if one's free)
509                                 for(i=0;i<MAX_GAME_BUFFERS;i++)
510                                 {
511                                         if(GameBuffer[i].game_type==GT_UNUSED)
512                                         {
513                                                 memcpy(&GameBuffer[i],&inpacket.data,sizeof(game_list));
514                                                 i=MAX_GAME_BUFFERS+1;
515                                         }
516                                 }
517                                 break;
518
519                         case GNT_GAME_COUNT_DATA:
520                                 //Here, inpacket.data contains the following structure
521                                 //struct {
522                                 //      int numusers;
523                                 //      char channel[];//Null terminated
524                                 //      }
525                                 //You can add whatever code, or callback, etc. you need to deal with this data
526
527                                 // let the PXO screen know about this data
528                                 int num_servers;
529                                 char channel[512];
530
531                                 // get the user count
532                                 memcpy(&num_servers,inpacket.data,sizeof(int));
533
534                                 // copy the channel name
535                                 SDL_strlcpy(channel, inpacket.data+sizeof(int), SDL_arraysize(channel));
536
537                                 // send it to the PXO screen                            
538                                 multi_pxo_channel_count_update(channel,num_servers);
539                                 break;
540
541                         case GNT_PEER_PING:
542                                 // send ping packet to specified client in attempt to open up a
543                                 // way through stateful firewalls, allowing client to talk later
544                                 net_addr clientaddr;
545
546                                 clientaddr.type = Multi_options_g.protocol;
547                                 memcpy(clientaddr.addr, inpacket.data, IP_ADDRESS_LENGTH);
548                                 memcpy(&clientaddr.port, inpacket.data+IP_ADDRESS_LENGTH, sizeof(short));
549                                 clientaddr.port = ntohs(clientaddr.port);
550
551                                 extern void send_ping(net_addr *addr);
552                                 send_ping(&clientaddr);
553                                 break;
554                         }
555                         AckPacket(inpacket.sig);                        
556                 }
557         }
558 }
559
560 void UpdateGameData(void *buffer)
561 {
562         SendingGameOver = 0;
563
564         switch(GameType){
565         case GT_FREESPACE:
566 #ifndef MAKE_FS1
567                 Int3();
568 #else
569                 memcpy(FreeSpaceTrackerGameData,buffer,sizeof(freespace_net_game_data));
570 #endif
571                 break;
572
573         case GT_FREESPACE2:
574 #ifdef MAKE_FS1
575                 Int3();
576 #else
577                 memcpy(FreeSpace2TrackerGameData,buffer,sizeof(freespace2_net_game_data));
578 #endif
579                 break;
580
581         default:
582                 Int3();
583                 break;
584         }
585 }
586
587 game_list * GetGameList()
588 {
589         static game_list gl;
590         for(int i=0;i<MAX_GAME_BUFFERS;i++)
591         {
592                 if(GameBuffer[i].game_type!=GT_UNUSED)
593                 {
594                         memcpy(&gl,&GameBuffer[i],sizeof(game_list));
595                         GameBuffer[i].game_type = GT_UNUSED;
596                         return &gl;
597                 }
598         }
599         return NULL;
600 }
601
602 void RequestGameList()
603 {
604         ubyte packet_data[sizeof(game_packet_header)];
605         int packet_length = 0;
606
607         GameListReq.len = GAME_HEADER_ONLY_SIZE;
608
609         packet_length = SerializeGamePacket(&GameListReq, packet_data);
610         PXO_SENDTO(GAMESOCK, (char *)&packet_data, packet_length, 0, (struct sockaddr *)&gtrackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
611 }
612
613 void RequestGameListWithFilter(void *filter)
614 {
615         ubyte packet_data[sizeof(game_packet_header)];
616         int packet_length = 0;
617
618         memcpy(&GameListReq.data,filter,sizeof(filter_game_list_struct));
619         GameListReq.len = GAME_HEADER_ONLY_SIZE+sizeof(filter_game_list_struct);
620
621         packet_length = SerializeGamePacket(&GameListReq, packet_data);
622         PXO_SENDTO(GAMESOCK, (char *)&packet_data, packet_length, 0, (struct sockaddr *)&gtrackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
623 }
624
625
626 /* REPLACED BELOW
627 void SendGameOver()
628 {
629         TrackerGameIsRunning = 0;
630         sendto(gamesock,(const char *)&GameOverPacket,GameOverPacket.len,0,(struct sockaddr *)&gtrackaddr,sizeof(struct sockaddr_in));
631 }
632 */
633
634 //Start New 7-9-98
635 int SendGameOver()
636 {
637         ubyte packet_data[sizeof(game_packet_header)];
638         int packet_length = 0;
639
640         if(SendingGameOver==2) 
641         {
642                 SendingGameOver = 0;    
643                 return 1;
644         }
645         if(SendingGameOver==1) 
646         {
647                 //Wait until it's sent.
648                 IdleGameTracker();
649                 return 0;
650         }
651         if(SendingGameOver==0)
652         {
653                 LastGameOverPacket = timer_get_milliseconds();
654                 FirstGameOverPacket = timer_get_milliseconds();
655                 SendingGameOver = 1;
656                 TrackerGameIsRunning = 0;
657
658                 packet_length = SerializeGamePacket(&GameOverPacket, packet_data);
659                 PXO_SENDTO(GAMESOCK, (char *)&packet_data, packet_length, 0, (struct sockaddr *)&gtrackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
660
661                 return 0;
662         }
663         return 0;
664 }
665 //End New 7-9-98
666
667 void AckPacket(int sig)
668 {
669         ubyte packet_data[sizeof(game_packet_header)];
670         int packet_length = 0;
671
672         TrackAckPacket.sig = sig;
673
674         packet_length = SerializeGamePacket(&TrackAckPacket, packet_data);
675         PXO_SENDTO(GAMESOCK, (char *)&packet_data, packet_length, 0, (struct sockaddr *)&gtrackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
676 }
677
678 void StartTrackerGame(void *buffer)
679 {
680         SendingGameOver = 0;
681
682         switch(GameType){
683         case GT_FREESPACE:
684 #ifndef MAKE_FS1
685                 Int3();
686 #else
687                 memcpy(FreeSpaceTrackerGameData,buffer,sizeof(freespace_net_game_data));
688 #endif
689                 break;
690
691         case GT_FREESPACE2:
692 #ifdef MAKE_FS1
693                 Int3();
694 #else
695                 memcpy(FreeSpace2TrackerGameData,buffer,sizeof(freespace2_net_game_data));
696 #endif
697                 break;
698
699         default:
700                 Int3();
701                 break;
702         }
703         TrackerGameIsRunning = 1;
704         LastTrackerUpdate = 0;  
705 }
706
707 //A new function
708 void RequestGameCountWithFilter(void *filter) 
709 {
710         game_packet_header GameCountReq;
711         ubyte packet_data[sizeof(game_packet_header)];
712         int packet_length = 0;
713
714 #ifdef MAKE_FS1
715         GameCountReq.game_type = GT_FREESPACE;
716 #else
717         GameCountReq.game_type = GT_FREESPACE2;
718 #endif
719         GameCountReq.type = GNT_GAME_COUNT_REQ;
720         GameCountReq.len = GAME_HEADER_ONLY_SIZE+sizeof(filter_game_list_struct);
721         memcpy(&GameCountReq.data, ((filter_game_list_struct*)filter)->channel, CHANNEL_LEN);
722
723         packet_length = SerializeGamePacket(&GameCountReq, packet_data);
724         PXO_SENDTO(GAMESOCK, (char *)&packet_data, packet_length, 0, (struct sockaddr *)&gtrackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
725 }