You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
250 lines
6.8 KiB
250 lines
6.8 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Provides an interface to enable positional audio support in Mumble |
|
// |
|
//===========================================================================// |
|
|
|
#if defined( WIN32 ) && !defined( _X360 ) |
|
#define WIN32_LEAN_AND_MEAN |
|
#include <windows.h> |
|
#elif defined( POSIX ) |
|
#include <sys/mman.h> |
|
#include <sys/types.h> |
|
#include <sys/stat.h> |
|
#include <fcntl.h> |
|
#endif |
|
|
|
#include "cbase.h" |
|
#include "shareddefs.h" |
|
#include "view.h" |
|
#include "mumble.h" |
|
|
|
#if !defined( _X360 ) && !defined( NO_STEAM ) |
|
#include "steam/isteamuser.h" |
|
#include "steam/steam_api.h" |
|
#endif |
|
|
|
const char *COM_GetModDirectory(); // return the mod dir (rather than the complete -game param, which can be a path) |
|
|
|
struct MumbleSharedMemory_t |
|
{ |
|
#ifdef WIN32 |
|
uint32 uiVersion; |
|
ulong uiTick; |
|
#else |
|
uint32_t uiVersion; |
|
uint32_t uiTick; |
|
#endif |
|
float fAvatarPosition[3]; |
|
float fAvatarFront[3]; |
|
float fAvatarTop[3]; |
|
wchar_t name[256]; |
|
float fCameraPosition[3]; |
|
float fCameraFront[3]; |
|
float fCameraTop[3]; |
|
wchar_t identity[256]; |
|
#ifdef WIN32 |
|
uint32 context_len; |
|
#else |
|
uint32_t context_len; |
|
#endif |
|
unsigned char context[256]; |
|
wchar_t description[2048]; |
|
}; |
|
|
|
MumbleSharedMemory_t *g_pMumbleMemory = NULL; |
|
#ifdef WIN32 |
|
HANDLE g_hMapObject = NULL; |
|
#endif |
|
|
|
ConVar sv_mumble_positionalaudio( "sv_mumble_positionalaudio", "1", FCVAR_REPLICATED, "Allows players using Mumble to have support for positional audio." ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Singleton |
|
//----------------------------------------------------------------------------- |
|
static CMumbleSystem g_MumbleSystem; |
|
|
|
IGameSystem *MumbleSystem() |
|
{ |
|
return &g_MumbleSystem; |
|
} |
|
|
|
bool CMumbleSystem::Init() |
|
{ |
|
m_bHasSetPlayerUniqueId = false; |
|
m_nTeamSetInUniqueId = 0; |
|
m_szSteamIDCurrentServer[0] = '\0'; |
|
m_cubSteamIDCurrentServer = 0; |
|
|
|
ListenForGameEvent( "server_spawn" ); |
|
|
|
return true; |
|
} |
|
|
|
void CMumbleSystem::LevelInitPostEntity() |
|
{ |
|
if ( g_pMumbleMemory ) |
|
return; |
|
|
|
#if defined( WIN32 ) && !defined( _X360 ) |
|
g_hMapObject = OpenFileMappingW( FILE_MAP_ALL_ACCESS, FALSE, L"MumbleLink" ); |
|
if ( g_hMapObject == NULL ) |
|
return; |
|
|
|
g_pMumbleMemory = (MumbleSharedMemory_t *) MapViewOfFile( g_hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(MumbleSharedMemory_t) ); |
|
if ( g_pMumbleMemory == NULL ) |
|
{ |
|
CloseHandle( g_hMapObject ); |
|
g_hMapObject = NULL; |
|
return; |
|
} |
|
#elif defined( ANDROID ) |
|
return; // TODO(JusicP): implement |
|
#elif defined( POSIX ) |
|
char memname[256]; |
|
V_sprintf_safe( memname, "/MumbleLink.%d", getuid() ); |
|
|
|
int shmfd = shm_open( memname, O_RDWR, S_IRUSR | S_IWUSR ); |
|
|
|
if ( shmfd < 0 ) |
|
{ |
|
return; |
|
} |
|
|
|
g_pMumbleMemory = (MumbleSharedMemory_t *)( mmap( NULL, sizeof(struct MumbleSharedMemory_t), PROT_READ | PROT_WRITE, MAP_SHARED, shmfd,0 ) ); |
|
|
|
if ( g_pMumbleMemory == (void *)(-1) ) |
|
{ |
|
g_pMumbleMemory = NULL; |
|
return; |
|
} |
|
#endif |
|
} |
|
|
|
void CMumbleSystem::LevelShutdownPreEntity() |
|
{ |
|
#if defined( WIN32 ) && !defined( _X360 ) |
|
if ( g_hMapObject ) |
|
{ |
|
CloseHandle( g_hMapObject ); |
|
g_pMumbleMemory = NULL; |
|
g_hMapObject = NULL; |
|
} |
|
#elif defined( POSIX ) |
|
if ( g_pMumbleMemory ) |
|
{ |
|
munmap( g_pMumbleMemory, sizeof(struct MumbleSharedMemory_t) ); |
|
g_pMumbleMemory = NULL; |
|
} |
|
#endif |
|
} |
|
|
|
void VectorToMumbleFloatArray( const Vector &vec, float *rgfl ) |
|
{ |
|
// Traditional left-handed coordinate system |
|
rgfl[0] = vec.x; // X |
|
rgfl[1] = vec.z; // Y is up |
|
rgfl[2] = vec.y; // Z is into the screen |
|
} |
|
|
|
void CMumbleSystem::PostRender() |
|
{ |
|
#ifndef NO_STEAM |
|
if ( !g_pMumbleMemory || !sv_mumble_positionalaudio.GetBool() ) |
|
return; |
|
|
|
if ( g_pMumbleMemory->uiVersion != 2 ) |
|
{ |
|
V_wcscpy_safe( g_pMumbleMemory->name, L"Source engine: " ); |
|
wchar_t wcsGameDir[MAX_PATH]; |
|
Q_UTF8ToUnicode( COM_GetModDirectory(), wcsGameDir, sizeof(wcsGameDir) ); |
|
V_wcscat_safe( g_pMumbleMemory->name, wcsGameDir ); |
|
|
|
V_wcscpy_safe( g_pMumbleMemory->description, L"Links Source engine games to Mumble." ); |
|
g_pMumbleMemory->uiVersion = 2; |
|
} |
|
|
|
g_pMumbleMemory->uiTick++; |
|
|
|
Vector vecOriginPlayer, vecOriginCamera = MainViewOrigin(); |
|
QAngle anglesPlayer, anglesCamera = MainViewAngles(); |
|
|
|
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); |
|
if ( pPlayer ) |
|
{ |
|
vecOriginPlayer = pPlayer->EyePosition(); |
|
anglesPlayer = pPlayer->GetAbsAngles(); |
|
} |
|
else |
|
{ |
|
vecOriginPlayer = vecOriginCamera; |
|
anglesPlayer = anglesCamera; |
|
} |
|
|
|
anglesPlayer.x = 0; |
|
|
|
Vector vecPlayerForward, vecPlayerUp, vecCameraForward, vecCameraUp; |
|
AngleVectors( anglesPlayer, &vecPlayerForward, NULL, &vecPlayerUp ); |
|
AngleVectors( anglesCamera, &vecCameraForward, NULL, &vecCameraUp ); |
|
|
|
// 1 Source unit is about one inch |
|
// 1 mumble unit = 1 meter |
|
vecOriginPlayer *= METERS_PER_INCH; |
|
vecOriginCamera *= METERS_PER_INCH; |
|
|
|
VectorToMumbleFloatArray( vecPlayerForward, g_pMumbleMemory->fAvatarFront ); |
|
VectorToMumbleFloatArray( vecPlayerUp, g_pMumbleMemory->fAvatarTop ); |
|
VectorToMumbleFloatArray( vecOriginPlayer, g_pMumbleMemory->fAvatarPosition ); |
|
|
|
VectorToMumbleFloatArray( vecCameraForward, g_pMumbleMemory->fCameraFront ); |
|
VectorToMumbleFloatArray( vecCameraUp, g_pMumbleMemory->fCameraTop ); |
|
VectorToMumbleFloatArray( vecOriginCamera, g_pMumbleMemory->fCameraPosition ); |
|
|
|
if ( pPlayer && m_bHasSetPlayerUniqueId && m_nTeamSetInUniqueId != pPlayer->GetTeamNumber() ) |
|
{ |
|
// Player changed team since we set the unique ID. Set it again. |
|
m_bHasSetPlayerUniqueId = false; |
|
} |
|
|
|
if ( !m_bHasSetPlayerUniqueId && steamapicontext && steamapicontext->SteamUser() ) |
|
{ |
|
CSteamID steamid = steamapicontext->SteamUser()->GetSteamID(); |
|
if ( steamid.IsValid() ) |
|
{ |
|
int unTeam = pPlayer ? pPlayer->GetTeamNumber() : 0; |
|
char szSteamId[256]; |
|
V_sprintf_safe( szSteamId, "universe:%u;account_type:%u;id:%u;instance:%u;team:%d", steamid.GetEUniverse(), steamid.GetEAccountType(), steamid.GetAccountID(), steamid.GetUnAccountInstance(), unTeam ); |
|
|
|
wchar_t wcsSteamId[256]; |
|
Q_UTF8ToUnicode( szSteamId, wcsSteamId, sizeof(wcsSteamId) ); |
|
|
|
// Identifier which uniquely identifies a certain player in a context. |
|
V_wcscpy_safe( g_pMumbleMemory->identity, wcsSteamId ); |
|
|
|
m_bHasSetPlayerUniqueId = true; |
|
m_nTeamSetInUniqueId = unTeam; |
|
} |
|
} |
|
|
|
// Context should be equal for players which should be able to hear each other positional and |
|
// differ for those who shouldn't (e.g. it could contain the server+port and team) |
|
memcpy( g_pMumbleMemory->context, &m_szSteamIDCurrentServer, m_cubSteamIDCurrentServer ); |
|
g_pMumbleMemory->context_len = m_cubSteamIDCurrentServer; |
|
#endif // NO_STEAM |
|
} |
|
|
|
void CMumbleSystem::FireGameEvent( IGameEvent *event ) |
|
{ |
|
#ifndef NO_STEAM |
|
const char *eventname = event->GetName(); |
|
|
|
if ( !eventname || !eventname[0] ) |
|
return; |
|
|
|
if ( !Q_strcmp( "server_spawn", eventname ) ) |
|
{ |
|
V_strcpy_safe( m_szSteamIDCurrentServer, event->GetString( "steamid", "" ) ); |
|
m_cubSteamIDCurrentServer = strlen( m_szSteamIDCurrentServer ) + 1; |
|
} |
|
#endif // NO_STEAM |
|
}
|
|
|