303 lines
9.3 KiB
C++
303 lines
9.3 KiB
C++
//-----------------------------------------------------------------------------
|
|
// 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
|