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