/* GameSpy Peer SDK Dan "Mr. Pants" Schoenblum dan@gamespy.com Copyright 1999-2007 GameSpy Industries, Inc devsupport@gamespy.com */ /************* ** INCLUDES ** *************/ #include "peerQR.h" #include "peerGlobalCallbacks.h" #include "peerPlayers.h" #include "peerAutoMatch.h" #include "peerOperations.h" /************** ** CALLBACKS ** **************/ #ifndef GSI_UNICODE #define piQRAddErrorCallback piQRAddErrorCallbackA #else #define piQRAddErrorCallback piQRAddErrorCallbackW #endif static void piQRServerKeyCallback ( int key, qr2_buffer_t buffer, PEER peer ) { PEER_CONNECTION; // check if we should report it if(connection->inRoom[StagingRoom] && (!connection->playing || (connection->reportingOptions & PEER_REPORT_INFO))) { // check if it's one of our keys switch(key) { case HOSTNAME_KEY: qr2_buffer_addA(buffer, NAMES[StagingRoom]); return; case NUMPLAYERS_KEY: qr2_buffer_add_int(buffer, connection->numPlayers[StagingRoom]); return; case MAXPLAYERS_KEY: if(!connection->maxPlayers) break; qr2_buffer_add_int(buffer, connection->maxPlayers); return; case GAMEMODE_KEY: if(connection->playing) break; qr2_buffer_addA(buffer, "openstaging"); return; case PASSWORD_KEY: qr2_buffer_add_int(buffer, connection->passwordedRoom?1:0); return; } } // we always report groupid if(key == GROUPID_KEY) { qr2_buffer_add_int(buffer, connection->reportingGroupID); return; } // pass it to the app if(connection->callbacks.qrServerKey) connection->callbacks.qrServerKey(peer, key, buffer, connection->callbacks.param); } static void piQRPlayerKeyCallback ( int key, int index, qr2_buffer_t buffer, PEER peer ) { piPlayer * player; PEER_CONNECTION; // check if we should report it if(connection->inRoom[StagingRoom] && (!connection->playing || (connection->reportingOptions & PEER_REPORT_PLAYERS))) { // check if it's a key we report if((key == PLAYER__KEY) || (key == PING__KEY)) { // convert the index to a player player = piFindPlayerByIndex(peer, StagingRoom, index); if(!player) qr2_buffer_addA(buffer, ""); else if(key == PLAYER__KEY) qr2_buffer_addA(buffer, player->nick); else if(key == PING__KEY) qr2_buffer_add_int(buffer, player->pingAverage); return; } } // pass it to the app if(connection->callbacks.qrPlayerKey) connection->callbacks.qrPlayerKey(peer, key, index, buffer, connection->callbacks.param); } static void piQRTeamKeyCallback ( int key, int index, qr2_buffer_t buffer, PEER peer ) { PEER_CONNECTION; // pass it to the app if(connection->callbacks.qrTeamKey) connection->callbacks.qrTeamKey(peer, key, index, buffer, connection->callbacks.param); } static void piQRKeyListCallback ( qr2_key_type type, qr2_keybuffer_t keyBuffer, PEER peer ) { PEER_CONNECTION; // add our own keys switch(type) { case key_server: if(!connection->autoMatchReporting) { qr2_keybuffer_add(keyBuffer, HOSTNAME_KEY); qr2_keybuffer_add(keyBuffer, GAMEMODE_KEY); if(connection->passwordedRoom) qr2_keybuffer_add(keyBuffer, PASSWORD_KEY); if(connection->reportingGroupID) qr2_keybuffer_add(keyBuffer, GROUPID_KEY); } qr2_keybuffer_add(keyBuffer, NUMPLAYERS_KEY); if(connection->maxPlayers) qr2_keybuffer_add(keyBuffer, MAXPLAYERS_KEY); break; case key_player: qr2_keybuffer_add(keyBuffer, PLAYER__KEY); qr2_keybuffer_add(keyBuffer, PING__KEY); break; default: break; } // pass it to the app if(connection->callbacks.qrKeyList) connection->callbacks.qrKeyList(peer, type, keyBuffer, connection->callbacks.param); } static int piQRCountCallback ( qr2_key_type type, PEER peer ) { PEER_CONNECTION; // we only handle player counts if(type == key_player) { // check if we should report it if(connection->inRoom[StagingRoom] && (!connection->playing || (connection->reportingOptions & PEER_REPORT_PLAYERS))) return connection->numPlayers[StagingRoom]; } // pass it to the app if(connection->callbacks.qrCount) return connection->callbacks.qrCount(peer, type, connection->callbacks.param); // app's not handling it return 0; } static void piQRAddErrorCallbackA ( qr2_error_t error, char * errorString, PEER peer ) { PEER_CONNECTION; // handle this if automatching if(peerIsAutoMatching(peer)) { PEERAutoMatchStatus status; // track the error connection->autoMatchQRFailed = PEERTrue; // switch to a new state if(connection->autoMatchSBFailed && (connection->autoMatchStatus != PEERStaging)) status = PEERFailed; else status = PEERSearching; piSetAutoMatchStatus(peer, status); return; } // pass it to the app if(connection->callbacks.qrAddError) { #ifndef GSI_UNICODE connection->callbacks.qrAddError(peer, error, errorString, connection->callbacks.param); #else unsigned short* errorString_W = UTF8ToUCS2StringAlloc(errorString); connection->callbacks.qrAddError(peer, error, errorString_W, connection->callbacks.param); gsifree(errorString_W); #endif } } #ifdef GSI_UNICODE static void piQRAddErrorCallbackW ( qr2_error_t error, unsigned short * errorString, PEER peer ) { char* errorString_A = UCS2ToUTF8StringAlloc(errorString); piQRAddErrorCallbackA(error, errorString_A, peer); gsifree(errorString_A); } #endif static void piQRNatNegotiateCallback ( int cookie, PEER peer ) { PEER_CONNECTION; // pass it to the app if(connection->callbacks.qrNatNegotiateCallback) connection->callbacks.qrNatNegotiateCallback(peer, cookie, connection->callbacks.param); } static void piQRPublicAddressCallback ( unsigned int ip, unsigned short port, PEER peer ) { PEER_CONNECTION; // pass it to the app if(connection->callbacks.qrPublicAddressCallback) connection->callbacks.qrPublicAddressCallback(peer, ip, port, connection->callbacks.param); } /************** ** FUNCTIONS ** **************/ PEERBool piStartReporting ( PEER peer, SOCKET socket, unsigned short port ) { int rcode; PEER_CONNECTION; // Check that we're not reporting. ////////////////////////////////// assert(!connection->queryReporting); if(connection->queryReporting) piStopReporting(peer); // Init qr2. //////////// if(socket == INVALID_SOCKET) { rcode = qr2_initA(&connection->queryReporting, NULL, PI_QUERYPORT, connection->title, connection->qrSecretKey, 1, connection->natNegotiate, piQRServerKeyCallback, piQRPlayerKeyCallback, piQRTeamKeyCallback, piQRKeyListCallback, piQRCountCallback, piQRAddErrorCallback, peer); } else { rcode = qr2_init_socketA(&connection->queryReporting, socket, port, connection->title, connection->qrSecretKey, 1, connection->natNegotiate, piQRServerKeyCallback, piQRPlayerKeyCallback, piQRTeamKeyCallback, piQRKeyListCallback, piQRCountCallback, piQRAddErrorCallback, peer); } if(rcode != 0) return PEERFalse; // Store the ID of the group room we're in. /////////////////////////////////////////// connection->reportingGroupID = connection->groupID; // Setup the nat-negotiate callback. //////////////////////////////////// qr2_register_natneg_callback(connection->queryReporting, piQRNatNegotiateCallback); // Set the public address callback. /////////////////////////////////// qr2_register_publicaddress_callback(connection->queryReporting, piQRPublicAddressCallback); // No options. ////////////// connection->reportingOptions = 0; return PEERTrue; } void piStopReporting ( PEER peer ) { PEER_CONNECTION; // Check that we're reporting. ////////////////////////////// if(!connection->queryReporting) return; // Clean up query-reporting. //////////////////////////// qr2_shutdown(connection->queryReporting); connection->queryReporting = NULL; } void piSendStateChanged ( PEER peer ) { PEER_CONNECTION; // Check that we're reporting. ////////////////////////////// if(!connection->queryReporting) return; // Send the statechanged. ///////////////////////// qr2_send_statechanged(connection->queryReporting); } void piQRThink ( PEER peer ) { PEER_CONNECTION; if(connection->queryReporting) qr2_think(connection->queryReporting); if(connection->autoMatchReporting) qr2_think(connection->autoMatchReporting); } PEERBool piStartAutoMatchReporting ( PEER peer ) { piOperation * operation; char autoMatchTitle[PI_TITLE_MAX_LEN]; int rcode; PEER_CONNECTION; // Check that we're not reporting. ////////////////////////////////// assert(!connection->autoMatchReporting); if(connection->autoMatchReporting) piStopAutoMatchReporting(peer); // Get the operation. ///////////////////// operation = connection->autoMatchOperation; assert(operation); // Setup the AutoMatch gamename. //////////////////////////////// strzcpy(autoMatchTitle, connection->title, sizeof(autoMatchTitle)); strzcat(autoMatchTitle, "am", sizeof(autoMatchTitle)); // Init qr2. //////////// if(operation->socket == INVALID_SOCKET) { rcode = qr2_initA(&connection->autoMatchReporting, NULL, PI_QUERYPORT, autoMatchTitle, connection->qrSecretKey, 1, connection->natNegotiate, piQRServerKeyCallback, piQRPlayerKeyCallback, piQRTeamKeyCallback, piQRKeyListCallback, piQRCountCallback, piQRAddErrorCallback, peer); } else { rcode = qr2_init_socketA(&connection->autoMatchReporting, operation->socket, operation->port, autoMatchTitle, connection->qrSecretKey, 1, connection->natNegotiate, piQRServerKeyCallback, piQRPlayerKeyCallback, piQRTeamKeyCallback, piQRKeyListCallback, piQRCountCallback, piQRAddErrorCallback, peer); // If we created the socket, hand over responsibility for it to qr2. //////////////////////////////////////////////////////////////////// if(operation->socketClose) { operation->socketClose = PEERFalse; connection->autoMatchReporting->read_socket = 1; } } // Set the error flag. ////////////////////// connection->autoMatchQRFailed = (rcode == 0)?PEERTrue:PEERFalse; // Return false if there was an error. ////////////////////////////////////// if(rcode != 0) return PEERFalse; // Setup the nat-negotiate callback. //////////////////////////////////// qr2_register_natneg_callback(connection->autoMatchReporting, piQRNatNegotiateCallback); // Set the public address callback. /////////////////////////////////// qr2_register_publicaddress_callback(connection->autoMatchReporting, piQRPublicAddressCallback); return PEERTrue; } void piStopAutoMatchReporting ( PEER peer ) { PEER_CONNECTION; // Check that we're reporting. ////////////////////////////// if(!connection->autoMatchReporting) return; // Clean up query-reporting. //////////////////////////// qr2_shutdown(connection->autoMatchReporting); connection->autoMatchReporting = NULL; // This socket should have been cleared after qr2_shutdown // but it's a copy of connection->autoMatchReporting->hbsock // Thus it needs to be cleared if it hasn't been already if (connection->autoMatchOperation->socket != INVALID_SOCKET) { connection->autoMatchOperation->socket = INVALID_SOCKET; } }