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