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