717 lines
20 KiB
C++
717 lines
20 KiB
C++
// XBLIVE_FRIENDS.CPP
|
|
//
|
|
// wrapper to the Xbox online friends API
|
|
//
|
|
|
|
#include "..\xbox\XBLive.h"
|
|
#include "..\xbox\XBoxCommon.h"
|
|
#include "..\xbox\XBVoice.h"
|
|
#include "..\game\q_shared.h"
|
|
#include "..\qcommon\qcommon.h"
|
|
#include "..\cgame\cg_local.h"
|
|
#include "../ui/ui_shared.h"
|
|
#include "../qcommon/stringed_ingame.h"
|
|
#include "../qcommon/xb_settings.h"
|
|
|
|
extern int RE_RegisterShaderNoMip( const char *name );
|
|
|
|
//DEFINES
|
|
|
|
#define OPEN_SLOTS_JOIN_THRESHOLD 1 // the number of public slots needed to be open to make a player joinable (TCR recommends 3)
|
|
#define JOINABLE_CHECK_RATE 400 // 400 ticks (8ish seconds) between checks
|
|
#define FRIEND_DISPLAY_LIMIT 10 // only display a maximum of 10 friends at a time
|
|
#define MAX_TITLE_LENGTH 16 // allow 16 wide characters for a title's name
|
|
#define GAME_TITLE "Jedi Knight: Jedi Academy"
|
|
|
|
//VARIABLES
|
|
|
|
XONLINE_FRIEND friendsList[MAX_FRIENDS]; // Our copy of the user's friends list
|
|
XONLINETASK_HANDLE friendsGeneralTask;
|
|
XONLINETASK_HANDLE enumerationTask;
|
|
bool friendsInitialized = false;
|
|
bool generatingFriends = false; // flag stating that we are currently enumerating the friends list
|
|
BYTE friendChosen = 0; // the friend selcted for action
|
|
int numFriends = 0; // number of friends found while enumerating list
|
|
DWORD current_state = 0; // flags depicting current state as perceived by friends
|
|
|
|
//PROTOTYPES
|
|
void XBL_F_GetTitleString(const DWORD titleID, char* titleString);
|
|
HRESULT XBL_F_Invite(void);
|
|
void XBL_F_JoinFriendUninvited( XONLINE_FRIEND* friendPlaying);
|
|
|
|
// ******************************************************
|
|
// INITIALIZING FUNCTIONS
|
|
// *******************************************************
|
|
|
|
// Initialize the friends functionality
|
|
//
|
|
void XBL_F_Init()
|
|
{
|
|
if( friendsInitialized )
|
|
return;
|
|
|
|
// We have no friends (yet)
|
|
numFriends = 0;
|
|
|
|
// system startup
|
|
//
|
|
HRESULT result = XOnlineFriendsStartup( NULL, &friendsGeneralTask );
|
|
if( result != S_OK )
|
|
{
|
|
Com_Printf("XBLive_F - Error %d in startup\n", result);
|
|
friendsGeneralTask = NULL;
|
|
return;
|
|
}
|
|
|
|
// Default state after logging in
|
|
XBL_F_SetState(XONLINE_FRIENDSTATE_FLAG_ONLINE, !Settings.appearOffline);
|
|
XBL_F_SetState(XONLINE_FRIENDSTATE_FLAG_PLAYING, false);
|
|
XBL_F_SetState(XONLINE_FRIENDSTATE_FLAG_VOICE, g_Voice.IsVoiceAllowed());
|
|
XBL_F_SetState(XONLINE_FRIENDSTATE_FLAG_JOINABLE, false);
|
|
|
|
friendsInitialized = true;
|
|
}
|
|
|
|
// generate the list of friends
|
|
//
|
|
void XBL_F_GenerateFriendsList()
|
|
{
|
|
if(!logged_on || generatingFriends)
|
|
return;
|
|
|
|
// generate the list of records
|
|
//
|
|
HRESULT result = XOnlineFriendsEnumerate( IN_GetMainController(), NULL, &enumerationTask );
|
|
if( result != S_OK )
|
|
{
|
|
Com_Printf("XBLive_F - Error %d using XOnlineFriendsEnumerate\n", result);
|
|
enumerationTask = NULL;
|
|
return;
|
|
}
|
|
|
|
// Reset friends count, set flag to retrieve friends during tick
|
|
numFriends = 0;
|
|
generatingFriends = true;
|
|
}
|
|
|
|
// ******************************************************
|
|
// CLEANUP FUNCTIONS
|
|
// *******************************************************
|
|
|
|
// stop generating the friends list
|
|
//
|
|
void XBL_F_ReleaseFriendsList()
|
|
{
|
|
if( !generatingFriends )
|
|
return;
|
|
|
|
// wait for enumeration to complete
|
|
//
|
|
if(enumerationTask)
|
|
{
|
|
HRESULT result = XOnlineFriendsEnumerateFinish( enumerationTask );
|
|
if( IS_ERROR(result) )
|
|
{
|
|
Com_Printf("XBLive_F - Error %d using XOnlineFriendsEnumerateFinish\n", result);
|
|
}
|
|
|
|
// finish any related tasks
|
|
//
|
|
do
|
|
{
|
|
result = XOnlineTaskContinue( enumerationTask );
|
|
if( IS_ERROR(result) )
|
|
{
|
|
Com_Printf("XBLive_F - Error %d enumerating friends list\n", result);
|
|
}
|
|
}
|
|
while( result != XONLINETASK_S_SUCCESS );
|
|
|
|
XOnlineTaskClose(enumerationTask);
|
|
enumerationTask = NULL;
|
|
}
|
|
|
|
// List isn't valid anymore, so no friends, and tell tick to stop generating
|
|
generatingFriends = false;
|
|
numFriends = 0;
|
|
}
|
|
|
|
// shutdown and cleanup friends functionality
|
|
//
|
|
void XBL_F_Cleanup()
|
|
{
|
|
HRESULT result;
|
|
|
|
XBL_F_ReleaseFriendsList();
|
|
|
|
// finish off incomplete tasks
|
|
//
|
|
if(friendsGeneralTask)
|
|
{
|
|
do
|
|
{
|
|
result = XOnlineTaskContinue( friendsGeneralTask );
|
|
if( IS_ERROR(result) )
|
|
{
|
|
Com_Printf("XBLive_F - Error %d finishing off friends general tasks\n", result);
|
|
XOnlineTaskClose(friendsGeneralTask);
|
|
friendsGeneralTask = NULL;
|
|
break;
|
|
}
|
|
}
|
|
while( result != XONLINETASK_S_RUNNING_IDLE );
|
|
XOnlineTaskClose(friendsGeneralTask);
|
|
friendsGeneralTask = NULL;
|
|
}
|
|
friendsInitialized = false;
|
|
}
|
|
|
|
// ******************************************************
|
|
// FUNCTIONS THE MENU SYSTEM USES TO COMMUNICATE TO FRIENDS
|
|
// *******************************************************
|
|
|
|
//Return the number of friends found while enumerating list
|
|
int XBL_F_GetNumFriends(void)
|
|
{
|
|
return numFriends;
|
|
}
|
|
|
|
// Set the higlighted friend for use when friend is selected
|
|
void XBL_F_SetChosenFriendIndex(const int index)
|
|
{
|
|
// Sanity check - this does get called before we're done enumerating some times!
|
|
assert( (index >= 0 && index < numFriends) || !numFriends );
|
|
|
|
// SOF2 had checks for less than zero - probably forgot to do a
|
|
// Menu_SetFeederSelection before opening the menu.
|
|
friendChosen = index;
|
|
|
|
// The UI calls this when the selection in the listbox moves. Set the status lines:
|
|
Cvar_Set( "fl_voiceLine", XBL_F_GetVoiceString( index ) );
|
|
Cvar_Set( "fl_statusLine", XBL_F_GetStatusString( index ) );
|
|
Cvar_Set( "fl_statusLine2", XBL_F_GetStatusString2( index ) );
|
|
}
|
|
|
|
XONLINE_FRIEND *XBL_F_GetChosenFriend( void )
|
|
{
|
|
if( friendChosen >= 0 && friendChosen < numFriends )
|
|
return &friendsList[friendChosen];
|
|
return NULL;
|
|
}
|
|
|
|
// Handle a players attempt to join a friends game from an invite when they are potentially using another title
|
|
//
|
|
HRESULT XBL_F_JoinGameFromInvite()
|
|
{
|
|
HRESULT result;
|
|
|
|
result = XOnlineFriendsAnswerGameInvite( IN_GetMainController(), &friendsList[friendChosen], XONLINE_GAMEINVITE_YES); // writes invite to HD if needed
|
|
if( result != S_OK )
|
|
{
|
|
assert(!"faliure to accept invite");
|
|
// report error
|
|
}
|
|
|
|
// figure out if you're playing the same game as your friend
|
|
//
|
|
BOOL playingSameGame = XOnlineTitleIdIsSameTitle( friendsList[friendChosen].dwTitleID );
|
|
|
|
// if you are
|
|
//
|
|
if( playingSameGame )
|
|
{
|
|
XBL_MM_ConnectViaSessionID( &friendsList[friendChosen].sessionID, true );
|
|
return S_OK;
|
|
}
|
|
|
|
// else if its a different game return the code to show popup
|
|
//
|
|
return -1;
|
|
}
|
|
|
|
// Handle a players attempt to join a friends game when they are potentially using another title
|
|
HRESULT XBL_F_JoinGame()
|
|
{
|
|
// Figure out if you're playing the same game as your friend
|
|
BOOL playingSameGame = XOnlineTitleIdIsSameTitle( friendsList[friendChosen].dwTitleID );
|
|
|
|
// If you are
|
|
if( playingSameGame )
|
|
{
|
|
XBL_MM_ConnectViaSessionID( &friendsList[friendChosen].sessionID, false );
|
|
return S_OK;
|
|
}
|
|
|
|
// It's a different game, write out the info:
|
|
HRESULT result = XOnlineFriendsJoinGame( IN_GetMainController(), &friendsList[friendChosen] );
|
|
assert( result == S_OK );
|
|
|
|
// Show insert disk popup
|
|
return -1;
|
|
}
|
|
|
|
// This handle all possbile actions that the user can perform on a selected friend
|
|
//
|
|
HRESULT XBL_F_PerformMenuAction(friendChoices_t action)
|
|
{
|
|
HRESULT result = S_OK;
|
|
|
|
switch(action)
|
|
{
|
|
case UI_F_FRIENDREQUESTED:
|
|
result = XOnlineFriendsRequest(IN_GetMainController(), friendsList[friendChosen].xuid);
|
|
break;
|
|
|
|
case UI_F_FRIENDACCEPTED:
|
|
result = XOnlineFriendsAnswerRequest(IN_GetMainController(), &friendsList[friendChosen], XONLINE_REQUEST_YES);
|
|
break;
|
|
|
|
case UI_F_FRIENDREMOVE:
|
|
result = XOnlineFriendsRemove(IN_GetMainController(), &friendsList[friendChosen]);
|
|
break;
|
|
|
|
case UI_F_FRIENDDECLINE:
|
|
result = XOnlineFriendsAnswerRequest(IN_GetMainController(), &friendsList[friendChosen], XONLINE_REQUEST_NO);
|
|
break;
|
|
|
|
case UI_F_FRIENDBLOCK:
|
|
result = XOnlineFriendsAnswerRequest(IN_GetMainController(), &friendsList[friendChosen], XONLINE_REQUEST_BLOCK);
|
|
break;
|
|
|
|
case UI_F_FRIENDCANCEL:
|
|
result = XOnlineFriendsRemove(IN_GetMainController(), &friendsList[friendChosen]);
|
|
break;
|
|
|
|
case UI_F_GAMEREQUESTED:
|
|
result = XBL_F_Invite();
|
|
break;
|
|
|
|
case UI_F_GAMEACCEPTED:
|
|
result = XBL_F_JoinGameFromInvite();
|
|
break;
|
|
|
|
case UI_F_GAMEDECLINE:
|
|
result = XOnlineFriendsAnswerGameInvite(IN_GetMainController(), &friendsList[friendChosen], XONLINE_GAMEINVITE_NO);
|
|
break;
|
|
|
|
case UI_F_GAMEFRIENDREMOVED:
|
|
result = XOnlineFriendsAnswerGameInvite(IN_GetMainController(), &friendsList[friendChosen], XONLINE_GAMEINVITE_REMOVE);
|
|
break;
|
|
|
|
case UI_F_GAMECANCEL:
|
|
result = XOnlineFriendsRevokeGameInvite(IN_GetMainController(), *Net_GetXNKID(), 1, &friendsList[friendChosen]);
|
|
break;
|
|
|
|
case UI_F_JOINSESSION:
|
|
result = XBL_F_JoinGame();
|
|
break;
|
|
|
|
case UI_F_TOGGLEONLINE:
|
|
if(current_state & XONLINE_FRIENDSTATE_FLAG_ONLINE)
|
|
XBL_F_SetState(XONLINE_FRIENDSTATE_FLAG_ONLINE, false);
|
|
else
|
|
XBL_F_SetState(XONLINE_FRIENDSTATE_FLAG_ONLINE, true);
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
// Returns the gamertag of the given friend - used by UI listbox renderer
|
|
const char *XBL_F_GetFriendName( const int index )
|
|
{
|
|
// Invalid index?
|
|
if( index < 0 || index >= numFriends )
|
|
return "";
|
|
|
|
return friendsList[index].szGamertag;
|
|
}
|
|
|
|
// Returns the voice icon to use in the friends list for the given friend
|
|
int XBL_F_GetVoiceIcon( const int index )
|
|
{
|
|
// Invalid index?
|
|
if( index < 0 || index >= numFriends )
|
|
return -1;
|
|
|
|
// Friend has no voice?
|
|
if( !(friendsList[index].dwFriendState & XONLINE_FRIENDSTATE_FLAG_VOICE) )
|
|
return -1;
|
|
|
|
// They have voice. Just check if they're muted.
|
|
if( g_Voice.IsMuted( friendsList[index].xuid ) )
|
|
return RE_RegisterShaderNoMip( "gfx/mp/voice_mute_icon" );
|
|
else
|
|
return RE_RegisterShaderNoMip( "gfx/mp/voice_on_icon" );
|
|
}
|
|
|
|
// Returns the full voice info status line for the given friend
|
|
const char *XBL_F_GetVoiceString( const int index )
|
|
{
|
|
// Invalid index?
|
|
if( index < 0 || index >= numFriends )
|
|
return "";
|
|
|
|
// Never display voice info for an offline friend
|
|
if( !(friendsList[index].dwFriendState & XONLINE_FRIENDSTATE_FLAG_ONLINE) )
|
|
return "";
|
|
|
|
// Friend has no voice?
|
|
if( !(friendsList[index].dwFriendState & XONLINE_FRIENDSTATE_FLAG_VOICE) )
|
|
return va( "%s %s", SE_GetString( "MENUS_VOICE" ), SE_GetString( "MENUS_OFF" ) );
|
|
|
|
// OK. Friend has voice. Just check if they're muted.
|
|
if( g_Voice.IsMuted( friendsList[index].xuid ) )
|
|
return va( "%s %s", SE_GetString( "MENUS_VOICE" ), SE_GetString( "MENUS_MUTED" ) );
|
|
else
|
|
return va( "%s %s", SE_GetString( "MENUS_VOICE" ), SE_GetString( "MENUS_ON" ) );
|
|
}
|
|
|
|
// Returns the status icon to use in the friends list for the given friend
|
|
int XBL_F_GetStatusIcon( const int index )
|
|
{
|
|
// Invalid index?
|
|
if( index < 0 || index >= numFriends )
|
|
return -1;
|
|
|
|
// We'll be using this a lot...
|
|
const XONLINE_FRIEND *curFriend = &friendsList[index];
|
|
|
|
// In order of importance
|
|
|
|
// Player has received a request to a game
|
|
if( curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_RECEIVEDINVITE )
|
|
return RE_RegisterShaderNoMip( "gfx/mp/game_received_icon" );
|
|
|
|
// Player has received a request to be a friend
|
|
if( curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_RECEIVEDREQUEST )
|
|
return RE_RegisterShaderNoMip( "gfx/mp/friend_received_icon" );
|
|
|
|
// Player has sent invite to game
|
|
if( (curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_SENTINVITE) &&
|
|
!(curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_INVITEACCEPTED) &&
|
|
!(curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_INVITEREJECTED) )
|
|
return RE_RegisterShaderNoMip( "gfx/mp/game_sent_icon" );
|
|
|
|
// Player has sent friend request
|
|
if( curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_SENTREQUEST )
|
|
return RE_RegisterShaderNoMip( "gfx/mp/friend_sent_icon" );
|
|
|
|
// Friend is online, or online and playing
|
|
if( (curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_PLAYING) ||
|
|
(curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_ONLINE) )
|
|
return RE_RegisterShaderNoMip( "gfx/mp/online_icon" );
|
|
|
|
// Friend is offline
|
|
return -1;
|
|
}
|
|
|
|
// Returns the online/offline status line for the given friend
|
|
const char *XBL_F_GetStatusString( const int index )
|
|
{
|
|
// Invalid index?
|
|
if( index < 0 || index >= numFriends )
|
|
return "";
|
|
|
|
// Some things we'lll be reusing a bit:
|
|
const XONLINE_FRIEND *curFriend = &friendsList[index];
|
|
char titleString[MAX_TITLENAME_LEN+1];
|
|
XBL_F_GetTitleString( curFriend->dwTitleID, titleString );
|
|
|
|
// In order of importance
|
|
|
|
// Friend is currently playing some game
|
|
if( curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_PLAYING )
|
|
return va( SE_GetString( "MENUS_PLAYING" ), titleString );
|
|
|
|
// Friend is online - not in a session
|
|
if( curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_ONLINE )
|
|
return va( SE_GetString( "MENUS_AVAILABLE_IN" ), titleString );
|
|
|
|
// Friend is offline
|
|
return SE_GetString( "MENUS_OFFLINE" );
|
|
}
|
|
|
|
// Returns the special case status info for the given friend
|
|
const char *XBL_F_GetStatusString2( const int index )
|
|
{
|
|
// Invalid index?
|
|
if( index < 0 || index >= numFriends )
|
|
return "";
|
|
|
|
// Some things we'lll be reusing a bit:
|
|
const XONLINE_FRIEND *curFriend = &friendsList[index];
|
|
char titleString[MAX_TITLENAME_LEN+1];
|
|
XBL_F_GetTitleString( curFriend->dwTitleID, titleString );
|
|
|
|
// In order of importance
|
|
|
|
// Player has received a game invite fom the indicated friend
|
|
if( curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_RECEIVEDINVITE )
|
|
return va( SE_GetString( "MENUS_WANTS_TO_PLAY" ), titleString ); // Cleared ui_gameInvite (now xbl_hudGame)
|
|
|
|
// Player has received a request to be a friend
|
|
if( curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_RECEIVEDREQUEST )
|
|
return SE_GetString( "MENUS_WANTS_TO_BE_FRIEND" ); // Cleared ui_friendInvite
|
|
|
|
// Player has sent invite to game
|
|
if( (curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_SENTINVITE) &&
|
|
!(curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_INVITEACCEPTED) &&
|
|
!(curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_INVITEREJECTED) )
|
|
return SE_GetString( "MENUS_INVITED_TO_PLAY" );
|
|
|
|
// Player has sent friend request
|
|
if( curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_SENTREQUEST )
|
|
return SE_GetString( "MENUS_YOU_ASKED_TO_BE_FRIEND" );
|
|
|
|
// No special state:
|
|
return "";
|
|
}
|
|
|
|
// Determines the friend status of a given user. Used by the player list
|
|
// code to set the state of each user, which is then used to determine icons.
|
|
FriendState XBL_F_GetFriendStatus( const XUID *xuid )
|
|
{
|
|
XONLINE_FRIEND *curFriend = NULL;
|
|
for( int i = 0; i < numFriends; i++ )
|
|
{
|
|
if( XOnlineAreUsersIdentical( &friendsList[i].xuid, xuid ) )
|
|
{
|
|
curFriend = &friendsList[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !curFriend )
|
|
return FRIEND_NO;
|
|
|
|
if( curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_SENTREQUEST )
|
|
{
|
|
return FRIEND_RQST;
|
|
}
|
|
|
|
if( curFriend->dwFriendState & XONLINE_FRIENDSTATE_FLAG_RECEIVEDREQUEST )
|
|
{
|
|
return FRIEND_RQST_RCV;
|
|
}
|
|
|
|
return FRIEND_YES;
|
|
}
|
|
|
|
// Send a friend invitation to this user.
|
|
void XBL_F_AddFriend( const XUID *pXuid )
|
|
{
|
|
// Send the request to the servers - VVFIXME - handle errors here?
|
|
HRESULT hr = XOnlineFriendsRequest( IN_GetMainController(), *pXuid );
|
|
}
|
|
|
|
// Cancel a friend invitation we may have sent to this user, and/or
|
|
// remove them from our friend list.
|
|
void XBL_F_RemoveFriend( const XUID *pXuid )
|
|
{
|
|
XONLINE_FRIEND *curFriend = NULL;
|
|
for( int i = 0; i < numFriends; i++ )
|
|
{
|
|
if( XOnlineAreUsersIdentical( &friendsList[i].xuid, pXuid ) )
|
|
{
|
|
curFriend = &friendsList[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Make sure we found them in our list
|
|
if( !curFriend )
|
|
return;
|
|
|
|
// OK. Remove them from the list
|
|
XOnlineFriendsRemove( IN_GetMainController(), curFriend );
|
|
}
|
|
|
|
// ******************************************************
|
|
// SUPPORT FUNCTIONS FOR MANAGING FRIENDS
|
|
// *******************************************************
|
|
|
|
// ******************************************************
|
|
// PUBLIC ** PUBLIC ** PUBLIC ** PUBLIC ** PUBLIC ** PUBLIC
|
|
// *******************************************************
|
|
// functionality to be run every game tick
|
|
//
|
|
void XBL_F_Tick()
|
|
{
|
|
if( !friendsInitialized )
|
|
return;
|
|
|
|
HRESULT result;
|
|
|
|
// Check if we are in a session with enough open public slots to make us joinable
|
|
XBL_F_CheckJoinableStatus( false );
|
|
|
|
// Process the general task, required during gameplay to handle invitations
|
|
if( friendsGeneralTask )
|
|
{
|
|
result = XOnlineTaskContinue( friendsGeneralTask );
|
|
if( IS_ERROR(result) )
|
|
{
|
|
Com_Printf("XBLive_F - Error %d running friends general tasks\n", result);
|
|
XOnlineTaskClose(friendsGeneralTask);
|
|
friendsGeneralTask = NULL;
|
|
}
|
|
}
|
|
|
|
// If the UI is currently displaying the friends list we must continue to update it
|
|
if( generatingFriends && enumerationTask )
|
|
{
|
|
// Keep enumerating
|
|
result = XOnlineTaskContinue( enumerationTask );
|
|
if( IS_ERROR(result) )
|
|
{
|
|
Com_Printf("XBLive_F - Error %d enumerating friends list\n", result);
|
|
}
|
|
|
|
// Do we have all the results?
|
|
if( result == XONLINETASK_S_RESULTS_AVAIL )
|
|
{
|
|
// Copy the list into our buffer
|
|
numFriends = XOnlineFriendsGetLatest(IN_GetMainController(), MAX_FRIENDS, &friendsList[0] );
|
|
|
|
// Also update the strings about the currently selected friend:
|
|
if( friendChosen >= 0 && friendChosen < numFriends )
|
|
{
|
|
Cvar_Set( "fl_voiceLine", XBL_F_GetVoiceString( friendChosen ) );
|
|
Cvar_Set( "fl_statusLine", XBL_F_GetStatusString( friendChosen ) );
|
|
Cvar_Set( "fl_statusLine2", XBL_F_GetStatusString2( friendChosen ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Change our state - set or remove a notification flag
|
|
void XBL_F_SetState( DWORD flag, bool set_flag )
|
|
{
|
|
if( set_flag )
|
|
current_state |= flag;
|
|
else
|
|
current_state &= ~flag;
|
|
|
|
HRESULT hr = XOnlineNotificationSetState(IN_GetMainController(), current_state, *Net_GetXNKID(), 0, NULL );
|
|
assert( hr == S_OK );
|
|
|
|
// VVFIXME - Move this shite into the calling code
|
|
//jsw//Hack for the menus
|
|
if(flag == XONLINE_FRIENDSTATE_FLAG_ONLINE)
|
|
{
|
|
if(set_flag)
|
|
Cvar_Set( "ui_xblivefriendonline", SE_GetString("XBL_ONLINE") );
|
|
else
|
|
Cvar_Set( "ui_xblivefriendonline", SE_GetString("XBL_OFFLINE") );
|
|
}
|
|
}
|
|
|
|
// Just a way to get our own state information (things that are in our friend state)
|
|
bool XBL_F_GetState( DWORD flag )
|
|
{
|
|
return current_state & flag;
|
|
}
|
|
|
|
// Invite a friend to join the current game
|
|
HRESULT XBL_F_Invite(void)
|
|
{
|
|
return XOnlineFriendsGameInvite( IN_GetMainController(), *Net_GetXNKID(), 1, &friendsList[friendChosen] );
|
|
}
|
|
|
|
// we need to change our client friend states to show that we're not in a session
|
|
//
|
|
void XBL_F_OnClientLeaveSession()
|
|
{
|
|
XBL_F_SetState( XONLINE_FRIENDSTATE_FLAG_JOINABLE, false );
|
|
XBL_F_SetState( XONLINE_FRIENDSTATE_FLAG_PLAYING, false );
|
|
}
|
|
|
|
bool XBL_F_FriendNotice( void )
|
|
{
|
|
return (logged_on && XOnlineGetNotification( IN_GetMainController(), XONLINE_NOTIFICATION_FRIEND_REQUEST ));
|
|
}
|
|
|
|
bool XBL_F_GameNotice( void )
|
|
{
|
|
return (logged_on && XOnlineGetNotification( IN_GetMainController(), XONLINE_NOTIFICATION_GAME_INVITE ));
|
|
}
|
|
|
|
// Get friend details based on gamertag
|
|
//
|
|
XONLINE_FRIEND* XBL_F_GetFriendFromName( char* name )
|
|
{
|
|
for( int i = 0; i < numFriends; i++ )
|
|
{
|
|
if( !strcmp( friendsList[i].szGamertag, name ) )
|
|
{
|
|
return &friendsList[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// ******************************************************
|
|
// PRIVATE ** PRIVATE ** PRIVATE ** PRIVATE ** PRIVATE ** PRIVATE
|
|
// *******************************************************
|
|
|
|
//Get the title string of game being played
|
|
void XBL_F_GetTitleString(const DWORD titleID, char* titleString)
|
|
{
|
|
// Is their title ID valid?
|
|
if( !titleID )
|
|
{
|
|
strcpy( titleString, "" );
|
|
return;
|
|
}
|
|
|
|
WCHAR playingWstr[MAX_TITLENAME_LEN+1];
|
|
int attempt = 0;
|
|
|
|
// MS suggests polling to get the string:
|
|
do
|
|
{
|
|
attempt++;
|
|
XOnlineFriendsGetTitleName( titleID, XC_LANGUAGE_ENGLISH, MAX_TITLE_LENGTH, playingWstr );
|
|
} while( playingWstr[0] == 0 && attempt < 10000 );
|
|
|
|
// If we got something, use it, else just print the title ID
|
|
if( attempt < 10000 )
|
|
wcharToChar( titleString, playingWstr );
|
|
else
|
|
sprintf( titleString, "%d", titleID );
|
|
}
|
|
|
|
// periodically update your joinability status (your session has X+ public slots open)
|
|
//
|
|
void XBL_F_CheckJoinableStatus( bool force )
|
|
{
|
|
// if not currently in a session, leave
|
|
//
|
|
if( !(current_state & XONLINE_FRIENDSTATE_FLAG_PLAYING) )
|
|
return;
|
|
|
|
static int pass = 0;
|
|
|
|
// if its time to check...
|
|
//
|
|
if( ++pass > JOINABLE_CHECK_RATE || force )
|
|
{
|
|
// Just count how many clients we have valid info for, and compare to maxclients!
|
|
// Need a special case for dedicated host though:
|
|
extern cvar_t *sv_maxclients;
|
|
int numClients = 0;
|
|
int maxClients = com_dedicated->integer ? sv_maxclients->integer : cgs.maxclients;
|
|
for( int i = 0; i < maxClients; ++i )
|
|
if( xbOnlineInfo.xbPlayerList[i].isActive )
|
|
numClients++;
|
|
|
|
// Set our joinable state based on the number of empty slots.
|
|
XBL_F_SetState( XONLINE_FRIENDSTATE_FLAG_JOINABLE, numClients < maxClients );
|
|
|
|
pass = 0;
|
|
}
|
|
}
|