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
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);
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);
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);
65 #define GAMESOCK gamesock
67 #define GAMESOCK Unreliable_socket
69 struct sockaddr_in gtrackaddr;
71 game_list GameBuffer[MAX_GAME_BUFFERS];
72 int GameType;//d3 or fs
74 unsigned int LastTrackerUpdate;
75 unsigned int LastSentToTracker;
76 unsigned int TrackerAckdUs;
77 unsigned int TrackerGameIsRunning;
79 game_packet_header TrackerGameData;
80 game_packet_header GameListReq;
81 game_packet_header TrackAckPacket;
82 game_packet_header GameOverPacket;
85 freespace_net_game_data *FreeSpaceTrackerGameData;
87 freespace2_net_game_data *FreeSpace2TrackerGameData;
91 unsigned int LastGameOverPacket;
92 unsigned int FirstGameOverPacket;
98 static int SerializeGamePacket(const game_packet_header *gph, ubyte *data)
102 PXO_ADD_UINT(gph->len);
103 PXO_ADD_DATA(gph->game_type);
106 // FS1 needs an extra 3 bytes padding
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);
118 // these have no other data
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)));
128 filter_game_list_struct *filter = (filter_game_list_struct *)&gph->data;
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
138 case GNT_GAMEUPDATE: {
139 pxo_net_game_data *game_data = (pxo_net_game_data *)&gph->data;
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);
152 for (i = 0; i < MAX_FREESPACE_PLAYERS; i++) {
153 PXO_ADD_DATA(game_data->players[i]);
156 for (i = 0; i < MAX_FREESPACE_PLAYERS; i++) {
157 PXO_ADD_INT(game_data->player_rank[i]);
161 PXO_ADD_DATA(game_data->channel);
162 PXO_ADD_DATA(game_data->pad); // for sizing, so gph->len will match
167 case GNT_GAME_COUNT_REQ: {
168 SDL_assert(gph->len == (GAME_HEADER_ONLY_SIZE+sizeof(filter_game_list_struct)));
170 filter_game_list_struct filter;
174 memcpy(filter.channel, gph->data, sizeof(filter.channel));
176 PXO_ADD_DATA(filter.channel);
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);
185 // we shouldn't be sending any other packet types
191 SDL_assert(packet_size >= (int)GAME_HEADER_ONLY_SIZE);
192 SDL_assert(packet_size == (int)gph->len);
197 static void DeserializeGamePacket(const ubyte *data, const int data_size, game_packet_header *gph)
202 memset(gph, 0, sizeof(game_packet_header));
204 // make sure we received a complete base packet
205 if (data_size < (int)GAME_HEADER_ONLY_SIZE) {
207 gph->type = 255; // invalid = 0xff
212 PXO_GET_UINT(gph->len);
213 PXO_GET_DATA(gph->game_type);
216 // FS1 has 3 bytes of padding here
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);
226 // sanity check data size to make sure we reveived all of the expected packet
228 // (the -1 is because psnet2 pops off one byte)
229 if ((int)gph->len-1 > data_size) {
231 if ((int)gph->len > data_size) {
243 case GNT_GAMELIST_DATA: {
244 game_list *games = (game_list *)&gph->data;
246 PXO_GET_DATA(games->game_type);
248 for (i = 0; i < MAX_GAME_LISTS_PER_PACKET; i++) {
249 PXO_GET_DATA(games->game_name[i]);
252 PXO_GET_DATA(games->pad); // padded bytes for alignment
254 for (i = 0; i < MAX_GAME_LISTS_PER_PACKET; i++) {
255 PXO_GET_UINT(games->game_server[i]);
258 for (i = 0; i < MAX_GAME_LISTS_PER_PACKET; i++) {
259 PXO_GET_USHORT(games->port[i]);
265 case GNT_GAME_COUNT_DATA: {
269 PXO_GET_INT(n_users);
271 SDL_strlcpy(channel, (char *)(data+offset), SDL_arraysize(channel));
272 offset += (strlen(channel) + 1);
274 memcpy(gph->data, &n_users, sizeof(int));
275 memcpy(gph->data+sizeof(int), channel, strlen(channel)+1);
280 case GNT_PEER_PING: {
284 PXO_GET_INT(ip_address);
285 PXO_GET_USHORT(port);
287 memcpy(gph->data, &ip_address, sizeof(int));
288 memcpy(gph->data+sizeof(int), &port, sizeof(ushort));
297 SDL_assert(offset == data_size);
301 int InitGameTrackerClient(int gametype)
303 struct sockaddr_in sockaddr;
307 LastTrackerUpdate = 0;
315 TrackerGameData.len = GAME_HEADER_ONLY_SIZE+sizeof(freespace_net_game_data);
324 TrackerGameData.len = GAME_HEADER_ONLY_SIZE+sizeof(freespace2_net_game_data);
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.
336 FreeSpaceTrackerGameData = (freespace_net_game_data *)&TrackerGameData.data;
338 FreeSpace2TrackerGameData = (freespace2_net_game_data *)&TrackerGameData.data;
341 GameListReq.game_type = (unsigned char)gametype;
342 GameListReq.type = GNT_GAMELIST_REQ;
343 GameListReq.len = GAME_HEADER_ONLY_SIZE;
345 TrackAckPacket.game_type = (unsigned char)gametype;
346 TrackAckPacket.len = GAME_HEADER_ONLY_SIZE;
347 TrackAckPacket.type = GNT_CLIENT_ACK;
349 GameOverPacket.game_type = (unsigned char)gametype;
350 GameOverPacket.len = GAME_HEADER_ONLY_SIZE;
351 GameOverPacket.type = GNT_GAMEOVER;
354 gamesock = socket(AF_INET,SOCK_DGRAM,0);
356 if ( gamesock == (SOCKET)INVALID_SOCKET )
358 mprintf(("Unable to open a socket.\n"));
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);
369 if (SOCKET_ERROR==bind(gamesock, (struct sockaddr*)&sockaddr, sizeof (sockaddr)))
371 mprintf(("Unable to bind a socket.\n"));
372 mprintf(("WSAGetLastError() returned %d.\n",WSAGetLastError()));
377 iaddr = inet_addr ( Multi_options_g.game_tracker_ip );
378 if ( iaddr == INADDR_NONE ) {
379 // first try and resolve by name
381 he = gethostbyname( Multi_options_g.game_tracker_ip );
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);
396 iaddr = ((in_addr *)(he->h_addr))->s_addr;
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 );
411 void IdleGameTracker()
414 struct timeval timeout;
415 ubyte packet_data[sizeof(game_packet_header)];
416 int packet_length = 0;
419 PSNET_TOP_LAYER_PROCESS();
424 if((TrackerGameIsRunning) && ((timer_get_seconds()-LastTrackerUpdate)>TRACKER_UPDATE_INTERVAL) && !SendingGameOver)
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 *)>rackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
430 LastTrackerUpdate = timer_get_seconds();
432 else if((TrackerGameIsRunning)&&(!TrackerAckdUs)&&((timer_get_milliseconds()-LastSentToTracker)>TRACKER_RESEND_TIME))
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 *)>rackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
438 LastTrackerUpdate = timer_get_seconds();
439 LastSentToTracker = timer_get_milliseconds();
444 if((timer_get_milliseconds()-LastGameOverPacket)>TRACKER_RESEND_TIME){
446 packet_length = SerializeGamePacket(&GameOverPacket, packet_data);
447 LastGameOverPacket = timer_get_milliseconds();
448 PXO_SENDTO(GAMESOCK, (char *)&packet_data, packet_length, 0, (struct sockaddr *)>rackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
451 else if((timer_get_milliseconds()-FirstGameOverPacket)>NET_ACK_TIMEOUT) {
452 //Giving up, it timed out.
462 FD_SET(GAMESOCK, &read_fds);
464 if(PXO_SELECT(GAMESOCK+1,&read_fds,NULL,NULL,&timeout, PSNET_TYPE_GAME_TRACKER))
468 struct sockaddr_in fromaddr;
470 game_packet_header inpacket;
473 addrsize = sizeof(struct sockaddr_in);
475 bytesin = PXO_RECVFROM(GAMESOCK, (char *)&packet_data, sizeof(game_packet_header), 0, (struct sockaddr *)&fromaddr, &addrsize, PSNET_TYPE_GAME_TRACKER);
478 DeserializeGamePacket(packet_data, bytesin, &inpacket);
481 // subtract one from the header
486 int wserr=WSAGetLastError();
487 mprintf(("RECVFROM() failure. WSAGetLastError() returned %d\n",wserr));
491 //Check to make sure the packets ok
492 if ( (bytesin > 0) && (bytesin == (int)inpacket.len) )
494 switch(inpacket.type)
497 //The server got our packet so we can stop sending now
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
506 case GNT_GAMELIST_DATA:
508 //Woohoo! Game data! put it in the buffer (if one's free)
509 for(i=0;i<MAX_GAME_BUFFERS;i++)
511 if(GameBuffer[i].game_type==GT_UNUSED)
513 memcpy(&GameBuffer[i],&inpacket.data,sizeof(game_list));
514 i=MAX_GAME_BUFFERS+1;
519 case GNT_GAME_COUNT_DATA:
520 //Here, inpacket.data contains the following structure
523 // char channel[];//Null terminated
525 //You can add whatever code, or callback, etc. you need to deal with this data
527 // let the PXO screen know about this data
531 // get the user count
532 memcpy(&num_servers,inpacket.data,sizeof(int));
534 // copy the channel name
535 SDL_strlcpy(channel, inpacket.data+sizeof(int), SDL_arraysize(channel));
537 // send it to the PXO screen
538 multi_pxo_channel_count_update(channel,num_servers);
542 // send ping packet to specified client in attempt to open up a
543 // way through stateful firewalls, allowing client to talk later
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);
551 extern void send_ping(net_addr *addr);
552 send_ping(&clientaddr);
555 AckPacket(inpacket.sig);
560 void UpdateGameData(void *buffer)
569 memcpy(FreeSpaceTrackerGameData,buffer,sizeof(freespace_net_game_data));
577 memcpy(FreeSpace2TrackerGameData,buffer,sizeof(freespace2_net_game_data));
587 game_list * GetGameList()
590 for(int i=0;i<MAX_GAME_BUFFERS;i++)
592 if(GameBuffer[i].game_type!=GT_UNUSED)
594 memcpy(&gl,&GameBuffer[i],sizeof(game_list));
595 GameBuffer[i].game_type = GT_UNUSED;
602 void RequestGameList()
604 ubyte packet_data[sizeof(game_packet_header)];
605 int packet_length = 0;
607 GameListReq.len = GAME_HEADER_ONLY_SIZE;
609 packet_length = SerializeGamePacket(&GameListReq, packet_data);
610 PXO_SENDTO(GAMESOCK, (char *)&packet_data, packet_length, 0, (struct sockaddr *)>rackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
613 void RequestGameListWithFilter(void *filter)
615 ubyte packet_data[sizeof(game_packet_header)];
616 int packet_length = 0;
618 memcpy(&GameListReq.data,filter,sizeof(filter_game_list_struct));
619 GameListReq.len = GAME_HEADER_ONLY_SIZE+sizeof(filter_game_list_struct);
621 packet_length = SerializeGamePacket(&GameListReq, packet_data);
622 PXO_SENDTO(GAMESOCK, (char *)&packet_data, packet_length, 0, (struct sockaddr *)>rackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
629 TrackerGameIsRunning = 0;
630 sendto(gamesock,(const char *)&GameOverPacket,GameOverPacket.len,0,(struct sockaddr *)>rackaddr,sizeof(struct sockaddr_in));
637 ubyte packet_data[sizeof(game_packet_header)];
638 int packet_length = 0;
640 if(SendingGameOver==2)
645 if(SendingGameOver==1)
647 //Wait until it's sent.
651 if(SendingGameOver==0)
653 LastGameOverPacket = timer_get_milliseconds();
654 FirstGameOverPacket = timer_get_milliseconds();
656 TrackerGameIsRunning = 0;
658 packet_length = SerializeGamePacket(&GameOverPacket, packet_data);
659 PXO_SENDTO(GAMESOCK, (char *)&packet_data, packet_length, 0, (struct sockaddr *)>rackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
667 void AckPacket(int sig)
669 ubyte packet_data[sizeof(game_packet_header)];
670 int packet_length = 0;
672 TrackAckPacket.sig = sig;
674 packet_length = SerializeGamePacket(&TrackAckPacket, packet_data);
675 PXO_SENDTO(GAMESOCK, (char *)&packet_data, packet_length, 0, (struct sockaddr *)>rackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);
678 void StartTrackerGame(void *buffer)
687 memcpy(FreeSpaceTrackerGameData,buffer,sizeof(freespace_net_game_data));
695 memcpy(FreeSpace2TrackerGameData,buffer,sizeof(freespace2_net_game_data));
703 TrackerGameIsRunning = 1;
704 LastTrackerUpdate = 0;
708 void RequestGameCountWithFilter(void *filter)
710 game_packet_header GameCountReq;
711 ubyte packet_data[sizeof(game_packet_header)];
712 int packet_length = 0;
715 GameCountReq.game_type = GT_FREESPACE;
717 GameCountReq.game_type = GT_FREESPACE2;
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);
723 packet_length = SerializeGamePacket(&GameCountReq, packet_data);
724 PXO_SENDTO(GAMESOCK, (char *)&packet_data, packet_length, 0, (struct sockaddr *)>rackaddr, sizeof(struct sockaddr_in), PSNET_TYPE_GAME_TRACKER);