openmohaa/code/gamespy/Peer/peerQR.c
2023-02-04 21:00:01 +01:00

494 lines
11 KiB
C

/*
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;
}
}