2 * Copyright (C) Volition, Inc. 2005. All rights reserved.
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
11 //Game Tracker client code
14 InitGameTracker(int game_type); //D3 or Freespace
16 //Call periodically so we can send our update, and make sure it is received
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);
25 //Call to signify end of game
27 RequestGameList();//Sends a GNT_GAMELIST_REQ packet to the server.
29 game_list * GetGameList();//returns a pointer to a game_list struct
35 #include <arpa/inet.h>
36 #include <netinet/in.h>
44 #include "multi_pxo.h"
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);
56 #define PXO_ADD_STRING(d) do { SDL_strlcpy((char*)(data+packet_size), d, sizeof(game_packet_header)-packet_size); packet_size += (SDL_strlen((char*)(data+packet_size)) + 1); } while (0)
61 struct sockaddr_in gtrackaddr;
63 game_list GameBuffer[MAX_GAME_BUFFERS];
64 int GameType;//d3 or fs
66 unsigned int LastTrackerUpdate;
67 unsigned int LastSentToTracker;
68 unsigned int TrackerAckdUs;
69 unsigned int TrackerGameIsRunning;
71 game_packet_header TrackerGameData;
72 game_packet_header GameListReq;
73 game_packet_header TrackAckPacket;
74 game_packet_header GameOverPacket;
77 freespace_net_game_data *FreeSpaceTrackerGameData;
79 freespace2_net_game_data *FreeSpace2TrackerGameData;
83 unsigned int LastGameOverPacket;
84 unsigned int FirstGameOverPacket;
90 static int SerializeGamePacket(const game_packet_header *gph, ubyte *data)
94 PXO_ADD_UINT(gph->len);
95 PXO_ADD_DATA(gph->game_type);
96 PXO_ADD_DATA(gph->junk); // not used, basically just padding for compatibility
97 PXO_ADD_INT(gph->type);
98 PXO_ADD_UINT(gph->sig);
101 // these have no other data
106 // this one may or may not have extra data
107 case GNT_GAMELIST_REQ: {
108 if (gph->len > GAME_HEADER_ONLY_SIZE) {
109 SDL_assert(gph->len == (GAME_HEADER_ONLY_SIZE+sizeof(filter_game_list_struct)));
111 filter_game_list_struct *filter = (filter_game_list_struct *)&gph->data;
113 PXO_ADD_INT(filter->rank);
114 PXO_ADD_STRING(filter->channel);
120 case GNT_GAMEUPDATE: {
121 freespace2_net_game_data *game_data = (freespace2_net_game_data *)&gph->data;
123 PXO_ADD_DATA(game_data->game_name);
124 PXO_ADD_INT(game_data->difficulty);
125 PXO_ADD_INT(game_data->type);
126 PXO_ADD_INT(game_data->state);
127 PXO_ADD_INT(game_data->max_players);
128 PXO_ADD_INT(game_data->current_num_players);
129 PXO_ADD_DATA(game_data->mission_name);
130 PXO_ADD_DATA(game_data->channel);
135 case GNT_GAME_COUNT_REQ: {
136 char channel[CHANNEL_LEN];
138 memcpy(channel, gph->data, sizeof(channel));
140 PXO_ADD_DATA(channel);
145 // we shouldn't be sending any other packet types
151 SDL_assert(packet_size >= (int)GAME_HEADER_ONLY_SIZE);
152 SDL_assert(packet_size == (int)gph->len);
157 static void DeserializeGamePacket(const ubyte *data, const int data_size, game_packet_header *gph)
162 memset(gph, 0, sizeof(game_packet_header));
164 // make sure we received a complete base packet
165 if (data_size < (int)GAME_HEADER_ONLY_SIZE) {
167 gph->type = 255; // invalid = 0xff
172 PXO_GET_UINT(gph->len);
173 PXO_GET_DATA(gph->game_type);
174 PXO_GET_DATA(gph->junk); // not used, basically just padding for compatibility
175 PXO_GET_INT(gph->type);
176 PXO_GET_UINT(gph->sig);
178 // sanity check data size to make sure we reveived all of the expected packet
179 // (not exactly sure what -1 is for, but that's how it is later)
180 if ((int)gph->len-1 > data_size) {
191 case GNT_GAMELIST_DATA: {
192 game_list *games = (game_list *)&gph->data;
194 PXO_GET_DATA(games->game_type);
196 for (i = 0; i < MAX_GAME_LISTS_PER_PACKET; i++) {
197 PXO_GET_DATA(games->game_name[i]);
200 PXO_GET_DATA(games->pad); // padded bytes for alignment
202 for (i = 0; i < MAX_GAME_LISTS_PER_PACKET; i++) {
203 PXO_GET_UINT(games->game_server[i]);
206 for (i = 0; i < MAX_GAME_LISTS_PER_PACKET; i++) {
207 PXO_GET_USHORT(games->port[i]);
213 case GNT_GAME_COUNT_DATA: {
217 PXO_GET_INT(n_users);
219 SDL_strlcpy(channel, (char *)(data+offset), SDL_arraysize(channel));
220 offset += (strlen(channel) + 1);
222 memcpy(gph->data, &n_users, sizeof(int));
223 memcpy(gph->data+sizeof(int), channel, strlen(channel)+1);
232 //SDL_assert(offset == data_size);
236 int InitGameTrackerClient(int gametype)
238 struct sockaddr_in sockaddr;
242 LastTrackerUpdate = 0;
250 TrackerGameData.len = GAME_HEADER_ONLY_SIZE+sizeof(freespace_net_game_data);
259 TrackerGameData.len = GAME_HEADER_ONLY_SIZE+sizeof(freespace2_net_game_data);
267 TrackerGameData.game_type = (unsigned char)gametype; //1==freespace (GT_FREESPACE), 2==D3, 3==tuberacer, etc.
268 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.
271 FreeSpaceTrackerGameData = (freespace_net_game_data *)&TrackerGameData.data;
273 FreeSpace2TrackerGameData = (freespace2_net_game_data *)&TrackerGameData.data;
276 GameListReq.game_type = (unsigned char)gametype;
277 GameListReq.type = GNT_GAMELIST_REQ;
278 GameListReq.len = GAME_HEADER_ONLY_SIZE;
280 TrackAckPacket.game_type = (unsigned char)gametype;
281 TrackAckPacket.len = GAME_HEADER_ONLY_SIZE;
282 TrackAckPacket.type = GNT_CLIENT_ACK;
284 GameOverPacket.game_type = (unsigned char)gametype;
285 GameOverPacket.len = GAME_HEADER_ONLY_SIZE;
286 GameOverPacket.type = GNT_GAMEOVER;
288 // gamesock = socket(AF_INET,SOCK_DGRAM,0);
291 if ( gamesock == INVALID_SOCKET )
293 printf("Unable to open a socket.\n");
298 memset( &sockaddr, 0, sizeof(struct sockaddr_in) );
299 sockaddr.sin_family = AF_INET;
300 sockaddr.sin_addr.s_addr = INADDR_ANY;
301 sockaddr.sin_port = 0;//htons(GAMEPORT);
304 if (SOCKET_ERROR==bind(gamesock, (struct sockaddr*)&sockaddr, sizeof (sockaddr)))
306 printf("Unable to bind a socket.\n");
307 printf("WSAGetLastError() returned %d.\n",WSAGetLastError());
312 iaddr = inet_addr ( Multi_options_g.game_tracker_ip );
313 if ( iaddr == INADDR_NONE ) {
314 // first try and resolve by name
316 he = gethostbyname( Multi_options_g.game_tracker_ip );
321 // try and resolve by address
322 unsigned int n_order = inet_addr(Multi_game_tracker_ip_address);
323 he = gethostbyaddr((char*)&n_order,4,PF_INET);
331 iaddr = ((in_addr *)(he->h_addr))->s_addr;
334 // This would be a good place to resolve the IP based on a domain name
335 gtrackaddr.sin_addr.s_addr = iaddr;
336 gtrackaddr.sin_family = AF_INET;
337 gtrackaddr.sin_port = htons( GAMEPORT );
346 void IdleGameTracker()
349 struct timeval timeout;
350 ubyte packet_data[sizeof(game_packet_header)];
351 int packet_length = 0;
353 PSNET_TOP_LAYER_PROCESS();
357 if((TrackerGameIsRunning) && ((timer_get_seconds()-LastTrackerUpdate)>TRACKER_UPDATE_INTERVAL) && !SendingGameOver)
359 //Time to update the tracker again
360 packet_length = SerializeGamePacket(&TrackerGameData, packet_data);
361 SENDTO(Unreliable_socket, (char *)&packet_data, packet_length, 0, (struct sockaddr *)>rackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
363 LastTrackerUpdate = timer_get_seconds();
365 else if((TrackerGameIsRunning)&&(!TrackerAckdUs)&&((timer_get_milliseconds()-LastSentToTracker)>TRACKER_RESEND_TIME))
367 //We still haven't been acked by the last packet and it's time to resend.
368 packet_length = SerializeGamePacket(&TrackerGameData, packet_data);
369 SENDTO(Unreliable_socket, (char *)&packet_data, packet_length, 0, (struct sockaddr *)>rackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
371 LastTrackerUpdate = timer_get_seconds();
372 LastSentToTracker = timer_get_milliseconds();
377 if((timer_get_milliseconds()-LastGameOverPacket)>TRACKER_RESEND_TIME){
379 packet_length = SerializeGamePacket(&GameOverPacket, packet_data);
380 LastGameOverPacket = timer_get_milliseconds();
381 SENDTO(Unreliable_socket, (char *)&packet_data, packet_length, 0, (struct sockaddr *)>rackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
384 else if((timer_get_milliseconds()-FirstGameOverPacket)>NET_ACK_TIMEOUT) {
385 //Giving up, it timed out.
395 FD_SET(Unreliable_socket, &read_fds);
397 if(SELECT(Unreliable_socket+1,&read_fds,NULL,NULL,&timeout, PSNET_TYPE_GAME_TRACKER))
399 unsigned int bytesin;
401 struct sockaddr_in fromaddr;
403 game_packet_header inpacket;
404 addrsize = sizeof(struct sockaddr_in);
406 bytesin = RECVFROM(Unreliable_socket, (char *)&packet_data, sizeof(game_packet_header), 0, (struct sockaddr *)&fromaddr, &addrsize, PSNET_TYPE_GAME_TRACKER);
407 DeserializeGamePacket(packet_data, bytesin, &inpacket);
410 int wserr=WSAGetLastError();
411 mprintf(("RECVFROM() failure. WSAGetLastError() returned %d\n",wserr));
415 // subtract one from the header
418 //Check to make sure the packets ok
419 if(bytesin==inpacket.len)
421 switch(inpacket.type)
424 //The server got our packet so we can stop sending now
427 // 7/13/98 -- because of the FreeSpace iterative frame process -- set this value to 0, instead
428 // of to 2 (as it originally was) since we call SendGameOver() only once. Once we get the ack
429 // from the server, we can assume that we are done.
430 // need to mark this as 0
433 case GNT_GAMELIST_DATA:
435 //Woohoo! Game data! put it in the buffer (if one's free)
436 for(i=0;i<MAX_GAME_BUFFERS;i++)
438 if(GameBuffer[i].game_type==GT_UNUSED)
440 memcpy(&GameBuffer[i],&inpacket.data,sizeof(game_list));
441 i=MAX_GAME_BUFFERS+1;
446 case GNT_GAME_COUNT_DATA:
447 //Here, inpacket.data contains the following structure
450 // char channel[];//Null terminated
452 //You can add whatever code, or callback, etc. you need to deal with this data
454 // let the PXO screen know about this data
458 // get the user count
459 memcpy(&num_servers,inpacket.data,sizeof(int));
461 // copy the channel name
462 SDL_strlcpy(channel, inpacket.data+sizeof(int), SDL_arraysize(channel));
464 // send it to the PXO screen
465 multi_pxo_channel_count_update(channel,num_servers);
468 AckPacket(inpacket.sig);
473 void UpdateGameData(void *buffer)
482 memcpy(FreeSpaceTrackerGameData,buffer,sizeof(freespace_net_game_data));
490 memcpy(FreeSpace2TrackerGameData,buffer,sizeof(freespace2_net_game_data));
500 game_list * GetGameList()
503 for(int i=0;i<MAX_GAME_BUFFERS;i++)
505 if(GameBuffer[i].game_type!=GT_UNUSED)
507 memcpy(&gl,&GameBuffer[i],sizeof(game_list));
508 GameBuffer[i].game_type = GT_UNUSED;
515 void RequestGameList()
517 ubyte packet_data[sizeof(game_packet_header)];
518 int packet_length = 0;
520 GameListReq.len = GAME_HEADER_ONLY_SIZE;
522 packet_length = SerializeGamePacket(&GameListReq, packet_data);
523 SENDTO(Unreliable_socket, (char *)&packet_data, packet_length, 0, (struct sockaddr *)>rackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
526 void RequestGameListWithFilter(void *filter)
528 ubyte packet_data[sizeof(game_packet_header)];
529 int packet_length = 0;
531 memcpy(&GameListReq.data,filter,sizeof(filter_game_list_struct));
532 GameListReq.len = GAME_HEADER_ONLY_SIZE+sizeof(filter_game_list_struct);
534 packet_length = SerializeGamePacket(&GameListReq, packet_data);
535 SENDTO(Unreliable_socket, (char *)&packet_data, packet_length, 0, (struct sockaddr *)>rackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
542 TrackerGameIsRunning = 0;
543 sendto(gamesock,(const char *)&GameOverPacket,GameOverPacket.len,0,(struct sockaddr *)>rackaddr,sizeof(struct sockaddr_in));
550 ubyte packet_data[sizeof(game_packet_header)];
551 int packet_length = 0;
553 if(SendingGameOver==2)
558 if(SendingGameOver==1)
560 //Wait until it's sent.
564 if(SendingGameOver==0)
566 LastGameOverPacket = timer_get_milliseconds();
567 FirstGameOverPacket = timer_get_milliseconds();
569 TrackerGameIsRunning = 0;
571 packet_length = SerializeGamePacket(&GameOverPacket, packet_data);
572 SENDTO(Unreliable_socket, (char *)&packet_data, packet_length, 0, (struct sockaddr *)>rackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
580 void AckPacket(int sig)
582 ubyte packet_data[sizeof(game_packet_header)];
583 int packet_length = 0;
585 TrackAckPacket.sig = sig;
587 packet_length = SerializeGamePacket(&TrackAckPacket, packet_data);
588 SENDTO(Unreliable_socket, (char *)&packet_data, packet_length, 0, (struct sockaddr *)>rackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
591 void StartTrackerGame(void *buffer)
600 memcpy(FreeSpaceTrackerGameData,buffer,sizeof(freespace_net_game_data));
608 memcpy(FreeSpace2TrackerGameData,buffer,sizeof(freespace2_net_game_data));
616 TrackerGameIsRunning = 1;
617 LastTrackerUpdate = 0;
621 void RequestGameCountWithFilter(void *filter)
623 game_packet_header GameCountReq;
624 ubyte packet_data[sizeof(game_packet_header)];
625 int packet_length = 0;
628 GameCountReq.game_type = GT_FREESPACE;
630 GameCountReq.game_type = GT_FREESPACE2;
632 GameCountReq.type = GNT_GAME_COUNT_REQ;
633 GameCountReq.len = GAME_HEADER_ONLY_SIZE+sizeof(filter_game_list_struct);
634 memcpy(&GameCountReq.data, ((filter_game_list_struct*)filter)->channel, sizeof(filter_game_list_struct) - 4);
636 packet_length = SerializeGamePacket(&GameCountReq, packet_data);
637 SENDTO(Unreliable_socket, (char *)&packet_data, packet_length, 0, (struct sockaddr *)>rackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);