Files
OpenRGB/NetworkServer.cpp
2025-05-02 16:21:39 -05:00

1179 lines
43 KiB
C++

/*---------------------------------------------------------*\
| NetworkServer.cpp |
| |
| OpenRGB SDK network server |
| |
| Adam Honse (CalcProgrammer1) 09 May 2020 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-only |
\*---------------------------------------------------------*/
#include <cstring>
#include "NetworkServer.h"
#include "LogManager.h"
#ifndef WIN32
#include <sys/ioctl.h>
#include <netinet/tcp.h>
#include <sys/types.h>
#include <arpa/inet.h>
#else
#include <ws2tcpip.h>
#endif
#include <memory.h>
#include <errno.h>
#include <stdlib.h>
#include <iostream>
const char yes = 1;
#ifdef WIN32
#include <Windows.h>
#else
#include <unistd.h>
#endif
using namespace std::chrono_literals;
NetworkClientInfo::NetworkClientInfo()
{
client_string = "Client";
client_ip = OPENRGB_SDK_HOST;
client_sock = INVALID_SOCKET;
client_listen_thread = nullptr;
client_protocol_version = 0;
}
NetworkClientInfo::~NetworkClientInfo()
{
if (client_sock != INVALID_SOCKET) {
LOG_INFO("[NetworkServer] Closing server connection: %s", client_ip.c_str());
delete client_listen_thread;
shutdown(client_sock, SD_RECEIVE);
closesocket(client_sock);
}
}
NetworkServer::NetworkServer(std::vector<RGBController *> &control)
: controllers(control)
{
host = OPENRGB_SDK_HOST;
port_num = OPENRGB_SDK_PORT;
server_online = false;
server_listening = false;
legacy_workaround_enabled = false;
for (int i = 0; i < MAXSOCK; i++) {
ConnectionThread[i] = nullptr;
}
profile_manager = nullptr;
}
NetworkServer::~NetworkServer()
{
StopServer();
}
void NetworkServer::ClientInfoChanged()
{
ClientInfoChangeMutex.lock();
/*---------------------------------------------------------*\
| Client info has changed, call the callbacks |
\*---------------------------------------------------------*/
for (unsigned int callback_idx = 0; callback_idx < ClientInfoChangeCallbacks.size();
callback_idx++) {
ClientInfoChangeCallbacks[callback_idx](ClientInfoChangeCallbackArgs[callback_idx]);
}
ClientInfoChangeMutex.unlock();
}
void NetworkServer::DeviceListChanged()
{
/*---------------------------------------------------------*\
| Indicate to the clients that the controller list has |
| changed |
\*---------------------------------------------------------*/
for (unsigned int client_idx = 0; client_idx < ServerClients.size(); client_idx++) {
SendRequest_DeviceListChanged(ServerClients[client_idx]->client_sock);
}
}
void NetworkServer::ServerListeningChanged()
{
ServerListeningChangeMutex.lock();
/*---------------------------------------------------------*\
| Server state has changed, call the callbacks |
\*---------------------------------------------------------*/
for (unsigned int callback_idx = 0; callback_idx < ServerListeningChangeCallbacks.size();
callback_idx++) {
ServerListeningChangeCallbacks[callback_idx](
ServerListeningChangeCallbackArgs[callback_idx]);
}
ServerListeningChangeMutex.unlock();
}
std::string NetworkServer::GetHost()
{
return host;
}
unsigned short NetworkServer::GetPort()
{
return port_num;
}
bool NetworkServer::GetOnline()
{
return server_online;
}
bool NetworkServer::GetListening()
{
return server_listening;
}
unsigned int NetworkServer::GetNumClients()
{
return (unsigned int) ServerClients.size();
}
const char *NetworkServer::GetClientString(unsigned int client_num)
{
const char *result;
ServerClientsMutex.lock();
if (client_num < ServerClients.size()) {
result = ServerClients[client_num]->client_string.c_str();
} else {
result = "";
}
ServerClientsMutex.unlock();
return result;
}
const char *NetworkServer::GetClientIP(unsigned int client_num)
{
const char *result;
ServerClientsMutex.lock();
if (client_num < ServerClients.size()) {
result = ServerClients[client_num]->client_ip.c_str();
} else {
result = "";
}
ServerClientsMutex.unlock();
return result;
}
unsigned int NetworkServer::GetClientProtocolVersion(unsigned int client_num)
{
unsigned int result;
ServerClientsMutex.lock();
if (client_num < ServerClients.size()) {
result = ServerClients[client_num]->client_protocol_version;
} else {
result = 0;
}
ServerClientsMutex.unlock();
return result;
}
void NetworkServer::RegisterClientInfoChangeCallback(NetServerCallback new_callback,
void *new_callback_arg)
{
ClientInfoChangeCallbacks.push_back(new_callback);
ClientInfoChangeCallbackArgs.push_back(new_callback_arg);
}
void NetworkServer::RegisterServerListeningChangeCallback(NetServerCallback new_callback,
void *new_callback_arg)
{
ServerListeningChangeCallbacks.push_back(new_callback);
ServerListeningChangeCallbackArgs.push_back(new_callback_arg);
}
void NetworkServer::SetHost(std::string new_host)
{
if (server_online == false) {
host = new_host;
}
}
void NetworkServer::SetLegacyWorkaroundEnable(bool enable)
{
legacy_workaround_enabled = enable;
}
void NetworkServer::SetPort(unsigned short new_port)
{
if (server_online == false) {
port_num = new_port;
}
}
void NetworkServer::StartServer()
{
int err;
struct addrinfo hints, *res, *result;
/*---------------------------------------------------------*\
| Start a TCP server and launch threads |
\*---------------------------------------------------------*/
char port_str[6];
snprintf(port_str, 6, "%d", port_num);
socket_count = 0;
/*---------------------------------------------------------*\
| Windows requires WSAStartup before using sockets |
\*---------------------------------------------------------*/
#ifdef WIN32
if (WSAStartup(MAKEWORD(2, 2), &wsa) != NO_ERROR) {
WSACleanup();
return;
}
#endif
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
err = getaddrinfo(host.c_str(), port_str, &hints, &result);
if (err) {
LOG_ERROR("[NetworkServer] Unable to get address.");
WSACleanup();
return;
}
/*---------------------------------------------------------*\
| Create a server socket for each address returned. |
\*---------------------------------------------------------*/
for (res = result; res && socket_count < MAXSOCK; res = res->ai_next) {
server_sock[socket_count] = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (server_sock[socket_count] == INVALID_SOCKET) {
LOG_ERROR("[NetworkServer] Network socket could not be created.");
WSACleanup();
return;
}
/*---------------------------------------------------------*\
| Bind the server socket |
\*---------------------------------------------------------*/
if (bind(server_sock[socket_count], res->ai_addr, res->ai_addrlen) == SOCKET_ERROR) {
if (errno == EADDRINUSE) {
LOG_ERROR("[NetworkServer] Could not bind network socket. Is port %hu already "
"being used?",
GetPort());
} else if (errno == EACCES) {
LOG_ERROR(
"[NetworkServer] Could not bind network socket. Access to socket was denied.");
} else if (errno == EBADF) {
LOG_ERROR("[NetworkServer] Could not bind network socket. sockfd is not a valid "
"file descriptor.");
} else if (errno == EINVAL) {
LOG_ERROR("[NetworkServer] Could not bind network socket. The socket is already "
"bound to an address, or addrlen is wrong, or addr is not a valid "
"address for this socket's domain.");
} else if (errno == ENOTSOCK) {
LOG_ERROR("[NetworkServer] Could not bind network socket. The file descriptor "
"sockfd does not refer to a socket.");
} else {
/*---------------------------------------------------------*\
| errno could be a Linux specific error, see: |
| https://man7.org/linux/man-pages/man2/bind.2.html |
\*---------------------------------------------------------*/
LOG_ERROR("[NetworkServer] Could not bind network socket. Error code: %d.", errno);
}
WSACleanup();
return;
}
/*---------------------------------------------------------*\
| Set socket options - no delay |
\*---------------------------------------------------------*/
setsockopt(server_sock[socket_count], IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
socket_count += 1;
}
freeaddrinfo(result);
server_online = true;
/*---------------------------------------------------------*\
| Start the connection thread |
\*---------------------------------------------------------*/
for (int curr_socket = 0; curr_socket < socket_count; curr_socket++) {
ConnectionThread[curr_socket] = new std::thread(&NetworkServer::ConnectionThreadFunction,
this,
curr_socket);
ConnectionThread[curr_socket]->detach();
}
}
void NetworkServer::StopServer()
{
int curr_socket;
server_online = false;
ServerClientsMutex.lock();
for (unsigned int client_idx = 0; client_idx < ServerClients.size(); client_idx++) {
delete ServerClients[client_idx];
}
ServerClients.clear();
for (curr_socket = 0; curr_socket < socket_count; curr_socket++) {
shutdown(server_sock[curr_socket], SD_RECEIVE);
closesocket(server_sock[curr_socket]);
}
ServerClientsMutex.unlock();
for (curr_socket = 0; curr_socket < socket_count; curr_socket++) {
if (ConnectionThread[curr_socket]) {
delete ConnectionThread[curr_socket];
ConnectionThread[curr_socket] = nullptr;
}
}
socket_count = 0;
/*---------------------------------------------------------*\
| Client info has changed, call the callbacks |
\*---------------------------------------------------------*/
ClientInfoChanged();
}
void NetworkServer::ConnectionThreadFunction(int socket_idx)
{
/*---------------------------------------------------------*\
| This thread handles client connections |
\*---------------------------------------------------------*/
LOG_INFO("[NetworkServer] Network connection thread started on port %hu", GetPort());
while (server_online == true) {
/*---------------------------------------------------------*\
| Create new socket for client connection |
\*---------------------------------------------------------*/
NetworkClientInfo *client_info = new NetworkClientInfo();
/*---------------------------------------------------------*\
| Listen for incoming client connection on the server |
| socket. This call blocks until a connection is |
| established |
\*---------------------------------------------------------*/
if (listen(server_sock[socket_idx], 10) < 0) {
LOG_INFO("[NetworkServer] Connection thread closed");
server_online = false;
return;
}
server_listening = true;
ServerListeningChanged();
/*---------------------------------------------------------*\
| Accept the client connection |
\*---------------------------------------------------------*/
client_info->client_sock = accept_select((int) server_sock[socket_idx]);
if (client_info->client_sock < 0) {
LOG_INFO("[NetworkServer] Connection thread closed");
server_online = false;
server_listening = false;
ServerListeningChanged();
return;
}
/*---------------------------------------------------------*\
| Get the new client socket and store it in the clients |
| vector |
\*---------------------------------------------------------*/
u_long arg = 0;
ioctlsocket(client_info->client_sock, FIONBIO, &arg);
setsockopt(client_info->client_sock, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
/*---------------------------------------------------------*\
| Discover the remote hosts IP |
\*---------------------------------------------------------*/
struct sockaddr_storage tmp_addr;
char ipstr[INET6_ADDRSTRLEN];
socklen_t len;
len = sizeof(tmp_addr);
getpeername(client_info->client_sock, (struct sockaddr *) &tmp_addr, &len);
if (tmp_addr.ss_family == AF_INET) {
struct sockaddr_in *s_4 = (struct sockaddr_in *) &tmp_addr;
inet_ntop(AF_INET, &s_4->sin_addr, ipstr, sizeof(ipstr));
client_info->client_ip = ipstr;
} else {
struct sockaddr_in6 *s_6 = (struct sockaddr_in6 *) &tmp_addr;
inet_ntop(AF_INET6, &s_6->sin6_addr, ipstr, sizeof(ipstr));
client_info->client_ip = ipstr;
}
/*---------------------------------------------------------*\
| We need to lock before the thread could possibly finish |
\*---------------------------------------------------------*/
ServerClientsMutex.lock();
/*---------------------------------------------------------*\
| Start a listener thread for the new client socket |
\*---------------------------------------------------------*/
client_info->client_listen_thread = new std::thread(&NetworkServer::ListenThreadFunction,
this,
client_info);
client_info->client_listen_thread->detach();
ServerClients.push_back(client_info);
ServerClientsMutex.unlock();
/*---------------------------------------------------------*\
| Client info has changed, call the callbacks |
\*---------------------------------------------------------*/
ClientInfoChanged();
}
LOG_INFO("[NetworkServer] Connection thread closed");
server_online = false;
server_listening = false;
ServerListeningChanged();
}
int NetworkServer::accept_select(int sockfd)
{
fd_set set;
struct timeval timeout;
while (1) {
timeout.tv_sec = TCP_TIMEOUT_SECONDS;
timeout.tv_usec = 0;
FD_ZERO(&set);
FD_SET(sockfd, &set);
int rv = select(sockfd + 1, &set, NULL, NULL, &timeout);
if (rv == SOCKET_ERROR || server_online == false) {
return -1;
} else if (rv == 0) {
continue;
} else {
return (accept((int) sockfd, NULL, NULL));
}
}
}
int NetworkServer::recv_select(SOCKET s, char *buf, int len, int flags)
{
fd_set set;
struct timeval timeout;
while (1) {
timeout.tv_sec = TCP_TIMEOUT_SECONDS;
timeout.tv_usec = 0;
FD_ZERO(&set);
FD_SET(s, &set);
int rv = select((int) s + 1, &set, NULL, NULL, &timeout);
if (rv == SOCKET_ERROR || server_online == false) {
return 0;
} else if (rv == 0) {
continue;
} else {
return (recv(s, buf, len, flags));
}
}
}
void NetworkServer::ListenThreadFunction(NetworkClientInfo *client_info)
{
SOCKET client_sock = client_info->client_sock;
LOG_INFO("[NetworkServer] Network server started");
/*---------------------------------------------------------*\
| This thread handles messages received from clients |
\*---------------------------------------------------------*/
while (server_online == true) {
NetPacketHeader header;
int bytes_read = 0;
char *data = NULL;
for (unsigned int i = 0; i < 4; i++) {
/*---------------------------------------------------------*\
| Read byte of magic |
\*---------------------------------------------------------*/
bytes_read = recv_select(client_sock, &header.pkt_magic[i], 1, 0);
if (bytes_read <= 0) {
LOG_ERROR("[NetworkServer] recv_select failed receiving magic, closing listener");
goto listen_done;
}
/*---------------------------------------------------------*\
| Test characters of magic "ORGB" |
\*---------------------------------------------------------*/
if (header.pkt_magic[i] != openrgb_sdk_magic[i]) {
LOG_ERROR("[NetworkServer] Invalid magic received");
continue;
}
}
/*---------------------------------------------------------*\
| If we get to this point, the magic is correct. Read the |
| rest of the header |
\*---------------------------------------------------------*/
bytes_read = 0;
do {
int tmp_bytes_read = 0;
tmp_bytes_read = recv_select(client_sock,
(char *) &header.pkt_dev_idx + bytes_read,
sizeof(header) - sizeof(header.pkt_magic) - bytes_read,
0);
bytes_read += tmp_bytes_read;
if (tmp_bytes_read <= 0) {
LOG_ERROR("[NetworkServer] recv_select failed receiving header, closing listener");
goto listen_done;
}
} while (bytes_read != sizeof(header) - sizeof(header.pkt_magic));
/*---------------------------------------------------------*\
| Header received, now receive the data |
\*---------------------------------------------------------*/
bytes_read = 0;
if (header.pkt_size > 0) {
data = new char[header.pkt_size];
do {
int tmp_bytes_read = 0;
tmp_bytes_read = recv_select(client_sock,
&data[(unsigned int) bytes_read],
header.pkt_size - bytes_read,
0);
if (tmp_bytes_read <= 0) {
LOG_ERROR(
"[NetworkServer] recv_select failed receiving data, closing listener");
goto listen_done;
}
bytes_read += tmp_bytes_read;
} while ((unsigned int) bytes_read < header.pkt_size);
}
/*---------------------------------------------------------*\
| Entire request received, select functionality based on |
| request ID |
\*---------------------------------------------------------*/
switch (header.pkt_id) {
case NET_PACKET_ID_REQUEST_CONTROLLER_COUNT:
SendReply_ControllerCount(client_sock);
break;
case NET_PACKET_ID_REQUEST_CONTROLLER_DATA: {
unsigned int protocol_version = 0;
if (header.pkt_size == sizeof(unsigned int)) {
memcpy(&protocol_version, data, sizeof(unsigned int));
}
SendReply_ControllerData(client_sock, header.pkt_dev_idx, protocol_version);
} break;
case NET_PACKET_ID_REQUEST_PROTOCOL_VERSION:
SendReply_ProtocolVersion(client_sock);
ProcessRequest_ClientProtocolVersion(client_sock, header.pkt_size, data);
break;
case NET_PACKET_ID_SET_CLIENT_NAME:
if (data == NULL) {
break;
}
ProcessRequest_ClientString(client_sock, header.pkt_size, data);
break;
case NET_PACKET_ID_RGBCONTROLLER_RESIZEZONE:
if (data == NULL) {
break;
}
if ((header.pkt_dev_idx < controllers.size())
&& (header.pkt_size == (2 * sizeof(int)))) {
int zone;
int new_size;
memcpy(&zone, data, sizeof(int));
memcpy(&new_size, data + sizeof(int), sizeof(int));
controllers[header.pkt_dev_idx]->ResizeZone(zone, new_size);
profile_manager->SaveProfile("sizes", true);
}
break;
case NET_PACKET_ID_RGBCONTROLLER_UPDATELEDS:
if (data == NULL) {
break;
}
/*---------------------------------------------------------*\
| Verify the color description size (first 4 bytes of data) |
| matches the packet size in the header |
| |
| If protocol version is 4 or below and the legacy SDK |
| compatibility workaround is enabled, ignore this check. |
| This allows backwards compatibility with old versions of |
| SDK applications that didn't properly implement the size |
| field. |
\*---------------------------------------------------------*/
if ((header.pkt_size == *((unsigned int *) data))
|| ((client_info->client_protocol_version <= 4) && (legacy_workaround_enabled))) {
if (header.pkt_dev_idx < controllers.size()) {
controllers[header.pkt_dev_idx]->SetColorDescription((unsigned char *) data);
controllers[header.pkt_dev_idx]->UpdateLEDs();
}
} else {
LOG_ERROR("[NetworkServer] UpdateLEDs packet has invalid size. Packet size: %d, "
"Data size: %d",
header.pkt_size,
*((unsigned int *) data));
goto listen_done;
}
break;
case NET_PACKET_ID_RGBCONTROLLER_UPDATEZONELEDS:
if (data == NULL) {
break;
}
/*---------------------------------------------------------*\
| Verify the color description size (first 4 bytes of data) |
| matches the packet size in the header |
| |
| If protocol version is 4 or below and the legacy SDK |
| compatibility workaround is enabled, ignore this check. |
| This allows backwards compatibility with old versions of |
| SDK applications that didn't properly implement the size |
| field. |
\*---------------------------------------------------------*/
if ((header.pkt_size == *((unsigned int *) data))
|| ((client_info->client_protocol_version <= 4) && (legacy_workaround_enabled))) {
if (header.pkt_dev_idx < controllers.size()) {
int zone;
memcpy(&zone, &data[sizeof(unsigned int)], sizeof(int));
controllers[header.pkt_dev_idx]->SetZoneColorDescription((unsigned char *) data);
controllers[header.pkt_dev_idx]->UpdateZoneLEDs(zone);
}
} else {
LOG_ERROR("[NetworkServer] UpdateZoneLEDs packet has invalid size. Packet size: "
"%d, Data size: %d",
header.pkt_size,
*((unsigned int *) data));
goto listen_done;
}
break;
case NET_PACKET_ID_RGBCONTROLLER_UPDATESINGLELED:
if (data == NULL) {
break;
}
/*---------------------------------------------------------*\
| Verify the single LED color description size (8 bytes) |
| matches the packet size in the header |
\*---------------------------------------------------------*/
if (header.pkt_size == (sizeof(int) + sizeof(RGBColor))) {
if (header.pkt_dev_idx < controllers.size()) {
int led;
memcpy(&led, data, sizeof(int));
controllers[header.pkt_dev_idx]->SetSingleLEDColorDescription(
(unsigned char *) data);
controllers[header.pkt_dev_idx]->UpdateSingleLED(led);
}
} else {
LOG_ERROR("[NetworkServer] UpdateSingleLED packet has invalid size. Packet size: "
"%d, Data size: %d",
header.pkt_size,
(sizeof(int) + sizeof(RGBColor)));
goto listen_done;
}
break;
case NET_PACKET_ID_RGBCONTROLLER_SETCUSTOMMODE:
if (header.pkt_dev_idx < controllers.size()) {
controllers[header.pkt_dev_idx]->SetCustomMode();
}
break;
case NET_PACKET_ID_RGBCONTROLLER_UPDATEMODE:
if (data == NULL) {
break;
}
/*---------------------------------------------------------*\
| Verify the mode description size (first 4 bytes of data) |
| matches the packet size in the header |
| |
| If protocol version is 4 or below and the legacy SDK |
| compatibility workaround is enabled, ignore this check. |
| This allows backwards compatibility with old versions of |
| SDK applications that didn't properly implement the size |
| field. |
\*---------------------------------------------------------*/
if ((header.pkt_size == *((unsigned int *) data))
|| ((client_info->client_protocol_version <= 4) && (legacy_workaround_enabled))) {
if (header.pkt_dev_idx < controllers.size()) {
controllers[header.pkt_dev_idx]
->SetModeDescription((unsigned char *) data,
client_info->client_protocol_version);
controllers[header.pkt_dev_idx]->UpdateMode();
}
} else {
LOG_ERROR("[NetworkServer] UpdateMode packet has invalid size. Packet size: %d, "
"Data size: %d",
header.pkt_size,
*((unsigned int *) data));
goto listen_done;
}
break;
case NET_PACKET_ID_RGBCONTROLLER_SAVEMODE:
if (data == NULL) {
break;
}
/*---------------------------------------------------------*\
| Verify the mode description size (first 4 bytes of data) |
| matches the packet size in the header |
| |
| If protocol version is 4 or below and the legacy SDK |
| compatibility workaround is enabled, ignore this check. |
| This allows backwards compatibility with old versions of |
| SDK applications that didn't properly implement the size |
| field. |
\*---------------------------------------------------------*/
if ((header.pkt_size == *((unsigned int *) data))
|| ((client_info->client_protocol_version <= 4) && (legacy_workaround_enabled))) {
if (header.pkt_dev_idx < controllers.size()) {
controllers[header.pkt_dev_idx]
->SetModeDescription((unsigned char *) data,
client_info->client_protocol_version);
controllers[header.pkt_dev_idx]->SaveMode();
}
}
break;
case NET_PACKET_ID_REQUEST_PROFILE_LIST:
SendReply_ProfileList(client_sock);
break;
case NET_PACKET_ID_REQUEST_SAVE_PROFILE:
if (data == NULL) {
break;
}
if (profile_manager) {
std::string profile_name;
profile_name.assign(data, header.pkt_size);
profile_manager->SaveProfile(profile_name);
}
break;
case NET_PACKET_ID_REQUEST_LOAD_PROFILE:
if (data == NULL) {
break;
}
if (profile_manager) {
std::string profile_name;
profile_name.assign(data, header.pkt_size);
profile_manager->LoadProfile(profile_name);
}
for (RGBController *controller : controllers) {
controller->UpdateLEDs();
}
break;
case NET_PACKET_ID_REQUEST_DELETE_PROFILE:
if (data == NULL) {
break;
}
if (profile_manager) {
std::string profile_name;
profile_name.assign(data, header.pkt_size);
profile_manager->DeleteProfile(profile_name);
}
break;
case NET_PACKET_ID_REQUEST_PLUGIN_LIST:
SendReply_PluginList(client_sock);
break;
case NET_PACKET_ID_PLUGIN_SPECIFIC: {
unsigned int plugin_pkt_type = *((unsigned int *) (data));
unsigned int plugin_pkt_size = header.pkt_size - (sizeof(unsigned int));
unsigned char *plugin_data = (unsigned char *) (data + sizeof(unsigned int));
if (header.pkt_dev_idx < plugins.size()) {
NetworkPlugin plugin = plugins[header.pkt_dev_idx];
unsigned char *output = plugin.callback(plugin.callback_arg,
plugin_pkt_type,
plugin_data,
&plugin_pkt_size);
if (output != nullptr) {
SendReply_PluginSpecific(client_sock, plugin_pkt_type, output, plugin_pkt_size);
}
}
break;
} break;
case NET_PACKET_ID_RGBCONTROLLER_CLEARSEGMENTS:
if (data == NULL) {
break;
}
if ((header.pkt_dev_idx < controllers.size()) && (header.pkt_size == sizeof(int))) {
int zone;
memcpy(&zone, data, sizeof(int));
controllers[header.pkt_dev_idx]->ClearSegments(zone);
profile_manager->SaveProfile("sizes", true);
}
break;
case NET_PACKET_ID_RGBCONTROLLER_ADDSEGMENT: {
/*---------------------------------------------------------*\
| Verify the segment description size (first 4 bytes of |
| data) matches the packet size in the header |
\*---------------------------------------------------------*/
if (header.pkt_size == *((unsigned int *) data)) {
if (header.pkt_dev_idx < controllers.size()) {
controllers[header.pkt_dev_idx]->SetSegmentDescription((unsigned char *) data);
profile_manager->SaveProfile("sizes", true);
}
}
} break;
}
delete[] data;
}
listen_done:
ServerClientsMutex.lock();
for (unsigned int this_idx = 0; this_idx < ServerClients.size(); this_idx++) {
if (ServerClients[this_idx] == client_info) {
delete client_info;
ServerClients.erase(ServerClients.begin() + this_idx);
break;
}
}
client_info = nullptr;
ServerClientsMutex.unlock();
/*---------------------------------------------------------*\
| Client info has changed, call the callbacks |
\*---------------------------------------------------------*/
ClientInfoChanged();
}
void NetworkServer::ProcessRequest_ClientProtocolVersion(SOCKET client_sock,
unsigned int data_size,
char *data)
{
unsigned int protocol_version = 0;
if (data_size == sizeof(unsigned int) && (data != NULL)) {
memcpy(&protocol_version, data, sizeof(unsigned int));
}
if (protocol_version > OPENRGB_SDK_PROTOCOL_VERSION) {
protocol_version = OPENRGB_SDK_PROTOCOL_VERSION;
}
ServerClientsMutex.lock();
for (unsigned int this_idx = 0; this_idx < ServerClients.size(); this_idx++) {
if (ServerClients[this_idx]->client_sock == client_sock) {
ServerClients[this_idx]->client_protocol_version = protocol_version;
break;
}
}
ServerClientsMutex.unlock();
/*---------------------------------------------------------*\
| Client info has changed, call the callbacks |
\*---------------------------------------------------------*/
ClientInfoChanged();
}
void NetworkServer::ProcessRequest_ClientString(SOCKET client_sock,
unsigned int data_size,
char *data)
{
ServerClientsMutex.lock();
for (unsigned int this_idx = 0; this_idx < ServerClients.size(); this_idx++) {
if (ServerClients[this_idx]->client_sock == client_sock) {
ServerClients[this_idx]->client_string.assign(data, data_size);
break;
}
}
ServerClientsMutex.unlock();
/*---------------------------------------------------------*\
| Client info has changed, call the callbacks |
\*---------------------------------------------------------*/
ClientInfoChanged();
}
void NetworkServer::SendReply_ControllerCount(SOCKET client_sock)
{
NetPacketHeader reply_hdr;
unsigned int reply_data;
InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_REQUEST_CONTROLLER_COUNT, sizeof(unsigned int));
reply_data = (unsigned int) controllers.size();
send(client_sock, (const char *) &reply_hdr, sizeof(NetPacketHeader), 0);
send(client_sock, (const char *) &reply_data, sizeof(unsigned int), 0);
}
void NetworkServer::SendReply_ControllerData(SOCKET client_sock,
unsigned int dev_idx,
unsigned int protocol_version)
{
if (dev_idx < controllers.size()) {
NetPacketHeader reply_hdr;
unsigned char *reply_data = controllers[dev_idx]->GetDeviceDescription(protocol_version);
unsigned int reply_size;
memcpy(&reply_size, reply_data, sizeof(reply_size));
InitNetPacketHeader(&reply_hdr, dev_idx, NET_PACKET_ID_REQUEST_CONTROLLER_DATA, reply_size);
send(client_sock, (const char *) &reply_hdr, sizeof(NetPacketHeader), 0);
send(client_sock, (const char *) reply_data, reply_size, 0);
delete[] reply_data;
}
}
void NetworkServer::SendReply_ProtocolVersion(SOCKET client_sock)
{
NetPacketHeader reply_hdr;
unsigned int reply_data;
InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_REQUEST_PROTOCOL_VERSION, sizeof(unsigned int));
reply_data = OPENRGB_SDK_PROTOCOL_VERSION;
send(client_sock, (const char *) &reply_hdr, sizeof(NetPacketHeader), 0);
send(client_sock, (const char *) &reply_data, sizeof(unsigned int), 0);
}
void NetworkServer::SendRequest_DeviceListChanged(SOCKET client_sock)
{
NetPacketHeader pkt_hdr;
InitNetPacketHeader(&pkt_hdr, 0, NET_PACKET_ID_DEVICE_LIST_UPDATED, 0);
send(client_sock, (char *) &pkt_hdr, sizeof(NetPacketHeader), 0);
}
void NetworkServer::SendReply_ProfileList(SOCKET client_sock)
{
if (!profile_manager) {
return;
}
NetPacketHeader reply_hdr;
unsigned char *reply_data = profile_manager->GetProfileListDescription();
unsigned int reply_size;
memcpy(&reply_size, reply_data, sizeof(reply_size));
InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_REQUEST_PROFILE_LIST, reply_size);
send(client_sock, (const char *) &reply_hdr, sizeof(NetPacketHeader), 0);
send(client_sock, (const char *) reply_data, reply_size, 0);
}
void NetworkServer::SendReply_PluginList(SOCKET client_sock)
{
unsigned int data_size = 0;
unsigned int data_ptr = 0;
/*---------------------------------------------------------*\
| Calculate data size |
\*---------------------------------------------------------*/
unsigned short num_plugins = (unsigned short) plugins.size();
data_size += sizeof(data_size);
data_size += sizeof(num_plugins);
for (unsigned int i = 0; i < num_plugins; i++) {
data_size += sizeof(unsigned short) * 3;
data_size += (unsigned int) strlen(plugins[i].name.c_str()) + 1;
data_size += (unsigned int) strlen(plugins[i].description.c_str()) + 1;
data_size += (unsigned int) strlen(plugins[i].version.c_str()) + 1;
data_size += sizeof(unsigned int) * 2;
}
/*---------------------------------------------------------*\
| Create data buffer |
\*---------------------------------------------------------*/
unsigned char *data_buf = new unsigned char[data_size];
/*---------------------------------------------------------*\
| Copy in data size |
\*---------------------------------------------------------*/
memcpy(&data_buf[data_ptr], &data_size, sizeof(data_size));
data_ptr += sizeof(data_size);
/*---------------------------------------------------------*\
| Copy in num_plugins |
\*---------------------------------------------------------*/
memcpy(&data_buf[data_ptr], &num_plugins, sizeof(num_plugins));
data_ptr += sizeof(num_plugins);
for (unsigned int i = 0; i < num_plugins; i++) {
/*---------------------------------------------------------*\
| Copy in plugin name (size+data) |
\*---------------------------------------------------------*/
unsigned short str_len = (unsigned short) strlen(plugins[i].name.c_str()) + 1;
memcpy(&data_buf[data_ptr], &str_len, sizeof(unsigned short));
data_ptr += sizeof(unsigned short);
strcpy((char *) &data_buf[data_ptr], plugins[i].name.c_str());
data_ptr += str_len;
/*---------------------------------------------------------*\
| Copy in plugin description (size+data) |
\*---------------------------------------------------------*/
str_len = (unsigned short) strlen(plugins[i].description.c_str()) + 1;
memcpy(&data_buf[data_ptr], &str_len, sizeof(unsigned short));
data_ptr += sizeof(unsigned short);
strcpy((char *) &data_buf[data_ptr], plugins[i].description.c_str());
data_ptr += str_len;
/*---------------------------------------------------------*\
| Copy in plugin version (size+data) |
\*---------------------------------------------------------*/
str_len = (unsigned short) strlen(plugins[i].version.c_str()) + 1;
memcpy(&data_buf[data_ptr], &str_len, sizeof(unsigned short));
data_ptr += sizeof(unsigned short);
strcpy((char *) &data_buf[data_ptr], plugins[i].version.c_str());
data_ptr += str_len;
/*---------------------------------------------------------*\
| Copy in plugin index (data) |
\*---------------------------------------------------------*/
memcpy(&data_buf[data_ptr], &i, sizeof(unsigned int));
data_ptr += sizeof(unsigned int);
/*---------------------------------------------------------*\
| Copy in plugin sdk version (data) |
\*---------------------------------------------------------*/
memcpy(&data_buf[data_ptr], &plugins[i].protocol_version, sizeof(unsigned int));
data_ptr += sizeof(unsigned int);
}
NetPacketHeader reply_hdr;
unsigned int reply_size;
memcpy(&reply_size, data_buf, sizeof(reply_size));
InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_REQUEST_PLUGIN_LIST, reply_size);
send(client_sock, (const char *) &reply_hdr, sizeof(NetPacketHeader), 0);
send(client_sock, (const char *) data_buf, reply_size, 0);
delete[] data_buf;
}
void NetworkServer::SendReply_PluginSpecific(SOCKET client_sock,
unsigned int pkt_type,
unsigned char *data,
unsigned int data_size)
{
NetPacketHeader reply_hdr;
InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_PLUGIN_SPECIFIC, data_size + sizeof(pkt_type));
send(client_sock, (const char *) &reply_hdr, sizeof(NetPacketHeader), 0);
send(client_sock, (const char *) &pkt_type, sizeof(pkt_type), 0);
send(client_sock, (const char *) data, data_size, 0);
delete[] data;
}
void NetworkServer::SetProfileManager(ProfileManagerInterface *profile_manager_pointer)
{
profile_manager = profile_manager_pointer;
}
void NetworkServer::RegisterPlugin(NetworkPlugin plugin)
{
plugins.push_back(plugin);
}
void NetworkServer::UnregisterPlugin(std::string plugin_name)
{
for (std::vector<NetworkPlugin>::iterator it = plugins.begin(); it != plugins.end(); it++) {
if (it->name == plugin_name) {
plugins.erase(it);
break;
}
}
}