From 2eb343d6e0b3c390777cca146b5cc299b348f5eb Mon Sep 17 00:00:00 2001 From: christian Date: Sat, 1 May 2021 00:44:48 +0200 Subject: [PATCH] updates by buttocks by fixing uneven bytes recv --- extension.cpp | 105 +++++++++++++++++++++++++++----------------------- extension.h | 6 ++- 2 files changed, 62 insertions(+), 49 deletions(-) diff --git a/extension.cpp b/extension.cpp index d6ac46d..e6f5fad 100644 --- a/extension.cpp +++ b/extension.cpp @@ -46,9 +46,16 @@ #include "CDetour/detours.h" #include "extension.h" +// voice packets are sent over unreliable netchannel +//#define NET_MAX_DATAGRAM_PAYLOAD 4000 // = maximum unreliable payload size +// voice packetsize = 64 | netchannel overflows at >4000 bytes +// with 22050 samplerate and 512 frames per packet -> 23.22ms per packet +// SVC_VoiceData overhead = 5 bytes +// sensible limit of 8 packets per frame = 552 bytes -> 185.76ms of voice data per frame +#define NET_MAX_VOICE_BYTES_FRAME (8 * (5 + 64)) + ConVar g_SmVoiceAddr("sm_voice_addr", "127.0.0.1", FCVAR_PROTECTED, "Voice server listen ip address."); ConVar g_SmVoicePort("sm_voice_port", "27020", FCVAR_PROTECTED, "Voice server listen port.", true, 1025.0, true, 65535.0); -ConVar g_SmVoiceLogging("sm_voice_logging", "0", FCVAR_PROTECTED, "Log voice packets."); /** * @file extension.cpp @@ -67,29 +74,6 @@ IServer *iserver = NULL; double g_fLastVoiceData[SM_MAXPLAYERS + 1]; int g_aFrameVoiceBytes[SM_MAXPLAYERS + 1]; -#define NET_MAX_VOICE_BYTES_FRAME 512 -#define NET_MAX_VOICE_BYTES_FRAME_LOG 1024 - -/* -// This is just the client_t->netchan.datagram buffer size (shouldn't ever need to be huge) -#define NET_MAX_VOICE_BYTES_FRAME 4000 // = maximum unreliable payload size -maybe try 1024 for starters -instead of 4096 -voice data should never be that big -actually I can give you a fool-proof number -since I did tests for torchlight -the packetsize is hardcoded 64 bytes -torchlight sends max 5 frames at once to clients -there are 512 frames per packet -sample rate 22050 -so one packet is 23.2ms -anyways, since torchlight only sends 5 packets at once, which is 5*64 = 320 bytes -make the limit > 384 (6 * 64) -I'm thinking the guy probably just sends a huge packet with max size 4000 or whatever to the server -and it puts it in the netchan, which seems to be 4000 max big -and then any other data in there overflows it -*/ - DETOUR_DECL_STATIC4(SV_BroadcastVoiceData, void, IClient *, pClient, int, nBytes, char *, data, int64, xuid) { if(g_Interface.OnBroadcastVoiceData(pClient, nBytes, data)) @@ -328,6 +312,12 @@ const sp_nativeinfo_t MyNatives[] = { NULL, NULL } }; +static void ListenSocketAction(void *pData) +{ + CVoice *pThis = (CVoice *)pData; + pThis->ListenSocket(); +} + void CVoice::SDK_OnAllLoaded() { sharesys->AddNatives(myself, MyNatives); @@ -366,9 +356,19 @@ void CVoice::SDK_OnAllLoaded() return; } + // This doesn't seem to work right away ... engine->ServerCommand("exec sourcemod/extension.Voice.cfg\n"); engine->ServerExecute(); + // ... delay starting listen server to next frame + smutils->AddFrameAction(ListenSocketAction, this); +} + +void CVoice::ListenSocket() +{ + if(m_PollFds > 0) + return; + sockaddr_in bindAddr; memset(&bindAddr, 0, sizeof(bindAddr)); bindAddr.sin_family = AF_INET; @@ -439,37 +439,25 @@ void CVoice::OnGameFrame(bool simulating) memset(g_aFrameVoiceBytes, 0, sizeof(g_aFrameVoiceBytes)); } - bool CVoice::OnBroadcastVoiceData(IClient *pClient, int nBytes, char *data) { - int client = pClient->GetPlayerSlot() + 1; - IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client); - + // Reject empty packets if(nBytes < 1) - { - smutils->LogMessage(myself, "%s (%s): Empty voice packet", pPlayer->GetName(), pPlayer->GetSteam2Id(true), data); return false; - } - // this is to log all voice packet - if (g_SmVoiceLogging.GetInt()) - smutils->LogMessage(myself, "%s (%s): %s", pPlayer->GetName(), pPlayer->GetSteam2Id(true), data); + int client = pClient->GetPlayerSlot() + 1; + + // Reject voice packet if we'd send more than NET_MAX_VOICE_BYTES_FRAME voice bytes from this client in the current frame. + // 5 = SVC_VoiceData header/overhead + g_aFrameVoiceBytes[client] += 5 + nBytes; + + if(g_aFrameVoiceBytes[client] > NET_MAX_VOICE_BYTES_FRAME) + return false; g_fLastVoiceData[client] = gpGlobals->curtime; - // Reject voice packet if we'd send more than NET_MAX_VOICE_BYTES_FRAME voice bytes from this client in the current frame. - g_aFrameVoiceBytes[client] += nBytes; - - if(g_aFrameVoiceBytes[client] > NET_MAX_VOICE_BYTES_FRAME) - { - if(g_aFrameVoiceBytes[client] > NET_MAX_VOICE_BYTES_FRAME_LOG) - { - smutils->LogMessage(myself, "%s (%s) voice overflow! %d > %d\n", pPlayer->GetName(), pPlayer->GetSteam2Id(true), g_aFrameVoiceBytes[client], NET_MAX_VOICE_BYTES_FRAME); - } - return false; - } return true; - } +} void CVoice::HandleNetwork() { @@ -503,6 +491,7 @@ void CVoice::HandleNetwork() m_aClients[Client].m_LastLength = 0; m_aClients[Client].m_LastValidData = 0.0; m_aClients[Client].m_New = true; + m_aClients[Client].m_UnEven = false; m_aPollFds[m_PollFds].fd = Socket; m_aPollFds[m_PollFds].events = POLLIN | POLLHUP; @@ -560,7 +549,16 @@ void CVoice::HandleNetwork() if(min(BytesAvailable, sizeof(aBuf)) > m_Buffer.CurrentFree() * sizeof(int16_t)) continue; - ssize_t Bytes = recv(pClient->m_Socket, aBuf, sizeof(aBuf), 0); + // Edge case: previously received data is uneven and last recv'd byte has to be prepended + int Shift = 0; + if(pClient->m_UnEven) + { + Shift = 1; + aBuf[0] = pClient->m_Remainder; + pClient->m_UnEven = false; + } + + ssize_t Bytes = recv(pClient->m_Socket, &aBuf[Shift], sizeof(aBuf) - Shift, 0); if(Bytes <= 0) { @@ -568,10 +566,21 @@ void CVoice::HandleNetwork() pClient->m_Socket = -1; m_aPollFds[PollFds].fd = -1; CompressPollFds = true; - smutils->LogMessage(myself, "Client %d disconnected!(1)\n", Client); + //smutils->LogMessage(myself, "Client %d disconnected!(1)\n", Client); continue; } + Bytes += Shift; + + // Edge case: data received is uneven (can't be divided by two) + // store last byte, drop it here and prepend it right before the next recv + if(Bytes & 1) + { + pClient->m_UnEven = true; + pClient->m_Remainder = aBuf[Bytes - 1]; + Bytes -= 1; + } + // Got data! OnDataReceived(pClient, (int16_t *)aBuf, Bytes / sizeof(int16_t)); diff --git a/extension.h b/extension.h index b0f3f77..492bc23 100644 --- a/extension.h +++ b/extension.h @@ -8,7 +8,7 @@ * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 3.0, as published by the * Free Software Foundation. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more @@ -139,6 +139,8 @@ public: void OnGameFrame(bool simulating); bool OnBroadcastVoiceData(IClient *pClient, int nBytes, char *data); + void ListenSocket(); + private: int m_ListenSocket; @@ -149,6 +151,8 @@ private: size_t m_LastLength; double m_LastValidData; bool m_New; + bool m_UnEven; + unsigned char m_Remainder; } m_aClients[MAX_CLIENTS]; struct pollfd m_aPollFds[1 + MAX_CLIENTS];