Files
Jedi-Academy/codemp/xbox/XBLive.cpp
2013-04-04 14:32:05 -07:00

1525 lines
44 KiB
C++

// XBLive.cpp
//
#include "xtl.h"
#include "XBLive.h"
#include "..\client\client.h"
#include "..\game\q_shared.h"
#include "..\qcommon\qcommon.h"
#include "..\xbox\xboxcommon.h"
#include "..\xbox\XBVoice.h"
#include "../cgame/cg_local.h"
#include "../client/cl_data.h"
#include "../qcommon/xb_settings.h"
#define _UI // I'm going to hell.
#include "..\ui\ui_shared.h"
#undef _UI
#include "..\qcommon\stringed_ingame.h"
#define SERVICE_COUNT 2 // number of services we currently wish to log into
#define CONNECTED_TICKS 20 // number of ticks between connection tests
XONLINETASK_HANDLE logonHandle;
XONLINE_USER XBLAccountusers[XONLINE_MAX_STORED_ONLINE_USERS] = {0}; // The accounts that are on stored on the console / mu
XONLINE_USER XBLLoggedOnUsers[XONLINE_MAX_LOGON_USERS] = {0}; // The users that want to logon onto xbox live
DWORD XBLservices[ SERVICE_COUNT ] = {0}; // desired services
bool logged_on = false; // flag to determine if we are logged onto xbox live
bool xbl_inited = false; // flag to determine if XBL is initialized
static DWORD numOfXBLiveAccounts = 0; // Number of current xbox live accounts found on machine
static int selectedAccountIndex = 0; // The selected account index into XBLAccountusers array that user wants to login with
// Moved here from XBVoice - makes more sense, I think
XBOnlineInfo xbOnlineInfo;
/*
Crazy insane lookup table of XBL error codes
*/
#ifdef _DEBUG
const char *getXBLErrorName(HRESULT hr)
{
switch( hr )
{
case E_OUTOFMEMORY: return "E_OUTOFMEMORY";
case WSAEADDRNOTAVAIL: return "WSAEADDRNOTAVAIL";
// case WSACONNRESET: return "WSACONNRESET";
case XONLINE_E_OVERFLOW: return "XONLINE_E_OVERFLOW";
case XONLINE_E_NO_SESSION: return "XONLINE_E_NO_SESSION";
case XONLINE_E_USER_NOT_LOGGED_ON: return "XONLINE_E_USER_NOT_LOGGED_ON";
case XONLINE_E_NO_GUEST_ACCESS: return "XONLINE_E_NO_GUEST_ACCESS";
case XONLINE_E_NOT_INITIALIZED: return "XONLINE_E_NOT_INITIALIZED";
case XONLINE_E_NO_USER: return "XONLINE_E_NO_USER";
case XONLINE_E_INTERNAL_ERROR: return "XONLINE_E_INTERNAL_ERROR";
case XONLINE_E_OUT_OF_MEMORY: return "XONLINE_E_OUT_OF_MEMORY";
case XONLINE_E_TASK_BUSY: return "XONLINE_E_TASK_BUSY";
case XONLINE_E_SERVER_ERROR: return "XONLINE_E_SERVER_ERROR";
case XONLINE_E_IO_ERROR: return "XONLINE_E_IO_ERROR";
case XONLINE_E_BAD_CONTENT_TYPE: return "XONLINE_E_BAD_CONTENT_TYPE";
case XONLINE_E_USER_NOT_PRESENT: return "XONLINE_E_USER_NOT_PRESENT";
case XONLINE_E_PROTOCOL_MISMATCH: return "XONLINE_E_PROTOCOL_MISMATCH";
case XONLINE_E_INVALID_SERVICE_ID: return "XONLINE_E_INVALID_SERVICE_ID";
case XONLINE_E_INVALID_REQUEST: return "XONLINE_E_INVALID_REQUEST";
case XONLINE_E_LOGON_NO_NETWORK_CONNECTION: return "XONLINE_E_LOGON_NO_NETWORK_CONNECTION";
case XONLINE_S_LOGON_CONNECTION_ESTABLISHED: return "XONLINE_S_LOGON_CONNECTION_ESTABLISHED";
case XONLINE_E_LOGON_CANNOT_ACCESS_SERVICE: return "XONLINE_E_LOGON_CANNOT_ACCESS_SERVICE";
case XONLINE_E_LOGON_UPDATE_REQUIRED: return "XONLINE_E_LOGON_UPDATE_REQUIRED";
case XONLINE_E_LOGON_SERVERS_TOO_BUSY: return "XONLINE_E_LOGON_SERVERS_TOO_BUSY";
case XONLINE_E_LOGON_CONNECTION_LOST: return "XONLINE_E_LOGON_CONNECTION_LOST";
case XONLINE_E_LOGON_KICKED_BY_DUPLICATE_LOGON: return "XONLINE_E_LOGON_KICKED_BY_DUPLICATE_LOGON";
case XONLINE_E_LOGON_INVALID_USER: return "XONLINE_E_LOGON_INVALID_USER";
case XONLINE_E_LOGON_SERVICE_NOT_REQUESTED: return "XONLINE_E_LOGON_SERVICE_NOT_REQUESTED";
case XONLINE_E_LOGON_SERVICE_NOT_AUTHORIZED: return "XONLINE_E_LOGON_SERVICE_NOT_AUTHORIZED";
case XONLINE_E_LOGON_SERVICE_TEMPORARILY_UNAVAILABLE: return "XONLINE_E_LOGON_SERVICE_TEMPORARILY_UNAVAILABLE";
case XONLINE_S_LOGON_USER_HAS_MESSAGE: return "XONLINE_S_LOGON_USER_HAS_MESSAGE";
case XONLINE_E_LOGON_USER_ACCOUNT_REQUIRES_MANAGEMENT: return "XONLINE_E_LOGON_USER_ACCOUNT_REQUIRES_MANAGEMENT";
case XONLINE_S_LOGON_COMMIT_USER_CHANGE: return "XONLINE_S_LOGON_COMMIT_USER_CHANGE";
case XONLINE_S_LOGON_USER_CHANGE_COMPLETE: return "XONLINE_S_LOGON_USER_CHANGE_COMPLETE";
case XONLINE_E_LOGON_CHANGE_USER_FAILED: return "XONLINE_E_LOGON_CHANGE_USER_FAILED";
case XONLINE_E_LOGON_MU_NOT_MOUNTED: return "XONLINE_E_LOGON_MU_NOT_MOUNTED";
case XONLINE_E_LOGON_MU_IO_ERROR: return "XONLINE_E_LOGON_MU_IO_ERROR";
case XONLINE_E_LOGON_NOT_LOGGED_ON: return "XONLINE_E_LOGON_NOT_LOGGED_ON";
case XONLINE_E_NOTIFICATION_BAD_CONTENT_TYPE: return "XONLINE_E_NOTIFICATION_BAD_CONTENT_TYPE";
case XONLINE_E_NOTIFICATION_REQUEST_TOO_SMALL: return "XONLINE_E_NOTIFICATION_REQUEST_TOO_SMALL";
case XONLINE_E_NOTIFICATION_INVALID_MESSAGE_TYPE: return "XONLINE_E_NOTIFICATION_INVALID_MESSAGE_TYPE";
case XONLINE_E_NOTIFICATION_NO_ADDRESS: return "XONLINE_E_NOTIFICATION_NO_ADDRESS";
case XONLINE_E_NOTIFICATION_INVALID_PUID: return "XONLINE_E_NOTIFICATION_INVALID_PUID";
case XONLINE_E_NOTIFICATION_NO_CONNECTION: return "XONLINE_E_NOTIFICATION_NO_CONNECTION";
case XONLINE_E_NOTIFICATION_SEND_FAILED: return "XONLINE_E_NOTIFICATION_SEND_FAILED";
case XONLINE_E_NOTIFICATION_RECV_FAILED: return "XONLINE_E_NOTIFICATION_RECV_FAILED";
case XONLINE_E_NOTIFICATION_MESSAGE_TRUNCATED: return "XONLINE_E_NOTIFICATION_MESSAGE_TRUNCATED";
case XONLINE_E_NOTIFICATION_SERVER_BUSY: return "XONLINE_E_NOTIFICATION_SERVER_BUSY";
case XONLINE_E_NOTIFICATION_LIST_FULL: return "XONLINE_E_NOTIFICATION_LIST_FULL";
case XONLINE_E_NOTIFICATION_BLOCKED: return "XONLINE_E_NOTIFICATION_BLOCKED";
case XONLINE_E_NOTIFICATION_FRIEND_PENDING: return "XONLINE_E_NOTIFICATION_FRIEND_PENDING";
case XONLINE_E_NOTIFICATION_FLUSH_TICKETS: return "XONLINE_E_NOTIFICATION_FLUSH_TICKETS";
case XONLINE_E_NOTIFICATION_TOO_MANY_REQUESTS: return "XONLINE_E_NOTIFICATION_TOO_MANY_REQUESTS";
case XONLINE_E_NOTIFICATION_USER_ALREADY_EXISTS: return "XONLINE_E_NOTIFICATION_USER_ALREADY_EXISTS";
case XONLINE_E_NOTIFICATION_USER_NOT_FOUND: return "XONLINE_E_NOTIFICATION_USER_NOT_FOUND";
case XONLINE_E_NOTIFICATION_OTHER_LIST_FULL: return "XONLINE_E_NOTIFICATION_OTHER_LIST_FULL";
case XONLINE_E_NOTIFICATION_SELF: return "XONLINE_E_NOTIFICATION_SELF";
case XONLINE_E_NOTIFICATION_SAME_TITLE: return "XONLINE_E_NOTIFICATION_SAME_TITLE";
case XONLINE_E_NOTIFICATION_NO_TASK: return "XONLINE_E_NOTIFICATION_NO_TASK";
case XONLINE_E_MATCH_INVALID_SESSION_ID: return "XONLINE_E_MATCH_INVALID_SESSION_ID";
case XONLINE_E_MATCH_INVALID_TITLE_ID: return "XONLINE_E_MATCH_INVALID_TITLE_ID";
case XONLINE_E_MATCH_INVALID_DATA_TYPE: return "XONLINE_E_MATCH_INVALID_DATA_TYPE";
case XONLINE_E_MATCH_REQUEST_TOO_SMALL: return "XONLINE_E_MATCH_REQUEST_TOO_SMALL";
case XONLINE_E_MATCH_REQUEST_TRUNCATED: return "XONLINE_E_MATCH_REQUEST_TRUNCATED";
case XONLINE_E_MATCH_INVALID_SEARCH_REQ: return "XONLINE_E_MATCH_INVALID_SEARCH_REQ";
case XONLINE_E_MATCH_INVALID_OFFSET: return "XONLINE_E_MATCH_INVALID_OFFSET";
case XONLINE_E_MATCH_INVALID_ATTR_TYPE: return "XONLINE_E_MATCH_INVALID_ATTR_TYPE";
case XONLINE_E_MATCH_INVALID_VERSION: return "XONLINE_E_MATCH_INVALID_VERSION";
case XONLINE_E_MATCH_OVERFLOW: return "XONLINE_E_MATCH_OVERFLOW";
case XONLINE_E_MATCH_INVALID_RESULT_COL: return "XONLINE_E_MATCH_INVALID_RESULT_COL";
case XONLINE_E_MATCH_INVALID_STRING: return "XONLINE_E_MATCH_INVALID_STRING";
case XONLINE_E_MATCH_STRING_TOO_LONG: return "XONLINE_E_MATCH_STRING_TOO_LONG";
case XONLINE_E_MATCH_BLOB_TOO_LONG: return "XONLINE_E_MATCH_BLOB_TOO_LONG";
case XONLINE_E_MATCH_INVALID_ATTRIBUTE_ID: return "XONLINE_E_MATCH_INVALID_ATTRIBUTE_ID";
case XONLINE_E_MATCH_SESSION_ALREADY_EXISTS: return "XONLINE_E_MATCH_SESSION_ALREADY_EXISTS";
case XONLINE_E_MATCH_CRITICAL_DB_ERR: return "XONLINE_E_MATCH_CRITICAL_DB_ERR";
case XONLINE_E_MATCH_NOT_ENOUGH_COLUMNS: return "XONLINE_E_MATCH_NOT_ENOUGH_COLUMNS";
case XONLINE_E_MATCH_PERMISSION_DENIED: return "XONLINE_E_MATCH_PERMISSION_DENIED";
case XONLINE_E_MATCH_INVALID_PART_SCHEME: return "XONLINE_E_MATCH_INVALID_PART_SCHEME";
case XONLINE_E_MATCH_INVALID_PARAM: return "XONLINE_E_MATCH_INVALID_PARAM";
case XONLINE_E_MATCH_DATA_TYPE_MISMATCH: return "XONLINE_E_MATCH_DATA_TYPE_MISMATCH";
case XONLINE_E_MATCH_SERVER_ERROR: return "XONLINE_E_MATCH_SERVER_ERROR";
case XONLINE_E_MATCH_NO_USERS: return "XONLINE_E_MATCH_NO_USERS";
case XONLINE_E_MATCH_INVALID_BLOB: return "XONLINE_E_MATCH_INVALID_BLOB";
case XONLINE_S_OFFERING_NEW_CONTENT: return "XONLINE_S_OFFERING_NEW_CONTENT";
case XONLINE_S_OFFERING_NO_NEW_CONTENT: return "XONLINE_S_OFFERING_NO_NEW_CONTENT";
case XONLINE_E_OFFERING_BAD_REQUEST: return "XONLINE_E_OFFERING_BAD_REQUEST";
case XONLINE_E_OFFERING_INVALID_USER: return "XONLINE_E_OFFERING_INVALID_USER";
case XONLINE_E_OFFERING_INVALID_OFFER_ID: return "XONLINE_E_OFFERING_INVALID_OFFER_ID";
case XONLINE_E_OFFERING_INELIGIBLE_FOR_OFFER: return "XONLINE_E_OFFERING_INELIGIBLE_FOR_OFFER";
case XONLINE_E_OFFERING_OFFER_EXPIRED: return "XONLINE_E_OFFERING_OFFER_EXPIRED";
case XONLINE_E_OFFERING_SERVICE_UNREACHABLE: return "XONLINE_E_OFFERING_SERVICE_UNREACHABLE";
case XONLINE_E_OFFERING_PURCHASE_BLOCKED: return "XONLINE_E_OFFERING_PURCHASE_BLOCKED";
case XONLINE_E_OFFERING_PURCHASE_DENIED: return "XONLINE_E_OFFERING_PURCHASE_DENIED";
case XONLINE_E_OFFERING_BILLING_SERVER_ERROR: return "XONLINE_E_OFFERING_BILLING_SERVER_ERROR";
case XONLINE_E_OFFERING_OFFER_NOT_CANCELABLE: return "XONLINE_E_OFFERING_OFFER_NOT_CANCELABLE";
case XONLINE_E_OFFERING_NOTHING_TO_CANCEL: return "XONLINE_E_OFFERING_NOTHING_TO_CANCEL";
case XONLINE_E_OFFERING_ALREADY_OWN_MAX: return "XONLINE_E_OFFERING_ALREADY_OWN_MAX";
case XONLINE_E_OFFERING_NO_CHARGE: return "XONLINE_E_OFFERING_NO_CHARGE";
case XONLINE_E_OFFERING_PERMISSION_DENIED: return "XONLINE_E_OFFERING_PERMISSION_DENIED";
case XONLINE_E_OFFERING_NAME_TAKEN: return "XONLINE_E_OFFERING_NAME_TAKEN";
case XONLINE_E_BILLING_AUTHORIZATION_FAILED: return "XONLINE_E_BILLING_AUTHORIZATION_FAILED";
case XONLINE_E_BILLING_CREDIT_CARD_EXPIRED: return "XONLINE_E_BILLING_CREDIT_CARD_EXPIRED";
case XONLINE_E_BILLING_NON_ACTIVE_ACCOUNT: return "XONLINE_E_BILLING_NON_ACTIVE_ACCOUNT";
case XONLINE_E_BILLING_INVALID_PAYMENT_INSTRUMENT_STATUS: return "XONLINE_E_BILLING_INVALID_PAYMENT_INSTRUMENT_STATUS";
case XONLINE_E_UODB_KEY_ALREADY_EXISTS: return "XONLINE_E_UODB_KEY_ALREADY_EXISTS";
case XONLINE_E_MSGSVR_INVALID_REQUEST: return "XONLINE_E_MSGSVR_INVALID_REQUEST";
case XONLINE_E_FEEDBACK_NULL_TARGET: return "XONLINE_E_FEEDBACK_NULL_TARGET";
case XONLINE_E_FEEDBACK_BAD_TYPE: return "XONLINE_E_FEEDBACK_BAD_TYPE";
case XONLINE_E_FEEDBACK_CANNOT_LOG: return "XONLINE_E_FEEDBACK_CANNOT_LOG";
case XONLINE_E_STAT_BAD_REQUEST: return "XONLINE_E_STAT_BAD_REQUEST";
case XONLINE_E_STAT_INVALID_TITLE_OR_LEADERBOARD: return "XONLINE_E_STAT_INVALID_TITLE_OR_LEADERBOARD";
case XONLINE_E_STAT_TOO_MANY_SPECS: return "XONLINE_E_STAT_TOO_MANY_SPECS";
case XONLINE_E_STAT_TOO_MANY_STATS: return "XONLINE_E_STAT_TOO_MANY_STATS";
case XONLINE_E_STAT_USER_NOT_FOUND: return "XONLINE_E_STAT_USER_NOT_FOUND";
case XONLINE_E_STAT_SET_FAILED_0: return "XONLINE_E_STAT_SET_FAILED_0";
case XONLINE_E_STAT_PERMISSION_DENIED: return "XONLINE_E_STAT_PERMISSION_DENIED";
case XONLINE_E_STAT_LEADERBOARD_WAS_RESET: return "XONLINE_E_STAT_LEADERBOARD_WAS_RESET";
case XONLINE_E_STAT_INVALID_ATTACHMENT: return "XONLINE_E_STAT_INVALID_ATTACHMENT";
case XONLINE_S_STAT_CAN_UPLOAD_ATTACHMENT: return "XONLINE_S_STAT_CAN_UPLOAD_ATTACHMENT";
case XONLINE_E_STORAGE_INVALID_REQUEST: return "XONLINE_E_STORAGE_INVALID_REQUEST";
case XONLINE_E_STORAGE_ACCESS_DENIED: return "XONLINE_E_STORAGE_ACCESS_DENIED";
case XONLINE_E_STORAGE_FILE_IS_TOO_BIG: return "XONLINE_E_STORAGE_FILE_IS_TOO_BIG";
case XONLINE_E_STORAGE_FILE_NOT_FOUND: return "XONLINE_E_STORAGE_FILE_NOT_FOUND";
case XONLINE_E_STORAGE_INVALID_ACCESS_TOKEN: return "XONLINE_E_STORAGE_INVALID_ACCESS_TOKEN";
case XONLINE_E_STORAGE_CANNOT_FIND_PATH: return "XONLINE_E_STORAGE_CANNOT_FIND_PATH";
case XONLINE_E_STORAGE_FILE_IS_ELSEWHERE: return "XONLINE_E_STORAGE_FILE_IS_ELSEWHERE";
case XONLINE_E_STORAGE_INVALID_STORAGE_PATH: return "XONLINE_E_STORAGE_INVALID_STORAGE_PATH";
case XONLINE_E_STORAGE_INVALID_FACILITY: return "XONLINE_E_STORAGE_INVALID_FACILITY";
default:
return "***UNKNOWN_XTL_ERROR***";
}
}
#endif
/*
=====================
Conversion to/from strings for X-structs, stolen from Wolf
=====================
*/
static void DataToHexString(const BYTE* pData, int dataLen, char* pBuffer)
{
char* pOrig = pBuffer;
memset(pBuffer, 0, 2*dataLen+1); //The hex version of each byte is 2 chars
for(int i = 0; i < dataLen; i++)
{
sprintf(pBuffer, "%02x\0", pData[i]);
pBuffer+=2;
}
}
static void HexStringToData(const char* pBuffer, int bufferLen, BYTE* pData)
{
memset(pData, 0, bufferLen/2);
char sByte[3];
for(int i = 0; i < bufferLen; i+=2)
{
sByte[0] = pBuffer[0];
sByte[1] = pBuffer[1];
sByte[2] = '\0';
BYTE b = strtol(sByte, NULL, 16);
*pData = b;
pData++;
pBuffer+=2;
}
}
void XnAddrToString(const XNADDR *pxna, char *buffer)
{
DataToHexString((const BYTE *)pxna, sizeof(XNADDR), buffer);
}
void StringToXnAddr(XNADDR* pxna, const char* buffer)
{
int len = strlen(buffer);
assert( (len + 1) == XNADDR_STRING_LEN );
HexStringToData(buffer, len, (BYTE *)pxna);
}
void XNKIDToString(const XNKID *pxnkid, char* buffer)
{
DataToHexString((const BYTE *)pxnkid, sizeof(XNKID), buffer);
}
void StringToXNKID(XNKID* pxnkid, const char* buffer)
{
int len = strlen(buffer);
assert( (len + 1) == XNKID_STRING_LEN );
HexStringToData(buffer, len, (BYTE *)pxnkid);
}
void XNKEYToString(const XNKEY *pxnkey, char* buffer)
{
DataToHexString((const BYTE *)pxnkey, sizeof(XNKEY), buffer);
}
void StringToXNKEY(XNKEY* pxnkey, const char* buffer)
{
int len = strlen(buffer);
assert( (len + 1) == XNKEY_STRING_LEN );
HexStringToData(buffer, len, (BYTE *)pxnkey);
}
void XUIDToString(const XUID *pxuid, char* buffer)
{
DataToHexString((const BYTE *)pxuid, sizeof(XUID), buffer);
}
void StringToXUID(XUID* pxuid, const char* buffer)
{
int len = strlen(buffer);
assert( (len + 1) == XUID_STRING_LEN );
HexStringToData(buffer, len, (BYTE *)pxuid);
}
/*
==================
System Link quick n' dirty utilities
==================
*/
// We keep a single static copy of this crap, just for system link games.
// We should probably be reusing session, but I'm lazy, and that's ugly.
struct XBLSysLinkData_t
{
XNKEY xnkey;
XNKID xnkid;
bool initialized;
bool listening;
int chosen;
XBLSysLinkData_t(void) : initialized(false), listening(false) { }
void Initialize(void)
{
if (initialized)
return;
INT err = XNetCreateKey(&xnkid, &xnkey);
assert( !err );
err = XNetRegisterKey(&xnkid, &xnkey);
assert( !err );
initialized = true;
}
};
static XBLSysLinkData_t sysLink;
static bool sysLinkInitialized = false;
const XNKID *SysLink_GetXNKID(void)
{
sysLink.Initialize();
return &sysLink.xnkid;
}
const XNKEY *SysLink_GetXNKEY(void)
{
sysLink.Initialize();
return &sysLink.xnkey;
}
// Handles broadcast packets received from system link servers advertising their existence
//
void Syslink_PacketEvent( msg_t *msg )
{
// We only deal with these if we're listening for servers
if( !sysLink.listening )
return;
// Throw away packets that are too small, or don't have the -1 "header":
if ( msg->cursize < 4 || *(int *)msg->data != -1 )
return;
// Start pulling data from the packet:
MSG_BeginReadingOOB( msg );
MSG_ReadLong( msg ); // skip the -1
// Grab the command:
char *s = MSG_ReadStringLine( msg );
Cmd_TokenizeString( s );
char *c = Cmd_Argv(0);
// The only valid command is infoResponse (though this is no longer an accurate term):
if ( Q_stricmp(c, "infoResponse") != 0)
return;
// Grab the real payload:
char *infoString = MSG_ReadString( msg );
// Decode the XNADDR first, so we can check for dupes:
XNADDR tempAddr;
StringToXnAddr( &tempAddr, Info_ValueForKey(infoString, "xnaddr") );
// Find a slot for it in the localServers array:
for ( int i = 0 ; i < MAX_OTHER_SERVERS ; i++ )
{
// Empty slot - this ordering is ok, as we always fill in order:
if ( !cls.localServers[i].lastUpdate )
break;
// Whenever we get a dupe, update the info:
if ( memcmp( &tempAddr, &cls.localServers[i].HostAddress, sizeof(XNADDR) ) == 0 )
break;
}
// Silently fail if there are too many servers on the LAN:
if ( i == MAX_OTHER_SERVERS )
return;
// Get the slot that we'll be filling in:
serverInfo_t *server = &cls.localServers[i];
// Increase the list size if this isn't a dupe:
if( !server->lastUpdate )
cls.numlocalservers++;
Q_strncpyz(server->mapName, Info_ValueForKey(infoString, "mapname"), sizeof(server->mapName));
server->gameType = atoi(Info_ValueForKey(infoString, "gametype"));
server->saberOnly = atoi(Info_ValueForKey(infoString, "wdisable"));
server->forceDisable = atoi(Info_ValueForKey(infoString, "fdisable"));
server->clients = atoi(Info_ValueForKey(infoString, "clients"));
server->maxClients = atoi(Info_ValueForKey(infoString, "sv_maxclients"));
// Re-stamp the entry with current time:
server->lastUpdate = Sys_Milliseconds();
StringToXnAddr(&server->HostAddress, Info_ValueForKey(infoString, "xnaddr"));
StringToXNKID(&server->SessionID, Info_ValueForKey(infoString, "xnkid"));
StringToXNKEY(&server->KeyExchangeKey, Info_ValueForKey(infoString, "xnkey"));
}
// Used by the UI to control whether or not we listen to system link advert packets:
void Syslink_Listen( bool bListen )
{
sysLink.listening = bListen;
// Mark all slots as unused (invisible) when we start listening:
if( bListen )
{
cls.numlocalservers = 0;
for( int i = 0; i < MAX_OTHER_SERVERS; ++i )
cls.localServers[i].lastUpdate = 0;
}
}
void Syslink_SetChosenServerIndex( const int index )
{
sysLink.chosen = index;
}
//=============================================================================
// Centralized function for handling connection setup and storage of server
// info into xbc. ALL connections should come through here at some point.
//
bool Net_XboxConnect(const XNKID *pxnkid, const XNKEY *pxnkey, const XNADDR *pxnaddr)
{
// 1) Register the session key
if ( XNetRegisterKey( pxnkid, pxnkey ) )
{
Com_Error( ERR_FATAL, "Unable to register key for connect\n" );
}
// 2) Convert the address to a winsock usable format
IN_ADDR pseudoIP;
if ( XNetXnAddrToInAddr( pxnaddr, pxnkid, &pseudoIP ) )
{
Com_Error( ERR_FATAL, "Couldn't convert XNADDR to IN_ADDR for connect\n" );
}
// We're going to connect, store this information off for reference
memcpy(&xbc.HostAddress, pxnaddr, sizeof(XNADDR));
memcpy(&xbc.KeyExchangeKey, pxnkey, sizeof(XNKEY));
memcpy(&xbc.SessionID, pxnkid, sizeof(XNKID));
memcpy(&xbc.SrvAddr, &pseudoIP, sizeof(IN_ADDR));
// Issue the connect command
Cbuf_ExecuteText( EXEC_APPEND, va( "connect %i.%i.%i.%i\n", pseudoIP.S_un.S_un_b.s_b1,
pseudoIP.S_un.S_un_b.s_b2,
pseudoIP.S_un.S_un_b.s_b3,
pseudoIP.S_un.S_un_b.s_b4 ) );
// Start up voice system as well - Live does this when you login
if( !logged_on )
g_Voice.Initialize();
return true;
}
// Tears down the current client connection. Should ONLY be called by non-servers,
// and ONLY when we're really leaving the session. We can't verify much though -
// as we can be called after SV_Shutdown has been called (see Com_Error for an example).
bool Net_XboxDisconnect(void)
{
XNetUnregisterKey( &xbc.SessionID );
memset(&xbc.SessionID, 0, sizeof(XNKID));
// Tell voice that we left the game
g_Voice.LeaveSession();
// If we're playing system link, shutdown voice entirely, Live needs to keep it up
if( !logged_on )
g_Voice.Shutdown();
if ( logged_on )
{
XBL_F_OnClientLeaveSession();
XBL_PL_OnClientLeaveSession();
}
// Blow away our player list and such. Servers also do this in SV_Shutdown()
memset( &xbOnlineInfo, 0, sizeof(xbOnlineInfo) );
return true;
}
// Translates the UI's indexes into server structs, as there can be holes in the list
serverInfo_t *SysLink_GetServer( int index )
{
if( index < 0 || index >= cls.numlocalservers )
return NULL;
for( int i = 0; i < MAX_OTHER_SERVERS; ++i )
{
// Is this entry valid?
if( cls.localServers[i].lastUpdate )
{
if( index == 0 )
return &cls.localServers[i];
index--;
}
}
// Should never happen
return NULL;
}
// System Link analog to the XBL_MM_Join_Server function. UI used to issue the connect
// directly, but we need to do key management, so we've moved the code over here.
bool SysLink_JoinServer( void )
{
if( sysLink.chosen < 0 || sysLink.chosen >= cls.numlocalservers )
return false;
serverInfo_t *server = SysLink_GetServer( sysLink.chosen );
if( !server )
return false;
return Net_XboxConnect(&server->SessionID, &server->KeyExchangeKey, &server->HostAddress);
}
// Gets your XNADDR. Used by syslink, and lots of other people.
const XNADDR *Net_GetXNADDR( DWORD *pStatus /* = NULL */)
{
static XNADDR hostAddr;
DWORD dwStatus;
do
{
dwStatus = XNetGetTitleXnAddr( &hostAddr );
} while ( dwStatus == XNET_GET_XNADDR_PENDING );
// Error checking
if ( dwStatus == XNET_GET_XNADDR_NONE )
{
assert(!"Error getting XBox title address.");
}
if (pStatus)
*pStatus = dwStatus;
return &hostAddr;
}
// Gets the XNKID for the current session - works for anyone
const XNKID *Net_GetXNKID(void)
{
// Clients ALWAYS store the session ID in xbc. Servers use either their syslink
// copy, or the one in the matchmaking structure.
if (!com_sv_running->integer)
return &xbc.SessionID;
else if (logged_on)
return &session.SessionID;
else
return SysLink_GetXNKID();
}
// Sets the account index into the array
//
void XBL_SetAccountIndex(const int index)
{
selectedAccountIndex = index;
}
// Used by the code that handles stored invitations
bool XBL_SetAccountIndexByXuid(const XUID *xuid)
{
for( int i = 0; i < numOfXBLiveAccounts; ++i )
{
if( XOnlineAreUsersIdentical( &XBLAccountusers[i].xuid, xuid ) )
{
selectedAccountIndex = i;
return true;
}
}
// Possible. Imagine someone accepts an invite with an MU account, then pulls it.
// There's even time to go to the dash and delete the account.
return false;
}
// Return the selected account index of the XBL user
//
int XBL_GetSelectedAccountIndex(void)
{
return selectedAccountIndex;
}
#define ACCOUNT_CHECK_FREQUENCY 300
// Returns the total number of accounts found on the console
//
DWORD XBL_GetNumAccounts( bool doNow )
{
// this is continuously called by the sign-in menu so use that to update resident accounts
// in case someone plugs in an MMU while on that screen - oh, just dont do it every frame!
//
static int pass = 0;
if( doNow )
pass = ACCOUNT_CHECK_FREQUENCY;
if( ++pass >= ACCOUNT_CHECK_FREQUENCY)
{
HRESULT result = XOnlineGetUsers( XBLAccountusers, &numOfXBLiveAccounts );
pass=0;
}
return numOfXBLiveAccounts;
}
// Returns theuser info based on index of array
//
XONLINE_USER* XBL_GetUserInfo(const int index)
{
if(index >= 0 && index < numOfXBLiveAccounts)
return &XBLAccountusers[index];
else
return NULL;
}
// Refresh the list of XBox Live users found on this system
//
HRESULT XBL_RefreshUserList()
{
// Wait for any inserted MUs to mount - may contain user info
//
while ( XGetDeviceEnumerationStatus() == XDEVICE_ENUMERATION_BUSY ) {}
// XOnlineStartup calls XNetStartup and WSAStartup
// already done in Net_Init() via main()
//
XONLINE_STARTUP_PARAMS xosp = { 0 };
HRESULT result = XOnlineStartup( &xosp );
if( result < S_OK )
{
Com_Printf("XBLive - Failed at start (XOnlineStartup) \n");
return result;
}
// Get accounts stored on the hard disk and XMUs
//
result = XOnlineGetUsers( XBLAccountusers, &numOfXBLiveAccounts );
if( result < S_OK )
{
Com_Printf("XBLive - Error trying to find accounts\n");
numOfXBLiveAccounts = 0;
}
return result;
}
// Set up the XBox Live functionality
//
HRESULT XBL_Init()
{
if( xbl_inited ) // already inited
{
Com_Printf("XBLive is already initialized\n");
return S_OK;
}
HRESULT result = XBL_RefreshUserList();
if( result < S_OK )
{
Com_Printf("XBLive - XBL_Init failed!\n");
}
else
{
xbl_inited = true;
}
memset(&xbOnlineInfo, 0, sizeof(XBOnlineInfo));
return result;
}
// Log onto Microsoft server. This is a long and complicated procedure.
// We break it up into stages, and tell the function where we want to resume.
//
bool XBL_Login( XBLoginState loginState )
{
assert( !logged_on );
// Stage ONE: Make sure that we have a valid account picked, then check for passcode.
if( loginState <= LOGIN_PASSCODE_CHECK )
{
// Shouldn't happen:
assert( numOfXBLiveAccounts > 0 );
// Selected index should be valid. Does it need a passcode?
if( XBLAccountusers[selectedAccountIndex].dwUserOptions & XONLINE_USER_OPTION_REQUIRE_PASSCODE )
{
// OK. Display the passcode popup
Menus_ActivateByName( "xbox_passcode" );
return false;
}
}
// Stage TWO: call XOnlineLogon, and check for system errors
if( loginState <= LOGIN_CONNECT )
{
// Select which services we want to log into
XBLservices[0] = XONLINE_MATCHMAKING_SERVICE;
XBLservices[1] = XONLINE_FEEDBACK_SERVICE;
// XOnlineLogon() allows a list of up to 4 players (1 per controller).
// The list must be a one-to-one match of controller to user in order
// for the online system to recognize which user is using which controller.
ZeroMemory( &XBLLoggedOnUsers, sizeof( XBLLoggedOnUsers ) );
// Put the user in the right port?
XBLLoggedOnUsers[ IN_GetMainController() ] = XBLAccountusers[selectedAccountIndex];
HRESULT result = XOnlineLogon( XBLLoggedOnUsers, XBLservices, SERVICE_COUNT, NULL, &logonHandle);
// Check return value
if( result == S_OK )
{
// OK. It worked. Carry on...
}
else if( result == XONLINE_E_LOGON_NO_NETWORK_CONNECTION )
{
// No connection. Show the popup.
UI_xboxErrorPopup( XB_POPUP_CANNOT_CONNECT );
return false;
}
else
{
// Should never happen
assert( 0 );
}
//
// We could break into two stages at this point, if we need to. Doesn't seem necessary
//
// Wait for the logon process to complete
do
{
result = XOnlineTaskContinue( logonHandle );
}
while( result == XONLINETASK_S_RUNNING );
// OK. What happened?
result = XOnlineLogonTaskGetResults( logonHandle );
switch( result )
{
case XONLINE_S_LOGON_CONNECTION_ESTABLISHED:
// No system errors. Still need to check users and services below.
break;
case XONLINE_E_LOGON_CANNOT_ACCESS_SERVICE:
// Network troubleshooter popup
UI_xboxErrorPopup( XB_POPUP_CANNOT_CONNECT );
return false;
case XONLINE_E_LOGON_CONNECTION_LOST:
// Network troubleshooter popup
UI_xboxErrorPopup( XB_POPUP_CANNOT_CONNECT );
return false;
case XONLINE_E_LOGON_INVALID_USER:
// Account management popup
UI_xboxErrorPopup( XB_POPUP_INVALID_USER );
return false;
case XONLINE_E_LOGON_KICKED_BY_DUPLICATE_LOGON:
// Kicked by dupe popup
UI_xboxErrorPopup( XB_POPUP_DUPLICATE_LOGON );
return false;
case XONLINE_E_LOGON_SERVERS_TOO_BUSY:
// Busy servers popup
UI_xboxErrorPopup( XB_POPUP_BUSY_SERVERS );
return false;
case XONLINE_E_LOGON_UPDATE_REQUIRED:
// Update required popup
UI_xboxErrorPopup( XB_POPUP_AUTOUPDATE );
return false;
default:
assert( 0 );
break;
}
}
// Stage THREE: Call XOnlineGetLogonUsers(), and check for user errors
if( loginState <= LOGIN_USER_ERRORS )
{
XONLINE_USER *updatedList = XOnlineGetLogonUsers();
// Should never happen:
assert( updatedList );
// Copy the results
memcpy(XBLLoggedOnUsers, updatedList, sizeof(XONLINE_USER) * XONLINE_MAX_LOGON_USERS);
// Check for user problems
for(int i = 0; i < XONLINE_MAX_LOGON_USERS; i++)
{
if( XBLLoggedOnUsers[i].xuid.qwUserID != 0 )
{
if( XBLLoggedOnUsers[i].hr == S_OK)
{
// No problem with this user. We only have one, so we could move on, but whatever...
continue;
}
if( XBLLoggedOnUsers[i].hr == XONLINE_S_LOGON_USER_HAS_MESSAGE )
{
// Optional message popup - force UI to not finish login
UI_xboxErrorPopup( XB_POPUP_OPTIONAL_MESSAGE );
return false;
}
else if( XBLLoggedOnUsers[i].hr == XONLINE_E_LOGON_USER_ACCOUNT_REQUIRES_MANAGEMENT )
{
// Required management popup - force UI to not finish login
UI_xboxErrorPopup( XB_POPUP_REQUIRED_MESSAGE );
return false;
}
else
{
// This should never happen
assert( 0 );
}
}
}
}
// Stage FOUR: Check for service errors
if( loginState <= LOGIN_SERVICE_ERRORS )
{
// VVFIXME - Need to check for these and handle them appropriately
// Also, above code needs to check for user restrictions (voice banning)
}
// Stage FIVE: Finish logging in. Fire up other systems
if( loginState <= LOGIN_FINISH )
{
logged_on = true;
// We initialize voice above, in Net_XboxConnect(). But if we're logging
// into live, we need to do it here, becuase we need to check for our voice
// headset when we set initial presence info. Gar!
g_Voice.Initialize();
XBL_F_Init();
//Set the player name to the Xbox live account name
Q_strncpyz(ClientManager::ActiveClient().autoName,
XBLLoggedOnUsers[ IN_GetMainController() ].szGamertag,
STRING_SIZE);
Cvar_SetValue( "xbl_liveconnected", 1 );
// OK. We're responsible for switching menus, too
Menus_CloseAll();
Menus_ActivateByName( "xbl_lobbymenu" );
}
return true;
}
// This is to cleanup logging in when an error is encountered
void XBL_AbortLogin(void)
{
if(logonHandle)
{
XOnlineTaskClose(logonHandle);
logonHandle = NULL;
}
}
// shutdown xbox live functionality
//
HRESULT XBL_Cleanup()
{
if(!logged_on)
return S_OK;
XBL_F_Cleanup();
g_Voice.Shutdown();
XBL_MM_Shutdown( false );
if(logonHandle)
{
XOnlineTaskClose(logonHandle);
logonHandle = NULL;
}
int result = XOnlineCleanup();
if( result == S_OK )
{
logged_on = false;
xbl_inited = false;
}
else
{
Com_Printf("XBLive - couldn't log off - failed to cleanup\n");
}
//friends stuff clean up
Cvar_Set("ui_gameinvite", "");
Cvar_Set("ui_friendinvite", "");
Cvar_Set("xbl_friendnotifiation", "");
return result;
}
// Simple wrapper around XNetGetEthernetLinkStatus()
bool Net_ConnectionStatus( void )
{
return (bool)(XNetGetEthernetLinkStatus());
}
// Set a CVAR flagging wether or not we currently have network connection
//
void XBL_Check_For_Net_Connection()
{
static DWORD stateBefore = XNET_ETHERNET_LINK_ACTIVE;
static int counter = CONNECTED_TICKS;
// Check status every 20 frames (a little more than 1Hz)
if(++counter > CONNECTED_TICKS)
{
counter = 0;
DWORD stateNow = XNetGetEthernetLinkStatus();
Cvar_SetValue( "xb_netConnected", stateNow );
// Did we just lose our connection? We need to take various actions depending on where we are:
bool lostConnection = ((stateNow != stateBefore) && (stateNow == 0));
if( !lostConnection )
{
stateBefore = stateNow;
return;
}
// Grab the current menu (if there is one):
menuDef_t *menu = Menu_GetFocused();
// Grab our xb_gameType, we'll need it:
int xbGT = Cvar_VariableIntegerValue( "xb_gameType" );
if( menu && !Q_stricmp(menu->window.name, "main") )
{
// If we're on the main menu, and SystemLink is highlighted, move focus.
// I need to do stupid tricks here, or the UI calls traps that fail!
itemDef_t *item = Menu_GetFocusedItem( menu );
if( item && !Q_stricmp(item->window.name, "linkButton") )
VM_Call( uivm, UI_KEY_EVENT, A_CURSOR_UP, qtrue ); // Send a "move the cursor up"
}
else if( cls.state == CA_DISCONNECTED && xbGT == 2 )
{
// We're in the front-end, and we've chosen system link:
UI_xboxErrorPopup( XB_POPUP_SYSTEM_LINK_LOST );
}
else if( cls.state == CA_DISCONNECTED && logged_on )
{
// We're in the front-end, and we've chosen Live! We wait until after login,
// because the login screen is where we prompt people to go to the troubleshooter:
UI_xboxErrorPopup( XB_POPUP_SYSTEM_LINK_LOST );
}
else if( cls.state > CA_DISCONNECTED && xbGT >= 2 )
{
// If we're playing (or started connecting) to SysLink or Live, bomb out:
Com_Error( ERR_DROP, "@MENUS_SYSTEM_LINK_LOST" );
}
stateBefore = stateNow;
}
}
bool XBL_PumpLogon( void )
{
if(logonHandle)
{
// Pump logon
HRESULT result = XOnlineTaskContinue( logonHandle );
result = XOnlineLogonTaskGetResults( logonHandle );
#ifdef _DEBUG
if ( FAILED( result ) )
Com_Printf("TaskContinue(logonHandle) failed: %s\n", getXBLErrorName(result));
#endif
if( result == XONLINE_S_LOGON_CONNECTION_ESTABLISHED || result == S_OK )
{
return true;
}
else if( result == XONLINE_E_LOGON_KICKED_BY_DUPLICATE_LOGON )
{
XBL_Cleanup();
Com_Error( ERR_DROP, "@MENUS_XBOX_DUPLICATE_LOGON" );
// UI_xboxErrorPopup( XB_POPUP_DUPLICATE_LOGON );
return false;
}
else
{
XBL_Cleanup();
Com_Error( ERR_DROP, "@MENUS_XBOX_LOST_CONNECTION" );
// UI_xboxErrorPopup( XB_POPUP_LOST_CONNECTION );
return false;
}
}
return true;
}
void UI_HandleXBLogonError(const int result);
// run once every game cycle
//
void XBL_Tick()
{
// check if this xbox has a network connection
//
XBL_Check_For_Net_Connection();
if( logged_on )
{
if( !XBL_PumpLogon() )
return;
// tick friends
//
XBL_F_Tick();
// tick matchmaking
//
XBL_MM_Tick();
}
// tick player list - this needs to happen in system link too!
//
XBL_PL_Tick();
// Tick Voice - do this elsewhere?
g_Voice.Tick();
// If we're currently listening for system link games, throw out any
// that we haven't heard from in some time:
if( sysLink.listening )
{
for( int i = 0; i < MAX_OTHER_SERVERS; ++i )
{
if( cls.localServers[i].lastUpdate &&
cls.localServers[i].lastUpdate + 8000 < Sys_Milliseconds() )
{
cls.localServers[i].lastUpdate = 0;
cls.numlocalservers--;
}
}
}
}
//----------------------------------------------------------------------------
// Name: XBL_CheckServer()
// Desc: checks server we are trying to connect to
//----------------------------------------------------------------------------
void XBL_CheckServer()
{
// VVFIXME - Does this work? At all? What the hell?
//don't test if in sys link OR server OR not considered playing yet
if(!logged_on || com_sv_running->integer ||!XBL_F_GetState(XONLINE_FRIENDSTATE_FLAG_PLAYING))
return;
static int timer = 0;
if(timer < Sys_Milliseconds())
{
timer = Sys_Milliseconds() + 4000;
if(!XBL_MM_IsSessionIDValid(Net_GetXNKID()))
Com_Error( ERR_DROP, SE_GetString("XBL_GAME_SESSION_NOT_AVAIL") );
}
}
//-----------------------------------------------------------------------------
// Xbox Error Popup support stuff
//-----------------------------------------------------------------------------
// What popup is active at the moment?
static xbErrorPopupType sPopup = XB_POPUP_NONE;
// Creates the requested popup, then displays it.
// Establishes context so proper action will be taken on a selection.
void UI_xboxErrorPopup(xbErrorPopupType popup)
{
// Set our context
sPopup = popup;
Cvar_Set( "xb_PopupTitle", "" );
// Set the menu cvars
switch( popup )
{
case XB_POPUP_CANNOT_CONNECT:
Cvar_Set( "xb_errMessage", "@MENUS_XBOX_CANNOT_CONNECT" );
break;
case XB_POPUP_AUTOUPDATE:
Cvar_Set( "xb_errMessage", "@MENUS_XBOX_REQUIRED_UPDATE" );
break;
case XB_POPUP_REQUIRED_MESSAGE:
Cvar_Set( "xb_errMessage", "@MENUS_XBOX_REQUIRED_MESSAGE" );
break;
case XB_POPUP_OPTIONAL_MESSAGE:
Cvar_Set( "xb_errMessage", "@MENUS_XBOX_OPTIONAL_MESSAGE" );
break;
case XB_POPUP_LOST_CONNECTION:
Cvar_Set( "xb_errMessage", "@MENUS_XBOX_LOST_CONNECTION" );
break;
case XB_POPUP_INVALID_USER:
Cvar_Set( "xb_errMessage", "@MENUS_XBOX_ACCOUNT_NOT_CURRENT" );
break;
case XB_POPUP_DUPLICATE_LOGON:
Cvar_Set( "xb_errMessage", "@MENUS_XBOX_DUPLICATE_LOGON" );
break;
case XB_POPUP_BUSY_SERVERS:
Cvar_Set( "xb_errMessage", "@MENUS_XBOX_LIVE_BUSY" );
break;
case XB_POPUP_CONFIRM_NEW_ACCOUNT:
Cvar_Set( "xb_errMessage", "@MENUS_XBOX_NEW_ACCOUNT_CONFIRM" );
break;
case XB_POPUP_CONFIRM_LOGOFF:
Cvar_Set( "xb_errMessage", "@MENUS_XBOX_SIGN_OUT" );
break;
case XB_POPUP_COM_ERROR:
Cvar_Set( "xb_errMessage", Cvar_VariableString( "com_errorMessage" ) );
break;
case XB_POPUP_QUICKMATCH_NO_RESULTS:
case XB_POPUP_OPTIMATCH_NO_RESULTS:
// Special case. These don't use the normal popup.
sPopup = XB_POPUP_NONE;
Menus_ActivateByName( "no_servers_popup" );
return; // <- Not a break!
case XB_POPUP_MATCHMAKING_ERROR:
Cvar_Set( "xb_errMessage", "@MENUS_XBOX_MATCHMAKING_ERROR" );
break;
case XB_POPUP_SYSTEM_LINK_LOST:
Cvar_Set( "xb_errMessage", "@MENUS_SYSTEM_LINK_LOST" );
break;
case XB_POPUP_QUIT_CONFIRM:
Cvar_Set( "xb_errMessage", "@MENUS_QUIT_CURRENT_HOSTED_GAME_STATUS_LOST");
Cvar_Set( "xb_PopupTitle", "@MENUS_QUIT" );
break;
case XB_POPUP_QUIT_HOST_CONFIRM:
Cvar_Set( "xb_errMessage", "@MENUS_QUIT_CURRENT_HOSTED_GAME_AND");
Cvar_Set( "xb_PopupTitle", "@MENUS_QUIT" );
break;
case XB_POPUP_HOST_JOIN_CONFIRM:
// Message here depends on whether or not we're the server:
if( com_sv_running->integer )
Cvar_Set( "xb_errMessage", "@MENUS_HOST_JOINING_OTHER" );
else
Cvar_Set( "xb_errMessage", "@MENUS_CLIENT_JOINING_OTHER" );
break;
case XB_POPUP_CONFIRM_FRIEND_REMOVE:
Cvar_Set( "xb_errMessage", va( SE_GetString( "MENUS_CONFIRM_FRIEND_REMOVE" ), Cvar_VariableString( "fl_selectedName" ) ) );
break;
case XB_POPUP_RESPAWN_NEEDED:
Cvar_Set( "xb_errMessage" , "@MENUS_RESPAWN_NEEDED");
break;
case XB_POPUP_CORRUPT_SETTINGS:
Cvar_Set( "xb_errMessage", "@MENUS_CORRUPT_SETTINGS" );
break;
default:
Com_Error( ERR_FATAL, "ERROR: Invalid popup type %i\n", popup );
}
// Display the menu
Menus_ActivateByName( "xbox_error_popup" );
}
// Accepts a response to the currently dislpayed popup.
// Does whatever is necessary based on which popup was visible, and what the response was.
void UI_xboxPopupResponse( void )
{
if( sPopup == XB_POPUP_NONE )
Com_Error( ERR_FATAL, "ERROR: Got a popup response with no valid context\n" );
int response = Cvar_VariableIntegerValue( "xb_errResponse" );
extern void Sys_Reboot( const char *reason );
// Remove remnant kruft:
if( response == 2 ) // && sPopup != XB_POPUP_OPTIONAL_MESSAGE )
{
// Do nothing.
return;
}
switch( sPopup )
{
case XB_POPUP_CANNOT_CONNECT:
if( response == 0 ) // A
{
// Won't return
Sys_Reboot( "net_config" );
}
else if( response == 1 ) // B
{
XBL_AbortLogin();
Menus_CloseAll();
Menus_ActivateByName( "main" );
return;
}
break;
case XB_POPUP_AUTOUPDATE:
if( response == 0 ) // A
{
// Won't return
XOnlineTitleUpdate( 0 );
}
else // B
{
XBL_AbortLogin();
Menus_CloseAll();
Menus_ActivateByName( "main" );
}
break;
case XB_POPUP_REQUIRED_MESSAGE:
if( response == 0 ) // A
{
// Won't return
Sys_Reboot( "manage_account" );
}
else // B
{
XBL_AbortLogin();
Menus_CloseAll();
Menus_ActivateByName( "main" );
}
break;
case XB_POPUP_OPTIONAL_MESSAGE:
if( response == 0 ) // A
{
// Won't return
Sys_Reboot( "manage_account" );
}
else if( response == 1 ) // B
{
// Resume logging in - special case as we can re-trigger a popup:
Menus_CloseByName( "xbox_error_popup" );
sPopup = XB_POPUP_NONE;
XBL_Login( LOGIN_SERVICE_ERRORS );
return;
}
break;
case XB_POPUP_LOST_CONNECTION:
if( response == 0 ) // A
{
XBL_Cleanup();
Menus_CloseAll();
Menus_ActivateByName( "main" );
}
else if( response == 1 ) // B
{
// Invalid response!
return;
}
break;
case XB_POPUP_INVALID_USER:
if( response == 0 ) // A
{
// Won't return
Sys_Reboot( "manage_account" );
}
else // B
{
XBL_AbortLogin();
Menus_CloseAll();
Menus_ActivateByName( "main" );
}
break;
case XB_POPUP_DUPLICATE_LOGON:
if( response == 0 ) // A
{
XBL_Cleanup();
Menus_CloseAll();
Menus_ActivateByName( "main" );
}
else if( response == 1 ) // B
{
// Invalid response!
return;
}
break;
case XB_POPUP_BUSY_SERVERS:
if( response == 0 ) // A
{
// Try again - special case because we could re-trigger a popup
XBL_AbortLogin();
Menus_CloseByName( "xbox_error_popup" );
sPopup = XB_POPUP_NONE;
XBL_Login( LOGIN_CONNECT );
return;
}
else // B
{
XBL_AbortLogin();
Menus_CloseAll();
Menus_ActivateByName( "main" );
}
break;
case XB_POPUP_CONFIRM_NEW_ACCOUNT:
if( response == 0 ) // A
{
// Won't return
Sys_Reboot( "new_account" );
}
else // B
{
// Close popup, leave user on account screen:
Menus_CloseByName( "xbox_error_popup" );
}
break;
case XB_POPUP_CONFIRM_LOGOFF:
if( response == 0) // A
{
// User wants to sign out
XBL_Cleanup();
Menus_CloseAll();
Menus_ActivateByName( "main" );
}
else // B
{
// The popup is gone, make sure that we're back on the lobby
Menus_CloseAll();
Menus_ActivateByName( "xbl_lobbymenu" );
}
break;
case XB_POPUP_COM_ERROR:
if( response == 0 ) // A
{
//XBL_Cleanup();
Menus_CloseAll();
if( logged_on )
Menus_ActivateByName( "xbl_lobbymenu" );
else
Menus_ActivateByName( "main" );
}
else if( response == 1 ) // B
{
// Invalid response!
return;
}
break;
case XB_POPUP_QUICKMATCH_NO_RESULTS:
case XB_POPUP_OPTIMATCH_NO_RESULTS:
// These don't use the normal popup menu
assert( 0 );
break;
case XB_POPUP_MATCHMAKING_ERROR:
if( response == 0 ) // A
{
// Some kind of fatal error with the quickmatch or optimatch
// Force the user back to the lobby
Menus_CloseAll();
Menus_ActivateByName( "xbl_lobbymenu" );
}
else
{
// Invalid response!
return;
}
break;
case XB_POPUP_SYSTEM_LINK_LOST:
if( response == 0 ) // A
{
XBL_Cleanup();
Menus_CloseAll();
Menus_ActivateByName( "main" );
}
else if( response == 1 ) // B
{
// Invalid response!
return;
}
break;
case XB_POPUP_QUIT_CONFIRM:
if( response == 0 ) // A continue
{
itemDef_t it;
it.parent = Menu_GetFocused();
Item_RunScript(&it, "uiScript Leave");
// Cbuf_ExecuteText( EXEC_APPEND, "disconnect\n" );
// trap_Key_SetCatcher( KEYCATCH_UI );
// Menus_CloseAll();
return;
}
else
{
Menus_CloseByName( "xbox_error_popup" );
}
break;
case XB_POPUP_QUIT_HOST_CONFIRM:
if( response == 0 ) // A continue
{
itemDef_t it;
it.parent = Menu_GetFocused();
Item_RunScript(&it, "uiScript Leave");
// Cbuf_ExecuteText( EXEC_APPEND, "disconnect\n" );
// trap_Key_SetCatcher( KEYCATCH_UI );
// Menus_CloseAll();
return;
}
else
{
Menus_CloseByName( "xbox_error_popup" );
}
break;
case XB_POPUP_HOST_JOIN_CONFIRM:
if( response == 0 ) // A continue
{
// User wants to join. Now we check for lag:
XONLINE_FRIEND* curFriend = XBL_F_GetChosenFriend();
if( curFriend && XBL_MM_ThisSessionIsLagging( &curFriend->sessionID ) )
{
Menus_CloseByName( "xbox_error_popup" );
Menus_ActivateByName( "slow_server_popup" );
}
else
{
// No lag. This function is designed for the case when the
// user agrees to join a low-ping server, but it does what we want:
Menus_CloseByName( "xbox_error_popup" );
XBL_MM_JoinLowQOSSever();
}
}
else
{
// User decided not to continue joining
XBL_MM_DontJoinLowQOSSever();
Menus_CloseByName( "xbox_error_popup" );
}
break;
case XB_POPUP_CONFIRM_FRIEND_REMOVE:
if( response == 0 ) // A confirm
{
// Remove them from the friends list
XBL_F_PerformMenuAction(UI_F_FRIENDREMOVE);
Menus_CloseByName( "xbox_error_popup" );
}
else
{
// Do nothing
Menus_CloseByName( "xbox_error_popup" );
}
break;
case XB_POPUP_RESPAWN_NEEDED:
if( response == 0 ) // A
{
Menus_CloseAll();
Menus_OpenByName("ingame");
}
else if( response == 1 ) // B
{
// Invalid response!
return;
}
break;
case XB_POPUP_CORRUPT_SETTINGS:
if( response == 0 ) // A
{
Settings.Delete();
Menus_CloseByName( "xbox_error_popup" );
extern void XB_Startup( XBStartupState startupState );
XB_Startup( STARTUP_COMBINED_SPACE_CHECK );
return;
}
else
{
// Invalid response!
return;
}
break;
default:
Com_Error( ERR_FATAL, "ERROR: Invalid popup type %i\n", sPopup );
}
// If we get here, the user gave a valid response to our popup. Clear the context:
sPopup = XB_POPUP_NONE;
}