diff --git a/docs/JSON-RPC.md b/docs/JSON-RPC.md index 36cc18e087..2edbafbf19 100644 --- a/docs/JSON-RPC.md +++ b/docs/JSON-RPC.md @@ -147,8 +147,10 @@ Results: | result.name | string | The musician’s name. | | result.skillLevel | string | The musician’s skill level (beginner, intermediate, expert, or null). | | result.countryId | number | The musician’s country ID (see QLocale::Country). | +| result.country | string | The musician’s country. | | result.city | string | The musician’s city. | | result.instrumentId | number | The musician’s instrument ID (see CInstPictures::GetTable). | +| result.instrument | string | The musician’s instrument. | | result.skillLevel | string | Your skill level (beginner, intermediate, expert, or null). | @@ -186,6 +188,23 @@ Results: | result.clients | array | The client list. See jamulusclient/clientListReceived for the format. | +### jamulusclient/pollServerList + +Request list of servers in a directory. + +Parameters: + +| Name | Type | Description | +| --- | --- | --- | +| params.directory | string | Socket address of directory to query. Example: anygenre1.jamulus.io:22124 | + +Results: + +| Name | Type | Description | +| --- | --- | --- | +| result | string | "ok" or "error" if bad arguments. | + + ### jamulusclient/sendChatText Sends a chat text message. @@ -445,8 +464,10 @@ Parameters: | params.clients[*].name | string | The musician’s name. | | params.clients[*].skillLevel | string | The musician’s skill level (beginner, intermediate, expert, or null). | | params.clients[*].countryId | number | The musician’s country ID (see QLocale::Country). | +| params.clients[*].country | string | The musician’s country. | | params.clients[*].city | string | The musician’s city. | | params.clients[*].instrumentId | number | The musician’s instrument ID (see CInstPictures::GetTable). | +| params.clients[*].instrument | string | The musician’s instrument. | ### jamulusclient/connected @@ -471,3 +492,43 @@ Parameters: | params | object | No parameters (empty object). | +### jamulusclient/recorderState + +Emitted when the client is connected to a server whose recorder state changes. + +Parameters: + +| Name | Type | Description | +| --- | --- | --- | +| params.state | number | The recorder state. | + + +### jamulusclient/serverInfoReceived + +Emitted when a server info is received. + +Parameters: + +| Name | Type | Description | +| --- | --- | --- | +| params.address | string | The server socket address. | +| params.pingtime | number | The round-trip ping time, in milliseconds. | +| params.numClients | number | The number of clients connected to the server. | + + +### jamulusclient/serverListReceived + +Emitted when the server list is received. + +Parameters: + +| Name | Type | Description | +| --- | --- | --- | +| params.servers | array | The server list. | +| params.servers[*].address | string | Socket address (ip_address:port). | +| params.servers[*].name | string | Server name. | +| params.servers[*].countryId | number | Server country ID (see QLocale::Country). | +| params.servers[*].country | string | Server country. | +| params.servers[*].city | string | Server city. | + + diff --git a/src/client.h b/src/client.h index dea4b4ebbc..7ccf9ab9b9 100644 --- a/src/client.h +++ b/src/client.h @@ -286,6 +286,11 @@ class CClient : public QObject Channel.GetBufErrorRates ( vecErrRates, dLimit, dMaxUpLimit ); } + //### TODO: BEGIN ###// + // Refactor this to use signal/slot mechanism. https://github.com/jamulussoftware/jamulus/pull/3479/files#r1976382416 + CProtocol* getConnLessProtocol() { return &ConnLessProtocol; } + //### TODO: END ###// + // settings CChannelCoreInfo ChannelInfo; QString strClientName; diff --git a/src/clientrpc.cpp b/src/clientrpc.cpp index ee992cbfd9..ff417aa6e3 100644 --- a/src/clientrpc.cpp +++ b/src/clientrpc.cpp @@ -54,8 +54,10 @@ CClientRpc::CClientRpc ( CClient* pClient, CRpcServer* pRpcServer, QObject* pare /// @param {string} params.clients[*].name - The musician’s name. /// @param {string} params.clients[*].skillLevel - The musician’s skill level (beginner, intermediate, expert, or null). /// @param {number} params.clients[*].countryId - The musician’s country ID (see QLocale::Country). + /// @param {string} params.clients[*].country - The musician’s country. /// @param {string} params.clients[*].city - The musician’s city. /// @param {number} params.clients[*].instrumentId - The musician’s instrument ID (see CInstPictures::GetTable). + /// @param {string} params.clients[*].instrument - The musician’s instrument. connect ( pClient, &CClient::ConClientListMesReceived, [=] ( CVector vecChanInfo ) { QJsonArray arrChanInfo; for ( const auto& chanInfo : vecChanInfo ) @@ -65,8 +67,10 @@ CClientRpc::CClientRpc ( CClient* pClient, CRpcServer* pRpcServer, QObject* pare { "name", chanInfo.strName }, { "skillLevel", SerializeSkillLevel ( chanInfo.eSkillLevel ) }, { "countryId", chanInfo.eCountry }, + { "country", QLocale::countryToString ( chanInfo.eCountry ) }, { "city", chanInfo.strCity }, { "instrumentId", chanInfo.iInstrument }, + { "instrument", CInstPictures::GetName ( chanInfo.iInstrument ) }, }; arrChanInfo.append ( objChanInfo ); } @@ -94,11 +98,87 @@ CClientRpc::CClientRpc ( CClient* pClient, CRpcServer* pRpcServer, QObject* pare } ); } ); + /// @rpc_notification jamulusclient/serverListReceived + /// @brief Emitted when the server list is received. + /// @param {array} params.servers - The server list. + /// @param {string} params.servers[*].address - Socket address (ip_address:port). + /// @param {string} params.servers[*].name - Server name. + /// @param {number} params.servers[*].countryId - Server country ID (see QLocale::Country). + /// @param {string} params.servers[*].country - Server country. + /// @param {string} params.servers[*].city - Server city. + connect ( pClient->getConnLessProtocol(), + &CProtocol::CLServerListReceived, + [=] ( CHostAddress /* unused */, CVector vecServerInfo ) { + QJsonArray arrServerInfo; + for ( const auto& serverInfo : vecServerInfo ) + { + QJsonObject objServerInfo{ + { "address", serverInfo.HostAddr.toString() }, + { "name", serverInfo.strName }, + { "countryId", serverInfo.eCountry }, + { "country", QLocale::countryToString ( serverInfo.eCountry ) }, + { "city", serverInfo.strCity }, + }; + arrServerInfo.append ( objServerInfo ); + pClient->CreateCLServerListPingMes ( serverInfo.HostAddr ); + } + pRpcServer->BroadcastNotification ( "jamulusclient/serverListReceived", + QJsonObject{ + { "servers", arrServerInfo }, + } ); + } ); + + /// @rpc_notification jamulusclient/serverInfoReceived + /// @brief Emitted when a server info is received. + /// @param {string} params.address - The server socket address. + /// @param {number} params.pingtime - The round-trip ping time, in milliseconds. + /// @param {number} params.numClients - The number of clients connected to the server. + connect ( pClient, &CClient::CLPingTimeWithNumClientsReceived, [=] ( CHostAddress InetAddr, int iPingTime, int iNumClients ) { + pRpcServer->BroadcastNotification ( + "jamulusclient/serverInfoReceived", + QJsonObject{ { "address", InetAddr.toString() }, { "pingTime", iPingTime }, { "numClients", iNumClients } } ); + } ); + /// @rpc_notification jamulusclient/disconnected /// @brief Emitted when the client is disconnected from the server. /// @param {object} params - No parameters (empty object). connect ( pClient, &CClient::Disconnected, [=]() { pRpcServer->BroadcastNotification ( "jamulusclient/disconnected", QJsonObject{} ); } ); + /// @rpc_notification jamulusclient/recorderState + /// @brief Emitted when the client is connected to a server whose recorder state changes. + /// @param {number} params.state - The recorder state. + connect ( pClient, &CClient::RecorderStateReceived, [=] ( const ERecorderState newRecorderState ) { + pRpcServer->BroadcastNotification ( "jamulusclient/recorderState", QJsonObject{ { "state", newRecorderState } } ); + } ); + + /// @rpc_method jamulusclient/pollServerList + /// @brief Request list of servers in a directory. + /// @param {string} params.directory - Socket address of directory to query. Example: anygenre1.jamulus.io:22124 + /// @result {string} result - "ok" or "error" if bad arguments. + pRpcServer->HandleMethod ( "jamulusclient/pollServerList", [=] ( const QJsonObject& params, QJsonObject& response ) { + auto jsonDirectoryIp = params["directory"]; + if ( !jsonDirectoryIp.isString() ) + { + response["error"] = CRpcServer::CreateJsonRpcError ( CRpcServer::iErrInvalidParams, "Invalid params: directory is not a string" ); + return; + } + + CHostAddress haDirectoryAddress; + if ( NetworkUtil().ParseNetworkAddress ( jsonDirectoryIp.toString(), haDirectoryAddress, false ) ) + { + // send the request for the server list + pClient->CreateCLReqServerListMes ( haDirectoryAddress ); + response["result"] = "ok"; + } + else + { + response["error"] = + CRpcServer::CreateJsonRpcError ( CRpcServer::iErrInvalidParams, "Invalid params: directory is not a valid socket address" ); + } + + response["result"] = "ok"; + } ); + /// @rpc_method jamulus/getMode /// @brief Returns the current mode, i.e. whether Jamulus is running as a server or client. /// @param {object} params - No parameters (empty object). @@ -126,16 +206,20 @@ CClientRpc::CClientRpc ( CClient* pClient, CRpcServer* pRpcServer, QObject* pare /// @result {string} result.name - The musician’s name. /// @result {string} result.skillLevel - The musician’s skill level (beginner, intermediate, expert, or null). /// @result {number} result.countryId - The musician’s country ID (see QLocale::Country). + /// @result {string} result.country - The musician’s country. /// @result {string} result.city - The musician’s city. /// @result {number} result.instrumentId - The musician’s instrument ID (see CInstPictures::GetTable). + /// @result {string} result.instrument - The musician’s instrument. /// @result {string} result.skillLevel - Your skill level (beginner, intermediate, expert, or null). pRpcServer->HandleMethod ( "jamulusclient/getChannelInfo", [=] ( const QJsonObject& params, QJsonObject& response ) { QJsonObject result{ // TODO: We cannot include "id" here is pClient->ChannelInfo is a CChannelCoreInfo which lacks that field. { "name", pClient->ChannelInfo.strName }, { "countryId", pClient->ChannelInfo.eCountry }, + { "country", QLocale::countryToString ( pClient->ChannelInfo.eCountry ) }, { "city", pClient->ChannelInfo.strCity }, { "instrumentId", pClient->ChannelInfo.iInstrument }, + { "instrument", CInstPictures::GetName ( pClient->ChannelInfo.iInstrument ) }, { "skillLevel", SerializeSkillLevel ( pClient->ChannelInfo.eSkillLevel ) }, }; response["result"] = result;