From 20f2e3d4af84f42960127b50de4bf2009aaff2f6 Mon Sep 17 00:00:00 2001 From: jrd Date: Fri, 5 Jun 2026 03:14:57 +0000 Subject: [PATCH 1/5] feat: add CLM request for channel level list (message ID 1028) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST (1028): a connectionless request that causes the server to immediately reply with the existing PROTMESSID_CLM_CHANNEL_LEVEL_LIST (1015) response. This follows the established CLM request/response pattern used by CLReqConnClientsList (1014 → 1013), CLReqVersionAndOS (1012 → 1011), and CLReqServerList (1007 → 1002). The implementation is zero-cost: vecChannelLevels is already computed every server tick in OnTimer(). The handler simply forwards that existing data to the requesting address. Use case: external monitors can poll silence/activity level on any Jamulus server by sending a single UDP datagram to the server's game port, without establishing an audio connection or requiring additional open ports. Co-Authored-By: Claude Sonnet 4.6 --- src/protocol.cpp | 11 +++++++++++ src/protocol.h | 5 ++++- src/server.cpp | 2 ++ src/server.h | 2 ++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/protocol.cpp b/src/protocol.cpp index a30534bc72..60a803f16c 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -922,6 +922,10 @@ void CProtocol::ParseConnectionLessMessageBody ( const CVector& vecbyMe EvaluateCLChannelLevelListMes ( InetAddr, vecbyMesBodyData ); break; + case PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST: + EvaluateCLReqChannelLevelListMes ( InetAddr ); + break; + case PROTMESSID_CLM_REGISTER_SERVER_RESP: EvaluateCLRegisterServerResp ( InetAddr, vecbyMesBodyData ); break; @@ -2563,6 +2567,13 @@ bool CProtocol::EvaluateCLChannelLevelListMes ( const CHostAddress& InetAddr, co return false; // no error } +bool CProtocol::EvaluateCLReqChannelLevelListMes ( const CHostAddress& InetAddr ) +{ + emit CLReqChannelLevelList ( InetAddr ); + + return false; // no error +} + void CProtocol::CreateCLRegisterServerResp ( const CHostAddress& InetAddr, const ESvrRegResult eResult ) { int iPos = 0; // init position pointer diff --git a/src/protocol.h b/src/protocol.h index 03ac9f328f..f24223b836 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -83,7 +83,8 @@ #define PROTMESSID_CLM_CHANNEL_LEVEL_LIST 1015 // channel level list #define PROTMESSID_CLM_REGISTER_SERVER_RESP 1016 // status of server registration request #define PROTMESSID_CLM_REGISTER_SERVER_EX 1017 // register server with extended information -#define PROTMESSID_CLM_RED_SERVER_LIST 1018 // reduced server list +#define PROTMESSID_CLM_RED_SERVER_LIST 1018 // reduced server list +#define PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST 1028 // request channel level list (connectionless) // special IDs #define PROTMESSID_SPECIAL_SPLIT_MESSAGE 2001 // a container for split messages @@ -281,6 +282,7 @@ class CProtocol : public QObject bool EvaluateCLConnClientsListMes ( const CHostAddress& InetAddr, const CVector& vecData ); bool EvaluateCLReqConnClientsListMes ( const CHostAddress& InetAddr ); bool EvaluateCLChannelLevelListMes ( const CHostAddress& InetAddr, const CVector& vecData ); + bool EvaluateCLReqChannelLevelListMes ( const CHostAddress& InetAddr ); bool EvaluateCLRegisterServerResp ( const CHostAddress& InetAddr, const CVector& vecData ); int iOldRecID; @@ -348,5 +350,6 @@ public slots: void CLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo ); void CLReqConnClientsList ( CHostAddress InetAddr ); void CLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ); + void CLReqChannelLevelList ( CHostAddress InetAddr ); void CLRegisterServerResp ( CHostAddress InetAddr, ESvrRegResult eStatus ); }; diff --git a/src/server.cpp b/src/server.cpp index d6cd2bf0cc..ab300781fd 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -280,6 +280,8 @@ CServer::CServer ( const int iNewMaxNumChan, QObject::connect ( &ConnLessProtocol, &CProtocol::CLReqConnClientsList, this, &CServer::OnCLReqConnClientsList ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLReqChannelLevelList, this, &CServer::OnCLReqChannelLevelList ); + QObject::connect ( &ServerListManager, &CServerListManager::SvrRegStatusChanged, this, &CServer::SvrRegStatusChanged ); QObject::connect ( &JamController, &recorder::CJamController::RestartRecorder, this, &CServer::RestartRecorder ); diff --git a/src/server.h b/src/server.h index 6a42a8f9b8..11b8a10aba 100644 --- a/src/server.h +++ b/src/server.h @@ -366,6 +366,8 @@ public slots: void OnCLReqConnClientsList ( CHostAddress InetAddr ) { ConnLessProtocol.CreateCLConnClientsListMes ( InetAddr, CreateChannelList() ); } + void OnCLReqChannelLevelList ( CHostAddress InetAddr ) { ConnLessProtocol.CreateCLChannelLevelListMes ( InetAddr, vecChannelLevels, GetNumberOfConnectedClients() ); } + void OnCLRegisterServerReceived ( CHostAddress InetAddr, CHostAddress LInetAddr, CServerCoreInfo ServerInfo ) { ServerListManager.Append ( InetAddr, LInetAddr, ServerInfo ); From f5199f33afca5252d20a637ad7b58dcf74a99e21 Mon Sep 17 00:00:00 2001 From: jrd Date: Wed, 15 Jul 2026 19:13:04 +0000 Subject: [PATCH 2/5] style: fix clang-format alignment for CLM channel level list request --- src/protocol.h | 4 ++-- src/server.h | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/protocol.h b/src/protocol.h index f24223b836..fae07dbc6e 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -83,8 +83,8 @@ #define PROTMESSID_CLM_CHANNEL_LEVEL_LIST 1015 // channel level list #define PROTMESSID_CLM_REGISTER_SERVER_RESP 1016 // status of server registration request #define PROTMESSID_CLM_REGISTER_SERVER_EX 1017 // register server with extended information -#define PROTMESSID_CLM_RED_SERVER_LIST 1018 // reduced server list -#define PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST 1028 // request channel level list (connectionless) +#define PROTMESSID_CLM_RED_SERVER_LIST 1018 // reduced server list +#define PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST 1028 // request channel level list (connectionless) // special IDs #define PROTMESSID_SPECIAL_SPLIT_MESSAGE 2001 // a container for split messages diff --git a/src/server.h b/src/server.h index 11b8a10aba..0b25558663 100644 --- a/src/server.h +++ b/src/server.h @@ -366,7 +366,10 @@ public slots: void OnCLReqConnClientsList ( CHostAddress InetAddr ) { ConnLessProtocol.CreateCLConnClientsListMes ( InetAddr, CreateChannelList() ); } - void OnCLReqChannelLevelList ( CHostAddress InetAddr ) { ConnLessProtocol.CreateCLChannelLevelListMes ( InetAddr, vecChannelLevels, GetNumberOfConnectedClients() ); } + void OnCLReqChannelLevelList ( CHostAddress InetAddr ) + { + ConnLessProtocol.CreateCLChannelLevelListMes ( InetAddr, vecChannelLevels, GetNumberOfConnectedClients() ); + } void OnCLRegisterServerReceived ( CHostAddress InetAddr, CHostAddress LInetAddr, CServerCoreInfo ServerInfo ) { From 44af7989e732537be38198fde38765fe36054b8d Mon Sep 17 00:00:00 2001 From: jrd Date: Wed, 2 Sep 2026 15:55:21 +0000 Subject: [PATCH 3/5] protocol: renumber CLM_REQ_CHANNEL_LEVEL_LIST from 1028 to 1025 1023 and 1024 are taken by the open TCP PR #3636 (PROTMESSID_CLM_TCP_OFFERED, PROTMESSID_CLM_CLIENT_ID), so 1025 is the lowest CLM ID free on main and unclaimed by any open PR. The define also moves to the end of the CLM block so the list stays in numeric order. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_019KZHjFTo73t39UURauvXvg --- src/protocol.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocol.h b/src/protocol.h index b8838e6537..01007ec3b2 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -106,11 +106,11 @@ #define PROTMESSID_CLM_REGISTER_SERVER_RESP 1016 // status of server registration request #define PROTMESSID_CLM_REGISTER_SERVER_EX 1017 // register server with extended information #define PROTMESSID_CLM_RED_SERVER_LIST 1018 // reduced server list -#define PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST 1028 // request channel level list (connectionless) #define PROTMESSID_CLM_SERVER_FEATURES 1019 // server features message #define PROTMESSID_CLM_REQ_SERVER_FEATURES 1020 // request server features #define PROTMESSID_CLM_WELCOME_MESSAGE 1021 // server welcome message #define PROTMESSID_CLM_REQ_WELCOME_MESSAGE 1022 // request server welcome message +#define PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST 1025 // request channel level list (connectionless) // special IDs #define PROTMESSID_SPECIAL_SPLIT_MESSAGE 2001 // a container for split messages From 10d732374dbc7c56d309d2f36da1c3399589dc00 Mon Sep 17 00:00:00 2001 From: jrd Date: Wed, 2 Sep 2026 22:01:52 +0000 Subject: [PATCH 4/5] protocol: document CLM_REQ_CHANNEL_LEVEL_LIST in the message reference Every other connectionless message has an entry in the protocol documentation block at the top of protocol.cpp; this one did not. The entry follows the PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST form: no payload, plus the one line of behaviour the handler implements (one PROTMESSID_CLM_CHANNEL_LEVEL_LIST back to the requesting address). Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_019KZHjFTo73t39UURauvXvg --- src/protocol.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/protocol.cpp b/src/protocol.cpp index 6b0ec816d8..f6ddbd555e 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -484,6 +484,14 @@ CONNECTION LESS MESSAGES note: does not have any data -> n = 0 + +- PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST: Request the channel level list + + note: does not have any data -> n = 0 + + the server replies with one PROTMESSID_CLM_CHANNEL_LEVEL_LIST to the + requesting address + */ #include "protocol.h" From ac863a7333e574ea7ebdbd4af971f707574cc42e Mon Sep 17 00:00:00 2001 From: jrd Date: Fri, 4 Sep 2026 03:53:21 +0000 Subject: [PATCH 5/5] protocol: place CLM_REQ_CHANNEL_LEVEL_LIST last in every block, matching the #define order Review asked that the new message keep the same position as its #define (the last entry in the CLM block) everywhere it appears, and that the connect() block in server.cpp keep one blank line between entries. This moves the Evaluate declaration and the signal in protocol.h, the switch case and the Evaluate definition in protocol.cpp, the connect() in server.cpp, and the slot in server.h to follow the PROTMESSID_CLM_REQ_WELCOME_MESSAGE entry in each block. No behaviour change: a headless build of this commit answers a 1025 request with one 1015 reply and still answers 1014 with 1013. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01S8KACM96jgMTGCLbqqta9Z --- src/protocol.cpp | 23 ++++++++++++----------- src/protocol.h | 4 ++-- src/server.cpp | 3 ++- src/server.h | 10 +++++----- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/protocol.cpp b/src/protocol.cpp index f6ddbd555e..f158ab0730 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -977,10 +977,6 @@ void CProtocol::ParseConnectionLessMessageBody ( const CVector& vecbyMe EvaluateCLChannelLevelListMes ( InetAddr, vecbyMesBodyData ); break; - case PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST: - EvaluateCLReqChannelLevelListMes ( InetAddr ); - break; - case PROTMESSID_CLM_REGISTER_SERVER_RESP: EvaluateCLRegisterServerResp ( InetAddr, vecbyMesBodyData ); break; @@ -992,6 +988,10 @@ void CProtocol::ParseConnectionLessMessageBody ( const CVector& vecbyMe case PROTMESSID_CLM_REQ_WELCOME_MESSAGE: EvaluateCLReqWelcomeMessageMes ( InetAddr ); break; + + case PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST: + EvaluateCLReqChannelLevelListMes ( InetAddr ); + break; } } @@ -2630,13 +2630,6 @@ bool CProtocol::EvaluateCLChannelLevelListMes ( const CHostAddress& InetAddr, co return false; // no error } -bool CProtocol::EvaluateCLReqChannelLevelListMes ( const CHostAddress& InetAddr ) -{ - emit CLReqChannelLevelList ( InetAddr ); - - return false; // no error -} - void CProtocol::CreateCLRegisterServerResp ( const CHostAddress& InetAddr, const ESvrRegResult eResult ) { int iPos = 0; // init position pointer @@ -2719,6 +2712,14 @@ void CProtocol::CreateCLWelcomeMessageMes ( const CHostAddress& InetAddr, const CreateAndImmSendConLessMessage ( PROTMESSID_CLM_WELCOME_MESSAGE, vecData, InetAddr ); } +bool CProtocol::EvaluateCLReqChannelLevelListMes ( const CHostAddress& InetAddr ) +{ + // invoke message action + emit CLReqChannelLevelList ( InetAddr ); + + return false; // no error +} + /******************************************************************************\ * Message generation and parsing * \******************************************************************************/ diff --git a/src/protocol.h b/src/protocol.h index 01007ec3b2..a86073ad9b 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -310,10 +310,10 @@ class CProtocol : public QObject bool EvaluateCLConnClientsListMes ( const CHostAddress& InetAddr, const CVector& vecData ); bool EvaluateCLReqConnClientsListMes ( const CHostAddress& InetAddr ); bool EvaluateCLChannelLevelListMes ( const CHostAddress& InetAddr, const CVector& vecData ); - bool EvaluateCLReqChannelLevelListMes ( const CHostAddress& InetAddr ); bool EvaluateCLRegisterServerResp ( const CHostAddress& InetAddr, const CVector& vecData ); bool EvaluateCLReqServerFeaturesMes ( const CHostAddress& InetAddr ); bool EvaluateCLReqWelcomeMessageMes ( const CHostAddress& InetAddr ); + bool EvaluateCLReqChannelLevelListMes ( const CHostAddress& InetAddr ); int iOldRecID; int iOldRecCnt; @@ -380,8 +380,8 @@ public slots: void CLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo ); void CLReqConnClientsList ( CHostAddress InetAddr ); void CLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ); - void CLReqChannelLevelList ( CHostAddress InetAddr ); void CLRegisterServerResp ( CHostAddress InetAddr, ESvrRegResult eStatus ); void CLReqServerFeatures ( CHostAddress InetAddr ); void CLReqWelcomeMessage ( CHostAddress InetAddr ); + void CLReqChannelLevelList ( CHostAddress InetAddr ); }; diff --git a/src/server.cpp b/src/server.cpp index 3780af9d28..a261192a96 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -292,11 +292,12 @@ CServer::CServer ( const int iNewMaxNumChan, QObject::connect ( &ConnLessProtocol, &CProtocol::CLReqConnClientsList, this, &CServer::OnCLReqConnClientsList ); - QObject::connect ( &ConnLessProtocol, &CProtocol::CLReqChannelLevelList, this, &CServer::OnCLReqChannelLevelList ); QObject::connect ( &ConnLessProtocol, &CProtocol::CLReqServerFeatures, this, &CServer::OnCLReqServerFeatures ); QObject::connect ( &ConnLessProtocol, &CProtocol::CLReqWelcomeMessage, this, &CServer::OnCLReqWelcomeMessage ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLReqChannelLevelList, this, &CServer::OnCLReqChannelLevelList ); + QObject::connect ( &ServerListManager, &CServerListManager::SvrRegStatusChanged, this, &CServer::SvrRegStatusChanged ); QObject::connect ( &JamController, &recorder::CJamController::RestartRecorder, this, &CServer::RestartRecorder ); diff --git a/src/server.h b/src/server.h index b986cac2a1..1471eceeb2 100644 --- a/src/server.h +++ b/src/server.h @@ -383,11 +383,6 @@ public slots: void OnCLReqConnClientsList ( CHostAddress InetAddr ) { ConnLessProtocol.CreateCLConnClientsListMes ( InetAddr, CreateChannelList() ); } - void OnCLReqChannelLevelList ( CHostAddress InetAddr ) - { - ConnLessProtocol.CreateCLChannelLevelListMes ( InetAddr, vecChannelLevels, GetNumberOfConnectedClients() ); - } - void OnCLRegisterServerReceived ( CHostAddress InetAddr, CHostAddress LInetAddr, CServerCoreInfo ServerInfo ) { ServerListManager.Append ( InetAddr, LInetAddr, ServerInfo ); @@ -410,6 +405,11 @@ public slots: void OnCLReqWelcomeMessage ( CHostAddress InetAddr ); + void OnCLReqChannelLevelList ( CHostAddress InetAddr ) + { + ConnLessProtocol.CreateCLChannelLevelListMes ( InetAddr, vecChannelLevels, GetNumberOfConnectedClients() ); + } + void OnCLDisconnection ( CHostAddress InetAddr ); void OnAboutToQuit();