Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/adminsystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1295,6 +1295,38 @@ CON_COMMAND_CHAT_FLAGS(setteam, "<name> <team (0-3)> - Set a player's team", ADM
if (iNumClients > 1)
PrintMultiAdminAction(nType, strCommandPlayerName, "moved", szAction);
}

CON_COMMAND_CHAT_FLAGS(savevc, "<name> save voicechat as PCM audio for test", ADMFLAG_CHEATS)
{
if (args.ArgC() < 2)
{
ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "Usage: !savevc <name>");
return;
}

int iNumClients = 0;
int pSlots[MAXPLAYERS];
ETargetType nType;
ZEPlayer* pTargetPlayer = nullptr;
bool bIsAdmin = !player || player->GetZEPlayer()->IsAdminFlagSet(ADMFLAG_GENERIC);
std::string strTarget = (!bIsAdmin || args.ArgC() == 1) ? "@me" : args[1];

if (!g_playerManager->CanTargetPlayers(player, strTarget.c_str(), iNumClients, pSlots, NO_RANDOM | NO_MULTIPLE | NO_BOT, nType))
return;

CCSPlayerController* pTarget = CCSPlayerController::FromSlot(pSlots[0]);
pTargetPlayer = pTarget->GetZEPlayer();

std::vector<int16_t> audio = pTargetPlayer->GetVoiceChat();

std::ofstream file(
"audio.pcm",
std::ios::binary);

file.write(
reinterpret_cast<const char*>(audio.data()),
audio.size() * sizeof(int16_t));
}
#endif

void CAdmin::SetFlags(uint64 iFlags)
Expand Down
1 change: 1 addition & 0 deletions src/cs2fixes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,7 @@ bool CS2Fixes::Hook_ProcessVoiceData(const CCLCMsg_VoiceData_t& msg)
RETURN_META_VALUE(MRES_IGNORED, true);

pPlayer->SetLastVoiceTime(GetGlobals()->curtime);
pPlayer->OnVoiceFrame(msg.audio());

RETURN_META_VALUE(MRES_IGNORED, true);
}
Expand Down
87 changes: 86 additions & 1 deletion src/playermanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ CConVar<int> g_cvarAdminImmunityTargetting("cs2f_admin_immunity", FCVAR_NONE, "M
CConVar<bool> g_cvarEnableMapSteamIds("cs2f_map_steamids_enable", FCVAR_NONE, "Whether to make Steam ID's available to maps", false);

ZEPlayerHandle::ZEPlayerHandle() :
m_Index(INVALID_ZEPLAYERHANDLE_INDEX){};
m_Index(INVALID_ZEPLAYERHANDLE_INDEX) {};

ZEPlayerHandle::ZEPlayerHandle(CPlayerSlot slot)
{
Expand Down Expand Up @@ -96,6 +96,51 @@ ZEPlayer* ZEPlayerHandle::Get() const
return pZEPlayer;
}

std::vector<int16_t> ZEPlayer::GetVoiceChat()
{
int error = 0;
OpusDecoder* decoder = opus_decoder_create(VOICECHAT_SAMPLERATE, VOICECHAT_CHANNELS, &error);
if (error != OPUS_OK || decoder == nullptr || m_dequeVoicechat.empty())
return std::vector<int16_t>();

std::vector<int16_t> pcm;
// maximum packet duration (120ms; 5760 for 48kHz),
const int frameBufLen = VOICECHAT_SAMPLERATE * 120 / 1000;
std::vector<int16_t> frameBuf(frameBufLen * VOICECHAT_CHANNELS);

for (const auto& [_, packet] : m_dequeVoicechat)
{
const unsigned char* data =
reinterpret_cast<const unsigned char*>(packet.data());
int dataLen = static_cast<int>(packet.size());

int samples = opus_decode(
decoder,
data,
dataLen,
frameBuf.data(),
frameBufLen,
0);

if (samples < 0)
{
Panic("ZEPlayer::GetVoiceChat(): failed to parse opus: %s\n", opus_strerror(samples));

break;
}
else
{
pcm.insert(
pcm.end(),
frameBuf.begin(),
frameBuf.begin() + samples * VOICECHAT_CHANNELS);
}
}

opus_decoder_destroy(decoder);
return pcm;
}

void ZEPlayer::OnSpawn()
{
SetSpeedMod(1.f);
Expand Down Expand Up @@ -759,6 +804,46 @@ void ZEPlayer::SetEntwatchHudSize(float flSize)
pText->m_flFontSize = m_flEntwatchHudSize;
}

void ZEPlayer::OnVoiceFrame(const CMsgVoiceAudio& msg)
{
// Message("OnVoiceFrame(...) "
// "slot=%d "
// "time=%f "
// "format=%d "
// "voice_data_size=%zu "
// "sequence_bytes=%d "
// "section_number=%u "
// "sample_rate=%u "
// "uncompressed_sample_offset=%u "
// "num_packets=%u "
// "voice_level=%f\n",
// m_slot,
// GetGlobals()->curtime,
// static_cast<int>(msg.format()),
// msg.voice_data().size(),
// msg.sequence_bytes(),
// msg.section_number(),
// msg.sample_rate(),
// msg.uncompressed_sample_offset(),
// msg.num_packets(),
// msg.voice_level());

// assume some things about the incoming voice chat
// - all data is somewhat realtime: packets can be out of order, causing corruption; allow it
// - the client controls the voice_level and sequence_bytes fields
if ((msg.format() != VOICEDATA_FORMAT_OPUS) || (msg.num_packets() > UINT8_MAX) || (msg.voice_data().size() > UINT8_MAX))
return;

// make a owned copy
std::string voice_data = msg.voice_data();
float time = GetGlobals()->curtime;
m_dequeVoicechat.push_back(std::make_pair(time, voice_data));
while ((!m_dequeVoicechat.empty()) && (m_dequeVoicechat.front().first < (time - VOICECHAT_SECONDS)))
m_dequeVoicechat.pop_front();

return;
}

void CPlayerManager::OnBotConnected(CPlayerSlot slot)
{
m_vecPlayers[slot.Get()] = new ZEPlayer(slot, true);
Expand Down
10 changes: 10 additions & 0 deletions src/playermanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@
#include "entity/cpointworldtext.h"
#include "entity/lights.h"
#include "gamesystem.h"
#include "netmessages.h"
#include "steam/isteamuser.h"
#include "steam/steam_api_common.h"
#include "steam/steamclientpublic.h"
#include "utlvector.h"
#include <deque>
#include <opus/opus.h>
#include <playerslot.h>

extern CConVar<bool> g_cvarFlashLightTransmitOthers;
Expand Down Expand Up @@ -56,6 +59,10 @@ extern CConVar<CUtlString> g_cvarFlashLightAttachment;
#define ZSOUNDS_PREF_KEY_NAME "zsounds"
#define INVALID_ZEPLAYERHANDLE_INDEX 0u

#define VOICECHAT_SAMPLERATE 48000
#define VOICECHAT_CHANNELS 1
#define VOICECHAT_SECONDS 60.0

static uint32 iZEPlayerHandleSerial = 0u; // this should actually be 3 bytes large, but no way enough players join in servers lifespan for this to be an issue

enum class ETargetType
Expand Down Expand Up @@ -273,6 +280,7 @@ class ZEPlayer
void SetEntwatchHudSize(float flSize);
void SetTopDefenderStatus(bool bStatus) { m_bTopDefender = bStatus; }
void SetLastVoiceTime(float flTime) { m_flLastVoiceTime = flTime; }
void OnVoiceFrame(const CMsgVoiceAudio& msg);
void SetBeaconEnabledTime(float flTime) { m_flBeaconEnabledTime = flTime; }

uint64 GetAdminFlags() { return m_iAdminFlags; }
Expand Down Expand Up @@ -325,6 +333,7 @@ class ZEPlayer
float GetEntwatchHudSize() { return m_flEntwatchHudSize; }
bool GetTopDefenderStatus() { return m_bTopDefender; }
float GetLastVoiceTime() { return m_flLastVoiceTime; }
std::vector<int16_t> GetVoiceChat();
float GetBeaconEnabledTime() { return m_flBeaconEnabledTime; }

void OnSpawn();
Expand Down Expand Up @@ -401,6 +410,7 @@ class ZEPlayer
float m_flEntwatchHudSize;
bool m_bTopDefender;
float m_flLastVoiceTime;
std::deque<std::pair<float, std::string>> m_dequeVoicechat;
float m_flBeaconEnabledTime;
};

Expand Down
Loading