Initial commit.

This commit is contained in:
Jim Gray
2013-04-04 14:32:05 -07:00
parent ba5c81da32
commit d71d53e8ec
2180 changed files with 1393544 additions and 1 deletions

View 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="&gt;=" right="0x2000005"/>
<filter left="0x7" op="&lt;=" 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

File diff suppressed because it is too large Load Diff

250
codemp/xbox/XBLive.h Normal file
View 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

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

302
codemp/xbox/XBVoice.h Normal file
View 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
View 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

View 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;
}

View 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

File diff suppressed because it is too large Load Diff

439
codemp/xbox/match.h Normal file
View 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
View 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
View 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
View 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
View 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