1525 lines
44 KiB
C++
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;
|
|
}
|