Initial commit.
This commit is contained in:
67
codemp/xbox/JediAcademy.xms
Normal file
67
codemp/xbox/JediAcademy.xms
Normal file
@@ -0,0 +1,67 @@
|
||||
<matchsim ver="1.0.5558.1" hexver="0x1000015b60001">
|
||||
<title id="0x4c41000b" name="Jedi Academy" sessionexpiration="86400">
|
||||
<schema>
|
||||
<attribute id="GameType" hexid="0x1" kind="int"/>
|
||||
<attribute id="CurrentMap" hexid="0x2" kind="int"/>
|
||||
<attribute id="SessionName" hexid="0x100003" kind="string" maxlen="16"/>
|
||||
<attribute id="FriendlyFire" hexid="0x4" kind="int"/>
|
||||
<attribute id="JediMastery" hexid="0x5" kind="int"/>
|
||||
<attribute id="TotalPlayers" hexid="0x7" kind="int"/>
|
||||
<attribute id="SaberOnly" hexid="0x8" kind="int"/>
|
||||
<attribute id="Dedicated" hexid="0x9" kind="int"/>
|
||||
</schema>
|
||||
|
||||
<queries>
|
||||
<query id="0x1" name="OptiMatch" maxresults="25" nopubok="0" qosprobe="1">
|
||||
<params>
|
||||
<param id="GameType" hexid="0x2000000" order="0" kind="int" nullok="1"/>
|
||||
<param id="CurrentMap" hexid="0x2000001" order="1" kind="int" nullok="1"/>
|
||||
<param id="MinimumPlayers" hexid="0x2000005" order="2" kind="int" nullok="0"/>
|
||||
<param id="MaximumPlayers" hexid="0x2000006" order="3" kind="int" nullok="0"/>
|
||||
<param id="FriendlyFire" hexid="0x2000002" order="4" kind="int" nullok="1"/>
|
||||
<param id="JediMastery" hexid="0x2000007" order="5" kind="int" nullok="1"/>
|
||||
<param id="SaberOnly" hexid="0x2000003" order="6" kind="int" nullok="1"/>
|
||||
<param id="Dedicated" hexid="0x2000004" order="7" kind="int" nullok="1"/>
|
||||
</params>
|
||||
<filters>
|
||||
<filter left="0x1" op="==" right="0x2000000"/>
|
||||
<filter left="0x2" op="==" right="0x2000001"/>
|
||||
<filter left="0x4" op="==" right="0x2000002"/>
|
||||
<filter left="0x7" op=">=" right="0x2000005"/>
|
||||
<filter left="0x7" op="<=" right="0x2000006"/>
|
||||
<filter left="0x5" op="==" right="0x2000007"/>
|
||||
<filter left="0x8" op="==" right="0x2000003"/>
|
||||
<filter left="0x9" op="==" right="0x2000004"/>
|
||||
</filters>
|
||||
<sortops>
|
||||
<sortop id="0x83000006" dir="ascending"/>
|
||||
</sortops>
|
||||
<returns>
|
||||
<return id="0x1"/>
|
||||
<return id="0x2"/>
|
||||
<return id="0x100003"/>
|
||||
<return id="0x4"/>
|
||||
<return id="0x5"/>
|
||||
<return id="0x8"/>
|
||||
<return id="0x9"/>
|
||||
</returns>
|
||||
</query>
|
||||
|
||||
<query id="0x2" name="JoinSessionByID" maxresults="1" nopubok="1" qosprobe="1">
|
||||
<params>
|
||||
<param id="SessionID" hexid="0x2000000" order="0" kind="int" nullok="0"/>
|
||||
</params>
|
||||
<filters>
|
||||
<filter left="0x2000000" op="==" right="0x83000001"/>
|
||||
</filters>
|
||||
<sortops>
|
||||
<sortop id="0x83000006" dir="ascending"/>
|
||||
</sortops>
|
||||
<returns/>
|
||||
</query>
|
||||
</queries></title>
|
||||
|
||||
|
||||
|
||||
|
||||
</matchsim>
|
||||
1524
codemp/xbox/XBLive.cpp
Normal file
1524
codemp/xbox/XBLive.cpp
Normal file
File diff suppressed because it is too large
Load Diff
250
codemp/xbox/XBLive.h
Normal file
250
codemp/xbox/XBLive.h
Normal file
@@ -0,0 +1,250 @@
|
||||
// XBLive.h
|
||||
//
|
||||
|
||||
#ifndef XBLIVE_H
|
||||
#define XBLIVE_H
|
||||
|
||||
|
||||
#include <xtl.h>
|
||||
#include <xonline.h>
|
||||
#include "match.h"
|
||||
#include "../qcommon/qcommon.h"
|
||||
|
||||
//constants
|
||||
//
|
||||
#define ANY_TYPE NULL
|
||||
|
||||
#define VOICE_ANY 0
|
||||
#define VOICE_ON 1
|
||||
#define VOICE_OFF 2
|
||||
|
||||
#define MINIMUM_QOS 2 // warn of lag if join below this
|
||||
|
||||
|
||||
// Friend states stored in clientInfo
|
||||
//
|
||||
enum FriendState
|
||||
{
|
||||
FRIEND_NO,
|
||||
FRIEND_YES,
|
||||
FRIEND_RQST,
|
||||
FRIEND_RQST_RCV,
|
||||
FRIEND_INV,
|
||||
FRIEND_INV_RCV
|
||||
};
|
||||
|
||||
//Possibe menu interactions for friends
|
||||
typedef enum
|
||||
{
|
||||
UI_F_FRIENDREQUESTED,
|
||||
UI_F_FRIENDACCEPTED,
|
||||
UI_F_FRIENDREMOVE,
|
||||
UI_F_FRIENDDECLINE,
|
||||
UI_F_FRIENDBLOCK,
|
||||
UI_F_FRIENDCANCEL,
|
||||
UI_F_GAMEREQUESTED,
|
||||
UI_F_GAMEACCEPTED,
|
||||
UI_F_GAMEDECLINE,
|
||||
UI_F_GAMECANCEL,
|
||||
UI_F_GAMEFRIENDREMOVED,
|
||||
UI_F_JOINSESSION,
|
||||
UI_F_TOGGLEONLINE
|
||||
} friendChoices_t;
|
||||
|
||||
|
||||
extern bool logged_on; // flag defined in XBLive.cpp indicating wether we are logged on to XBox Live or not
|
||||
extern XONLINE_USER XBLLoggedOnUsers[XONLINE_MAX_LOGON_USERS]; // The users logged on to Live
|
||||
extern XONLINETASK_HANDLE logonHandle; // The sooper handle that sometimes needs to be serviced in other files
|
||||
|
||||
// A few things use the CSession directly to check if we're running a server, for example
|
||||
extern CSession session;
|
||||
|
||||
// different ways we can join a game
|
||||
//
|
||||
enum JoinType
|
||||
{
|
||||
VIA_FRIEND_JOIN,
|
||||
VIA_FRIEND_INVITE,
|
||||
VIA_QUICKMATCH,
|
||||
VIA_OPTIMATCH,
|
||||
};
|
||||
|
||||
// XBox Live functionality
|
||||
//
|
||||
|
||||
#ifdef _DEBUG
|
||||
const char *getXBLErrorName(HRESULT hr);
|
||||
#endif
|
||||
|
||||
// Stages of logging in. It's a complicated process - this makes it a little easier
|
||||
//
|
||||
enum XBLoginState
|
||||
{
|
||||
LOGIN_PASSCODE_CHECK,
|
||||
LOGIN_CONNECT,
|
||||
LOGIN_USER_ERRORS,
|
||||
LOGIN_SERVICE_ERRORS,
|
||||
LOGIN_FINISH,
|
||||
};
|
||||
|
||||
bool XBL_PumpLogon( void );
|
||||
void XBL_Tick();
|
||||
HRESULT XBL_Init();
|
||||
bool XBL_Login(XBLoginState loginState);
|
||||
void XBL_AbortLogin(void);
|
||||
HRESULT XBL_Cleanup();
|
||||
HRESULT XBL_RefreshUserList();
|
||||
DWORD XBL_GetNumAccounts(bool doNow);
|
||||
int XBL_GetSelectedAccountIndex(void);
|
||||
void XBL_SetAccountIndex(const int index);
|
||||
bool XBL_SetAccountIndexByXuid(const XUID *xuid);
|
||||
XONLINE_USER* XBL_GetUserInfo(const int index);
|
||||
void XBL_DisplayLobbyMenu();
|
||||
|
||||
const XNKID *SysLink_GetXNKID(void);
|
||||
const XNKEY *SysLink_GetXNKEY(void);
|
||||
const XNADDR *Net_GetXNADDR( DWORD *pStatus = NULL );
|
||||
bool Net_ConnectionStatus(void);
|
||||
|
||||
|
||||
void Syslink_PacketEvent( msg_t *msg );
|
||||
|
||||
// Utility to get the current session's XNKID. Works for both server and client,
|
||||
// in both Live and system link
|
||||
const XNKID *Net_GetXNKID(void);
|
||||
bool Net_XboxConnect(const XNKID *pxnkid, const XNKEY *pxnkey, const XNADDR *pxnaddr);
|
||||
bool Net_XboxDisconnect(void);
|
||||
|
||||
// match making functionality
|
||||
//
|
||||
void XBL_MM_Tick();
|
||||
void XBL_MM_Advertise();
|
||||
bool XBL_MM_QuickMatch(ULONGLONG GameType); // Optional: X_MATCH_NULL_INTEGER to omit
|
||||
// Same arguments as COptiMatchQuery::Query(), except for map, which is sent in as the name
|
||||
int XBL_MM_Find_Session(ULONGLONG GameType, // Optional: X_MATCH_NULL_INTEGER to omit
|
||||
const char *mapName, // Optional: "any" to omit
|
||||
ULONGLONG MinimumPlayers,
|
||||
ULONGLONG MaximumPlayers,
|
||||
ULONGLONG FriendlyFire, // Optional: X_MATCH_NULL_INTEGER to omit
|
||||
ULONGLONG JediMastery, // Optional: X_MATCH_NULL_INTEGER to omit
|
||||
ULONGLONG SaberOnly, // Optional: X_MATCH_NULL_INTEGER to omit
|
||||
ULONGLONG Dedicated); // Optional: X_MATCH_NULL_INTEGER to omit
|
||||
DWORD XBL_MM_RemovePlayer( bool usePrivateSlot );
|
||||
DWORD XBL_MM_AddPlayer( bool usePrivateSlot );
|
||||
bool XBL_MM_PublicSlotAvailable( void );
|
||||
bool XBL_MM_ConnectViaSessionID( const XNKID *sessionID, bool invited );
|
||||
void XBL_MM_Shutdown( bool processLogon );
|
||||
bool XBL_MM_ThisSessionIsLagging( const XNKID *sessionID );
|
||||
bool XBL_MM_IsSessionIDValid( const XNKID *sessionID );
|
||||
void XBL_MM_JoinServer( void );
|
||||
void XBL_MM_DontJoinLowQOSSever( void );
|
||||
void XBL_MM_JoinLowQOSSever( void );
|
||||
void XBL_MM_SetJoinType( JoinType );
|
||||
bool XBL_MM_CanUsePrivateSlot( void );
|
||||
void XBL_MM_SetServerJoinSlot(int);
|
||||
|
||||
// Now used in the UI as well...
|
||||
int mapNameToIndex(const char *mapname);
|
||||
const char *mapIndexToLongName(int index);
|
||||
extern const int MAP_ARRAY_SIZE;
|
||||
|
||||
// System Link functions used to communicate with the UI
|
||||
int Syslink_GetNumServers( void );
|
||||
//const char* Syslink_GetServerName( const int index );
|
||||
const char* Syslink_GetServerMap( const int index );
|
||||
const char* Syslink_GetServerClients( const int index );
|
||||
const char* Syslink_GetServerGametype( const int index );
|
||||
int Syslink_GetServerSaberOnly( const int index );
|
||||
int Syslink_GetServerDisableForce( const int index );
|
||||
void Syslink_SetChosenServerIndex( const int index );
|
||||
void Syslink_Listen( bool bListen );
|
||||
|
||||
// Optimatch functions used to communicate with the UI
|
||||
int XBL_MM_GetNumServers( void );
|
||||
const char* XBL_MM_GetServerName( const int index );
|
||||
const char* XBL_MM_GetServerMap( const int index );
|
||||
const char* XBL_MM_GetServerClients( const int index );
|
||||
const char* XBL_MM_GetServerGametype( const int index );
|
||||
int XBL_MM_GetServerSaberOnly( const int index );
|
||||
int XBL_MM_GetServerDisableForce( const int index );
|
||||
int XBL_MM_GetServerPing( const int index );
|
||||
void XBL_MM_SetChosenServerIndex( const int index );
|
||||
void XBL_MM_CancelProbing( void );
|
||||
|
||||
// System link analog to MM
|
||||
bool SysLink_JoinServer( void );
|
||||
|
||||
// friends functionality
|
||||
//
|
||||
void XBL_F_Init( void );
|
||||
void XBL_F_Tick( void );
|
||||
void XBL_F_Cleanup( void );
|
||||
void XBL_F_ReleaseFriendsList( void );
|
||||
void XBL_F_GenerateFriendsList( void );
|
||||
void XBL_F_SetState( DWORD flag, bool set_flag );
|
||||
bool XBL_F_GetState( DWORD flag );
|
||||
void XBL_F_OnClientLeaveSession( void );
|
||||
// Friend functions used to communicate with UI
|
||||
int XBL_F_GetNumFriends( void );
|
||||
XONLINE_FRIEND* XBL_F_GetChosenFriend( void );
|
||||
XONLINE_FRIEND* XBL_F_GetFriendFromName( char* );
|
||||
void XBL_F_SetChosenFriendIndex( const int index );
|
||||
HRESULT XBL_F_PerformMenuAction( friendChoices_t action );
|
||||
void XBL_F_CheckJoinableStatus( bool force );
|
||||
|
||||
const char* XBL_F_GetFriendName( const int index );
|
||||
int XBL_F_GetStatusIcon( const int index );
|
||||
const char* XBL_F_GetStatusString( const int index );
|
||||
const char* XBL_F_GetStatusString2( const int index );
|
||||
int XBL_F_GetVoiceIcon( const int index );
|
||||
const char* XBL_F_GetVoiceString( const int index );
|
||||
|
||||
FriendState XBL_F_GetFriendStatus( const XUID* xuid );
|
||||
void XBL_F_AddFriend( const XUID *pXuid );
|
||||
void XBL_F_RemoveFriend( const XUID *pXuid );
|
||||
void XBL_F_GetTitleString( const DWORD titleID, char* titleString );
|
||||
|
||||
bool XBL_F_FriendNotice( void );
|
||||
bool XBL_F_GameNotice( void );
|
||||
|
||||
// player list functionality
|
||||
//
|
||||
void XBL_PL_Tick(void);
|
||||
void XBL_PL_OnClientLeaveSession();
|
||||
void XBL_PL_RemoveActivePeer(XUID* xuid, int index);
|
||||
// Should include XBoxCommon.h at the top, or put all of this in one file, but whatever...
|
||||
struct XBPlayerInfo;
|
||||
void XBL_PL_CheckHistoryList(XBPlayerInfo *newInfo);
|
||||
void XBL_PL_UpdatePlayerName( int index, const char *newName );
|
||||
|
||||
// Player functions used to communicate with UI
|
||||
void XBL_PL_Init( void );
|
||||
void XBL_PL_Cleanup( void );
|
||||
int XBL_PL_GetNumPlayers( void );
|
||||
bool XBL_PL_SetCurrentInfo( void );
|
||||
void XBL_PL_SetPlayerIndex( const int index );
|
||||
const char* XBL_PL_GetPlayerName( const int index );
|
||||
int XBL_PL_GetVoiceIcon( const int index );
|
||||
int XBL_PL_GetStatusIcon( const int index );
|
||||
|
||||
// Actions taken by the user via the playerlist popup window:
|
||||
void XBL_PL_ToggleMute( void );
|
||||
void XBL_PL_SendFeedBack( void );
|
||||
void XBL_PL_KickPlayer( void );
|
||||
void XBL_PL_MakeFriend( void );
|
||||
void XBL_PL_CancelFriend( void );
|
||||
|
||||
void XBL_CheckServer();
|
||||
|
||||
// handy little buggers
|
||||
//
|
||||
void charToWchar( WCHAR* wc, char* c );
|
||||
void wcharToChar( char* c, WCHAR* wc );
|
||||
|
||||
// stores cross title info from Single Player
|
||||
//
|
||||
extern BYTE CrossTitle_InviteType;
|
||||
extern XNKID CrossTitle_TargetSessionID;
|
||||
extern char CrossTitle_FriendsGamerTag[XONLINE_GAMERTAG_SIZE];
|
||||
|
||||
#endif // XBLIVE_H
|
||||
716
codemp/xbox/XBLive_Friends.cpp
Normal file
716
codemp/xbox/XBLive_Friends.cpp
Normal file
@@ -0,0 +1,716 @@
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
883
codemp/xbox/XBLive_MM.cpp
Normal file
883
codemp/xbox/XBLive_MM.cpp
Normal file
@@ -0,0 +1,883 @@
|
||||
// XBLive_MM.CPP
|
||||
//
|
||||
// Wrappers and extra utilities based around the matchsim generated code.
|
||||
// Interface is borrowed from the SOF2 code, but modified quite a bit.
|
||||
//
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SERVER SIDE
|
||||
|
||||
#include "xblive.h"
|
||||
#include "xonline.h"
|
||||
#include "match.h"
|
||||
#include "xboxcommon.h"
|
||||
#include "..\server\server.h"
|
||||
#include "../ui/ui_local.h"
|
||||
#include "../qcommon/cm_local.h"
|
||||
#include "../client/client.h"
|
||||
|
||||
|
||||
// All servers are rated from 1 to 4
|
||||
#define MAX_QOS 4
|
||||
|
||||
|
||||
// External tools for servers to grab info from when advertising:
|
||||
extern vmCvar_t g_friendlyFire;
|
||||
extern vmCvar_t g_maxForceRank;
|
||||
extern qboolean HasSetSaberOnly(void);
|
||||
extern int RE_RegisterShaderNoMip( const char *name );
|
||||
|
||||
|
||||
// Hardcoded list of map names, so that current map can be an index rather than string
|
||||
// This needs to stay up-to-date.
|
||||
const char *mapArray[] = {
|
||||
"mp/ctf1",
|
||||
"mp/ctf2",
|
||||
"mp/ctf3",
|
||||
"mp/ctf4",
|
||||
"mp/ctf5",
|
||||
"mp/duel1",
|
||||
"mp/duel2",
|
||||
"mp/duel3",
|
||||
"mp/duel4",
|
||||
"mp/duel5",
|
||||
"mp/duel6",
|
||||
"mp/duel7",
|
||||
"mp/duel8",
|
||||
"mp/duel9",
|
||||
"mp/duel10",
|
||||
"mp/ffa1",
|
||||
"mp/ffa2",
|
||||
"mp/ffa3",
|
||||
"mp/ffa4",
|
||||
"mp/ffa5",
|
||||
"mp/siege_hoth",
|
||||
"mp/siege_desert",
|
||||
"mp/siege_korriban",
|
||||
};
|
||||
const int MAP_ARRAY_SIZE = sizeof(mapArray) / sizeof(mapArray[0]);
|
||||
|
||||
// Long names of all the maps. This should be done some other way.
|
||||
const char *mapLongArray[] = {
|
||||
"@MENUS_IMPERIAL_DROP_ZONE_ABR",
|
||||
"@MENUS_HOTH_WASTELAND_ABR",
|
||||
"@MENUS_YAVIN_HILLTOPS_ABR",
|
||||
"@MENUS_CORUSCANT_STREETS_ABR",
|
||||
"@MENUS_FACTORY_ABR",
|
||||
"@MENUS_BESPIN_COURTYARD_ABR",
|
||||
"@MENUS_GENERATOR_ROOM_ABR",
|
||||
"@MENUS_IMPERIAL_SHAFT_ABR",
|
||||
"@MENUS_IMPERIAL_CONTROL_ROOM_ABR",
|
||||
"@MENUS_TASPIR_LANDING_ABR",
|
||||
"@MENUS_YAVIN_TRAINING_AREA_ABR",
|
||||
"@MENUS_RANCOR_PIT_ABR",
|
||||
"@MENUS_ABANDONED_CITY_ABR",
|
||||
"@MENUS_HOTH_CANYON_ABR",
|
||||
"@MENUS_VJUN_FUEL_PROCESSING_ABR",
|
||||
"@MENUS_VJUN_SENTINEL_ABR",
|
||||
"@MENUS_KORRIBAN_TOMBS_ABR",
|
||||
"@MENUS_TATOOINE_CITY_ABR",
|
||||
"@MENUS_RIFT_SANCTUARY_ABR",
|
||||
"@MENUS_TASPIR_ABR",
|
||||
"@MENUS_HOTH_ATTACK_LOWER_ABR",
|
||||
"@MENUS_DESERT_RESCUE_LOWER_ABR",
|
||||
"@MENUS_KORRIBAN_VALLEY_LOWER_ABR",
|
||||
};
|
||||
|
||||
const char *gameTypeArray[] = {
|
||||
"@MENUS_FREE_FOR_ALL_ABR",
|
||||
"",
|
||||
"",
|
||||
"@MENUS_DUEL_ABR",
|
||||
"@MENUS_POWERDUEL_ABR",
|
||||
"",
|
||||
"@MENUS_TEAM_FFA_ABR",
|
||||
"@MENUS_SIEGE_ABR",
|
||||
"@MENUS_CAPTURE_THE_FLAG_ABR",
|
||||
"",
|
||||
};
|
||||
const int GAMETYPE_ARRAY_SIZE = sizeof(gameTypeArray) / sizeof(gameTypeArray[0]);
|
||||
|
||||
// Our session, assuming that we're a server
|
||||
CSession session; // from match.h
|
||||
|
||||
// Our two query objects, one used for optimatch and quickmatch,
|
||||
// the other for joining games via session ID
|
||||
COptiMatchQuery query; // from match.h
|
||||
CJoinSessionByIDQuery queryByID; // from match.h
|
||||
|
||||
// Info about the session we're currently in
|
||||
XBLClientData_t xbc;
|
||||
JoinType joinVia;
|
||||
|
||||
// The currently selected optimatch game (from the UI's list)
|
||||
int optiMatchIndex = 0;
|
||||
|
||||
// make a wide char string from a char string
|
||||
//
|
||||
void charToWchar( WCHAR* wc, char* c)
|
||||
{
|
||||
assert( wc );
|
||||
|
||||
if(!c)
|
||||
{
|
||||
wc[0] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
wsprintfW(wc, L"%hs", c);
|
||||
}
|
||||
|
||||
// make a char string from a widechar string
|
||||
//
|
||||
void wcharToChar( char* c, WCHAR* wc )
|
||||
{
|
||||
assert(c && wc);
|
||||
|
||||
sprintf(c, "%ls", wc);
|
||||
}
|
||||
|
||||
|
||||
// Easy conversion between map name and index values
|
||||
|
||||
// Returns MAP_ARRAY_SIZE if map isn't found
|
||||
int mapNameToIndex(const char *mapname)
|
||||
{
|
||||
for ( int index = 0; index < MAP_ARRAY_SIZE; ++index)
|
||||
if (!stricmp(mapname, mapArray[index]))
|
||||
return index;
|
||||
return MAP_ARRAY_SIZE;
|
||||
}
|
||||
|
||||
// Returns pointer to the table, don't stomp. NULL if not valid
|
||||
const char *mapIndexToName(int index)
|
||||
{
|
||||
if (index >= 0 && index < MAP_ARRAY_SIZE)
|
||||
return mapArray[index];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Returns pointer to the table, don't stomp. NULL if not valid
|
||||
const char *mapIndexToLongName(int index)
|
||||
{
|
||||
if (index >= 0 && index < MAP_ARRAY_SIZE)
|
||||
return mapLongArray[index];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Returns pointer to the table, don't stomp. NULL if not valid
|
||||
const char *gameTypeIndexToName(int index)
|
||||
{
|
||||
if (index >= 0 && index < GAMETYPE_ARRAY_SIZE)
|
||||
return gameTypeArray[index];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// increment the player count advertised on this session
|
||||
//
|
||||
DWORD XBL_MM_AddPlayer( bool usePrivateSlot )
|
||||
{
|
||||
if (!logged_on || !session.Exists())
|
||||
return -1;
|
||||
|
||||
// SOF2 had a really bad system that checked for server's friends. We do what
|
||||
// MS says is good - all invites/joins go into private, everyone else goes public.
|
||||
if( usePrivateSlot && session.PrivateOpen )
|
||||
{
|
||||
session.PrivateFilled++;
|
||||
session.PrivateOpen--;
|
||||
session.TotalPlayers++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Either joined via matchmaking, or we don't have any more private slots
|
||||
// Give them a public slot.
|
||||
if ( session.PublicOpen )
|
||||
{
|
||||
session.PublicFilled++;
|
||||
session.PublicOpen--;
|
||||
session.TotalPlayers++;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
session.Update();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// decrement the player count advertised on this session
|
||||
//
|
||||
DWORD XBL_MM_RemovePlayer( bool usePrivateSlot )
|
||||
{
|
||||
if(!logged_on || !session.Exists())
|
||||
return -1;
|
||||
|
||||
// The value we're given only tells us if they were eligble for a private slot when
|
||||
// they joined, but they may have actually ended up in a public slot. As such, this
|
||||
// can get pretty wacky. Oh well.
|
||||
if ( usePrivateSlot && session.PrivateFilled )
|
||||
{
|
||||
session.PrivateFilled--;
|
||||
session.PrivateOpen++;
|
||||
session.TotalPlayers--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( session.PublicFilled )
|
||||
{
|
||||
session.PublicFilled--;
|
||||
session.PublicOpen++;
|
||||
session.TotalPlayers--;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
session.Update();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Is there a public slot available? SV_DirectConnect needs to know so it can reject
|
||||
// clients when several people try to connect at once:
|
||||
bool XBL_MM_PublicSlotAvailable( void )
|
||||
{
|
||||
return session.PublicOpen;
|
||||
}
|
||||
|
||||
// SOF2 had some silly two-stage thing. They stored off the session parms here, then used
|
||||
// a couple globals to delay advertisement until later. I'm going to try and avoid that
|
||||
void XBL_MM_Init_Session()
|
||||
{
|
||||
// Fill in # of slots. OpenPublic is total slots, minus one for server, minus # reserved for private
|
||||
session.PrivateFilled = 0;
|
||||
session.PrivateOpen = sv_privateClients->integer;
|
||||
session.PublicFilled = (com_dedicated->integer ? 0 : 1); // Non-dedicated server fills a slot
|
||||
session.PublicOpen = sv_maxclients->integer - (session.PrivateOpen + session.PublicFilled);
|
||||
session.TotalPlayers = session.PublicFilled;
|
||||
|
||||
// Get current map index, and gametype
|
||||
int index = mapNameToIndex( sv_mapname->string );
|
||||
if (index == MAP_ARRAY_SIZE)
|
||||
{
|
||||
Com_Error( ERR_FATAL, "Bad map name: %s\n", sv_mapname->string );
|
||||
}
|
||||
session.CurrentMap = index;
|
||||
session.GameType = sv_gametype->integer;
|
||||
|
||||
// Copy the host's gamertag to the session name
|
||||
XONLINE_USER* pHostAccount = XBL_GetUserInfo( XBL_GetSelectedAccountIndex() );
|
||||
WCHAR sessionName[XONLINE_GAMERTAG_SIZE] = { 0 };
|
||||
if ( pHostAccount )
|
||||
{
|
||||
charToWchar( sessionName, pHostAccount->szGamertag );
|
||||
}
|
||||
else
|
||||
{
|
||||
charToWchar( sessionName, "unknown" );
|
||||
}
|
||||
session.SetSessionName( sessionName );
|
||||
|
||||
// All other game options:
|
||||
session.FriendlyFire = g_friendlyFire.integer;
|
||||
session.JediMastery = g_maxForceRank.integer;
|
||||
session.SaberOnly = HasSetSaberOnly();
|
||||
session.Dedicated = com_dedicated->integer;
|
||||
|
||||
// Actually create the session. If we don't call Process immediately, it explodes
|
||||
HRESULT hr = session.Create();
|
||||
if (hr != S_OK)
|
||||
{
|
||||
Com_Error( ERR_DROP, "Failed to create session: 0x%x\n", hr );
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if( !XBL_PumpLogon() )
|
||||
return;
|
||||
hr = session.Process();
|
||||
} while ( session.IsCreating() );
|
||||
|
||||
// VVFIXME - Better error handling
|
||||
if ( !session.Exists() )
|
||||
{
|
||||
Com_Error( ERR_DROP, "Failed to create session #2: 0x%x\n", hr );
|
||||
}
|
||||
|
||||
// Fix for a bug. Server was using Notification API before advertising, so for
|
||||
// the first few seconds, had the wrong sessionID, and thus couldn't invite.
|
||||
// Force an update now:
|
||||
XBL_F_CheckJoinableStatus( true );
|
||||
}
|
||||
|
||||
void XBL_MM_Update_Session()
|
||||
{
|
||||
// VVFIXME - Do we need to ensure that slot counts are right?
|
||||
// Our gamertag hasn't changed (I hope) so we leave that alone.
|
||||
|
||||
// Get current map index, and gametype
|
||||
int index = mapNameToIndex( sv_mapname->string );
|
||||
if (index == MAP_ARRAY_SIZE)
|
||||
{
|
||||
Com_Error( ERR_FATAL, "Bad map name: %s\n", sv_mapname->string );
|
||||
}
|
||||
session.CurrentMap = index;
|
||||
session.GameType = sv_gametype->integer;
|
||||
|
||||
// All other game options:
|
||||
session.FriendlyFire = g_friendlyFire.integer;
|
||||
session.JediMastery = g_maxForceRank.integer;
|
||||
session.SaberOnly = HasSetSaberOnly();
|
||||
session.Dedicated = com_dedicated->integer;
|
||||
|
||||
// Update the advertised session info
|
||||
session.Update();
|
||||
}
|
||||
|
||||
// Ensure that our session is being advertised correctly
|
||||
// Will update map name after a level change, etc...
|
||||
void XBL_MM_Advertise()
|
||||
{
|
||||
// If not on xboxlive dont post server
|
||||
if(!logged_on)
|
||||
return;
|
||||
|
||||
if ( session.Exists() )
|
||||
{
|
||||
// Our session is already being advertised, update it
|
||||
XBL_MM_Update_Session();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Brand new session
|
||||
XBL_MM_Init_Session();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Finish off and tidy up match making
|
||||
void XBL_MM_Shutdown( bool processLogon )
|
||||
{
|
||||
// We're killing our session, no matter what
|
||||
session.Delete();
|
||||
|
||||
// XDK code to finish this off, while not letting the logon task expire,
|
||||
// except if we're already leaving live, where we can get into a recursive
|
||||
// com_error situation:
|
||||
HRESULT hr;
|
||||
do
|
||||
{
|
||||
if( processLogon && !XBL_PumpLogon() )
|
||||
return;
|
||||
hr = session.Process();
|
||||
} while ( session.IsDeleting() );
|
||||
}
|
||||
|
||||
|
||||
// Search for and join a good online server
|
||||
// GameType is optional - X_MATCH_NULL_INTEGER to omit
|
||||
bool XBL_MM_QuickMatch(ULONGLONG GameType)
|
||||
{
|
||||
// Reuse our optimatch query object. VVFIXME - needs to be torn down first?
|
||||
|
||||
HRESULT hr = query.Query(
|
||||
GameType,
|
||||
X_MATCH_NULL_INTEGER, // CurrentMap
|
||||
0, // MinimumPlayers
|
||||
8, // MaximumPlayers
|
||||
X_MATCH_NULL_INTEGER, // FriendlyFire
|
||||
X_MATCH_NULL_INTEGER, // JediMastery
|
||||
X_MATCH_NULL_INTEGER, // SaberOnly
|
||||
X_MATCH_NULL_INTEGER); // Dedicated
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
UI_xboxErrorPopup( XB_POPUP_MATCHMAKING_ERROR );
|
||||
return false;
|
||||
}
|
||||
// Keep servicing the query until it completes.
|
||||
// The logon task must also be serviced in order to remain connected.
|
||||
do
|
||||
{
|
||||
if( !XBL_PumpLogon() )
|
||||
return false;
|
||||
hr = query.Process();
|
||||
} while( query.IsRunning() );
|
||||
|
||||
if( !query.Succeeded() )
|
||||
{
|
||||
UI_xboxErrorPopup( XB_POPUP_MATCHMAKING_ERROR );
|
||||
return false;
|
||||
}
|
||||
|
||||
// VVFIXME - Need to do probing, and pick best session, not just the first one
|
||||
if (!query.Results.Size())
|
||||
{
|
||||
UI_xboxErrorPopup( XB_POPUP_QUICKMATCH_NO_RESULTS );
|
||||
return false;
|
||||
}
|
||||
|
||||
COptiMatchResult &server(query.Results[0]);
|
||||
|
||||
XBL_MM_SetJoinType( VIA_QUICKMATCH );
|
||||
// VVFIXME - Experiment, leave this out so that the screen doesn't get trashed
|
||||
// right as we connect?
|
||||
// Menus_CloseAll();
|
||||
Net_XboxConnect(&server.SessionID, &server.KeyExchangeKey, &server.HostAddress);
|
||||
/*
|
||||
// warn of lag and join(or not) via ui
|
||||
//
|
||||
if( cls.globalServers[bestSesh].ping < MINIMUM_QOS )
|
||||
{
|
||||
joinServerSlot = bestSesh;
|
||||
Cvar_Set(CVAR_UI_XBOXREBOOTTITLE, " ");
|
||||
Cvar_Set(CVAR_UI_XBOXREBOOTMESSAGE, StringTable_Get(XSTR_GAMEPLAY_AFFECTED_BY_NETWORK));
|
||||
//Menus_ActivateByName("ingame_small_bgd");
|
||||
Menus_CloseByName("quickmatch_popup");
|
||||
Menus_ActivateByName("xblive_slow_warning");
|
||||
return false;
|
||||
}
|
||||
XBL_MM_JoinServer( bestSesh );
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// work out the value of a servers QoS
|
||||
//
|
||||
int getQoSValue( XNQOSINFO* qosInfo )
|
||||
{
|
||||
if ( !qosInfo )
|
||||
return 0; // Sentinel for an as-yet-unknown ping
|
||||
|
||||
WORD avgPing = qosInfo->wRttMedInMsecs;
|
||||
if (avgPing < 150)
|
||||
return 3;
|
||||
else if (avgPing < 250)
|
||||
return 2;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Find currently running sessions
|
||||
int XBL_MM_Find_Session(ULONGLONG GameType, // Optional: X_MATCH_NULL_INTEGER to omit
|
||||
const char *mapName, // Optional: "any" to omit
|
||||
ULONGLONG MinimumPlayers,
|
||||
ULONGLONG MaximumPlayers,
|
||||
ULONGLONG FriendlyFire, // Optional: X_MATCH_NULL_INTEGER to omit
|
||||
ULONGLONG JediMastery, // Optional: X_MATCH_NULL_INTEGER to omit
|
||||
ULONGLONG SaberOnly, // Optional: X_MATCH_NULL_INTEGER to omit
|
||||
ULONGLONG Dedicated) // Optional: X_MATCH_NULL_INTEGER to omit
|
||||
{
|
||||
// Kill off a previous query that's still running
|
||||
query.Cancel();
|
||||
|
||||
ULONGLONG CurrentMap = mapNameToIndex( mapName );
|
||||
if( CurrentMap == MAP_ARRAY_SIZE )
|
||||
CurrentMap = X_MATCH_NULL_INTEGER;
|
||||
|
||||
HRESULT hr = query.Query(
|
||||
GameType,
|
||||
CurrentMap,
|
||||
MinimumPlayers,
|
||||
MaximumPlayers,
|
||||
FriendlyFire,
|
||||
JediMastery,
|
||||
SaberOnly,
|
||||
Dedicated);
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
Com_Error( ERR_DROP, "@MENUS_XBOX_LOST_CONNECTION" );
|
||||
}
|
||||
// Keep servicing the query until it completes.
|
||||
// The logon task must also be serviced in order
|
||||
// to remain connected.
|
||||
do
|
||||
{
|
||||
if( !XBL_PumpLogon() )
|
||||
return 0;
|
||||
hr = query.Process();
|
||||
} while( query.IsRunning() );
|
||||
|
||||
if( !query.Succeeded() )
|
||||
{
|
||||
Com_Error( ERR_DROP, "@MENUS_XBOX_LOST_CONNECTION" );
|
||||
}
|
||||
|
||||
if (!query.Results.Size())
|
||||
{
|
||||
// VVFIXME - handle search that returns no results
|
||||
Com_Printf("No games found in query\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The above gets all results, and does initial (can we connect) probing.
|
||||
// We wait for that, then begin real probing here, which is done async.
|
||||
query.Probe();
|
||||
optiMatchIndex = 0;
|
||||
|
||||
return query.Results.Size();
|
||||
}
|
||||
|
||||
|
||||
// Joins the specified server from the optimatch results list
|
||||
void XBL_MM_JoinServer( void )
|
||||
{
|
||||
// Sanity check
|
||||
if( optiMatchIndex < 0 || optiMatchIndex >= query.Results.Size() )
|
||||
return;
|
||||
|
||||
COptiMatchResult *res = &query.Results[ optiMatchIndex ];
|
||||
|
||||
XBL_MM_SetJoinType( VIA_OPTIMATCH );
|
||||
Net_XboxConnect( &res->SessionID, &res->KeyExchangeKey, &res->HostAddress );
|
||||
|
||||
query.Cancel();
|
||||
}
|
||||
|
||||
|
||||
// Single function to run a query by session ID. Several other functions
|
||||
// use this to get various results (or join servers)
|
||||
static HRESULT runSessionIDQuery( const XNKID *sessionID )
|
||||
{
|
||||
HRESULT hr;
|
||||
hr = queryByID.Query( *(ULONGLONG *)sessionID );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
Com_Error( ERR_FATAL, "Session ID query failed with 0x%x\n", hr );
|
||||
}
|
||||
|
||||
// Keep servicing the query until it completes.
|
||||
// The logon task must also be serviced in order to remain connected.
|
||||
do
|
||||
{
|
||||
if( !XBL_PumpLogon() )
|
||||
return 0;
|
||||
hr = queryByID.Process();
|
||||
} while( queryByID.IsRunning() );
|
||||
|
||||
if ( !queryByID.Succeeded() )
|
||||
{
|
||||
Com_Error( ERR_FATAL, "Session ID query failed #2 with 0x%x\n", hr );
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Returns true if the sessions traffic performs lower than minimum levels for invite
|
||||
bool XBL_MM_ThisSessionIsLagging( const XNKID *sessionID )
|
||||
{
|
||||
// DO the query first...
|
||||
runSessionIDQuery( sessionID );
|
||||
|
||||
// This happens if it's a different title, for example.
|
||||
if ( !queryByID.Results.Size() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start probing
|
||||
queryByID.Probe();
|
||||
|
||||
// Keep probing, and keep the logon task serviced.
|
||||
// VVFIXME - Timeout here?
|
||||
HRESULT hr;
|
||||
do
|
||||
{
|
||||
if( !XBL_PumpLogon() )
|
||||
return false;
|
||||
hr = queryByID.Process();
|
||||
} while( queryByID.IsProbing() );
|
||||
|
||||
return getQoSValue( queryByID.Results[0].pQosInfo ) < MINIMUM_QOS;
|
||||
}
|
||||
|
||||
namespace ui
|
||||
{
|
||||
extern void Menus_CloseAll( void );
|
||||
}
|
||||
|
||||
// Connnect to a game we have the session ID for
|
||||
bool XBL_MM_ConnectViaSessionID( const XNKID *sessionID, bool invited )
|
||||
{
|
||||
// Do the query...
|
||||
runSessionIDQuery( sessionID );
|
||||
|
||||
// No results? (Should only be one)
|
||||
if ( !queryByID.Results.Size() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CJoinSessionByIDResult &server(queryByID.Results[0]);
|
||||
if (server.PrivateOpen < 1 && server.PublicOpen < 1)
|
||||
{
|
||||
// No slots available
|
||||
return false;
|
||||
}
|
||||
|
||||
// OK. If we're currently hosting a dedicated server, we need to clean things up:
|
||||
if( com_dedicated->integer )
|
||||
{
|
||||
// Marks us as no longer joinable or playing:
|
||||
XBL_F_OnClientLeaveSession();
|
||||
|
||||
// Code copied from "Leave" in ui_main.c
|
||||
Cvar_SetValue( "cl_paused", 0 );
|
||||
Key_SetCatcher( KEYCATCH_UI );
|
||||
Cvar_SetValue( "cl_running", 1 );
|
||||
SV_Shutdown( "Server quit\n" );
|
||||
|
||||
CL_ShutdownUI();
|
||||
extern void RE_Shutdown( qboolean destroyWindow );
|
||||
RE_Shutdown( qfalse );
|
||||
CL_Disconnect( qtrue );
|
||||
|
||||
Cvar_SetValue( "dedicated", 0 );
|
||||
Cvar_SetValue( "ui_dedicated", 0 );
|
||||
CL_FlushMemory();
|
||||
|
||||
ui::Menus_CloseAll();
|
||||
}
|
||||
else if( cls.state == CA_ACTIVE )
|
||||
{
|
||||
// If we were already playing, we need to disconnect earlier than normal,
|
||||
// to clean up XBL stuff. Don't delete textures, we need to draw the connect screen:
|
||||
CL_Disconnect( qtrue, qfalse );
|
||||
Net_XboxDisconnect();
|
||||
}
|
||||
|
||||
Net_XboxConnect(&server.SessionID, &server.KeyExchangeKey, &server.HostAddress);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if session exists
|
||||
bool XBL_MM_IsSessionIDValid(const XNKID *sessionID)
|
||||
{
|
||||
// Do the query...
|
||||
runSessionIDQuery( sessionID );
|
||||
|
||||
// Did we get any hits?
|
||||
return bool( queryByID.Results.Size() );
|
||||
}
|
||||
|
||||
// Remember how we started connecting to a session, in case it turns out to suck
|
||||
void XBL_MM_SetJoinType(JoinType way)
|
||||
{
|
||||
joinVia = way;
|
||||
}
|
||||
|
||||
// Are we eligible for a private slot in the game we're connecting to?
|
||||
bool XBL_MM_CanUsePrivateSlot( void )
|
||||
{
|
||||
return ((joinVia == VIA_FRIEND_JOIN) ||
|
||||
(joinVia == VIA_FRIEND_INVITE));
|
||||
}
|
||||
|
||||
extern void UI_JoinSession();
|
||||
extern void UI_JoinInvite();
|
||||
|
||||
// Join a server even after recieving a message saying it was low QoS
|
||||
void XBL_MM_JoinLowQOSSever()
|
||||
{
|
||||
if( joinVia == VIA_FRIEND_JOIN )
|
||||
{
|
||||
UI_JoinSession();
|
||||
}
|
||||
|
||||
else if( joinVia == VIA_FRIEND_INVITE)
|
||||
{
|
||||
UI_JoinInvite();
|
||||
}
|
||||
|
||||
/*
|
||||
else if( joinVia == VIA_OPTIMATCH )
|
||||
{
|
||||
XBL_MM_JoinServer( joinServerSlot );
|
||||
}
|
||||
|
||||
else if( joinVia == VIA_QUICKMATCH )
|
||||
{
|
||||
XBL_MM_JoinServer( joinServerSlot );
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// Bail out of joining a server after recieving a message saying it was low QoS
|
||||
void XBL_MM_DontJoinLowQOSSever()
|
||||
{
|
||||
// If we were originally invited, make sure to decline the invitation
|
||||
if( joinVia == VIA_FRIEND_INVITE )
|
||||
{
|
||||
HRESULT result = XBL_F_PerformMenuAction(UI_F_GAMEDECLINE);
|
||||
}
|
||||
|
||||
// All other scenarios require no extra work
|
||||
}
|
||||
|
||||
// run this code every game tick
|
||||
// will only be called while logged on
|
||||
//
|
||||
void XBL_MM_Tick()
|
||||
{
|
||||
// VVFIXME - SOF2 re-advertised after some crazy timeout.
|
||||
|
||||
// New version just ticks the session object as well, it does nothing if it's not real
|
||||
HRESULT hr = session.Process();
|
||||
#ifdef _DEBUG
|
||||
if ( FAILED( hr ) )
|
||||
Com_Printf("session.Process() failed: %s\n", getXBLErrorName(hr));
|
||||
#endif
|
||||
|
||||
// The only time we need to do async work on the query is when probing for QoS
|
||||
if ( query.IsProbing() )
|
||||
{
|
||||
query.Process();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Big pile of functions that the UI uses to pull system link results for display
|
||||
//
|
||||
int Syslink_GetNumServers( void )
|
||||
{
|
||||
return cls.numlocalservers;
|
||||
}
|
||||
|
||||
extern serverInfo_t *SysLink_GetServer( int index );
|
||||
|
||||
const char *Syslink_GetServerMap( const int index )
|
||||
{
|
||||
serverInfo_t *s = SysLink_GetServer( index );
|
||||
|
||||
int mapIndex = mapNameToIndex( s->mapName );
|
||||
if( mapIndex == MAP_ARRAY_SIZE )
|
||||
return "";
|
||||
|
||||
return mapIndexToLongName( mapIndex );
|
||||
}
|
||||
|
||||
const char *Syslink_GetServerClients( const int index )
|
||||
{
|
||||
serverInfo_t *s = SysLink_GetServer( index );
|
||||
|
||||
return va( "%d/%d", s->clients, s->maxClients );
|
||||
}
|
||||
|
||||
const char *Syslink_GetServerGametype( const int index )
|
||||
{
|
||||
serverInfo_t *s = SysLink_GetServer( index );
|
||||
|
||||
return gameTypeIndexToName( s->gameType );
|
||||
}
|
||||
|
||||
int Syslink_GetServerSaberOnly( const int index )
|
||||
{
|
||||
serverInfo_t *s = SysLink_GetServer( index );
|
||||
|
||||
if( s->saberOnly )
|
||||
return RE_RegisterShaderNoMip( "gfx/mp/saber_only" );
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Syslink_GetServerDisableForce( const int index )
|
||||
{
|
||||
serverInfo_t *s = SysLink_GetServer( index );
|
||||
|
||||
if( s->forceDisable )
|
||||
return RE_RegisterShaderNoMip( "gfx/mp/noforce_bw" );
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
//
|
||||
// Big pile of functions that the UI uses to pull optimatch results for display
|
||||
//
|
||||
int XBL_MM_GetNumServers( void )
|
||||
{
|
||||
return query.Results.Size();
|
||||
}
|
||||
|
||||
const char *XBL_MM_GetServerName( const int index )
|
||||
{
|
||||
static char retVal[XATTRIB_SESSION_NAME_MAX_LEN+1];
|
||||
|
||||
wcharToChar( retVal, query.Results[index].SessionName );
|
||||
return retVal;
|
||||
}
|
||||
|
||||
const char *XBL_MM_GetServerMap( const int index )
|
||||
{
|
||||
return mapIndexToLongName( query.Results[index].CurrentMap );
|
||||
}
|
||||
|
||||
const char *XBL_MM_GetServerClients( const int index )
|
||||
{
|
||||
int openSlots = query.Results[index].PublicOpen + query.Results[index].PrivateOpen;
|
||||
int filledSlots = query.Results[index].PublicFilled + query.Results[index].PrivateFilled;
|
||||
return va( "%d/%d", filledSlots, filledSlots + openSlots );
|
||||
}
|
||||
|
||||
const char *XBL_MM_GetServerGametype( const int index )
|
||||
{
|
||||
return gameTypeIndexToName( query.Results[index].GameType );
|
||||
}
|
||||
|
||||
int XBL_MM_GetServerSaberOnly( const int index )
|
||||
{
|
||||
if( query.Results[index].SaberOnly )
|
||||
return RE_RegisterShaderNoMip( "gfx/mp/saber_only" );
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int XBL_MM_GetServerDisableForce( const int index )
|
||||
{
|
||||
if( query.Results[index].JediMastery == 0 )
|
||||
return RE_RegisterShaderNoMip( "gfx/mp/noforce_bw" );
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int XBL_MM_GetServerPing( const int index )
|
||||
{
|
||||
int pingVal = getQoSValue( query.Results[index].pQosInfo );
|
||||
|
||||
switch( pingVal )
|
||||
{
|
||||
case 1: // Bad
|
||||
return RE_RegisterShaderNoMip( "gfx/mp/dot_red" );
|
||||
case 2: // Average
|
||||
return RE_RegisterShaderNoMip( "gfx/mp/dot_yellow" );
|
||||
case 3: // Good
|
||||
return RE_RegisterShaderNoMip( "gfx/mp/dot_green" );
|
||||
case 0: // Unknown
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void XBL_MM_SetChosenServerIndex( const int index )
|
||||
{
|
||||
// Just save this away:
|
||||
optiMatchIndex = index;
|
||||
|
||||
// Also set the cvar that UI uses to draw the host's gamertag in the status line
|
||||
Cvar_Set( "xbl_gamertag", XBL_MM_GetServerName( index ) );
|
||||
}
|
||||
|
||||
// Used by the UI when the user picks a server to join, or backs out of the results screen
|
||||
void XBL_MM_CancelProbing( void )
|
||||
{
|
||||
query.Cancel();
|
||||
}
|
||||
636
codemp/xbox/XBLive_PL.cpp
Normal file
636
codemp/xbox/XBLive_PL.cpp
Normal file
@@ -0,0 +1,636 @@
|
||||
// XBLive_PL.cpp
|
||||
//
|
||||
// Player List functionality for XBox Live
|
||||
//
|
||||
|
||||
|
||||
#include "XBLive.h"
|
||||
#include "XBoxCommon.h"
|
||||
#include "..\cgame\cg_local.h"
|
||||
#include "../qcommon/qcommon.h"
|
||||
#include "XBVoice.h"
|
||||
#include "../server/server.h"
|
||||
#include "../ui/ui_shared.h"
|
||||
|
||||
extern int RE_RegisterShaderNoMip( const char *name );
|
||||
extern const char *SE_GetString( const char *psPackageAndStringReference );
|
||||
|
||||
// Icon names that make up the cycle when someone is talking:
|
||||
const char *XBL_SPEAKINGCYCLE_PL[] =
|
||||
{
|
||||
"gfx/mp/speaking_1_icon",
|
||||
"gfx/mp/speaking_2_icon",
|
||||
"gfx/mp/voice_on_icon",
|
||||
};
|
||||
#define MAX_SPEAK_CYCLE 3
|
||||
#define SPEAK_CYCLE_DELAY 10
|
||||
|
||||
//DEFINES
|
||||
#define PLAYERSLIST_UPDATE_RATE 60
|
||||
|
||||
//VARIABLES
|
||||
|
||||
int playerIndex = 0; // Player index chosen from the list
|
||||
int selectedIndex = 0; // Index of currently selected player - if the list changes during popup
|
||||
XUID selectedXuid; // XUID of selected player, in case the list REALLY changes
|
||||
WCHAR selectedGamertag[XONLINE_GAMERTAG_SIZE]; // g-tag of selected player, "
|
||||
int speakCycleIndex = 0; // index of current frame in speak cycle animation
|
||||
int speakCycleDelay = 0; // frames since last cycle increment
|
||||
XBHistory xbOfflineHistory;
|
||||
bool playerListActive = false; // Determine if play list is active
|
||||
BYTE tickCount = 0; // Amount of tick counts that have passed
|
||||
|
||||
// ******************************************************
|
||||
// INITIALIZING FUNCTIONS
|
||||
// *******************************************************
|
||||
void XBL_PL_Init(void)
|
||||
{
|
||||
playerListActive = true;
|
||||
tickCount = PLAYERSLIST_UPDATE_RATE;
|
||||
XBL_F_GenerateFriendsList();
|
||||
}
|
||||
|
||||
// ******************************************************
|
||||
// CLEANUP FUNCTIONS
|
||||
// *******************************************************
|
||||
void XBL_PL_Cleanup(void)
|
||||
{
|
||||
playerListActive = false;
|
||||
XBL_F_ReleaseFriendsList();
|
||||
}
|
||||
|
||||
// when this client leaves the session
|
||||
//
|
||||
void XBL_PL_OnClientLeaveSession()
|
||||
{
|
||||
memset( &xbOfflineHistory, 0, sizeof(xbOfflineHistory) );
|
||||
}
|
||||
|
||||
// **********************************************************
|
||||
// FUNCTIONS THE MENU SYSTEM USES TO COMMUNICATE TO PLAYERS *
|
||||
// **********************************************************
|
||||
|
||||
// Internally, the current selection is stored in playerIndex
|
||||
// This value is absolute - all mapping from pseudo-values in the UI is
|
||||
// done in the set/get functions. Values for playerIndex:
|
||||
// -1 : Divider bar
|
||||
// 0 .. (MAX_ONLINE_PLAYERS-1) : Active players (should never be ours)
|
||||
// MAX_ONLINE_PLAYERS .. +10 : History entries
|
||||
//
|
||||
// HISTORY_OFFSET is defined to be MAX_ONLINE_PLAYERS for ease of use
|
||||
|
||||
// The UI has a different set of values, because they need to map to
|
||||
// the entries in the listbox directly. Thus, indexes coming from the UI
|
||||
// will be:
|
||||
// 0 .. (clients-2) : Players, other than ourselves
|
||||
// (clients-1) : Divider
|
||||
// clients .. +10 : History
|
||||
|
||||
// Get the number of active players --- makes me sad way of doing it :(
|
||||
int XBL_PL_GetNumActivePlayers( void )
|
||||
{
|
||||
int count = 0;
|
||||
// Loop through the player lists and test the active flag
|
||||
for(int i=0; i<MAX_ONLINE_PLAYERS; i++)
|
||||
{
|
||||
if(xbOnlineInfo.xbPlayerList[i].isActive)
|
||||
count++;
|
||||
}
|
||||
|
||||
return count - 1; //do not count yourself
|
||||
}
|
||||
|
||||
// Get the number of active entries in the history list
|
||||
int XBL_PL_GetNumActiveHistory( void )
|
||||
{
|
||||
int count = 0;
|
||||
// Loop through the player lists and test the active flag
|
||||
for(int i = 0; i < MAX_OFFLINE_HISTORY; i++)
|
||||
{
|
||||
if(xbOfflineHistory.historyList[i].isActive)
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// Get the total number of players in the player list (active+history-you)
|
||||
int XBL_PL_GetNumPlayers( void )
|
||||
{
|
||||
return XBL_PL_GetNumActivePlayers() + XBL_PL_GetNumActiveHistory();
|
||||
}
|
||||
|
||||
|
||||
// Converts a UI index into an index into the xbPlayerList array
|
||||
int XBL_PL_IndexToPlayerIndexOnline(const int index)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for(int i=0; i<MAX_ONLINE_PLAYERS; i++)
|
||||
{
|
||||
// Scan through all active players other than ourself
|
||||
if(xbOnlineInfo.xbPlayerList[i].isActive && xbOnlineInfo.localIndex != i)
|
||||
{
|
||||
if(count == index)
|
||||
return i;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Converts a pseudo-UI index into an index into the xbOfflineHistory array
|
||||
int XBL_PL_IndexToPlayerIndexHistory(const int index)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for(int i=0; i<MAX_OFFLINE_HISTORY; i++)
|
||||
{
|
||||
// Scan through all active entries in the history list
|
||||
if(xbOfflineHistory.historyList[i].isActive)
|
||||
{
|
||||
// When playerIndex points into the history list, it is offset so we know
|
||||
if(count == index)
|
||||
return (i + HISTORY_OFFSET);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Very handy utility, converts a UI player list index (from the feeder)
|
||||
// into a valid playerIndex (as above)
|
||||
int XBL_PL_IndexToPlayerIndex(const int index)
|
||||
{
|
||||
int numActive = XBL_PL_GetNumActivePlayers();
|
||||
|
||||
if(index < numActive)
|
||||
{
|
||||
// It's a low (current player) index
|
||||
return XBL_PL_IndexToPlayerIndexOnline( index );
|
||||
}
|
||||
else if(index == numActive)
|
||||
{
|
||||
// It's the index of the divider
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's a history index - shift down
|
||||
return XBL_PL_IndexToPlayerIndexHistory( index - (numActive + 1) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Set the stored playerIndex from the UI's index into the player list
|
||||
void XBL_PL_SetPlayerIndex(const int index)
|
||||
{
|
||||
playerIndex = XBL_PL_IndexToPlayerIndex( index );
|
||||
}
|
||||
|
||||
|
||||
// This sets all the cvars used in the playerlist popup window
|
||||
bool XBL_PL_SetCurrentInfo( void )
|
||||
{
|
||||
// We always set the date, regardless:
|
||||
SYSTEMTIME time;
|
||||
GetSystemTime(&time);
|
||||
Cvar_Set("pl_sessionDate", va("%d/%d/%d", time.wMonth, time.wDay, time.wYear) );
|
||||
|
||||
if(playerIndex >= 0 && playerIndex < MAX_ONLINE_PLAYERS)
|
||||
{
|
||||
// Sanity check - dedicated server is in last slot, has no clientinfo
|
||||
assert( xbOnlineInfo.xbPlayerList[playerIndex].isActive );
|
||||
|
||||
// Selected player is in the current game's players list
|
||||
if( xbOnlineInfo.xbPlayerList[playerIndex].isActive )
|
||||
{
|
||||
// And they're active. That's good. OK, copy things into cvars
|
||||
Cvar_Set( "pl_selectedName", xbOnlineInfo.xbPlayerList[playerIndex].name );
|
||||
|
||||
if(xbOnlineInfo.xbPlayerList[playerIndex].flags & MUTED_PLAYER)
|
||||
Cvar_Set("pl_selectedMute", "1");
|
||||
else
|
||||
Cvar_Set("pl_selectedMute", "0");
|
||||
|
||||
// Only show the friend-related options if logged into live:
|
||||
if( logged_on )
|
||||
{
|
||||
if( xbOnlineInfo.xbPlayerList[playerIndex].friendshipStatus == FRIEND_RQST )
|
||||
Cvar_Set("pl_selectedFriend", "1");
|
||||
else if( xbOnlineInfo.xbPlayerList[playerIndex].friendshipStatus == FRIEND_YES )
|
||||
Cvar_Set("pl_selectedFriend", "2");
|
||||
else
|
||||
Cvar_Set("pl_selectedFriend", "0");
|
||||
}
|
||||
else
|
||||
{
|
||||
Cvar_Set("pl_selectedFriend", "-1");
|
||||
}
|
||||
|
||||
if(com_sv_running->integer) {
|
||||
if(svs.clients[playerIndex].state == CS_ACTIVE) {
|
||||
Cvar_Set("pl_allowKick", "1");
|
||||
} else {
|
||||
Cvar_Set("pl_allowKick", "0");
|
||||
}
|
||||
} else {
|
||||
Cvar_Set("pl_allowKick", "0");
|
||||
}
|
||||
|
||||
|
||||
// Remember this index, so that if people join/leave, we don't use the popup
|
||||
// to do something to the wrong player!
|
||||
selectedIndex = playerIndex;
|
||||
// Remember the XUID and gamertag, so that we can many things even if the
|
||||
// list does change while we're in the popup
|
||||
selectedXuid = xbOnlineInfo.xbPlayerList[selectedIndex].xuid;
|
||||
charToWchar( selectedGamertag, xbOnlineInfo.xbPlayerList[selectedIndex].name );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if(playerIndex >= HISTORY_OFFSET && playerIndex < HISTORY_OFFSET + MAX_OFFLINE_HISTORY)
|
||||
{
|
||||
int index = playerIndex - HISTORY_OFFSET;
|
||||
|
||||
// Sanity check
|
||||
assert( xbOfflineHistory.historyList[index].isActive );
|
||||
|
||||
if(xbOfflineHistory.historyList[index].isActive)
|
||||
{
|
||||
// OK. Copy things into cvars
|
||||
Cvar_Set( "pl_selectedName", xbOfflineHistory.historyList[index].name );
|
||||
|
||||
if(xbOfflineHistory.historyList[index].flags & MUTED_PLAYER)
|
||||
Cvar_Set("pl_selectedMute", "1");
|
||||
else
|
||||
Cvar_Set("pl_selectedMute", "0");
|
||||
|
||||
// Only show the friend-related options if logged into live:
|
||||
if( logged_on )
|
||||
{
|
||||
if(xbOfflineHistory.historyList[index].friendshipStatus == FRIEND_RQST)
|
||||
Cvar_Set("pl_selectedFriend", "1");
|
||||
else if(xbOfflineHistory.historyList[index].friendshipStatus == FRIEND_YES)
|
||||
Cvar_Set("pl_selectedFriend", "2");
|
||||
else
|
||||
Cvar_Set("pl_selectedFriend", "0");
|
||||
}
|
||||
else
|
||||
{
|
||||
Cvar_Set("pl_selectedFriend", "-1");
|
||||
}
|
||||
|
||||
// Can never kick someone that isn't playing
|
||||
Cvar_Set( "pl_allowKick", "0" );
|
||||
|
||||
// Remember this index, (with the OFFSET!) so that if people join/leave,
|
||||
// we don't use the popup to do something to the wrong player!
|
||||
selectedIndex = playerIndex;
|
||||
// Remember the XUID and gamertag, so that we can many things even if the
|
||||
// list does change while we're in the popup
|
||||
selectedXuid = xbOfflineHistory.historyList[index].xuid;
|
||||
charToWchar( selectedGamertag, xbOfflineHistory.historyList[index].name );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//set to all 0 by default
|
||||
Cvar_Set("pl_selectedName", "");
|
||||
Cvar_Set("pl_selectedMute", "0");
|
||||
Cvar_Set("pl_selectedFriend", "0");
|
||||
Cvar_Set("pl_allowKick", "0");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Different from SOF2 version. Returns the name of the player at location
|
||||
// `index' in the UI's player list. Handles player/history/divider.
|
||||
const char *XBL_PL_GetPlayerName(const int index)
|
||||
{
|
||||
int pIndex = XBL_PL_IndexToPlayerIndex( index );
|
||||
|
||||
if(pIndex < 0)
|
||||
{
|
||||
// The divider - VVFIXME
|
||||
return SE_GetString( "MENUS_RECENT_PLAYERS" );
|
||||
}
|
||||
else if(pIndex < MAX_ONLINE_PLAYERS)
|
||||
{
|
||||
// It's an active player
|
||||
return xbOnlineInfo.xbPlayerList[pIndex].name;
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's a history index
|
||||
return xbOfflineHistory.historyList[pIndex - HISTORY_OFFSET].name;
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the shader index for the voice icon to display (-1 for none)
|
||||
int XBL_PL_GetVoiceIcon(const int index)
|
||||
{
|
||||
int pIndex = XBL_PL_IndexToPlayerIndex( index );
|
||||
|
||||
if(pIndex < 0)
|
||||
{
|
||||
// Divider
|
||||
return -1;
|
||||
}
|
||||
else if(pIndex < MAX_ONLINE_PLAYERS)
|
||||
{
|
||||
// Active player
|
||||
if(xbOnlineInfo.xbPlayerList[pIndex].flags & MUTED_PLAYER)
|
||||
return RE_RegisterShaderNoMip( "gfx/mp/voice_mute_icon" );
|
||||
else if(g_Voice.IsTalking(xbOnlineInfo.xbPlayerList[pIndex].xuid))
|
||||
return RE_RegisterShaderNoMip( XBL_SPEAKINGCYCLE_PL[speakCycleIndex] );
|
||||
else if(xbOnlineInfo.xbPlayerList[pIndex].flags & VOICE_CAN_SEND)
|
||||
return RE_RegisterShaderNoMip( "gfx/mp/voice_on_icon" );
|
||||
// else if(xbOnlineInfo.xbPlayerList[pIndex].flags & VOICE_CAN_RECV)
|
||||
// return RE_RegisterShaderNoMip( "gfx/mp/voice_speakers_icon" );
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// History player
|
||||
if(xbOfflineHistory.historyList[pIndex - HISTORY_OFFSET].flags & MUTED_PLAYER)
|
||||
return RE_RegisterShaderNoMip( "gfx/mp/voice_mute_icon" );
|
||||
// VVFIXME - Removed these. I don't think history players should have "current" voice status
|
||||
// else if(xbOfflineHistory.historyList[pIndex - HISTORY_OFFSET].flags & VOICE_ENABLED)
|
||||
// return XBL_ICONSASCIIVALUE_PL[XBL_PL_ICON_VOICEON];
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the current status icon to display
|
||||
int XBL_PL_GetStatusIcon(const int index)
|
||||
{
|
||||
int pIndex = XBL_PL_IndexToPlayerIndex( index );
|
||||
|
||||
if(pIndex < 0)
|
||||
{
|
||||
// Divider
|
||||
return -1;
|
||||
}
|
||||
else if(pIndex < MAX_ONLINE_PLAYERS)
|
||||
{
|
||||
// Active player
|
||||
if( xbOnlineInfo.xbPlayerList[pIndex].friendshipStatus == FRIEND_RQST_RCV)
|
||||
return RE_RegisterShaderNoMip( "gfx/mp/friend_received_icon" );
|
||||
else if( xbOnlineInfo.xbPlayerList[pIndex].friendshipStatus == FRIEND_RQST)
|
||||
return RE_RegisterShaderNoMip( "gfx/mp/friend_sent_icon" );
|
||||
else if( xbOnlineInfo.xbPlayerList[pIndex].friendshipStatus == FRIEND_YES)
|
||||
return RE_RegisterShaderNoMip( "gfx/mp/online_icon" );
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// History player
|
||||
if( xbOfflineHistory.historyList[pIndex - HISTORY_OFFSET].friendshipStatus == FRIEND_RQST)
|
||||
return RE_RegisterShaderNoMip( "gfx/mp/friend_sent_icon" );
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Quick functions for validating the stored playerIndex value before we do anything
|
||||
static bool validStoredActiveIndex( void )
|
||||
{
|
||||
return ( selectedIndex >= 0 &&
|
||||
selectedIndex < MAX_ONLINE_PLAYERS &&
|
||||
xbOnlineInfo.xbPlayerList[selectedIndex].isActive &&
|
||||
XOnlineAreUsersIdentical( &xbOnlineInfo.xbPlayerList[selectedIndex].xuid, &selectedXuid ) );
|
||||
}
|
||||
|
||||
static bool validStoredHistoryIndex( void )
|
||||
{
|
||||
int hIndex = selectedIndex - HISTORY_OFFSET;
|
||||
return ( hIndex >= 0 &&
|
||||
hIndex < MAX_OFFLINE_HISTORY &&
|
||||
xbOfflineHistory.historyList[hIndex].isActive &&
|
||||
XOnlineAreUsersIdentical( &xbOfflineHistory.historyList[hIndex].xuid, &selectedXuid ) );
|
||||
}
|
||||
|
||||
// Toggle mute status for the player we stored in selectedIndex.
|
||||
// VVFIXME - This could be more clever, and not depend on index still being valid.
|
||||
void XBL_PL_ToggleMute( void )
|
||||
{
|
||||
// Offset index, if this is a history player
|
||||
int hIndex = selectedIndex - HISTORY_OFFSET;
|
||||
|
||||
if( validStoredActiveIndex() )
|
||||
{
|
||||
// Active player
|
||||
if( xbOnlineInfo.xbPlayerList[selectedIndex].flags & MUTED_PLAYER )
|
||||
{
|
||||
// Was muted. Un-mute them.
|
||||
g_Voice.SetMute(xbOnlineInfo.xbPlayerList[selectedIndex].xuid, false);
|
||||
xbOnlineInfo.xbPlayerList[selectedIndex].flags &= ~MUTED_PLAYER;
|
||||
Cvar_Set( "pl_selectedMute", "0" );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wasn't muted. Mute them.
|
||||
g_Voice.SetMute(xbOnlineInfo.xbPlayerList[selectedIndex].xuid, true);
|
||||
xbOnlineInfo.xbPlayerList[selectedIndex].flags |= MUTED_PLAYER;
|
||||
Cvar_Set( "pl_selectedMute", "1" );
|
||||
}
|
||||
}
|
||||
else if( validStoredHistoryIndex() )
|
||||
{
|
||||
if( xbOfflineHistory.historyList[hIndex].flags & MUTED_PLAYER )
|
||||
{
|
||||
// Was muted. Un-mute them.
|
||||
g_Voice.SetMute( xbOfflineHistory.historyList[hIndex].xuid, false );
|
||||
xbOfflineHistory.historyList[hIndex].flags &= ~MUTED_PLAYER;
|
||||
Cvar_Set( "pl_selectedMute", "0" );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wasn't muted. Mute them.
|
||||
g_Voice.SetMute( xbOfflineHistory.historyList[hIndex].xuid, true );
|
||||
xbOfflineHistory.historyList[hIndex].flags |= MUTED_PLAYER;
|
||||
Cvar_Set( "pl_selectedMute", "1" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send feedback about the player we stored in selectedIndex.
|
||||
// Feedback type is pulled from the cvar set by the UI.
|
||||
void XBL_PL_SendFeedBack( void )
|
||||
{
|
||||
// All necessary information is stored away, no vaildation needed
|
||||
XONLINETASK_HANDLE feedbackTask;
|
||||
XONLINE_FEEDBACK_TYPE feedback = (XONLINE_FEEDBACK_TYPE)Cvar_VariableIntegerValue("pl_feedbackType");
|
||||
XONLINE_FEEDBACK_PARAMS feedbackParams;
|
||||
feedbackParams.lpStringParam = &selectedGamertag[0];
|
||||
|
||||
HRESULT hr = XOnlineFeedbackSend( IN_GetMainController(), selectedXuid, feedback, &feedbackParams, NULL, &feedbackTask );
|
||||
if( hr != S_OK )
|
||||
return;
|
||||
|
||||
do
|
||||
{
|
||||
hr = XOnlineTaskContinue( feedbackTask );
|
||||
} while( hr != XONLINETASK_S_SUCCESS );
|
||||
|
||||
XOnlineTaskClose( feedbackTask );
|
||||
}
|
||||
|
||||
// Kick a player via the player list (they've been stored in playerIndex already)
|
||||
void XBL_PL_KickPlayer( void )
|
||||
{
|
||||
// Sanity checks
|
||||
assert( com_sv_running->integer &&
|
||||
selectedIndex >= 0 &&
|
||||
selectedIndex < DEDICATED_SERVER_INDEX );
|
||||
|
||||
// Make sure they're still around, and it's still the same person
|
||||
// This action will ALWAYS require validation
|
||||
if( !validStoredActiveIndex() )
|
||||
return;
|
||||
|
||||
client_t *cl = &svs.clients[selectedIndex];
|
||||
|
||||
SV_DropClient( cl, "@MENUS_YOU_WERE_KICKED" );
|
||||
cl->lastPacketTime = svs.time; // in case there is a funny zombie
|
||||
}
|
||||
|
||||
// Send a friend request to a player via the player list
|
||||
// (They've already been stored in playerIndex)
|
||||
void XBL_PL_MakeFriend( void )
|
||||
{
|
||||
// Selected user doesn't need to be valid anymore, we just need the XUID
|
||||
XBL_F_AddFriend( &selectedXuid );
|
||||
// Update, we've sent a request, this will change the button displayed
|
||||
Cvar_Set( "pl_selectedFriend", "1" );
|
||||
}
|
||||
|
||||
// Cancel a friend request or remove a friend from our friend list, via the player list
|
||||
// (They've already been stored in playerIndex)
|
||||
void XBL_PL_CancelFriend( void )
|
||||
{
|
||||
// Selected user doesn't need to be valid anymore, we just need the XUID
|
||||
XBL_F_RemoveFriend( &selectedXuid );
|
||||
// We've canceled, this will change button displayed
|
||||
Cvar_Set( "pl_selectedFriend", "0" );
|
||||
}
|
||||
|
||||
// **********************************************************
|
||||
// PUBLIC ** PUBLIC ** PUBLIC ** PUBLIC ** PUBLIC ** PUBLIC *
|
||||
// **********************************************************
|
||||
|
||||
void XBL_PL_Tick(void)
|
||||
{
|
||||
if(playerListActive)
|
||||
{
|
||||
speakCycleDelay++;
|
||||
if(speakCycleDelay > SPEAK_CYCLE_DELAY)
|
||||
{
|
||||
speakCycleDelay = 0;
|
||||
speakCycleIndex++;
|
||||
if(speakCycleIndex >= MAX_SPEAK_CYCLE)
|
||||
speakCycleIndex = 0;
|
||||
}
|
||||
|
||||
// Remaining code only applies if we're on Xbox Live
|
||||
if( !logged_on )
|
||||
return;
|
||||
|
||||
if( ++tickCount < PLAYERSLIST_UPDATE_RATE )
|
||||
return;
|
||||
|
||||
tickCount = 0; // reset
|
||||
|
||||
// Scan through player list and update friendstatus
|
||||
for( int i = 0; i < MAX_ONLINE_PLAYERS; i++)
|
||||
{
|
||||
if(xbOnlineInfo.xbPlayerList[i].isActive)
|
||||
xbOnlineInfo.xbPlayerList[i].friendshipStatus = XBL_F_GetFriendStatus(&xbOnlineInfo.xbPlayerList[i].xuid);
|
||||
}
|
||||
// Scan through history list and update friendstatus
|
||||
for( int i = 0; i < MAX_OFFLINE_HISTORY; i++)
|
||||
{
|
||||
if(xbOfflineHistory.historyList[i].isActive)
|
||||
xbOfflineHistory.historyList[i].friendshipStatus = XBL_F_GetFriendStatus(&xbOfflineHistory.historyList[i].xuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//call when another client leaves the game
|
||||
void XBL_PL_RemoveActivePeer(XUID* xuid, int index)
|
||||
{
|
||||
// Don't re-add them if they're already in:
|
||||
int i;
|
||||
for(i = 0; i < MAX_OFFLINE_HISTORY; i++)
|
||||
{
|
||||
if(xbOfflineHistory.historyList[i].isActive && XOnlineAreUsersIdentical(xuid, &xbOfflineHistory.historyList[i].xuid))
|
||||
return;
|
||||
}
|
||||
|
||||
// Search for inactive slot, or oldest active slot:
|
||||
int oldestIndex = 0;
|
||||
DWORD oldestStamp = xbOfflineHistory.historyList[0].stamp;
|
||||
for( i = 0; i < MAX_OFFLINE_HISTORY; ++i )
|
||||
{
|
||||
// Inactive slots win
|
||||
if( !xbOfflineHistory.historyList[i].isActive )
|
||||
break;
|
||||
|
||||
// Older?
|
||||
if( xbOfflineHistory.historyList[i].stamp < oldestStamp )
|
||||
{
|
||||
oldestStamp = xbOfflineHistory.historyList[i].stamp;
|
||||
oldestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Grab the entry:
|
||||
XBHistoryEntry* entry;
|
||||
if( i == MAX_OFFLINE_HISTORY )
|
||||
entry = &xbOfflineHistory.historyList[oldestIndex];
|
||||
else
|
||||
entry = &xbOfflineHistory.historyList[i];
|
||||
|
||||
memcpy(&entry->xuid, xuid, sizeof(XUID));
|
||||
if(index >= 0)
|
||||
{
|
||||
memcpy(entry->name, &xbOnlineInfo.xbPlayerList[index].name, sizeof(char) * XONLINE_GAMERTAG_SIZE);
|
||||
entry->friendshipStatus = xbOnlineInfo.xbPlayerList[index].friendshipStatus;
|
||||
entry->flags = xbOnlineInfo.xbPlayerList[index].flags;
|
||||
}
|
||||
entry->isActive = true;
|
||||
|
||||
// Re-stamp this entry, advance timestamp:
|
||||
entry->stamp = xbOfflineHistory.stamp++;
|
||||
}
|
||||
|
||||
void XBL_PL_CheckHistoryList(XBPlayerInfo *newInfo)
|
||||
{
|
||||
if(!logged_on)
|
||||
return;
|
||||
for(int i = 0; i < MAX_OFFLINE_HISTORY; i++)
|
||||
{
|
||||
if(xbOfflineHistory.historyList[i].isActive && XOnlineAreUsersIdentical(&newInfo->xuid, &xbOfflineHistory.historyList[i].xuid))
|
||||
{
|
||||
xbOfflineHistory.historyList[i].isActive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called when a user's userinfo changes, and their name needs to be updated:
|
||||
void XBL_PL_UpdatePlayerName( int index, const char *newName )
|
||||
{
|
||||
// People should never be able to change their names while logged into Live!
|
||||
// Fuck it. Making this work is impossible, given the number of inane name
|
||||
// changes that come from the server.
|
||||
|
||||
// Just copy it over:
|
||||
Q_strncpyz( xbOnlineInfo.xbPlayerList[index].name, newName, sizeof(xbOnlineInfo.xbPlayerList[index].name) );
|
||||
}
|
||||
1473
codemp/xbox/XBVoice.cpp
Normal file
1473
codemp/xbox/XBVoice.cpp
Normal file
File diff suppressed because it is too large
Load Diff
302
codemp/xbox/XBVoice.h
Normal file
302
codemp/xbox/XBVoice.h
Normal file
@@ -0,0 +1,302 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// File: XBVoice.h
|
||||
//
|
||||
// Stolen from June XDK SimpleVoice sample, then butchered
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef _XBVOICE_H
|
||||
#define _XBVOICE_H
|
||||
|
||||
#pragma warning( disable: 4786 )
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <xtl.h>
|
||||
#include <xonline.h>
|
||||
#include <xhv.h>
|
||||
#include "xbsocket.h"
|
||||
#include "xbSockAddr.h"
|
||||
#include "XHVVoiceManager.h"
|
||||
#include "XBoxCommon.h"
|
||||
#include "../win32/win_input.h"
|
||||
#include "../game/q_shared.h"
|
||||
|
||||
// Which "channel" are we talking on - right now it's just team or everyone.
|
||||
// And in a non-team game, it doesn't matter.
|
||||
enum VoiceChannel
|
||||
{
|
||||
CHAN_PRIMARY,
|
||||
CHAN_ALT
|
||||
};
|
||||
|
||||
|
||||
// Sending out one network packet for each 20ms packet worth of voice would
|
||||
// waste a lot of bandwidth in packet overhead. Instead, we'll coalesce many
|
||||
// voice packets together, and send at regular intervals
|
||||
const int VOICE_PACKET_INTERVAL = 100; // 100ms = 10 times/s
|
||||
const DWORD MAX_VOICE_PER_PACKET = 5; // 5 packets per player = 100ms
|
||||
|
||||
//
|
||||
// Message payloads - Packed to minimize network traffic
|
||||
//
|
||||
#pragma pack( push )
|
||||
#pragma pack( 1 )
|
||||
|
||||
enum VOICEINFO
|
||||
{
|
||||
VOICEINFO_NOVOICE, // We have voice turned off, or have been banned
|
||||
VOICEINFO_SPEAKERS, // We can receive voice, but can't send it
|
||||
VOICEINFO_HAVEVOICE, // We have an active communicator (CAN_SEND & CAN_RECV)
|
||||
VOICEINFO_ADDREMOTEMUTE,
|
||||
VOICEINFO_REMOVEREMOTEMUTE,
|
||||
};
|
||||
|
||||
// The VOICEINFO message is a little bit strange - since it
|
||||
// must be sent reliably, it gets relayed by the host. This
|
||||
// means we need to identify the source and destination players
|
||||
// inside the packet itself. We use refIndex indices.
|
||||
struct MsgVoiceInfo
|
||||
{
|
||||
VOICEINFO action; // Action requested
|
||||
int srcRefIndex; // refIndex of sending source player
|
||||
int dstRefIndex; // refIndex of destination player
|
||||
};
|
||||
|
||||
// This is dependent on the time per voice packet:
|
||||
// CompressedSize = 2 + ( TimeInMs * 8 / 20 )
|
||||
// 20ms -> 10 Bytes
|
||||
// 40ms -> 18 Bytes
|
||||
// 60ms -> 26 Bytes
|
||||
// 80ms -> 34 Bytes
|
||||
// 100ms -> 42 Bytes
|
||||
#define COMPRESSED_VOICE_SIZE 10
|
||||
struct VoicePacket
|
||||
{
|
||||
BYTE byData[COMPRESSED_VOICE_SIZE];
|
||||
};
|
||||
|
||||
struct MsgVoiceData
|
||||
{
|
||||
WORD wVoicePackets;
|
||||
VoicePacket VoicePackets[MAX_VOICE_PER_PACKET];
|
||||
};
|
||||
#pragma pack( pop )
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: class InfoMessage
|
||||
// Desc: Informational voice message object sent between players and hosts
|
||||
//-----------------------------------------------------------------------------
|
||||
class InfoMessage
|
||||
{
|
||||
MsgVoiceInfo m_VoiceInfo;
|
||||
|
||||
public:
|
||||
|
||||
explicit InfoMessage( ) { }
|
||||
~InfoMessage() {}
|
||||
|
||||
int GetSize() const { return sizeof(*this); }
|
||||
MsgVoiceInfo& GetMsgVoiceInfo() { return m_VoiceInfo; }
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: class VoiceMessage
|
||||
// Desc: Message object containing voice packets, sent between players
|
||||
//-----------------------------------------------------------------------------
|
||||
class VoiceMessage
|
||||
{
|
||||
WORD dataSize; // Needed for winsock to realize there's no actual data
|
||||
MsgVoiceData m_VoiceData;
|
||||
|
||||
public:
|
||||
explicit VoiceMessage( ) : dataSize( 0 ) { }
|
||||
~VoiceMessage() {}
|
||||
|
||||
int GetSize() const { return sizeof(*this); }
|
||||
MsgVoiceData& GetMsgVoiceData() { return m_VoiceData; }
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: class PendingMessage
|
||||
// Desc: Used for parsing Messages out of a TCP stream. Since fragments
|
||||
// of a given message could come in individually, they must be
|
||||
// buffered until complete
|
||||
//-----------------------------------------------------------------------------
|
||||
class PendingMessage
|
||||
{
|
||||
public:
|
||||
PendingMessage() : m_nBytesReceived( 0 ) { }
|
||||
|
||||
HRESULT Read( SOCKET sock ); // Read message from socket
|
||||
VOID Reset() { m_nBytesReceived = 0; }
|
||||
|
||||
InfoMessage m_msg; // Buffer space for message
|
||||
INT m_nBytesReceived; // # of bytes received in message
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: struct ClientSocket
|
||||
// Desc: Keeps track of socket and associated data for reliable client
|
||||
// connections
|
||||
//-----------------------------------------------------------------------------
|
||||
struct ClientSocket
|
||||
{
|
||||
SOCKET sock; // Socket
|
||||
SOCKADDR_IN sa; // Address of client
|
||||
PendingMessage msgPending; // Pending message
|
||||
bool inUse; // Is this socket active?
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: class CVoiceManager
|
||||
// Desc: Main class that provides all voice functionality
|
||||
//-----------------------------------------------------------------------------
|
||||
class CVoiceManager : public ITitleXHV
|
||||
{
|
||||
// typedef std::vector< ClientSocket > SocketList;
|
||||
|
||||
CXBSocket m_DirectSock; // Direct socket for voice data
|
||||
CXBSocket m_ReliableSock; // Reliable socket (or listen socket for host)
|
||||
// SocketList m_ClientSockets; // Server's reliable sockets
|
||||
ClientSocket m_ClientSockets[MAX_CLIENTS]; // Server's reliable sockets
|
||||
|
||||
PendingMessage m_msgPending; // Pending message for client
|
||||
VoiceMessage m_msgVoiceData; // Voice data packet for buffering voice
|
||||
|
||||
int m_VoiceTimer; // Time of last voice send
|
||||
bool m_bRunning; // Are we in a game?
|
||||
bool m_bInitialized; // TRUE if we've initialized
|
||||
bool m_bVoiceDisabled; // Master cutoff switch
|
||||
|
||||
CXHVVoiceManager m_XHVVoiceManager; // Voice Chat engine
|
||||
|
||||
int m_VoiceMask; // Which voice mask to use
|
||||
VoiceChannel m_Channel; // Who are we talking to?
|
||||
|
||||
enum MuteState
|
||||
{
|
||||
MUTE_IDLE,
|
||||
MUTE_WORKING,
|
||||
MUTE_REFRESH
|
||||
};
|
||||
|
||||
MuteState m_MuteState; // Simple state to refresh mute list after work
|
||||
XONLINE_MUTELISTUSER m_MuteList[MAX_MUTELISTUSERS]; // Local copy of the user's mute list
|
||||
DWORD m_MuteListSize; // Number of (valid) entries in mute list
|
||||
XONLINETASK_HANDLE m_MuteListTask; // Task handle for mute list operations
|
||||
|
||||
vec3_t m_ClientPositions[MAX_CLIENTS]; // X,Y,Z of each client in super low-res (-128..128)
|
||||
|
||||
public:
|
||||
// ITitleXHV callback functions
|
||||
STDMETHODIMP CommunicatorStatusUpdate( DWORD dwPort, XHV_VOICE_COMMUNICATOR_STATUS status );
|
||||
STDMETHODIMP LocalChatDataReady( DWORD dwPort, DWORD dwSize, PVOID pData );
|
||||
|
||||
// Highest level interface
|
||||
CVoiceManager();
|
||||
HRESULT Initialize();
|
||||
void Shutdown();
|
||||
HRESULT Tick();
|
||||
|
||||
// For entering and leaving the current game's chat
|
||||
void JoinSession( void );
|
||||
void LeaveSession( void );
|
||||
|
||||
// Called by each client when a player joins or leaves the game
|
||||
HRESULT OnPlayerJoined( XBPlayerInfo *plyrInfo );
|
||||
HRESULT OnPlayerDisconnect( XBPlayerInfo *pPlayer );
|
||||
|
||||
// Do we have a voice communicator at the moment?
|
||||
bool CommunicatorPresent( void );
|
||||
// Do we have a communicator, and are we allowed to use it?
|
||||
bool IsVoiceAllowed( void );
|
||||
|
||||
// Are we listening to voice through our speakers? Simple pass-through
|
||||
BOOL VoiceThroughSpeakers( void ) { return m_XHVVoiceManager.GetVoiceThroughSpeakers(); }
|
||||
|
||||
// Pass through to XHV stuff:
|
||||
BOOL IsTalking( XUID xuid ) { return m_XHVVoiceManager.IsTalking(xuid, IN_GetMainController()); }
|
||||
|
||||
// Set muting state for the specified player, update our global mute list
|
||||
void SetMute( XUID xuid, BOOL bMuted );
|
||||
|
||||
// If the specified player muted?
|
||||
bool IsMuted( XUID xuid );
|
||||
|
||||
// Voice mask interface - get or set the current voice mask index
|
||||
int GetVoiceMask( void );
|
||||
void SetVoiceMask( int maskIndex );
|
||||
|
||||
// Master switch interface
|
||||
bool GetVoiceDisabled( void );
|
||||
|
||||
// Controls both major options, with built-in error checking:
|
||||
void SetVoiceOptions( int voiceMode );
|
||||
int GetVoiceMode( void );
|
||||
|
||||
// For setting our active channel
|
||||
void SetChannel( VoiceChannel chan );
|
||||
|
||||
// Re-calculate our list of voice targets:
|
||||
void UpdateVoiceTargets( void );
|
||||
|
||||
// Used to update our (staggered update) client positions for voice proximity:
|
||||
void SetClientPosition( int index, short pos[3] )
|
||||
{
|
||||
m_ClientPositions[index][0] = pos[0];
|
||||
m_ClientPositions[index][1] = pos[1];
|
||||
m_ClientPositions[index][2] = pos[2];
|
||||
}
|
||||
|
||||
#ifndef FINAL_BUILD
|
||||
// For debugging purposes:
|
||||
void DrawVoiceStats( void );
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Send messages
|
||||
void SendVoiceInfo( VOICEINFO action, XBPlayerInfo *pDestPlayer );
|
||||
void SendVoiceDataToAll();
|
||||
int SendVoiceMessage( const VoiceMessage* pMsg, const CXBSockAddr* psaDest = NULL );
|
||||
int SendInfoMessage( const InfoMessage* pMsg, const CXBSockAddr* psaDest = NULL );
|
||||
|
||||
// Receive messages
|
||||
void ProcessDirectMessage();
|
||||
void ProcessReliableMessage();
|
||||
|
||||
// Process incoming messages
|
||||
void ProcessVoiceInfo( const MsgVoiceInfo&, const CXBSockAddr& );
|
||||
void ProcessVoiceData( const MsgVoiceData&, const CXBSockAddr& );
|
||||
|
||||
// Manage the user's mute list
|
||||
void UpdateMuteList( void );
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: class MatchInAddr
|
||||
// Desc: Predicate functor used to match on IN_ADDRs in player lists
|
||||
//-----------------------------------------------------------------------------
|
||||
struct MatchInAddr
|
||||
{
|
||||
IN_ADDR ia;
|
||||
explicit MatchInAddr( const CXBSockAddr& sa ) : ia( sa.GetInAddr() ) { }
|
||||
explicit MatchInAddr( const in_addr& addr ) : ia( addr ) { }
|
||||
bool operator()( const XBPlayerInfo& playerInfo )
|
||||
{
|
||||
return playerInfo.inAddr.s_addr == ia.s_addr;
|
||||
}
|
||||
bool operator()( const ClientSocket& cs )
|
||||
{
|
||||
return cs.sa.sin_addr.s_addr == ia.s_addr;
|
||||
}
|
||||
};
|
||||
|
||||
// The voice system:
|
||||
extern CVoiceManager g_Voice;
|
||||
|
||||
#endif // _XBVOICE_H
|
||||
147
codemp/xbox/XBoxCommon.h
Normal file
147
codemp/xbox/XBoxCommon.h
Normal file
@@ -0,0 +1,147 @@
|
||||
#ifndef _XBOX_COMMON_H
|
||||
#define _XBOX_COMMON_H
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
//
|
||||
// SHARED NETWORKING DATA
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
#include "Xtl.h"
|
||||
#include "xonline.h"
|
||||
|
||||
// This is (MAX_CLIENTS + 1) - but a dedicated server is the only
|
||||
// thing that can take the last slot - 0..11 are reserved for clients.
|
||||
#define MAX_ONLINE_PLAYERS 11
|
||||
#define DEDICATED_SERVER_INDEX (MAX_ONLINE_PLAYERS - 1)
|
||||
|
||||
#define XBOX_PORT 1000
|
||||
|
||||
// There are separate flags for being able to receive voice, and able to send voice.
|
||||
// Because of voice through speakers, anyone has the potential to receive. By
|
||||
// default, everyone does. Other people don't need to know if clients are using
|
||||
// voice through speakers, except to change their icon in player/friends list.
|
||||
//
|
||||
// Note that VOICE_CAN_SEND implies VOICE_CAN_RECV, but not the other way around.
|
||||
|
||||
#define VOICE_CAN_RECV 0x01 // Can recv voice (headset or spkrs) and has their voice sockets up
|
||||
#define VOICE_CAN_SEND 0x02 // Can send voice (headset)
|
||||
#define MUTED_PLAYER 0x04 // We have muted this player
|
||||
#define REMOTE_MUTED 0x08 // This player has us muted
|
||||
|
||||
// Information about the session we're currently playing in, filled in
|
||||
// when joining either a live or syslink game.
|
||||
struct XBLClientData_t
|
||||
{
|
||||
XNKEY KeyExchangeKey;
|
||||
XNADDR HostAddress;
|
||||
XNKID SessionID;
|
||||
IN_ADDR SrvAddr;
|
||||
};
|
||||
extern XBLClientData_t xbc;
|
||||
|
||||
|
||||
struct XBPlayerInfo
|
||||
{
|
||||
XUID xuid;
|
||||
XNADDR xbAddr;
|
||||
IN_ADDR inAddr;
|
||||
char name[XONLINE_GAMERTAG_SIZE];
|
||||
int refIndex;
|
||||
DWORD flags;
|
||||
bool isActive;
|
||||
int friendshipStatus;
|
||||
|
||||
explicit XBPlayerInfo(int refNum = -1) : refIndex(refNum), isActive(false) { }
|
||||
};
|
||||
|
||||
struct XBOnlineInfo
|
||||
{
|
||||
BYTE localIndex; // index into xbPlayerList
|
||||
XBPlayerInfo xbPlayerList[MAX_ONLINE_PLAYERS]; // list of players
|
||||
};
|
||||
extern XBOnlineInfo xbOnlineInfo;
|
||||
|
||||
|
||||
#define MAX_OFFLINE_HISTORY 10
|
||||
#define HISTORY_OFFSET MAX_ONLINE_PLAYERS
|
||||
|
||||
class XBHistoryEntry
|
||||
{
|
||||
public:
|
||||
XUID xuid;
|
||||
char name[XONLINE_GAMERTAG_SIZE];
|
||||
DWORD flags;
|
||||
bool isActive;
|
||||
int friendshipStatus;
|
||||
|
||||
DWORD stamp; // Used to locate oldest entry
|
||||
|
||||
explicit XBHistoryEntry() : isActive(false) { }
|
||||
};
|
||||
|
||||
class XBHistory
|
||||
{
|
||||
public:
|
||||
DWORD stamp;
|
||||
XBHistoryEntry historyList[MAX_OFFLINE_HISTORY];
|
||||
|
||||
explicit XBHistory( void ) : stamp( 0 ) { }
|
||||
};
|
||||
extern XBHistory xbOfflineHistory;
|
||||
|
||||
|
||||
// All important Xbox stuff is sent inside infostrings, so we convert
|
||||
// it to hex with the following utilities. (Thanks to Wolf)
|
||||
#define XNADDR_STRING_LEN (2*sizeof(XNADDR)+1)
|
||||
void XnAddrToString(const XNADDR *pxna, char* buffer);
|
||||
void StringToXnAddr(XNADDR* pxna, const char* buffer);
|
||||
|
||||
#define XNKID_STRING_LEN (2*sizeof(XNKID)+1)
|
||||
void XNKIDToString(const XNKID *pxnkid, char* buffer);
|
||||
void StringToXNKID(XNKID* pxnkid, const char* buffer);
|
||||
|
||||
#define XNKEY_STRING_LEN (2*sizeof(XNKEY)+1)
|
||||
void XNKEYToString(const XNKEY *pxnkey, char* buffer);
|
||||
void StringToXNKEY(XNKEY* pxnkey, const char* buffer);
|
||||
|
||||
#define XUID_STRING_LEN (2*sizeof(XUID)+1)
|
||||
void XUIDToString(const XUID *pxuid, char* buffer);
|
||||
void StringToXUID(XUID* pxuid, const char* buffer);
|
||||
|
||||
|
||||
//
|
||||
// Xbox Error Popup Constants
|
||||
//
|
||||
// These are really UI data, but we put them here to avoid cluttering the PC headers
|
||||
// even more. The error popup system needs a context when it returns a value to know
|
||||
// what to do. One of these is saved off when we create the popup, and then it's
|
||||
// queried when we get a response so we know what we're doing.
|
||||
enum xbErrorPopupType
|
||||
{
|
||||
XB_POPUP_NONE,
|
||||
XB_POPUP_CANNOT_CONNECT,
|
||||
XB_POPUP_AUTOUPDATE,
|
||||
XB_POPUP_REQUIRED_MESSAGE,
|
||||
XB_POPUP_OPTIONAL_MESSAGE,
|
||||
XB_POPUP_LOST_CONNECTION,
|
||||
XB_POPUP_INVALID_USER,
|
||||
XB_POPUP_DUPLICATE_LOGON,
|
||||
XB_POPUP_BUSY_SERVERS,
|
||||
XB_POPUP_CONFIRM_NEW_ACCOUNT,
|
||||
XB_POPUP_CONFIRM_LOGOFF,
|
||||
XB_POPUP_COM_ERROR,
|
||||
XB_POPUP_QUICKMATCH_NO_RESULTS,
|
||||
XB_POPUP_OPTIMATCH_NO_RESULTS,
|
||||
XB_POPUP_MATCHMAKING_ERROR,
|
||||
XB_POPUP_SYSTEM_LINK_LOST,
|
||||
XB_POPUP_QUIT_CONFIRM,
|
||||
XB_POPUP_QUIT_HOST_CONFIRM,
|
||||
XB_POPUP_HOST_JOIN_CONFIRM,
|
||||
XB_POPUP_CONFIRM_FRIEND_REMOVE,
|
||||
XB_POPUP_RESPAWN_NEEDED,
|
||||
XB_POPUP_CORRUPT_SETTINGS,
|
||||
};
|
||||
void UI_xboxErrorPopup(xbErrorPopupType popup);
|
||||
void UI_xboxPopupResponse(void);
|
||||
|
||||
#endif
|
||||
413
codemp/xbox/XHVVoiceManager.cpp
Normal file
413
codemp/xbox/XHVVoiceManager.cpp
Normal file
@@ -0,0 +1,413 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// File: XHVVoiceManager.cpp
|
||||
//
|
||||
// Desc: Wraps the XHV voice engine and provides a simple interface to the
|
||||
// game
|
||||
//
|
||||
// Hist: 05.06.03 - New for the June 2003 XDK
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "XHVVoiceManager.h"
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: CXHVVoiceManager (ctor)
|
||||
// Desc: Initializes member variables
|
||||
//-----------------------------------------------------------------------------
|
||||
CXHVVoiceManager::CXHVVoiceManager()
|
||||
{
|
||||
m_pXHVEngine = NULL;
|
||||
m_dwNumLocalTalkers = 0;
|
||||
m_dwMaxRemoteTalkers = 0;
|
||||
m_dwNumRemoteTalkers = 0;
|
||||
// m_pPlayerVoiceInfo = NULL;
|
||||
m_bVoiceThroughSpeakers = FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: ~CXHVVoiceManager (dtor)
|
||||
// Desc: Performs any final cleanup that is needed
|
||||
//-----------------------------------------------------------------------------
|
||||
CXHVVoiceManager::~CXHVVoiceManager()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Initialize
|
||||
// Desc: Initializes the XHV voice manager
|
||||
//-----------------------------------------------------------------------------
|
||||
HRESULT CXHVVoiceManager::Initialize( XHV_RUNTIME_PARAMS* pXHVParams )
|
||||
{
|
||||
assert( pXHVParams );
|
||||
|
||||
// Create the engine
|
||||
HRESULT hr = XHVEngineCreate( pXHVParams, &m_pXHVEngine );
|
||||
if( FAILED( hr ) )
|
||||
return hr;
|
||||
|
||||
// Enable voicechat and loopback modes only
|
||||
m_pXHVEngine->EnableProcessingMode( XHV_LOOPBACK_MODE );
|
||||
m_pXHVEngine->EnableProcessingMode( XHV_VOICECHAT_MODE );
|
||||
// m_pXHVEngine->EnableProcessingMode( XHV_SR_MODE );
|
||||
|
||||
m_dwNumLocalTalkers = pXHVParams->dwMaxLocalTalkers;
|
||||
m_dwMaxRemoteTalkers = pXHVParams->dwMaxRemoteTalkers;
|
||||
|
||||
// Register each local talker immediately, but leave them inactive
|
||||
for( DWORD i = 0; i < m_dwNumLocalTalkers; i++ )
|
||||
{
|
||||
m_pXHVEngine->RegisterLocalTalker( i );
|
||||
m_pXHVEngine->SetProcessingMode( i, XHV_INACTIVE_MODE );
|
||||
}
|
||||
|
||||
// Allocate space for the PlayerVoiceInfo structs
|
||||
// m_pPlayerVoiceInfo = new PlayerVoiceInfo[ m_dwMaxRemoteTalkers ];
|
||||
// if( NULL == m_pPlayerVoiceInfo )
|
||||
// {
|
||||
// Shutdown();
|
||||
// return E_OUTOFMEMORY;
|
||||
// }
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Shutdown
|
||||
// Desc: Shuts down the object
|
||||
//-----------------------------------------------------------------------------
|
||||
HRESULT CXHVVoiceManager::Shutdown()
|
||||
{
|
||||
if( m_pXHVEngine )
|
||||
{
|
||||
m_pXHVEngine->Release();
|
||||
m_pXHVEngine = NULL;
|
||||
}
|
||||
|
||||
// if( m_pPlayerVoiceInfo )
|
||||
// {
|
||||
// delete[] m_pPlayerVoiceInfo;
|
||||
// m_pPlayerVoiceInfo = NULL;
|
||||
// }
|
||||
|
||||
m_dwNumLocalTalkers = 0;
|
||||
m_dwMaxRemoteTalkers = 0;
|
||||
m_dwNumRemoteTalkers = 0;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: RegisterRemoteTalker
|
||||
// Desc: Registers a new remote talker
|
||||
//-----------------------------------------------------------------------------
|
||||
HRESULT CXHVVoiceManager::RegisterRemoteTalker( XUID xuidRemoteTalker )
|
||||
{
|
||||
// XHV allows you to call RegisterRemoteTalker more than once with the
|
||||
// same XUID. For convenience, we'll emulate that behavior
|
||||
if( SUCCEEDED( FindPlayerVoiceInfo( xuidRemoteTalker, NULL ) ) )
|
||||
return S_OK;
|
||||
|
||||
// Verify we're within our limits
|
||||
assert( m_dwNumRemoteTalkers < m_dwMaxRemoteTalkers );
|
||||
|
||||
// Register the remote talker with XHV
|
||||
HRESULT hr;
|
||||
hr = m_pXHVEngine->RegisterRemoteTalker( xuidRemoteTalker );
|
||||
if( FAILED( hr ) )
|
||||
return hr;
|
||||
|
||||
// Set up a new PlayerVoiceInfo struct for the player
|
||||
PlayerVoiceInfo* pRemoteTalker = &m_pPlayerVoiceInfo[ m_dwNumRemoteTalkers ];
|
||||
pRemoteTalker->xuid = xuidRemoteTalker;
|
||||
for( DWORD i = 0; i < XGetPortCount(); i++ )
|
||||
{
|
||||
pRemoteTalker->bMuted[ i ] = FALSE;
|
||||
pRemoteTalker->bRemoteMuted[ i ] = FALSE;
|
||||
pRemoteTalker->priority[ i ] = XHV_PLAYBACK_PRIORITY_NEVER;
|
||||
}
|
||||
|
||||
++m_dwNumRemoteTalkers;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: UnregisterRemoteTalker
|
||||
// Desc: Unregisters an existing remote talker
|
||||
//-----------------------------------------------------------------------------
|
||||
HRESULT CXHVVoiceManager::UnregisterRemoteTalker( XUID xuidRemoteTalker )
|
||||
{
|
||||
// Find the entry to remove from our PlayerVoiceInfo list
|
||||
DWORD dwEntry;
|
||||
if( FAILED( FindPlayerVoiceInfo( xuidRemoteTalker, &dwEntry ) ) )
|
||||
return E_FAIL;
|
||||
|
||||
// Remove the entry by overwriting it with the last entry in the list
|
||||
memcpy( &m_pPlayerVoiceInfo[ dwEntry ],
|
||||
&m_pPlayerVoiceInfo[ m_dwNumRemoteTalkers - 1 ],
|
||||
sizeof( PlayerVoiceInfo ) );
|
||||
--m_dwNumRemoteTalkers;
|
||||
|
||||
// Unregister the remote talker with XHV
|
||||
HRESULT hr = m_pXHVEngine->UnregisterRemoteTalker( xuidRemoteTalker );
|
||||
if( FAILED( hr ) )
|
||||
return hr;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: ClearRemoteTalkers
|
||||
// Desc: Unregisters all remaining remote talkers
|
||||
//-----------------------------------------------------------------------------
|
||||
HRESULT CXHVVoiceManager::ClearRemoteTalkers()
|
||||
{
|
||||
for( DWORD i = 0; i < m_dwNumRemoteTalkers; i++ )
|
||||
{
|
||||
m_pXHVEngine->UnregisterRemoteTalker( m_pPlayerVoiceInfo[ i ].xuid );
|
||||
}
|
||||
m_dwNumRemoteTalkers = 0;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: SetRemoteTalkerPriority
|
||||
// Desc: Sets the priority of a remote talker.
|
||||
//-----------------------------------------------------------------------------
|
||||
HRESULT CXHVVoiceManager::SetRemoteTalkerPriority( XUID xuidRemoteTalker,
|
||||
DWORD dwPort,
|
||||
XHV_PLAYBACK_PRIORITY priority )
|
||||
{
|
||||
DWORD dwEntry;
|
||||
if( FAILED( FindPlayerVoiceInfo( xuidRemoteTalker, &dwEntry ) ) )
|
||||
return E_FAIL;
|
||||
|
||||
// Set our copy of the priority
|
||||
PlayerVoiceInfo* pInfo = &m_pPlayerVoiceInfo[ dwEntry ];
|
||||
pInfo->priority[ dwPort ] = priority;
|
||||
|
||||
// Recalculate priorities for the remote talker
|
||||
if( FAILED( RecalculatePriorities( pInfo ) ) )
|
||||
return E_FAIL;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: RecalculatePriorities
|
||||
// Desc: Calculates and sets the appropriate priorities of the remote talker.
|
||||
// If Voice Through Speakers is disabled, it sets the priority for each
|
||||
// local talker based upon their priority for the remote talker and
|
||||
// their mute settings, and sets speaker priority to NEVER.
|
||||
// If Voice Through Speakers is enabled, it sets the priority for each
|
||||
// local talker to NEVER, and sets speaker priority based on the
|
||||
// following rules:
|
||||
// 1) If any local talker has muted the remote talker, set to NEVER
|
||||
// 2) If the remote talker has muted any local talker, set to NEVER
|
||||
// 3) Otherwise, priority is the max of each local talker's priority
|
||||
//-----------------------------------------------------------------------------
|
||||
HRESULT CXHVVoiceManager::RecalculatePriorities( PlayerVoiceInfo* pInfo )
|
||||
{
|
||||
if( !m_bVoiceThroughSpeakers )
|
||||
{
|
||||
// Set appropriate local talker priorities
|
||||
for( DWORD dwPort = 0; dwPort < m_dwNumLocalTalkers; dwPort++ )
|
||||
{
|
||||
XHV_PLAYBACK_PRIORITY priority = pInfo->priority[ dwPort ];
|
||||
if( pInfo->bMuted[ dwPort ] || pInfo->bRemoteMuted[ dwPort ] )
|
||||
{
|
||||
priority = XHV_PLAYBACK_PRIORITY_NEVER;
|
||||
}
|
||||
|
||||
m_pXHVEngine->SetPlaybackPriority( pInfo->xuid,
|
||||
dwPort,
|
||||
priority );
|
||||
}
|
||||
|
||||
// Set speaker priority to NEVER
|
||||
m_pXHVEngine->SetPlaybackPriority( pInfo->xuid,
|
||||
XHV_PLAYBACK_TO_SPEAKERS,
|
||||
XHV_PLAYBACK_PRIORITY_NEVER );
|
||||
}
|
||||
else
|
||||
{
|
||||
XHV_PLAYBACK_PRIORITY priority = XHV_PLAYBACK_PRIORITY_NEVER;
|
||||
|
||||
// Turn off local talker headphone output, and calculate
|
||||
// maximum priority while accounting for muting
|
||||
for( DWORD dwPort = 0; dwPort < m_dwNumLocalTalkers; dwPort++ )
|
||||
{
|
||||
m_pXHVEngine->SetPlaybackPriority( pInfo->xuid,
|
||||
dwPort,
|
||||
XHV_PLAYBACK_PRIORITY_NEVER );
|
||||
|
||||
// If anyone has muted or been muted by the remote talker,
|
||||
// must set priority to NEVER
|
||||
if( pInfo->bMuted[ dwPort ] || pInfo->bRemoteMuted[ dwPort ] )
|
||||
{
|
||||
priority = XHV_PLAYBACK_PRIORITY_NEVER;
|
||||
break;
|
||||
}
|
||||
|
||||
// Calculate maximum priority - lower values correspond
|
||||
// to higher priority
|
||||
if( pInfo->priority[ dwPort ] < priority )
|
||||
priority = pInfo->priority[ dwPort ];
|
||||
}
|
||||
|
||||
m_pXHVEngine->SetPlaybackPriority( pInfo->xuid,
|
||||
XHV_PLAYBACK_TO_SPEAKERS,
|
||||
priority );
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: SetVoiceThroughSpeakers
|
||||
// Desc: Turns voice-through-speakers mode off and on
|
||||
//-----------------------------------------------------------------------------
|
||||
HRESULT CXHVVoiceManager::SetVoiceThroughSpeakers( BOOL bVoiceThroughSpeakers )
|
||||
{
|
||||
m_bVoiceThroughSpeakers = bVoiceThroughSpeakers;
|
||||
|
||||
// Recalculate the priorities for each remote talker
|
||||
for( DWORD i = 0; i < m_dwNumRemoteTalkers; i++ )
|
||||
{
|
||||
RecalculatePriorities( &m_pPlayerVoiceInfo[ i ] );
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: SetVoiceThroughSpeakers
|
||||
// Desc: Turns voice-through-speakers mode off and on
|
||||
//-----------------------------------------------------------------------------
|
||||
BOOL CXHVVoiceManager::GetVoiceThroughSpeakers( VOID )
|
||||
{
|
||||
return m_bVoiceThroughSpeakers;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: SetMute
|
||||
// Desc: Mutes/UnMutes a remote talker for a given local talker
|
||||
//-----------------------------------------------------------------------------
|
||||
HRESULT CXHVVoiceManager::SetMute( XUID xuidRemoteTalker, DWORD dwPort, BOOL bMuted )
|
||||
{
|
||||
DWORD dwEntry;
|
||||
if( FAILED( FindPlayerVoiceInfo( xuidRemoteTalker, &dwEntry ) ) )
|
||||
return E_FAIL;
|
||||
|
||||
PlayerVoiceInfo* pInfo = &m_pPlayerVoiceInfo[ dwEntry ];
|
||||
pInfo->bMuted[ dwPort ] = bMuted;
|
||||
RecalculatePriorities( pInfo );
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: SetRemoteMute
|
||||
// Desc: RemoteMutes/RemoteUnMutes a remote talker for a given local talker
|
||||
//-----------------------------------------------------------------------------
|
||||
HRESULT CXHVVoiceManager::SetRemoteMute( XUID xuidRemoteTalker, DWORD dwPort, BOOL bRemoteMuted )
|
||||
{
|
||||
DWORD dwEntry;
|
||||
if( FAILED( FindPlayerVoiceInfo( xuidRemoteTalker, &dwEntry ) ) )
|
||||
return E_FAIL;
|
||||
|
||||
PlayerVoiceInfo* pInfo = &m_pPlayerVoiceInfo[ dwEntry ];
|
||||
pInfo->bRemoteMuted[ dwPort ] = bRemoteMuted;
|
||||
RecalculatePriorities( pInfo );
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: IsTalking
|
||||
// Desc: Determines whether a remote talker is currently talking to a local
|
||||
// talker.
|
||||
//-----------------------------------------------------------------------------
|
||||
BOOL CXHVVoiceManager::IsTalking( XUID xuidRemoteTalker, DWORD dwPort )
|
||||
{
|
||||
// First, see if the remote talker is currently talking
|
||||
BOOL bIsTalking = m_pXHVEngine->IsTalking( xuidRemoteTalker );
|
||||
|
||||
// Although they're talking, a local player may not hear them due to
|
||||
// muting - possibly even another local talker being muted, if Voice
|
||||
// Through Speakers is on.
|
||||
if( bIsTalking )
|
||||
{
|
||||
DWORD dwEntry;
|
||||
if( FAILED( FindPlayerVoiceInfo( xuidRemoteTalker, &dwEntry ) ) )
|
||||
return FALSE;
|
||||
PlayerVoiceInfo* pInfo = &m_pPlayerVoiceInfo[ dwEntry ];
|
||||
|
||||
if( !m_bVoiceThroughSpeakers )
|
||||
{
|
||||
// Voice Through Speakers OFF - check this local talker's
|
||||
// mute settings
|
||||
if( pInfo->bMuted[ dwPort ] || pInfo->bRemoteMuted[ dwPort ] )
|
||||
bIsTalking = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Voice Through Speakers ON - check every local talker's
|
||||
// mute settings
|
||||
for( DWORD i = 0; i < m_dwNumLocalTalkers; i++ )
|
||||
{
|
||||
if( pInfo->bMuted[ i ] || pInfo->bRemoteMuted[ i ] )
|
||||
bIsTalking = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bIsTalking;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: FindPlayerVoiceInfo
|
||||
// Desc: Helper function to find an entry in the player voice info list
|
||||
//-----------------------------------------------------------------------------
|
||||
HRESULT CXHVVoiceManager::FindPlayerVoiceInfo( XUID xuidRemoteTalker, DWORD* pdwEntry )
|
||||
{
|
||||
for( DWORD i = 0; i < m_dwNumRemoteTalkers; i++ )
|
||||
{
|
||||
if( XOnlineAreUsersIdentical( &m_pPlayerVoiceInfo[i].xuid,
|
||||
&xuidRemoteTalker ) )
|
||||
{
|
||||
if( pdwEntry )
|
||||
{
|
||||
*pdwEntry = i;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
84
codemp/xbox/XHVVoiceManager.h
Normal file
84
codemp/xbox/XHVVoiceManager.h
Normal file
@@ -0,0 +1,84 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// File: XHVVoiceManager.h
|
||||
//
|
||||
// Desc: Wraps the XHV voice engine and provides a simple interface to the
|
||||
// game
|
||||
//
|
||||
// Hist: 05.06.03 - New for the June 2003 XDK
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef _XHVVOICEMANAGER_H_
|
||||
#define _XHVVOICEMANAGER_H_
|
||||
|
||||
#include <xtl.h>
|
||||
#include <xonline.h>
|
||||
#include <xhv.h>
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: struct PlayerVoiceInfo
|
||||
// Desc: Used to keep track of voice-related details about each player
|
||||
//-----------------------------------------------------------------------------
|
||||
struct PlayerVoiceInfo
|
||||
{
|
||||
XUID xuid;
|
||||
XHV_PLAYBACK_PRIORITY priority[4];
|
||||
BOOL bMuted[4];
|
||||
BOOL bRemoteMuted[4];
|
||||
};
|
||||
|
||||
class CXHVVoiceManager
|
||||
{
|
||||
public:
|
||||
CXHVVoiceManager();
|
||||
~CXHVVoiceManager();
|
||||
|
||||
HRESULT Initialize( XHV_RUNTIME_PARAMS* pXHVParams );
|
||||
HRESULT Shutdown();
|
||||
|
||||
// These functions are simple passthrough functions to m_pXHVEngine:
|
||||
HRESULT SetCallbackInterface( ITitleXHV* pTitleXHV )
|
||||
{ return m_pXHVEngine->SetCallbackInterface( pTitleXHV ); }
|
||||
HRESULT DoWork()
|
||||
{ return m_pXHVEngine->DoWork(); }
|
||||
HRESULT SetVoiceMask( DWORD dwPort, const XHV_VOICE_MASK* pVoiceMask )
|
||||
{ return m_pXHVEngine->SetVoiceMask( dwPort, pVoiceMask ); }
|
||||
HRESULT GetLocalTalkerStatus( DWORD dwPort, XHV_LOCAL_TALKER_STATUS* pLocalTalkerStatus )
|
||||
{ return m_pXHVEngine->GetLocalTalkerStatus( dwPort, pLocalTalkerStatus ); }
|
||||
HRESULT SetMaxPlaybackStreamsCount( DWORD dwStreamsCount )
|
||||
{ return m_pXHVEngine->SetMaxPlaybackStreamsCount( dwStreamsCount ); }
|
||||
HRESULT SubmitIncomingVoicePacket( XUID xuidRemoteTalker, VOID* pvData, DWORD dwSize )
|
||||
{ return m_pXHVEngine->SubmitIncomingVoicePacket( xuidRemoteTalker, pvData, dwSize ); }
|
||||
HRESULT SetProcessingMode( DWORD dwPort, XHV_PROCESSING_MODE processingMode )
|
||||
{ return m_pXHVEngine->SetProcessingMode( dwPort, processingMode ); }
|
||||
|
||||
// These functions wrap handling remote talkers, so that we can manage
|
||||
// the list of PlayerVoiceInfo structs
|
||||
HRESULT RegisterRemoteTalker( XUID xuidRemoteTalker );
|
||||
HRESULT UnregisterRemoteTalker( XUID xuidRemoteTalker );
|
||||
HRESULT ClearRemoteTalkers();
|
||||
HRESULT SetRemoteTalkerPriority( XUID xuidRemoteTalker, DWORD dwPort, XHV_PLAYBACK_PRIORITY priority );
|
||||
|
||||
// These functions handle higher-level logical operations such as
|
||||
// muting and voice-through-speaker control
|
||||
HRESULT SetVoiceThroughSpeakers( BOOL bVoiceThroughSpeakers );
|
||||
BOOL GetVoiceThroughSpeakers( VOID );
|
||||
HRESULT SetMute( XUID xuidRemoteTalker, DWORD dwPort, BOOL bMuted );
|
||||
HRESULT SetRemoteMute( XUID xuidRemoteTalker, DWORD dwPort, BOOL bRemoteMuted );
|
||||
BOOL IsTalking( XUID xuidRemoteTalker, DWORD dwPort );
|
||||
|
||||
protected:
|
||||
HRESULT FindPlayerVoiceInfo( XUID xuidRemoteTalker, DWORD* pdwEntry );
|
||||
HRESULT RecalculatePriorities( PlayerVoiceInfo* pInfo );
|
||||
|
||||
PXHVENGINE m_pXHVEngine;
|
||||
DWORD m_dwNumLocalTalkers;
|
||||
DWORD m_dwMaxRemoteTalkers;
|
||||
DWORD m_dwNumRemoteTalkers;
|
||||
PlayerVoiceInfo m_pPlayerVoiceInfo[10]; // Avoid calling new!
|
||||
|
||||
BOOL m_bVoiceThroughSpeakers;
|
||||
};
|
||||
|
||||
#endif // _XHVVOICEMANAGER_H_
|
||||
1469
codemp/xbox/match.cpp
Normal file
1469
codemp/xbox/match.cpp
Normal file
File diff suppressed because it is too large
Load Diff
439
codemp/xbox/match.h
Normal file
439
codemp/xbox/match.h
Normal file
@@ -0,0 +1,439 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Xbox Matchmaking Definitions for Title: Jedi Academy [0x4C41000B]
|
||||
// Generated by MatchSim
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <xtl.h>
|
||||
#include <xonline.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Constants
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Attribute IDs ID num Data Type
|
||||
// ------ ----------------------------
|
||||
const DWORD XATTRIB_GAME_TYPE = 0x0001 | X_ATTRIBUTE_DATATYPE_INTEGER;
|
||||
const DWORD XATTRIB_CURRENT_MAP = 0x0002 | X_ATTRIBUTE_DATATYPE_INTEGER;
|
||||
const DWORD XATTRIB_SESSION_NAME = 0x0003 | X_ATTRIBUTE_DATATYPE_STRING;
|
||||
const DWORD XATTRIB_FRIENDLY_FIRE = 0x0004 | X_ATTRIBUTE_DATATYPE_INTEGER;
|
||||
const DWORD XATTRIB_JEDI_MASTERY = 0x0005 | X_ATTRIBUTE_DATATYPE_INTEGER;
|
||||
const DWORD XATTRIB_TOTAL_PLAYERS = 0x0007 | X_ATTRIBUTE_DATATYPE_INTEGER;
|
||||
const DWORD XATTRIB_SABER_ONLY = 0x0008 | X_ATTRIBUTE_DATATYPE_INTEGER;
|
||||
const DWORD XATTRIB_DEDICATED = 0x0009 | X_ATTRIBUTE_DATATYPE_INTEGER;
|
||||
|
||||
|
||||
//
|
||||
// Attribute Maximum Lengths
|
||||
// (for strings, this doesn't include the terminating NULL)
|
||||
//
|
||||
const DWORD XATTRIB_SESSION_NAME_MAX_LEN = 16; // SessionName attribute
|
||||
|
||||
|
||||
// Specify X_MATCH_NULL_INTEGER for optional integer query arguments
|
||||
const ULONGLONG X_MATCH_NULL_INTEGER = 0x7FFFFFFFFFFFFFFFui64;
|
||||
|
||||
// Maximum number of sessions returned by OptiMatch query
|
||||
const DWORD MAX_OPTI_MATCH_RESULTS = 25;
|
||||
|
||||
// Maximum number of sessions returned by JoinSessionByID query
|
||||
const DWORD MAX_JOIN_SESSION_BY_ID_RESULTS = 1;
|
||||
|
||||
// Number of QoS probes
|
||||
const DWORD NUM_QOS_PROBES = 8;
|
||||
|
||||
// Maximum bandwidth to consume for QoS probes
|
||||
const DWORD QOS_BITS_PER_SEC = 16000;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Types
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Helper class for getting/setting blob attributes
|
||||
class CBlob
|
||||
{
|
||||
public:
|
||||
CBlob() : m_wLength( 0 ), m_pvData( NULL ) {}
|
||||
CBlob( WORD wLength, const PVOID pvData ) : m_wLength( wLength), m_pvData( pvData) {}
|
||||
CBlob( const CBlob & b ) { *this = b; }
|
||||
CBlob & operator=( const CBlob & b )
|
||||
{ m_wLength = b.Length; m_pvData = b.Data; return *this; }
|
||||
BOOL operator==( const CBlob & b ) const
|
||||
{ return m_wLength == b.Length && memcmp( m_pvData, b.Data, b.Length ) == 0; }
|
||||
BOOL IsNull() { return m_pvData == NULL; }
|
||||
__declspec( property( put = SetData, get=GetData ) ) const PVOID Data;
|
||||
const PVOID GetData() const { return m_pvData; }
|
||||
VOID SetData( const PVOID pvData ) { m_pvData = pvData; }
|
||||
__declspec( property( put = SetLength, get=GetLength ) ) WORD Length;
|
||||
WORD GetLength() const { return m_wLength; }
|
||||
VOID SetLength( WORD wLength ) { m_wLength = wLength; }
|
||||
private:
|
||||
WORD m_wLength;
|
||||
PVOID m_pvData;
|
||||
};
|
||||
|
||||
// A null blob is defined as having a NULL pointer
|
||||
#define NULL_BLOB CBlob( 0, NULL )
|
||||
|
||||
// Macro for declaring "blob literals" (much like the _T() macro does for strings)
|
||||
#define B( length, ptr ) CBlob( length, (const PVOID) ptr )
|
||||
|
||||
class CSession
|
||||
{
|
||||
public:
|
||||
CSession();
|
||||
~CSession();
|
||||
|
||||
HRESULT Create();
|
||||
HRESULT Update();
|
||||
HRESULT Delete();
|
||||
HRESULT Process();
|
||||
|
||||
VOID Reset();
|
||||
|
||||
DWORD PublicFilled;
|
||||
DWORD PublicOpen;
|
||||
DWORD PrivateFilled;
|
||||
DWORD PrivateOpen;
|
||||
XNKEY KeyExchangeKey;
|
||||
XNKID SessionID;
|
||||
|
||||
BOOL IsUpdating() const { return m_State == STATE_UPDATING; }
|
||||
BOOL IsDeleting() const { return m_State == STATE_DELETING; }
|
||||
BOOL IsCreating() const { return m_State == STATE_CREATING; }
|
||||
BOOL Exists() const { return m_State == STATE_ACTIVE || IsUpdating(); }
|
||||
|
||||
// Attribute Accessors
|
||||
|
||||
BOOL IsListening() const { return m_bListening; }
|
||||
__declspec( property( put = SetQosResponse, get = GetQosResponse ) ) CBlob QosResponse;
|
||||
CBlob GetQosResponse();
|
||||
VOID SetQosResponse( CBlob Value );
|
||||
VOID Listen( BOOL bEnable = TRUE, DWORD dwBitsPerSec = 0 );
|
||||
static const DWORD NO_WAIT = 1;
|
||||
|
||||
__declspec( property( put = SetGameType, get=GetGameType ) ) ULONGLONG GameType;
|
||||
ULONGLONG GetGameType();
|
||||
VOID SetGameType( ULONGLONG Value );
|
||||
|
||||
__declspec( property( put = SetCurrentMap, get=GetCurrentMap ) ) ULONGLONG CurrentMap;
|
||||
ULONGLONG GetCurrentMap();
|
||||
VOID SetCurrentMap( ULONGLONG Value );
|
||||
|
||||
__declspec( property( put = SetSessionName, get=GetSessionName ) ) const WCHAR * SessionName;
|
||||
const WCHAR * GetSessionName();
|
||||
VOID SetSessionName( const WCHAR * Value );
|
||||
|
||||
__declspec( property( put = SetFriendlyFire, get=GetFriendlyFire ) ) ULONGLONG FriendlyFire;
|
||||
ULONGLONG GetFriendlyFire();
|
||||
VOID SetFriendlyFire( ULONGLONG Value );
|
||||
|
||||
__declspec( property( put = SetJediMastery, get=GetJediMastery ) ) ULONGLONG JediMastery;
|
||||
ULONGLONG GetJediMastery();
|
||||
VOID SetJediMastery( ULONGLONG Value );
|
||||
|
||||
__declspec( property( put = SetTotalPlayers, get=GetTotalPlayers ) ) ULONGLONG TotalPlayers;
|
||||
ULONGLONG GetTotalPlayers();
|
||||
VOID SetTotalPlayers( ULONGLONG Value );
|
||||
|
||||
__declspec( property( put = SetSaberOnly, get=GetSaberOnly ) ) ULONGLONG SaberOnly;
|
||||
ULONGLONG GetSaberOnly();
|
||||
VOID SetSaberOnly( ULONGLONG Value );
|
||||
|
||||
__declspec( property( put = SetDedicated, get=GetDedicated ) ) ULONGLONG Dedicated;
|
||||
ULONGLONG GetDedicated();
|
||||
VOID SetDedicated( ULONGLONG Value );
|
||||
|
||||
private:
|
||||
|
||||
// The m_Attributes array is accessed using predefined constants:
|
||||
enum
|
||||
{
|
||||
GAME_TYPE_INDEX,
|
||||
CURRENT_MAP_INDEX,
|
||||
SESSION_NAME_INDEX,
|
||||
FRIENDLY_FIRE_INDEX,
|
||||
JEDI_MASTERY_INDEX,
|
||||
TOTAL_PLAYERS_INDEX,
|
||||
SABER_ONLY_INDEX,
|
||||
DEDICATED_INDEX,
|
||||
NUM_ATTRIBUTES
|
||||
};
|
||||
|
||||
XONLINE_ATTRIBUTE m_Attributes[NUM_ATTRIBUTES];
|
||||
|
||||
// Storage for the SessionName string attribute
|
||||
WCHAR m_strSessionName[XATTRIB_SESSION_NAME_MAX_LEN+1];
|
||||
|
||||
|
||||
// Qos listening
|
||||
struct QosQEntry
|
||||
{
|
||||
XNKID SessionID; // Session to unregister
|
||||
DWORD dwStartTick; // Time entry was added
|
||||
struct QosQEntry *pNext; // Next item in the queue
|
||||
};
|
||||
class CSessionQosQ
|
||||
{
|
||||
public:
|
||||
CSessionQosQ();
|
||||
VOID Add( XNKID & SessionID, DWORD dwStartTick );
|
||||
VOID Remove( XNKID &SessionID );
|
||||
VOID Dequeue();
|
||||
const QosQEntry *Head() const { return m_pHead; }
|
||||
const QosQEntry *Tail() const { return m_pTail; }
|
||||
private:
|
||||
|
||||
QosQEntry *m_pHead;
|
||||
QosQEntry *m_pTail;
|
||||
};
|
||||
|
||||
CSessionQosQ m_SessionQosQ;
|
||||
BOOL m_bListening; // Listening for Qos probes
|
||||
CBlob m_QosResponse;
|
||||
VOID PurgeSessionQ( BOOL fRemoveAll = FALSE );
|
||||
VOID PurgeSessionQHead();
|
||||
|
||||
|
||||
|
||||
HRESULT ProcessStateCreateSession();
|
||||
HRESULT ProcessStateUpdateSession();
|
||||
HRESULT ProcessStateDeleteSession();
|
||||
HRESULT ProcessStateActiveSession();
|
||||
|
||||
VOID Close();
|
||||
VOID SetupAttributes();
|
||||
|
||||
XONLINETASK_HANDLE m_hSessionTask;
|
||||
|
||||
enum STATE
|
||||
{
|
||||
STATE_IDLE,
|
||||
STATE_CREATING,
|
||||
STATE_UPDATING,
|
||||
STATE_DELETING,
|
||||
STATE_ACTIVE
|
||||
};
|
||||
|
||||
STATE m_State;
|
||||
BOOL m_bKeyRegistered;
|
||||
BOOL m_bUpdate;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// COptiMatchResult represents a single return result from the
|
||||
// OptiMatch query
|
||||
//
|
||||
#pragma pack(push, 1)
|
||||
class COptiMatchResult
|
||||
{
|
||||
public:
|
||||
// The query return attributes must come first, and in the order
|
||||
// returned by the query
|
||||
ULONGLONG GameType;
|
||||
ULONGLONG CurrentMap;
|
||||
WCHAR SessionName[XATTRIB_SESSION_NAME_MAX_LEN+1];
|
||||
ULONGLONG FriendlyFire;
|
||||
ULONGLONG JediMastery;
|
||||
ULONGLONG SaberOnly;
|
||||
ULONGLONG Dedicated;
|
||||
|
||||
|
||||
XNKID SessionID;
|
||||
XNKEY KeyExchangeKey;
|
||||
XNADDR HostAddress;
|
||||
DWORD PublicOpen;
|
||||
DWORD PrivateOpen;
|
||||
DWORD PublicFilled;
|
||||
DWORD PrivateFilled;
|
||||
XNQOSINFO* pQosInfo;
|
||||
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
|
||||
// Collection of results for the OptiMatch query
|
||||
class COptiMatchQueryResults
|
||||
{
|
||||
public:
|
||||
COptiMatchQueryResults() { m_dwSize = 0; }
|
||||
DWORD Size() { return m_dwSize; }
|
||||
COptiMatchResult &operator[]( DWORD i ) { return v[i]; }
|
||||
private:
|
||||
void Clear() { m_dwSize = 0; }
|
||||
void Remove( DWORD i )
|
||||
{
|
||||
assert( i < m_dwSize );
|
||||
if( i < m_dwSize )
|
||||
{
|
||||
m_dwSize--;
|
||||
memcpy( &v[i], &v[i+1], sizeof( v[0] ) * ( m_dwSize - i ) );
|
||||
}
|
||||
}
|
||||
void SetSize( DWORD dwSize ) { m_dwSize = dwSize; }
|
||||
friend class COptiMatchQuery;
|
||||
COptiMatchResult v[MAX_OPTI_MATCH_RESULTS];
|
||||
DWORD m_dwSize;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Query object for OptiMatch query (id 0x1)
|
||||
//
|
||||
class COptiMatchQuery
|
||||
{
|
||||
public:
|
||||
COptiMatchQuery();
|
||||
~COptiMatchQuery();
|
||||
COptiMatchQueryResults Results;
|
||||
|
||||
HRESULT Process();
|
||||
void Cancel();
|
||||
void Clear();
|
||||
BOOL Done() const { return m_State == STATE_DONE; }
|
||||
BOOL Succeeded() const { return SUCCEEDED( m_hrQuery ); }
|
||||
HRESULT Query(
|
||||
ULONGLONG GameType, // Optional: X_MATCH_NULL_INTEGER to omit
|
||||
ULONGLONG CurrentMap, // Optional: X_MATCH_NULL_INTEGER to omit
|
||||
ULONGLONG MinimumPlayers,
|
||||
ULONGLONG MaximumPlayers,
|
||||
ULONGLONG FriendlyFire, // Optional: X_MATCH_NULL_INTEGER to omit
|
||||
ULONGLONG JediMastery, // Optional: X_MATCH_NULL_INTEGER to omit
|
||||
ULONGLONG SaberOnly, // Optional: X_MATCH_NULL_INTEGER to omit
|
||||
ULONGLONG Dedicated // Optional: X_MATCH_NULL_INTEGER to omit
|
||||
);
|
||||
BOOL IsRunning() const { return m_State == STATE_RUNNING || m_State == STATE_PROBING_CONNECTIVITY; }
|
||||
BOOL IsProbing() const { return m_State == STATE_PROBING_BANDWIDTH; }
|
||||
HRESULT Probe();
|
||||
private:
|
||||
|
||||
// Quality of Service
|
||||
const XNADDR *m_rgpXnAddr[ MAX_OPTI_MATCH_RESULTS ];
|
||||
const XNKID *m_rgpXnKid [ MAX_OPTI_MATCH_RESULTS ];
|
||||
const XNKEY *m_rgpXnKey [ MAX_OPTI_MATCH_RESULTS ];
|
||||
XNQOS *m_pXnQos;
|
||||
enum STATE
|
||||
{
|
||||
STATE_IDLE,
|
||||
STATE_RUNNING,
|
||||
STATE_PROBING_CONNECTIVITY,
|
||||
STATE_PROBING_BANDWIDTH,
|
||||
STATE_DONE
|
||||
};
|
||||
|
||||
STATE m_State;
|
||||
HRESULT m_hrQuery;
|
||||
XONLINETASK_HANDLE m_hSearchTask;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// CJoinSessionByIDResult represents a single return result from the
|
||||
// JoinSessionByID query
|
||||
//
|
||||
#pragma pack(push, 1)
|
||||
class CJoinSessionByIDResult
|
||||
{
|
||||
public:
|
||||
|
||||
XNKID SessionID;
|
||||
XNKEY KeyExchangeKey;
|
||||
XNADDR HostAddress;
|
||||
DWORD PublicOpen;
|
||||
DWORD PrivateOpen;
|
||||
DWORD PublicFilled;
|
||||
DWORD PrivateFilled;
|
||||
XNQOSINFO* pQosInfo;
|
||||
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
|
||||
// Collection of results for the JoinSessionByID query
|
||||
class CJoinSessionByIDQueryResults
|
||||
{
|
||||
public:
|
||||
CJoinSessionByIDQueryResults() { m_dwSize = 0; }
|
||||
DWORD Size() { return m_dwSize; }
|
||||
CJoinSessionByIDResult &operator[]( DWORD i ) { return v[i]; }
|
||||
private:
|
||||
void Clear() { m_dwSize = 0; }
|
||||
void Remove( DWORD i )
|
||||
{
|
||||
assert( i < m_dwSize );
|
||||
if( i < m_dwSize )
|
||||
{
|
||||
m_dwSize--;
|
||||
memcpy( &v[i], &v[i+1], sizeof( v[0] ) * ( m_dwSize - i ) );
|
||||
}
|
||||
}
|
||||
void SetSize( DWORD dwSize ) { m_dwSize = dwSize; }
|
||||
friend class CJoinSessionByIDQuery;
|
||||
CJoinSessionByIDResult v[MAX_JOIN_SESSION_BY_ID_RESULTS];
|
||||
DWORD m_dwSize;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Query object for JoinSessionByID query (id 0x2)
|
||||
//
|
||||
class CJoinSessionByIDQuery
|
||||
{
|
||||
public:
|
||||
CJoinSessionByIDQuery();
|
||||
~CJoinSessionByIDQuery();
|
||||
CJoinSessionByIDQueryResults Results;
|
||||
|
||||
HRESULT Process();
|
||||
void Cancel();
|
||||
void Clear();
|
||||
BOOL Done() const { return m_State == STATE_DONE; }
|
||||
BOOL Succeeded() const { return SUCCEEDED( m_hrQuery ); }
|
||||
HRESULT Query(
|
||||
ULONGLONG SessionID
|
||||
);
|
||||
BOOL IsRunning() const { return m_State == STATE_RUNNING || m_State == STATE_PROBING_CONNECTIVITY; }
|
||||
BOOL IsProbing() const { return m_State == STATE_PROBING_BANDWIDTH; }
|
||||
HRESULT Probe();
|
||||
private:
|
||||
|
||||
// Quality of Service
|
||||
const XNADDR *m_rgpXnAddr[ MAX_JOIN_SESSION_BY_ID_RESULTS ];
|
||||
const XNKID *m_rgpXnKid [ MAX_JOIN_SESSION_BY_ID_RESULTS ];
|
||||
const XNKEY *m_rgpXnKey [ MAX_JOIN_SESSION_BY_ID_RESULTS ];
|
||||
XNQOS *m_pXnQos;
|
||||
enum STATE
|
||||
{
|
||||
STATE_IDLE,
|
||||
STATE_RUNNING,
|
||||
STATE_PROBING_CONNECTIVITY,
|
||||
STATE_PROBING_BANDWIDTH,
|
||||
STATE_DONE
|
||||
};
|
||||
|
||||
STATE m_State;
|
||||
HRESULT m_hrQuery;
|
||||
XONLINETASK_HANDLE m_hSearchTask;
|
||||
};
|
||||
136
codemp/xbox/xbSockAddr.cpp
Normal file
136
codemp/xbox/xbSockAddr.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// File: XbSockAddr.cpp
|
||||
//
|
||||
// Desc: Wraps SOCKADDR_IN object
|
||||
//
|
||||
// Hist: 05.17.01 - New for June XDK release
|
||||
// 08.08.01 - Moved to common framework
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "XbSockAddr.h"
|
||||
#include <cassert>
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: CXBSockAddr()
|
||||
// Desc: Create from SOCKADDR_IN
|
||||
//-----------------------------------------------------------------------------
|
||||
CXBSockAddr::CXBSockAddr( const SOCKADDR_IN& sa )
|
||||
:
|
||||
sockaddr_in()
|
||||
{
|
||||
assert( sa.sin_family == AF_INET );
|
||||
sin_family = AF_INET;
|
||||
sin_addr = sa.sin_addr;
|
||||
sin_port = sa.sin_port;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: CXBSockAddr()
|
||||
// Desc: Create from IP address and port
|
||||
//-----------------------------------------------------------------------------
|
||||
CXBSockAddr::CXBSockAddr( DWORD inAddr, WORD wPort )
|
||||
:
|
||||
sockaddr_in()
|
||||
{
|
||||
sin_family = AF_INET;
|
||||
sin_addr.s_addr = inAddr;
|
||||
sin_port = htons( wPort );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: CXBSockAddr()
|
||||
// Desc: Create from IN_ADDR and port
|
||||
//-----------------------------------------------------------------------------
|
||||
CXBSockAddr::CXBSockAddr( const IN_ADDR& inAddr, WORD wPort )
|
||||
:
|
||||
sockaddr_in()
|
||||
{
|
||||
sin_family = AF_INET;
|
||||
sin_addr = inAddr;
|
||||
sin_port = htons( wPort );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: GetInAddr()
|
||||
// Desc: Extract IN_ADDR
|
||||
//-----------------------------------------------------------------------------
|
||||
IN_ADDR CXBSockAddr::GetInAddr() const
|
||||
{
|
||||
return sin_addr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: GetPtr()
|
||||
// Desc: Direct (constant) access
|
||||
//-----------------------------------------------------------------------------
|
||||
const SOCKADDR_IN* CXBSockAddr::GetPtr() const
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: GetAddr()
|
||||
// Desc: Socket IP address
|
||||
//-----------------------------------------------------------------------------
|
||||
DWORD CXBSockAddr::GetAddr() const
|
||||
{
|
||||
return( ntohl( sin_addr.s_addr ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: GetPort()
|
||||
// Desc: Port number
|
||||
//-----------------------------------------------------------------------------
|
||||
WORD CXBSockAddr::GetPort() const
|
||||
{
|
||||
return( ntohs( sin_port ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: GetStr()
|
||||
// Desc: Address in dotted decimal (a.b.c.d) format, with optional port
|
||||
// specifier (a.b.c.d:p). Incoming string buffer must have enough
|
||||
// room for result (16 WCHARS if no port, 22 if port).
|
||||
//-----------------------------------------------------------------------------
|
||||
VOID CXBSockAddr::GetStr( WCHAR* strAddr, BOOL bIncludePort ) const
|
||||
{
|
||||
assert( strAddr != NULL );
|
||||
|
||||
INT iChars = wsprintfW( strAddr, L"%d.%d.%d.%d",
|
||||
sin_addr.S_un.S_un_b.s_b1,
|
||||
sin_addr.S_un.S_un_b.s_b2,
|
||||
sin_addr.S_un.S_un_b.s_b3,
|
||||
sin_addr.S_un.S_un_b.s_b4 );
|
||||
|
||||
if( bIncludePort )
|
||||
{
|
||||
WCHAR strPort[8];
|
||||
wsprintfW( strPort, L":%d", GetPort() );
|
||||
lstrcpyW( strAddr + iChars, strPort );
|
||||
}
|
||||
}
|
||||
45
codemp/xbox/xbSockAddr.h
Normal file
45
codemp/xbox/xbSockAddr.h
Normal file
@@ -0,0 +1,45 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// File: XbSockAddr.h
|
||||
//
|
||||
// Desc: Wraps SOCKADDR_IN object
|
||||
//
|
||||
// Hist: 05.17.01 - New for June XDK release
|
||||
// 08.08.01 - Moved to common framework
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef XBSOCKADDR_H
|
||||
#define XBSOCKADDR_H
|
||||
|
||||
#include <xtl.h>
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: class CXBSockAddr
|
||||
// Desc: Xbox socket address object
|
||||
//-----------------------------------------------------------------------------
|
||||
class CXBSockAddr : private SOCKADDR_IN
|
||||
{
|
||||
public:
|
||||
|
||||
explicit CXBSockAddr( const SOCKADDR_IN& sa );
|
||||
CXBSockAddr( DWORD inAddr, WORD wPort );
|
||||
CXBSockAddr( const IN_ADDR& inAddr, WORD wPort );
|
||||
|
||||
IN_ADDR GetInAddr() const;
|
||||
const SOCKADDR_IN* GetPtr() const;
|
||||
DWORD GetAddr() const;
|
||||
WORD GetPort() const;
|
||||
VOID GetStr( WCHAR*, BOOL bIncludePort=TRUE ) const;
|
||||
|
||||
private:
|
||||
|
||||
// Not used, so not defined
|
||||
CXBSockAddr();
|
||||
CXBSockAddr( const CXBSockAddr& );
|
||||
|
||||
};
|
||||
|
||||
#endif // XBSOCKADDR_H
|
||||
432
codemp/xbox/xbsocket.cpp
Normal file
432
codemp/xbox/xbsocket.cpp
Normal file
@@ -0,0 +1,432 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// File: XbSocket.cpp
|
||||
//
|
||||
// Desc: Wraps SOCKET object
|
||||
//
|
||||
// Hist: 05.17.01 - New for June XDK release
|
||||
// 08.08.01 - Moved to common framework
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "XbSocket.h"
|
||||
#include <cassert>
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: CXBSocket()
|
||||
// Desc: Associate existing socket with object. Object will close the socket
|
||||
// when it is destroyed.
|
||||
//-----------------------------------------------------------------------------
|
||||
CXBSocket::CXBSocket( SOCKET sock )
|
||||
:
|
||||
m_Socket( sock )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: CXBSocket()
|
||||
// Desc: Create a socket of the given type
|
||||
//-----------------------------------------------------------------------------
|
||||
CXBSocket::CXBSocket( SocketType type )
|
||||
:
|
||||
m_Socket( INVALID_SOCKET )
|
||||
{
|
||||
BOOL bSuccess = Open( type );
|
||||
assert( bSuccess );
|
||||
(VOID)bSuccess;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: CXBSocket()
|
||||
// Desc: Create a socket of the given type/protocol
|
||||
//-----------------------------------------------------------------------------
|
||||
CXBSocket::CXBSocket( INT iType, INT iProtocol )
|
||||
:
|
||||
m_Socket( INVALID_SOCKET )
|
||||
{
|
||||
BOOL bSuccess = Open( iType, iProtocol );
|
||||
assert( bSuccess );
|
||||
(VOID)bSuccess;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: ~CXBSocket()
|
||||
// Desc: Close and release socket
|
||||
//-----------------------------------------------------------------------------
|
||||
CXBSocket::~CXBSocket()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Open()
|
||||
// Desc: Open a socket of the given type
|
||||
//-----------------------------------------------------------------------------
|
||||
BOOL CXBSocket::Open( SocketType type )
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case Type_UDP:
|
||||
return Open( SOCK_DGRAM, IPPROTO_UDP );
|
||||
|
||||
case Type_TCP:
|
||||
return Open( SOCK_STREAM, IPPROTO_TCP );
|
||||
|
||||
default:
|
||||
assert( type == Type_VDP );
|
||||
return Open( SOCK_DGRAM, IPPROTO_VDP );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Open()
|
||||
// Desc: Open a socket of the given type/protocol
|
||||
//-----------------------------------------------------------------------------
|
||||
BOOL CXBSocket::Open( INT iType, INT iProtocol )
|
||||
{
|
||||
Close();
|
||||
m_Socket = socket( AF_INET, iType, iProtocol );
|
||||
return( m_Socket != INVALID_SOCKET );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: IsOpen()
|
||||
// Desc: TRUE if socket is open
|
||||
//-----------------------------------------------------------------------------
|
||||
BOOL CXBSocket::IsOpen() const
|
||||
{
|
||||
return( m_Socket != INVALID_SOCKET );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Close()
|
||||
// Desc: Close socket
|
||||
//-----------------------------------------------------------------------------
|
||||
INT CXBSocket::Close()
|
||||
{
|
||||
INT iResult = 0;
|
||||
if( m_Socket != INVALID_SOCKET )
|
||||
{
|
||||
iResult = closesocket( m_Socket );
|
||||
m_Socket = INVALID_SOCKET;
|
||||
}
|
||||
return iResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Accept()
|
||||
// Desc: Permit incoming connection attempt
|
||||
//-----------------------------------------------------------------------------
|
||||
SOCKET CXBSocket::Accept( SOCKADDR_IN* pSockAddr )
|
||||
{
|
||||
assert( m_Socket != INVALID_SOCKET );
|
||||
INT iSize = sizeof( SOCKADDR_IN );
|
||||
|
||||
SOCKET sockResult = accept( m_Socket, (sockaddr*)(pSockAddr), &iSize );
|
||||
|
||||
if( sockResult != INVALID_SOCKET && pSockAddr != NULL )
|
||||
assert( iSize == sizeof( SOCKADDR_IN ) );
|
||||
return sockResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Bind()
|
||||
// Desc: Associate local address with socket
|
||||
//-----------------------------------------------------------------------------
|
||||
INT CXBSocket::Bind( const SOCKADDR_IN* pSockAddr )
|
||||
{
|
||||
assert( m_Socket != INVALID_SOCKET );
|
||||
assert( pSockAddr != NULL );
|
||||
assert( pSockAddr->sin_family == AF_INET );
|
||||
|
||||
INT iResult = bind( m_Socket, (const sockaddr*)(pSockAddr),
|
||||
sizeof( SOCKADDR_IN ) );
|
||||
return iResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Connect()
|
||||
// Desc: Connect socket
|
||||
//-----------------------------------------------------------------------------
|
||||
INT CXBSocket::Connect( const SOCKADDR_IN* pSockAddr )
|
||||
{
|
||||
assert( m_Socket != INVALID_SOCKET );
|
||||
assert( pSockAddr != NULL );
|
||||
assert( pSockAddr->sin_family == AF_INET );
|
||||
|
||||
INT iResult = connect( m_Socket, (const sockaddr*)(pSockAddr),
|
||||
sizeof( SOCKADDR_IN ) );
|
||||
return iResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: GetSocket()
|
||||
// Desc: Returns the socket handle
|
||||
//-----------------------------------------------------------------------------
|
||||
SOCKET CXBSocket::GetSocket() const
|
||||
{
|
||||
return m_Socket;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: GetSockName()
|
||||
// Desc: Get socket "name"
|
||||
//-----------------------------------------------------------------------------
|
||||
INT CXBSocket::GetSockName( SOCKADDR_IN* pSockAddr ) const
|
||||
{
|
||||
assert( m_Socket != INVALID_SOCKET );
|
||||
assert( pSockAddr != NULL );
|
||||
INT iSize = sizeof( SOCKADDR_IN );
|
||||
|
||||
INT iResult = getsockname( m_Socket, (sockaddr*)(pSockAddr), &iSize );
|
||||
|
||||
if( iResult != SOCKET_ERROR )
|
||||
assert( iSize == sizeof( SOCKADDR_IN ) );
|
||||
return iResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: GetSockOpt()
|
||||
// Desc: Get socket option
|
||||
//-----------------------------------------------------------------------------
|
||||
INT CXBSocket::GetSockOpt( INT iLevel, INT iName, VOID* pValue, INT* piSize ) const
|
||||
{
|
||||
assert( m_Socket != INVALID_SOCKET );
|
||||
assert( pValue != NULL );
|
||||
assert( piSize != NULL );
|
||||
|
||||
INT iResult = getsockopt( m_Socket, iLevel, iName, (CHAR*)(pValue), piSize );
|
||||
return iResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: IoCtlSocket()
|
||||
// Desc: Configure socket I/O mode
|
||||
//-----------------------------------------------------------------------------
|
||||
INT CXBSocket::IoCtlSocket( LONG nCmd, DWORD* pArg )
|
||||
{
|
||||
assert( m_Socket != INVALID_SOCKET );
|
||||
assert( pArg != NULL );
|
||||
|
||||
INT iResult = ioctlsocket( m_Socket, nCmd, pArg );
|
||||
return iResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Listen()
|
||||
// Desc: Listen for incoming connection
|
||||
//-----------------------------------------------------------------------------
|
||||
INT CXBSocket::Listen( INT iBacklog )
|
||||
{
|
||||
assert( m_Socket != INVALID_SOCKET );
|
||||
|
||||
INT iResult = listen( m_Socket, iBacklog );
|
||||
return iResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Recv()
|
||||
// Desc: Receive data on socket
|
||||
//-----------------------------------------------------------------------------
|
||||
INT CXBSocket::Recv( VOID* pBuffer, INT iBytes )
|
||||
{
|
||||
assert( m_Socket != INVALID_SOCKET );
|
||||
assert( pBuffer != NULL );
|
||||
assert( iBytes >= 0 );
|
||||
|
||||
INT iResult = recv( m_Socket, (CHAR*)(pBuffer), iBytes, 0 );
|
||||
return iResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: RecvFrom()
|
||||
// Desc: Receive data on socket and report source address
|
||||
//-----------------------------------------------------------------------------
|
||||
INT CXBSocket::RecvFrom( VOID* pBuffer, INT iBytes, SOCKADDR_IN* pSockAddr )
|
||||
{
|
||||
assert( m_Socket != INVALID_SOCKET );
|
||||
assert( pBuffer != NULL );
|
||||
assert( iBytes >= 0 );
|
||||
INT iSize = sizeof( SOCKADDR_IN );
|
||||
|
||||
INT iResult = recvfrom( m_Socket, (CHAR*)(pBuffer), iBytes, 0,
|
||||
(sockaddr*)(pSockAddr), &iSize );
|
||||
if( iResult != SOCKET_ERROR && pSockAddr != NULL )
|
||||
assert( iSize == sizeof( SOCKADDR_IN ) );
|
||||
|
||||
return iResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Select()
|
||||
// Desc: Does a select call to check status of socket - returns separate
|
||||
// BOOLs for read, write, and error
|
||||
//-----------------------------------------------------------------------------
|
||||
INT CXBSocket::Select( BOOL* pbRead, BOOL* pbWrite, BOOL* pbError )
|
||||
{
|
||||
assert( m_Socket != INVALID_SOCKET );
|
||||
|
||||
INT iResultTotal = 0;
|
||||
|
||||
timeval tv = {0};
|
||||
if( pbRead )
|
||||
{
|
||||
fd_set fdsRead = {0};
|
||||
FD_SET( m_Socket, &fdsRead );
|
||||
|
||||
INT iResult = select( 0, &fdsRead, NULL, NULL, &tv );
|
||||
assert( iResult != SOCKET_ERROR );
|
||||
|
||||
*pbRead = ( iResult == 1 );
|
||||
iResultTotal += iResult;
|
||||
}
|
||||
|
||||
if( pbWrite )
|
||||
{
|
||||
fd_set fdsWrite = {0};
|
||||
FD_SET( m_Socket, &fdsWrite );
|
||||
|
||||
INT iResult = select( 0, NULL, &fdsWrite, NULL, &tv );
|
||||
assert( iResult != SOCKET_ERROR );
|
||||
|
||||
*pbWrite = ( iResult == 1 );
|
||||
iResultTotal += iResult;
|
||||
}
|
||||
|
||||
if( pbError )
|
||||
{
|
||||
fd_set fdsError = {0};
|
||||
FD_SET( m_Socket, &fdsError );
|
||||
|
||||
INT iResult = select( 0, NULL, NULL, &fdsError, &tv );
|
||||
assert( iResult != SOCKET_ERROR );
|
||||
|
||||
*pbError = ( iResult == 1 );
|
||||
iResultTotal += iResult;
|
||||
}
|
||||
|
||||
return iResultTotal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Send()
|
||||
// Desc: Send data on socket
|
||||
//-----------------------------------------------------------------------------
|
||||
INT CXBSocket::Send( const VOID* pBuffer, INT iBytes )
|
||||
{
|
||||
assert( m_Socket != INVALID_SOCKET );
|
||||
assert( pBuffer != NULL );
|
||||
assert( iBytes >= 0 );
|
||||
|
||||
INT iResult = send( m_Socket, (const CHAR*)(pBuffer), iBytes, 0 );
|
||||
return iResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: SendTo()
|
||||
// Desc: Send data on socket to specific destination
|
||||
//-----------------------------------------------------------------------------
|
||||
INT CXBSocket::SendTo( const VOID* pBuffer, INT iBytes, const SOCKADDR_IN* pSockAddr )
|
||||
{
|
||||
assert( m_Socket != INVALID_SOCKET );
|
||||
assert( pBuffer != NULL );
|
||||
assert( iBytes >= 0 );
|
||||
|
||||
INT iResult = sendto( m_Socket, (const CHAR*)(pBuffer), iBytes, 0,
|
||||
(const sockaddr*)(pSockAddr), sizeof( SOCKADDR_IN ) );
|
||||
return iResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: SetSockOpt()
|
||||
// Desc: Set socket option
|
||||
//-----------------------------------------------------------------------------
|
||||
INT CXBSocket::SetSockOpt( INT iLevel, INT iName, const VOID* pValue, INT iBytes )
|
||||
{
|
||||
assert( m_Socket != INVALID_SOCKET );
|
||||
assert( pValue != NULL );
|
||||
|
||||
INT iResult = setsockopt( m_Socket, iLevel, iName, (const CHAR*)(pValue),
|
||||
iBytes );
|
||||
return iResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Shutdown()
|
||||
// Desc: Disabled sending and/or receiving on socket
|
||||
//-----------------------------------------------------------------------------
|
||||
INT CXBSocket::Shutdown( INT iHow )
|
||||
{
|
||||
assert( m_Socket != INVALID_SOCKET );
|
||||
|
||||
INT iResult = shutdown( m_Socket, iHow );
|
||||
return iResult;
|
||||
}
|
||||
68
codemp/xbox/xbsocket.h
Normal file
68
codemp/xbox/xbsocket.h
Normal file
@@ -0,0 +1,68 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// File: XbSocket.h
|
||||
//
|
||||
// Desc: Wraps SOCKET object
|
||||
//
|
||||
// Hist: 05.17.01 - New for June XDK release
|
||||
// 08.08.01 - Moved to common framework
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef XBSOCKET_H
|
||||
#define XBSOCKET_H
|
||||
|
||||
#include <xtl.h>
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: class Socket
|
||||
// Desc: Xbox socket object
|
||||
//-----------------------------------------------------------------------------
|
||||
class CXBSocket
|
||||
{
|
||||
SOCKET m_Socket;
|
||||
|
||||
public:
|
||||
|
||||
enum SocketType
|
||||
{
|
||||
Type_UDP,
|
||||
Type_TCP,
|
||||
Type_VDP
|
||||
};
|
||||
|
||||
explicit CXBSocket( SOCKET = INVALID_SOCKET );
|
||||
explicit CXBSocket( SocketType );
|
||||
CXBSocket( INT iType, INT iProtocol );
|
||||
~CXBSocket();
|
||||
|
||||
BOOL Open( SocketType );
|
||||
BOOL Open( INT iType, INT iProtocol );
|
||||
BOOL IsOpen() const;
|
||||
INT Close();
|
||||
SOCKET Accept( SOCKADDR_IN* = NULL );
|
||||
INT Bind( const SOCKADDR_IN* );
|
||||
INT Connect( const SOCKADDR_IN* );
|
||||
SOCKET GetSocket() const;
|
||||
INT GetSockName( SOCKADDR_IN* ) const;
|
||||
INT GetSockOpt( INT iLevel, INT iName, VOID* pValue, INT* piSize ) const;
|
||||
INT IoCtlSocket( LONG nCmd, DWORD* pArg );
|
||||
INT Listen( INT iBacklog = SOMAXCONN );
|
||||
INT Recv( VOID* pBuffer, INT iBytes );
|
||||
INT RecvFrom( VOID* pBuffer, INT iBytes, SOCKADDR_IN* = NULL );
|
||||
INT Select( BOOL* pbRead, BOOL* pbWrite, BOOL* pbError );
|
||||
INT Send( const VOID* pBuffer, INT iBytes );
|
||||
INT SendTo( const VOID* pBuffer, INT iBytes, const SOCKADDR_IN* = NULL );
|
||||
INT SetSockOpt( INT iLevel, INT iName, const VOID* pValue, INT iBytes );
|
||||
INT Shutdown( INT iHow );
|
||||
|
||||
private:
|
||||
|
||||
CXBSocket( const CXBSocket& );
|
||||
CXBSocket& operator=( const CXBSocket& );
|
||||
|
||||
};
|
||||
|
||||
#endif // XBSOCKET_H
|
||||
Reference in New Issue
Block a user