Some changes
This commit is contained in:
parent
7fdd272d76
commit
7dcf4a54ef
@ -1 +1 @@
|
|||||||
Subproject commit 32b1ef973d44c668fc2e74735491db3896a32c2d
|
Subproject commit fe633dc99a6ea91c2c0a8cb13a51a7587a54cb2e
|
@ -257,7 +257,7 @@ target_link_libraries(PermMapHelper
|
|||||||
|
|
||||||
SET(CPACK_PACKAGE_VERSION_MAJOR "1")
|
SET(CPACK_PACKAGE_VERSION_MAJOR "1")
|
||||||
SET(CPACK_PACKAGE_VERSION_MINOR "4")
|
SET(CPACK_PACKAGE_VERSION_MINOR "4")
|
||||||
SET(CPACK_PACKAGE_VERSION_PATCH "18")
|
SET(CPACK_PACKAGE_VERSION_PATCH "19")
|
||||||
if (BUILD_TYPE_NAME EQUAL OFF)
|
if (BUILD_TYPE_NAME EQUAL OFF)
|
||||||
SET(CPACK_PACKAGE_VERSION_DATA "beta")
|
SET(CPACK_PACKAGE_VERSION_DATA "beta")
|
||||||
elseif (BUILD_TYPE_NAME STREQUAL "")
|
elseif (BUILD_TYPE_NAME STREQUAL "")
|
||||||
|
@ -994,6 +994,10 @@ std::shared_ptr<Properties> DatabaseHelper::loadClientProperties(const std::shar
|
|||||||
column = "client_nickname";
|
column = "client_nickname";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case property::CONNECTION_CLIENT_IP:
|
||||||
|
column = "client_ip";
|
||||||
|
break;
|
||||||
|
|
||||||
case property::CLIENT_LASTCONNECTED:
|
case property::CLIENT_LASTCONNECTED:
|
||||||
column = "client_last_connected";
|
column = "client_last_connected";
|
||||||
break;
|
break;
|
||||||
@ -1326,10 +1330,10 @@ void DatabaseHelper::listDatabaseClients(
|
|||||||
client.client_ip = values[index++];
|
client.client_ip = values[index++];
|
||||||
|
|
||||||
assert(names[index] == "client_created");
|
assert(names[index] == "client_created");
|
||||||
client.client_last_connected = values[index++];
|
client.client_created = values[index++];
|
||||||
|
|
||||||
assert(names[index] == "client_last_connected");
|
assert(names[index] == "client_last_connected");
|
||||||
client.client_created = values[index++];
|
client.client_last_connected = values[index++];
|
||||||
|
|
||||||
assert(names[index] == "client_total_connections");
|
assert(names[index] == "client_total_connections");
|
||||||
client.client_total_connections = values[index++];
|
client.client_total_connections = values[index++];
|
||||||
|
@ -36,6 +36,14 @@ ConnectedClient::~ConnectedClient() {
|
|||||||
memtrack::freed<ConnectedClient>(this);
|
memtrack::freed<ConnectedClient>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ConnectedClient::loadDataForCurrentServer() {
|
||||||
|
auto result = DataClient::loadDataForCurrentServer();
|
||||||
|
if(!result) return false;
|
||||||
|
|
||||||
|
this->properties()[property::CONNECTION_CLIENT_IP] = this->getLoggingPeerIp();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<ConnectionInfoData> ConnectedClient::request_connection_info(const std::shared_ptr<ConnectedClient> &receiver, bool& send_temp) {
|
std::shared_ptr<ConnectionInfoData> ConnectedClient::request_connection_info(const std::shared_ptr<ConnectedClient> &receiver, bool& send_temp) {
|
||||||
auto& info = this->connection_info;
|
auto& info = this->connection_info;
|
||||||
|
|
||||||
|
@ -375,6 +375,8 @@ namespace ts {
|
|||||||
std::weak_ptr<MusicClient> subscribed_bot;
|
std::weak_ptr<MusicClient> subscribed_bot;
|
||||||
std::weak_ptr<ts::music::Playlist> _subscribed_playlist{};
|
std::weak_ptr<ts::music::Playlist> _subscribed_playlist{};
|
||||||
|
|
||||||
|
bool loadDataForCurrentServer() override;
|
||||||
|
|
||||||
virtual void tick(const std::chrono::system_clock::time_point &time);
|
virtual void tick(const std::chrono::system_clock::time_point &time);
|
||||||
//Locked by everything who has something todo with command handling
|
//Locked by everything who has something todo with command handling
|
||||||
threads::Mutex command_lock; /* Note: This mutex must be recursive! */
|
threads::Mutex command_lock; /* Note: This mutex must be recursive! */
|
||||||
|
@ -124,7 +124,7 @@ enum WhisperType {
|
|||||||
CHANNEL_COMMANDER = 2,
|
CHANNEL_COMMANDER = 2,
|
||||||
ALL = 3,
|
ALL = 3,
|
||||||
|
|
||||||
ECHO_TEXT = 0x10,
|
ECHO = 0x10,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum WhisperTarget {
|
enum WhisperTarget {
|
||||||
@ -184,7 +184,7 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
deque<shared_ptr<SpeakingClient>> available_clients;
|
deque<shared_ptr<SpeakingClient>> available_clients;
|
||||||
if(type == WhisperType::ECHO_TEXT) {
|
if(type == WhisperType::ECHO) {
|
||||||
available_clients.push_back(dynamic_pointer_cast<SpeakingClient>(this->ref()));
|
available_clients.push_back(dynamic_pointer_cast<SpeakingClient>(this->ref()));
|
||||||
} else {
|
} else {
|
||||||
for(const auto& client : this->server->getClients()) {
|
for(const auto& client : this->server->getClients()) {
|
||||||
@ -269,21 +269,22 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo
|
|||||||
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(cl);
|
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(cl);
|
||||||
return !speakingClient->shouldReceiveVoiceWhisper(self_lock);
|
return !speakingClient->shouldReceiveVoiceWhisper(self_lock);
|
||||||
}), available_clients.end());
|
}), available_clients.end());
|
||||||
}
|
|
||||||
|
|
||||||
if(available_clients.empty()) {
|
if(available_clients.empty()) {
|
||||||
if(update_whisper_error(this->speak_last_no_whisper_target)) {
|
if(update_whisper_error(this->speak_last_no_whisper_target)) {
|
||||||
command_result result{error::whisper_no_targets};
|
command_result result{error::whisper_no_targets};
|
||||||
this->notifyError(result);
|
this->notifyError(result);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
if(available_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save<size_t>()) {
|
||||||
if(available_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save<size_t>()) {
|
if(update_whisper_error(this->speak_last_too_many_whisper_targets)) {
|
||||||
if(update_whisper_error(this->speak_last_too_many_whisper_targets)) {
|
command_result result{error::whisper_too_many_targets};
|
||||||
command_result result{error::whisper_too_many_targets};
|
this->notifyError(result);
|
||||||
this->notifyError(result);
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Create the packet data
|
//Create the packet data
|
||||||
|
@ -3,98 +3,96 @@
|
|||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
#include "ConnectedClient.h"
|
#include "ConnectedClient.h"
|
||||||
|
|
||||||
namespace ts {
|
namespace ts::server {
|
||||||
namespace server {
|
class VirtualServer;
|
||||||
class VirtualServer;
|
class SpeakingClient : public ConnectedClient {
|
||||||
class SpeakingClient : public ConnectedClient {
|
public:
|
||||||
public:
|
struct VoicePacketFlags {
|
||||||
struct VoicePacketFlags {
|
bool encrypted : 1;
|
||||||
bool encrypted : 1;
|
bool head : 1;
|
||||||
bool head : 1;
|
bool fragmented : 1; /* used by MONO. IDK What this is */
|
||||||
bool fragmented : 1; /* used by MONO. IDK What this is */
|
bool new_protocol : 1;
|
||||||
bool new_protocol : 1;
|
char _unused : 4;
|
||||||
char _unused : 4;
|
|
||||||
|
|
||||||
VoicePacketFlags() : encrypted{false}, head{false}, fragmented{false}, new_protocol{false}, _unused{0} { }
|
VoicePacketFlags() : encrypted{false}, head{false}, fragmented{false}, new_protocol{false}, _unused{0} { }
|
||||||
};
|
};
|
||||||
static_assert(sizeof(VoicePacketFlags) == 1);
|
static_assert(sizeof(VoicePacketFlags) == 1);
|
||||||
|
|
||||||
enum HandshakeState {
|
enum HandshakeState {
|
||||||
BEGIN,
|
BEGIN,
|
||||||
IDENTITY_PROOF,
|
IDENTITY_PROOF,
|
||||||
SUCCEEDED
|
SUCCEEDED
|
||||||
};
|
};
|
||||||
enum IdentityType : uint8_t {
|
enum IdentityType : uint8_t {
|
||||||
TEASPEAK_FORUM,
|
TEASPEAK_FORUM,
|
||||||
TEAMSPEAK,
|
TEAMSPEAK,
|
||||||
NICKNAME,
|
NICKNAME,
|
||||||
|
|
||||||
UNSET = 0xff
|
UNSET = 0xff
|
||||||
};
|
};
|
||||||
|
|
||||||
SpeakingClient(sql::SqlManager* a, const std::shared_ptr<VirtualServer>& b) : ConnectedClient(a, b) {
|
SpeakingClient(sql::SqlManager* a, const std::shared_ptr<VirtualServer>& b) : ConnectedClient(a, b) {
|
||||||
speak_begin = std::chrono::system_clock::now();
|
speak_begin = std::chrono::system_clock::now();
|
||||||
speak_last_packet = std::chrono::system_clock::now();
|
speak_last_packet = std::chrono::system_clock::now();
|
||||||
};
|
};
|
||||||
~SpeakingClient() override = default;
|
~SpeakingClient() override = default;
|
||||||
|
|
||||||
//Voice
|
//Voice
|
||||||
virtual void send_voice_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0;
|
virtual void send_voice_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0;
|
||||||
virtual bool shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender);
|
virtual bool shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender);
|
||||||
|
|
||||||
//Whisper
|
//Whisper
|
||||||
bool shouldReceiveVoiceWhisper(const std::shared_ptr<ConnectedClient> &sender);
|
bool shouldReceiveVoiceWhisper(const std::shared_ptr<ConnectedClient> &sender);
|
||||||
virtual void send_voice_whisper_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0;
|
virtual void send_voice_whisper_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0;
|
||||||
|
|
||||||
inline std::chrono::milliseconds takeSpokenTime() {
|
inline std::chrono::milliseconds takeSpokenTime() {
|
||||||
auto time = this->speak_time;
|
auto time = this->speak_time;
|
||||||
this->speak_time = std::chrono::milliseconds(0);
|
this->speak_time = std::chrono::milliseconds(0);
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
protected:
|
protected:
|
||||||
void tick(const std::chrono::system_clock::time_point &time) override;
|
void tick(const std::chrono::system_clock::time_point &time) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
public:
|
public:
|
||||||
void updateChannelClientProperties(bool channel_lock, bool notify) override;
|
void updateChannelClientProperties(bool channel_lock, bool notify) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
command_result handleCommand(Command &command) override;
|
command_result handleCommand(Command &command) override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void handlePacketVoice(const pipes::buffer_view&, bool head, bool fragmented);
|
void handlePacketVoice(const pipes::buffer_view&, bool head, bool fragmented);
|
||||||
virtual void handlePacketVoiceWhisper(const pipes::buffer_view&, bool /* new */);
|
virtual void handlePacketVoiceWhisper(const pipes::buffer_view&, bool /* new */);
|
||||||
|
|
||||||
void processJoin();
|
void processJoin();
|
||||||
void processLeave();
|
void processLeave();
|
||||||
|
|
||||||
virtual command_result handleCommandHandshakeBegin(Command&);
|
virtual command_result handleCommandHandshakeBegin(Command&);
|
||||||
virtual command_result handleCommandHandshakeIdentityProof(Command &);
|
virtual command_result handleCommandHandshakeIdentityProof(Command &);
|
||||||
virtual command_result handleCommandClientInit(Command&);
|
virtual command_result handleCommandClientInit(Command&);
|
||||||
|
|
||||||
void triggerVoiceEnd();
|
void triggerVoiceEnd();
|
||||||
inline void updateSpeak(bool onlyUpdate, const std::chrono::system_clock::time_point &time);
|
inline void updateSpeak(bool onlyUpdate, const std::chrono::system_clock::time_point &time);
|
||||||
std::chrono::milliseconds speak_accuracy = std::chrono::seconds{1};
|
std::chrono::milliseconds speak_accuracy = std::chrono::seconds{1};
|
||||||
|
|
||||||
threads::Mutex speak_lock;
|
threads::Mutex speak_lock;
|
||||||
std::chrono::milliseconds speak_time = std::chrono::milliseconds{0};
|
std::chrono::milliseconds speak_time = std::chrono::milliseconds{0};
|
||||||
std::chrono::system_clock::time_point speak_begin;
|
std::chrono::system_clock::time_point speak_begin;
|
||||||
std::chrono::system_clock::time_point speak_last_packet;
|
std::chrono::system_clock::time_point speak_last_packet;
|
||||||
|
|
||||||
std::chrono::system_clock::time_point speak_last_no_whisper_target;
|
std::chrono::system_clock::time_point speak_last_no_whisper_target;
|
||||||
std::chrono::system_clock::time_point speak_last_too_many_whisper_targets;
|
std::chrono::system_clock::time_point speak_last_too_many_whisper_targets;
|
||||||
|
|
||||||
permission::v2::PermissionFlaggedValue max_idle_time{permission::v2::empty_permission_flagged_value};
|
permission::v2::PermissionFlaggedValue max_idle_time{permission::v2::empty_permission_flagged_value};
|
||||||
struct {
|
struct {
|
||||||
HandshakeState state{HandshakeState::BEGIN};
|
HandshakeState state{HandshakeState::BEGIN};
|
||||||
|
|
||||||
IdentityType identityType{IdentityType::UNSET};
|
IdentityType identityType{IdentityType::UNSET};
|
||||||
std::string proof_message;
|
std::string proof_message;
|
||||||
//TeamSpeak
|
//TeamSpeak
|
||||||
std::shared_ptr<ecc_key> identityKey;
|
std::shared_ptr<ecc_key> identityKey;
|
||||||
//TeaSpeak
|
//TeaSpeak
|
||||||
std::shared_ptr<Json::Value> identityData;
|
std::shared_ptr<Json::Value> identityData;
|
||||||
} handshake;
|
} handshake;
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
}
|
@ -475,6 +475,22 @@ bool QueryClient::handleMessage(const pipes::buffer_view& message) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(auto non_escape{command.find_first_not_of('\r')}; non_escape == std::string::npos) {
|
||||||
|
logTrace(LOG_QUERY, "[{}:{}] Got query idle command (\\r)", this->getLoggingPeerIp(), this->getPeerPort());
|
||||||
|
CMD_RESET_IDLE; //if idle time over 5 min than connection drop
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
command = command.substr(non_escape);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(auto non_escape{command.find_first_not_of('\n')}; non_escape == std::string::npos) {
|
||||||
|
logTrace(LOG_QUERY, "[{}:{}] Got query idle command (\\n)", this->getLoggingPeerIp(), this->getPeerPort());
|
||||||
|
CMD_RESET_IDLE; //if idle time over 5 min than connection drop
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
command = command.substr(non_escape);
|
||||||
|
}
|
||||||
|
|
||||||
if((uint8_t) command[0] == 255) {
|
if((uint8_t) command[0] == 255) {
|
||||||
string commands{};
|
string commands{};
|
||||||
|
|
||||||
|
@ -145,7 +145,7 @@ std::string VoiceBridge::generate_answer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoiceBridge::execute_tick() {
|
void VoiceBridge::execute_tick() {
|
||||||
if(!this->_voice_channel) {
|
if(!this->voice_channel_) {
|
||||||
if(this->offer_timestamp.time_since_epoch().count() > 0 && this->offer_timestamp + chrono::seconds{20} < chrono::system_clock::now()) {
|
if(this->offer_timestamp.time_since_epoch().count() > 0 && this->offer_timestamp + chrono::seconds{20} < chrono::system_clock::now()) {
|
||||||
this->offer_timestamp = chrono::system_clock::time_point();
|
this->offer_timestamp = chrono::system_clock::time_point();
|
||||||
this->connection->callback_setup_fail(rtc::PeerConnection::ConnectionComponent::BASE, "setup timeout");
|
this->connection->callback_setup_fail(rtc::PeerConnection::ConnectionComponent::BASE, "setup timeout");
|
||||||
@ -182,26 +182,47 @@ void VoiceBridge::handle_media_stream(const std::shared_ptr<rtc::Channel> &undef
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoiceBridge::handle_data_channel(const std::shared_ptr<rtc::DataChannel> &channel) {
|
void VoiceBridge::handle_data_channel(const std::shared_ptr<rtc::DataChannel> &channel) {
|
||||||
if(channel->lable() == "main") {
|
if(channel->lable() == "main" || channel->lable() == "voice") {
|
||||||
this->_voice_channel = channel;
|
this->voice_channel_ = channel;
|
||||||
debugMessage(this->server_id(), "{} Got voice channel!", CLIENT_STR_LOG_PREFIX_(this->owner()));
|
debugMessage(this->server_id(), "{} Got voice channel!", CLIENT_STR_LOG_PREFIX_(this->owner()));
|
||||||
this->callback_initialized();
|
this->callback_initialized();
|
||||||
|
|
||||||
|
weak_ptr<rtc::DataChannel> weak_channel = channel;
|
||||||
|
channel->callback_binary = [&, weak_channel](const pipes::buffer_view& buffer) {
|
||||||
|
if(buffer.length() < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this->callback_voice_data(buffer.view(2), buffer[0] == 1, buffer[1] == 1); /* buffer.substr(2), buffer[0] == 1, buffer[1] == 1 */
|
||||||
|
};
|
||||||
|
|
||||||
|
channel->callback_close = [&, weak_channel] {
|
||||||
|
auto channel_ref = weak_channel.lock();
|
||||||
|
if(channel_ref == this->voice_channel_) {
|
||||||
|
this->voice_channel_ = nullptr;
|
||||||
|
//TODO may callback?
|
||||||
|
debugMessage(this->server_id(), "{} Voice channel disconnected!", CLIENT_STR_LOG_PREFIX_(this->owner()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if(channel->lable() == "voice-whisper") {
|
||||||
|
this->voice_whisper_channel_ = channel;
|
||||||
|
debugMessage(this->server_id(), "{} Got voice whisper channel", CLIENT_STR_LOG_PREFIX_(this->owner()));
|
||||||
|
|
||||||
|
weak_ptr<rtc::DataChannel> weak_channel = channel;
|
||||||
|
channel->callback_binary = [&, weak_channel](const pipes::buffer_view& buffer) {
|
||||||
|
if(buffer.length() < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this->callback_voice_whisper_data(buffer.view(2), buffer[0] == 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
channel->callback_close = [&, weak_channel] {
|
||||||
|
auto channel_ref = weak_channel.lock();
|
||||||
|
if(channel_ref == this->voice_whisper_channel_) {
|
||||||
|
this->voice_whisper_channel_ = nullptr;
|
||||||
|
debugMessage(this->server_id(), "{} Voice whisper channel has been closed.", CLIENT_STR_LOG_PREFIX_(this->owner()));
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
weak_ptr<rtc::DataChannel> weak_channel = channel;
|
|
||||||
channel->callback_binary = [&, weak_channel](const pipes::buffer_view& buffer) {
|
|
||||||
if(buffer.length() < 2)
|
|
||||||
return;
|
|
||||||
this->callback_voice_data(buffer.view(2), buffer[0] == 1, buffer[1] == 1); /* buffer.substr(2), buffer[0] == 1, buffer[1] == 1 */
|
|
||||||
};
|
|
||||||
|
|
||||||
channel->callback_close = [&, channel] {
|
|
||||||
if(channel == this->_voice_channel) {
|
|
||||||
this->_voice_channel = nullptr;
|
|
||||||
//TODO may callback?
|
|
||||||
debugMessage(this->server_id(), "{} Voice channel disconnected!", CLIENT_STR_LOG_PREFIX_(this->owner()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoiceBridge::handle_audio_data(const std::shared_ptr<rtc::MediaChannel> &channel, const pipes::buffer_view &data, size_t payload_offset) {
|
void VoiceBridge::handle_audio_data(const std::shared_ptr<rtc::MediaChannel> &channel, const pipes::buffer_view &data, size_t payload_offset) {
|
||||||
|
@ -12,12 +12,14 @@ namespace ts {
|
|||||||
class VoiceBridge {
|
class VoiceBridge {
|
||||||
public:
|
public:
|
||||||
typedef std::function<void(const pipes::buffer_view&, bool, bool)> cb_voice_data;
|
typedef std::function<void(const pipes::buffer_view&, bool, bool)> cb_voice_data;
|
||||||
|
typedef std::function<void(const pipes::buffer_view&, bool)> cb_voice_whisper_data;
|
||||||
typedef std::function<void(const rtc::IceCandidate&)> cb_ice_candidate;
|
typedef std::function<void(const rtc::IceCandidate&)> cb_ice_candidate;
|
||||||
typedef std::function<void(const std::string& /* sdpMid */, int /* sdpMLineIndex */)> cb_ice_candidate_finish;
|
typedef std::function<void(const std::string& /* sdpMid */, int /* sdpMLineIndex */)> cb_ice_candidate_finish;
|
||||||
typedef std::function<void()> cb_initialized;
|
typedef std::function<void()> cb_initialized;
|
||||||
typedef std::function<void()> cb_failed;
|
typedef std::function<void()> cb_failed;
|
||||||
|
|
||||||
std::shared_ptr<rtc::DataChannel> voice_channel() { return this->_voice_channel; }
|
std::shared_ptr<rtc::DataChannel> voice_channel() { return this->voice_channel_; }
|
||||||
|
std::shared_ptr<rtc::DataChannel> voice_whisper_channel() { return this->voice_whisper_channel_; }
|
||||||
|
|
||||||
explicit VoiceBridge(const std::shared_ptr<server::WebClient>&);
|
explicit VoiceBridge(const std::shared_ptr<server::WebClient>&);
|
||||||
virtual ~VoiceBridge();
|
virtual ~VoiceBridge();
|
||||||
@ -31,6 +33,7 @@ namespace ts {
|
|||||||
cb_ice_candidate callback_ice_candidate;
|
cb_ice_candidate callback_ice_candidate;
|
||||||
cb_ice_candidate_finish callback_ice_candidate_finished;
|
cb_ice_candidate_finish callback_ice_candidate_finished;
|
||||||
cb_voice_data callback_voice_data;
|
cb_voice_data callback_voice_data;
|
||||||
|
cb_voice_whisper_data callback_voice_whisper_data;
|
||||||
cb_initialized callback_initialized;
|
cb_initialized callback_initialized;
|
||||||
cb_failed callback_failed;
|
cb_failed callback_failed;
|
||||||
|
|
||||||
@ -48,7 +51,10 @@ namespace ts {
|
|||||||
std::weak_ptr<server::WebClient> _owner;
|
std::weak_ptr<server::WebClient> _owner;
|
||||||
std::chrono::system_clock::time_point offer_timestamp;
|
std::chrono::system_clock::time_point offer_timestamp;
|
||||||
std::unique_ptr<rtc::PeerConnection> connection;
|
std::unique_ptr<rtc::PeerConnection> connection;
|
||||||
std::shared_ptr<rtc::DataChannel> _voice_channel;
|
|
||||||
|
std::shared_ptr<rtc::DataChannel> voice_channel_;
|
||||||
|
std::shared_ptr<rtc::DataChannel> voice_whisper_channel_;
|
||||||
|
|
||||||
std::weak_ptr<rtc::AudioChannel> _audio_channel;
|
std::weak_ptr<rtc::AudioChannel> _audio_channel;
|
||||||
struct {
|
struct {
|
||||||
uint16_t packet_id = 0;
|
uint16_t packet_id = 0;
|
||||||
|
@ -156,22 +156,29 @@ void WebClient::sendJson(const Json::Value& json) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebClient::sendCommand(const ts::Command &command, bool low) {
|
void WebClient::sendCommand(const ts::Command &command, bool low) {
|
||||||
Json::Value value = command.buildJson();
|
if(this->allow_raw_commands) {
|
||||||
value["type"] = "command";
|
Json::Value value{};
|
||||||
this->sendJson(value);
|
value["type"] = "command-raw";
|
||||||
|
value["payload"] = command.build();
|
||||||
|
this->sendJson(value);
|
||||||
|
} else {
|
||||||
|
Json::Value value = command.buildJson();
|
||||||
|
value["type"] = "command";
|
||||||
|
this->sendJson(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebClient::sendCommand(const ts::command_builder &command, bool low) {
|
void WebClient::sendCommand(const ts::command_builder &command, bool low) {
|
||||||
#if false
|
if(this->allow_raw_commands) {
|
||||||
Json::Value value{};
|
Json::Value value{};
|
||||||
value["type"] = "command2";
|
value["type"] = "command-raw";
|
||||||
value["payload"] = command.build();
|
value["payload"] = command.build();
|
||||||
this->sendJson(value);
|
this->sendJson(value);
|
||||||
#else
|
} else {
|
||||||
auto data = command.build();
|
auto data = command.build();
|
||||||
Command parsed_command = Command::parse(pipes::buffer_view{data.data(), data.length()}, true, false);
|
Command parsed_command = Command::parse(pipes::buffer_view{data.data(), data.length()}, true, false);
|
||||||
this->sendCommand(parsed_command, low);
|
this->sendCommand(parsed_command, low);
|
||||||
#endif
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebClient::close_connection(const std::chrono::system_clock::time_point& timeout) {
|
bool WebClient::close_connection(const std::chrono::system_clock::time_point& timeout) {
|
||||||
@ -418,7 +425,7 @@ void WebClient::disconnectFinal() {
|
|||||||
this->handle->unregisterConnection(static_pointer_cast<WebClient>(self_lock));
|
this->handle->unregisterConnection(static_pointer_cast<WebClient>(self_lock));
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::CharReaderBuilder json_reader_builder = []{
|
Json::CharReaderBuilder json_reader_builder = []() noexcept {
|
||||||
Json::CharReaderBuilder reader_builder;
|
Json::CharReaderBuilder reader_builder;
|
||||||
|
|
||||||
return reader_builder;
|
return reader_builder;
|
||||||
@ -432,7 +439,8 @@ void WebClient::handleMessage(const std::string &message) {
|
|||||||
unique_ptr<Json::CharReader> reader{json_reader_builder.newCharReader()};
|
unique_ptr<Json::CharReader> reader{json_reader_builder.newCharReader()};
|
||||||
|
|
||||||
string error_message;
|
string error_message;
|
||||||
if(!reader->parse(message.data(),message.data() + message.length(), &val, &error_message)) throw Json::Exception("Could not parse payload! (" + error_message + ")");
|
if(!reader->parse(message.data(),message.data() + message.length(), &val, &error_message))
|
||||||
|
throw Json::Exception("Could not parse payload! (" + error_message + ")");
|
||||||
} catch (const std::exception& ex) {
|
} catch (const std::exception& ex) {
|
||||||
logError(this->server->getServerId(), "Could not parse web message! Message: " + string(ex.what()));
|
logError(this->server->getServerId(), "Could not parse web message! Message: " + string(ex.what()));
|
||||||
logTrace(this->server->getServerId(), "Payload: " + message);
|
logTrace(this->server->getServerId(), "Payload: " + message);
|
||||||
@ -458,7 +466,7 @@ void WebClient::handleMessage(const std::string &message) {
|
|||||||
} else if(val["type"].asString() == "WebRTC") {
|
} else if(val["type"].asString() == "WebRTC") {
|
||||||
auto subType = val["request"].asString();
|
auto subType = val["request"].asString();
|
||||||
if(subType == "create") {
|
if(subType == "create") {
|
||||||
unique_lock voice_bridge_lock(this->voice_bridge_lock);
|
std::unique_lock voice_bridge_lock_{this->voice_bridge_lock};
|
||||||
if(this->voice_bridge) {
|
if(this->voice_bridge) {
|
||||||
logError(this->server->getServerId(), "[{}] Tried to register a WebRTC channel twice!", CLIENT_STR_LOG_PREFIX_(this));
|
logError(this->server->getServerId(), "[{}] Tried to register a WebRTC channel twice!", CLIENT_STR_LOG_PREFIX_(this));
|
||||||
|
|
||||||
@ -467,14 +475,19 @@ void WebClient::handleMessage(const std::string &message) {
|
|||||||
lock = nullptr;
|
lock = nullptr;
|
||||||
}).detach();
|
}).detach();
|
||||||
}
|
}
|
||||||
//TODO test if bridge already exists!
|
|
||||||
this->voice_bridge = make_unique<web::VoiceBridge>(dynamic_pointer_cast<WebClient>(_this.lock())); //FIXME Add config
|
this->voice_bridge = make_unique<web::VoiceBridge>(dynamic_pointer_cast<WebClient>(this->ref())); //FIXME Add config
|
||||||
|
|
||||||
this->voice_bridge->callback_voice_data = [&](const pipes::buffer_view& buffer, bool a, bool b) {
|
this->voice_bridge->callback_voice_data = [&](const pipes::buffer_view& buffer, bool a, bool b) {
|
||||||
/* may somehow get the "real" packet size? */
|
/* may somehow get the "real" packet size? */
|
||||||
this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::VOICE, buffer.length());
|
this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::VOICE, buffer.length());
|
||||||
this->handlePacketVoice(buffer, a, b);
|
this->handlePacketVoice(buffer, a, b);
|
||||||
};
|
};
|
||||||
|
this->voice_bridge->callback_voice_whisper_data = [&](const pipes::buffer_view& buffer, bool a) {
|
||||||
|
/* may somehow get the "real" packet size? */
|
||||||
|
this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::VOICE, buffer.length());
|
||||||
|
this->handlePacketVoiceWhisper(buffer, a);
|
||||||
|
};
|
||||||
this->voice_bridge->callback_initialized = [&](){
|
this->voice_bridge->callback_initialized = [&](){
|
||||||
debugMessage(this->getServerId(), "{} Voice bridge initialized!", CLIENT_STR_LOG_PREFIX);
|
debugMessage(this->getServerId(), "{} Voice bridge initialized!", CLIENT_STR_LOG_PREFIX);
|
||||||
};
|
};
|
||||||
@ -521,7 +534,7 @@ void WebClient::handleMessage(const std::string &message) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto vbp = &*this->voice_bridge;
|
auto vbp = &*this->voice_bridge;
|
||||||
voice_bridge_lock.unlock();
|
voice_bridge_lock_.unlock();
|
||||||
shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
|
shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
|
||||||
|
|
||||||
if(vbp != &*this->voice_bridge) {
|
if(vbp != &*this->voice_bridge) {
|
||||||
@ -628,6 +641,8 @@ void WebClient::handleMessage(const std::string &message) {
|
|||||||
}
|
}
|
||||||
this->js_ping.last_response = system_clock::now();
|
this->js_ping.last_response = system_clock::now();
|
||||||
this->js_ping.value = duration_cast<nanoseconds>(this->js_ping.last_response - this->js_ping.last_request);
|
this->js_ping.value = duration_cast<nanoseconds>(this->js_ping.last_response - this->js_ping.last_request);
|
||||||
|
} else if(val["type"].asString() == "enable-raw-commands") {
|
||||||
|
this->allow_raw_commands = true;
|
||||||
}
|
}
|
||||||
} catch (const std::exception& ex) {
|
} catch (const std::exception& ex) {
|
||||||
logError(this->server->getServerId(), "Could not handle json packet! Message {}", ex.what());
|
logError(this->server->getServerId(), "Could not handle json packet! Message {}", ex.what());
|
||||||
@ -664,21 +679,18 @@ command_result WebClient::handleCommandClientInit(Command &command) {
|
|||||||
bool WebClient::shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender) {
|
bool WebClient::shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender) {
|
||||||
shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
|
shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
|
||||||
if(!this->voice_bridge || !this->voice_bridge->voice_channel()) return false;
|
if(!this->voice_bridge || !this->voice_bridge->voice_channel()) return false;
|
||||||
|
|
||||||
return SpeakingClient::shouldReceiveVoice(sender);
|
return SpeakingClient::shouldReceiveVoice(sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebClient::handlePacketVoiceWhisper(const pipes::buffer_view &string, bool flag) {
|
|
||||||
shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
|
|
||||||
if(!this->voice_bridge || !this->voice_bridge->voice_channel()) return;
|
|
||||||
SpeakingClient::handlePacketVoiceWhisper(string, flag);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebClient::send_voice_packet(const pipes::buffer_view &view, const SpeakingClient::VoicePacketFlags &flags) {
|
void WebClient::send_voice_packet(const pipes::buffer_view &view, const SpeakingClient::VoicePacketFlags &flags) {
|
||||||
shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
|
std::shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
|
||||||
if(this->voice_bridge) {
|
if(this->voice_bridge) {
|
||||||
auto channel = this->voice_bridge->voice_channel();
|
auto channel = this->voice_bridge->voice_channel();
|
||||||
if(channel) {
|
if(channel) {
|
||||||
channel->send(view);
|
channel->send(view);
|
||||||
|
read_voice_bridge_lock.unlock();
|
||||||
|
|
||||||
/* may somehow get the "real" packet size? */
|
/* may somehow get the "real" packet size? */
|
||||||
this->connectionStatistics->logOutgoingPacket(stats::ConnectionStatistics::category::VOICE, view.length());
|
this->connectionStatistics->logOutgoingPacket(stats::ConnectionStatistics::category::VOICE, view.length());
|
||||||
}
|
}
|
||||||
@ -686,6 +698,15 @@ void WebClient::send_voice_packet(const pipes::buffer_view &view, const Speaking
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebClient::send_voice_whisper_packet(const pipes::buffer_view &view, const SpeakingClient::VoicePacketFlags &flags) {
|
void WebClient::send_voice_whisper_packet(const pipes::buffer_view &view, const SpeakingClient::VoicePacketFlags &flags) {
|
||||||
logError(this->server->getServerId(), "Web client got whisper packet");
|
std::shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
|
||||||
//As well log the data!
|
if(this->voice_bridge) {
|
||||||
|
auto channel = this->voice_bridge->voice_whisper_channel();
|
||||||
|
if(channel) {
|
||||||
|
channel->send(view);
|
||||||
|
read_voice_bridge_lock.unlock();
|
||||||
|
|
||||||
|
/* may somehow get the "real" packet size? */
|
||||||
|
this->connectionStatistics->logOutgoingPacket(stats::ConnectionStatistics::category::VOICE, view.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,105 +14,103 @@
|
|||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
#include <EventLoop.h>
|
#include <EventLoop.h>
|
||||||
|
|
||||||
namespace ts {
|
namespace ts::server {
|
||||||
namespace server {
|
class WebControlServer;
|
||||||
class WebControlServer;
|
|
||||||
|
|
||||||
class WebClient : public SpeakingClient {
|
class WebClient : public SpeakingClient {
|
||||||
friend class WebControlServer;
|
friend class WebControlServer;
|
||||||
public:
|
public:
|
||||||
WebClient(WebControlServer*, int socketFd);
|
WebClient(WebControlServer*, int socketFd);
|
||||||
~WebClient() override;
|
~WebClient() override;
|
||||||
|
|
||||||
void sendJson(const Json::Value&);
|
void sendJson(const Json::Value&);
|
||||||
void sendCommand(const ts::Command &command, bool low = false) override;
|
void sendCommand(const ts::Command &command, bool low) override;
|
||||||
void sendCommand(const ts::command_builder &command, bool low) override;
|
void sendCommand(const ts::command_builder &command, bool low) override;
|
||||||
|
|
||||||
bool disconnect(const std::string &reason) override;
|
bool disconnect(const std::string &reason) override;
|
||||||
bool close_connection(const std::chrono::system_clock::time_point& timeout = std::chrono::system_clock::time_point()) override;
|
bool close_connection(const std::chrono::system_clock::time_point& timeout = std::chrono::system_clock::time_point()) override;
|
||||||
|
|
||||||
bool shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender) override;
|
bool shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender) override;
|
||||||
|
|
||||||
inline std::chrono::nanoseconds client_ping() { return this->client_ping_layer_7(); }
|
|
||||||
inline std::chrono::nanoseconds client_ping_layer_5() { return this->ping.value; }
|
|
||||||
inline std::chrono::nanoseconds client_ping_layer_7() { return this->js_ping.value; }
|
|
||||||
protected:
|
|
||||||
void handlePacketVoiceWhisper(const pipes::buffer_view &string, bool) override;
|
|
||||||
|
|
||||||
protected:
|
inline std::chrono::nanoseconds client_ping() { return this->client_ping_layer_7(); }
|
||||||
void tick(const std::chrono::system_clock::time_point&) override; /* Every 500ms */
|
inline std::chrono::nanoseconds client_ping_layer_5() { return this->ping.value; }
|
||||||
|
inline std::chrono::nanoseconds client_ping_layer_7() { return this->js_ping.value; }
|
||||||
|
|
||||||
void applySelfLock(const std::shared_ptr<WebClient> &cl){ _this = cl; }
|
protected:
|
||||||
private:
|
void tick(const std::chrono::system_clock::time_point&) override; /* Every 500ms */
|
||||||
WebControlServer* handle;
|
|
||||||
std::chrono::time_point<std::chrono::system_clock> connectedTimestamp;
|
|
||||||
|
|
||||||
std::shared_mutex voice_bridge_lock;
|
void applySelfLock(const std::shared_ptr<WebClient> &cl){ _this = cl; }
|
||||||
std::unique_ptr<web::VoiceBridge> voice_bridge;
|
private:
|
||||||
int file_descriptor;
|
WebControlServer* handle;
|
||||||
|
std::chrono::time_point<std::chrono::system_clock> connectedTimestamp;
|
||||||
|
|
||||||
std::mutex event_lock;
|
std::shared_mutex voice_bridge_lock;
|
||||||
::event* readEvent;
|
std::unique_ptr<web::VoiceBridge> voice_bridge;
|
||||||
::event* writeEvent;
|
int file_descriptor;
|
||||||
|
|
||||||
struct {
|
std::mutex event_lock;
|
||||||
uint8_t current_id = 0;
|
::event* readEvent;
|
||||||
std::chrono::system_clock::time_point last_request;
|
::event* writeEvent;
|
||||||
std::chrono::system_clock::time_point last_response;
|
|
||||||
|
|
||||||
std::chrono::nanoseconds value{};
|
struct {
|
||||||
std::chrono::nanoseconds timeout{2000};
|
uint8_t current_id = 0;
|
||||||
} ping;
|
std::chrono::system_clock::time_point last_request;
|
||||||
|
std::chrono::system_clock::time_point last_response;
|
||||||
|
|
||||||
struct {
|
std::chrono::nanoseconds value{};
|
||||||
uint8_t current_id = 0;
|
std::chrono::nanoseconds timeout{2000};
|
||||||
std::chrono::system_clock::time_point last_request;
|
} ping;
|
||||||
std::chrono::system_clock::time_point last_response;
|
|
||||||
|
|
||||||
std::chrono::nanoseconds value{};
|
struct {
|
||||||
std::chrono::nanoseconds timeout{2000};
|
uint8_t current_id = 0;
|
||||||
} js_ping;
|
std::chrono::system_clock::time_point last_request;
|
||||||
|
std::chrono::system_clock::time_point last_response;
|
||||||
|
|
||||||
std::mutex queue_lock;
|
std::chrono::nanoseconds value{};
|
||||||
std::deque<pipes::buffer> queue_read;
|
std::chrono::nanoseconds timeout{2000};
|
||||||
std::deque<pipes::buffer> queue_write;
|
} js_ping;
|
||||||
threads::Mutex execute_lock; /* needs to be recursive! */
|
|
||||||
|
|
||||||
std::thread flush_thread;
|
std::mutex queue_lock;
|
||||||
std::recursive_mutex close_lock;
|
std::deque<pipes::buffer> queue_read;
|
||||||
private:
|
std::deque<pipes::buffer> queue_write;
|
||||||
void initialize();
|
threads::Mutex execute_lock; /* needs to be recursive! */
|
||||||
|
|
||||||
bool ssl_detected = false;
|
std::thread flush_thread;
|
||||||
bool ssl_encrypted = true;
|
std::recursive_mutex close_lock;
|
||||||
pipes::SSL ssl_handler;
|
private:
|
||||||
pipes::WebSocket ws_handler;
|
void initialize();
|
||||||
void handleMessageRead(int, short, void*);
|
|
||||||
void handleMessageWrite(int, short, void*);
|
|
||||||
void enqueue_raw_packet(const pipes::buffer_view& /* buffer */);
|
|
||||||
|
|
||||||
void processNextMessage(const std::chrono::system_clock::time_point& /* scheduled */);
|
bool allow_raw_commands{false};
|
||||||
void registerMessageProcess();
|
bool ssl_detected{false};
|
||||||
|
bool ssl_encrypted{true};
|
||||||
|
pipes::SSL ssl_handler;
|
||||||
|
pipes::WebSocket ws_handler;
|
||||||
|
void handleMessageRead(int, short, void*);
|
||||||
|
void handleMessageWrite(int, short, void*);
|
||||||
|
void enqueue_raw_packet(const pipes::buffer_view& /* buffer */);
|
||||||
|
|
||||||
std::shared_ptr<event::ProxiedEventEntry<WebClient>> event_handle_packet;
|
void processNextMessage(const std::chrono::system_clock::time_point& /* scheduled */);
|
||||||
|
void registerMessageProcess();
|
||||||
|
|
||||||
//WS events
|
std::shared_ptr<event::ProxiedEventEntry<WebClient>> event_handle_packet;
|
||||||
void onWSConnected();
|
|
||||||
void onWSDisconnected(const std::string& reason);
|
|
||||||
void onWSMessage(const pipes::WSMessage&);
|
|
||||||
protected:
|
|
||||||
void disconnectFinal();
|
|
||||||
void handleMessage(const std::string &);
|
|
||||||
|
|
||||||
public:
|
//WS events
|
||||||
void send_voice_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override;
|
void onWSConnected();
|
||||||
void send_voice_whisper_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override;
|
void onWSDisconnected(const std::string& reason);
|
||||||
|
void onWSMessage(const pipes::WSMessage&);
|
||||||
|
protected:
|
||||||
|
void disconnectFinal();
|
||||||
|
void handleMessage(const std::string &);
|
||||||
|
|
||||||
protected:
|
public:
|
||||||
|
void send_voice_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override;
|
||||||
|
void send_voice_whisper_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override;
|
||||||
|
|
||||||
command_result handleCommand(Command &command) override;
|
protected:
|
||||||
command_result handleCommandClientInit(Command &command) override;
|
|
||||||
};
|
command_result handleCommand(Command &command) override;
|
||||||
}
|
command_result handleCommandClientInit(Command &command) override;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
@ -79,9 +79,19 @@ do { \
|
|||||||
} \
|
} \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
std::string replace_all(std::string str, const std::string& from, const std::string& to) {
|
||||||
|
size_t start_pos = 0;
|
||||||
|
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||||
|
str.replace(start_pos, from.length(), to);
|
||||||
|
start_pos += to.length();
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T, size_t N>
|
template <typename T, size_t N>
|
||||||
inline bool execute_commands(sql::SqlManager* sql, std::string& error, const std::array<T, N>& commands) {
|
inline bool execute_commands(sql::SqlManager* sql, std::string& error, const std::array<T, N>& commands) {
|
||||||
std::string insert_or_ignore{sql->getType() == sql::TYPE_SQLITE ? "INSERT OR IGNORE" : "INSERT IGNORE"};
|
std::string insert_or_ignore{sql->getType() == sql::TYPE_SQLITE ? "INSERT OR IGNORE" : "INSERT IGNORE"};
|
||||||
|
std::string auto_increment{sql->getType() == sql::TYPE_SQLITE ? "AUTOINCREMENT" : "AUTO_INCREMENT"};
|
||||||
for(const auto& cmd : commands) {
|
for(const auto& cmd : commands) {
|
||||||
std::string command{cmd};
|
std::string command{cmd};
|
||||||
|
|
||||||
@ -89,8 +99,8 @@ inline bool execute_commands(sql::SqlManager* sql, std::string& error, const std
|
|||||||
return !std::isspace(ch);
|
return !std::isspace(ch);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if(command.starts_with("[INSERT_OR_IGNORE]"))
|
command = replace_all(command, "[INSERT_OR_IGNORE]", insert_or_ignore);
|
||||||
command = insert_or_ignore + command.substr(18);
|
command = replace_all(command, "[AUTO_INCREMENT]", auto_increment);
|
||||||
|
|
||||||
auto result = sql::command(sql, command).execute();
|
auto result = sql::command(sql, command).execute();
|
||||||
if(!result) {
|
if(!result) {
|
||||||
@ -451,7 +461,7 @@ bool SqlDataManager::update_database(std::string &error) {
|
|||||||
case 12: {
|
case 12: {
|
||||||
constexpr static std::string_view kCreateClientsV2{R"(
|
constexpr static std::string_view kCreateClientsV2{R"(
|
||||||
CREATE TABLE `clients_v2` (
|
CREATE TABLE `clients_v2` (
|
||||||
`client_database_id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
`client_database_id` INTEGER NOT NULL PRIMARY KEY [AUTO_INCREMENT],
|
||||||
`client_unique_id` VARCHAR(40) UNIQUE,
|
`client_unique_id` VARCHAR(40) UNIQUE,
|
||||||
`client_created` BIGINT,
|
`client_created` BIGINT,
|
||||||
`client_login_name` VARCHAR(20) UNIQUE
|
`client_login_name` VARCHAR(20) UNIQUE
|
||||||
@ -515,7 +525,7 @@ bool SqlDataManager::update_database(std::string &error) {
|
|||||||
"CREATE INDEX `idx_properties_serverid_id_value` ON `properties` (`serverId`, `id`, `key`);",
|
"CREATE INDEX `idx_properties_serverid_id_value` ON `properties` (`serverId`, `id`, `key`);",
|
||||||
"CREATE INDEX `idx_properties_serverid_id_type` ON `properties` (`serverId`, `id`, `type`);",
|
"CREATE INDEX `idx_properties_serverid_id_type` ON `properties` (`serverId`, `id`, `type`);",
|
||||||
|
|
||||||
"CREATE TABLE `groups_v2` (`serverId` INT NOT NULL, `groupId` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, `target` INT, `type` INT, `displayName` VARCHAR(128), `original_group_id` INTEGER);",
|
"CREATE TABLE `groups_v2` (`serverId` INT NOT NULL, `groupId` INTEGER NOT NULL PRIMARY KEY [AUTO_INCREMENT], `target` INT, `type` INT, `displayName` VARCHAR(128), `original_group_id` INTEGER);",
|
||||||
"INSERT INTO `groups_v2` (`serverId`, `groupId`, `target`, `type`, `displayName`) SELECT `serverId`, `groupId`, `target`, `type`, `displayName` FROM `groups`;",
|
"INSERT INTO `groups_v2` (`serverId`, `groupId`, `target`, `type`, `displayName`) SELECT `serverId`, `groupId`, `target`, `type`, `displayName` FROM `groups`;",
|
||||||
"DROP TABLE `groups`;",
|
"DROP TABLE `groups`;",
|
||||||
"ALTER TABLE `groups_v2` RENAME TO groups;",
|
"ALTER TABLE `groups_v2` RENAME TO groups;",
|
||||||
|
Loading…
Reference in New Issue
Block a user