Some changes

This commit is contained in:
WolverinDEV 2020-08-13 12:58:19 +02:00
parent 7fdd272d76
commit 7dcf4a54ef
13 changed files with 310 additions and 225 deletions

@ -1 +1 @@
Subproject commit 32b1ef973d44c668fc2e74735491db3896a32c2d
Subproject commit fe633dc99a6ea91c2c0a8cb13a51a7587a54cb2e

View File

@ -257,7 +257,7 @@ target_link_libraries(PermMapHelper
SET(CPACK_PACKAGE_VERSION_MAJOR "1")
SET(CPACK_PACKAGE_VERSION_MINOR "4")
SET(CPACK_PACKAGE_VERSION_PATCH "18")
SET(CPACK_PACKAGE_VERSION_PATCH "19")
if (BUILD_TYPE_NAME EQUAL OFF)
SET(CPACK_PACKAGE_VERSION_DATA "beta")
elseif (BUILD_TYPE_NAME STREQUAL "")

View File

@ -994,6 +994,10 @@ std::shared_ptr<Properties> DatabaseHelper::loadClientProperties(const std::shar
column = "client_nickname";
break;
case property::CONNECTION_CLIENT_IP:
column = "client_ip";
break;
case property::CLIENT_LASTCONNECTED:
column = "client_last_connected";
break;
@ -1326,10 +1330,10 @@ void DatabaseHelper::listDatabaseClients(
client.client_ip = values[index++];
assert(names[index] == "client_created");
client.client_last_connected = values[index++];
client.client_created = values[index++];
assert(names[index] == "client_last_connected");
client.client_created = values[index++];
client.client_last_connected = values[index++];
assert(names[index] == "client_total_connections");
client.client_total_connections = values[index++];

View File

@ -36,6 +36,14 @@ ConnectedClient::~ConnectedClient() {
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) {
auto& info = this->connection_info;

View File

@ -375,6 +375,8 @@ namespace ts {
std::weak_ptr<MusicClient> subscribed_bot;
std::weak_ptr<ts::music::Playlist> _subscribed_playlist{};
bool loadDataForCurrentServer() override;
virtual void tick(const std::chrono::system_clock::time_point &time);
//Locked by everything who has something todo with command handling
threads::Mutex command_lock; /* Note: This mutex must be recursive! */

View File

@ -124,7 +124,7 @@ enum WhisperType {
CHANNEL_COMMANDER = 2,
ALL = 3,
ECHO_TEXT = 0x10,
ECHO = 0x10,
};
enum WhisperTarget {
@ -184,7 +184,7 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo
#endif
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()));
} else {
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);
return !speakingClient->shouldReceiveVoiceWhisper(self_lock);
}), available_clients.end());
}
if(available_clients.empty()) {
if(update_whisper_error(this->speak_last_no_whisper_target)) {
command_result result{error::whisper_no_targets};
this->notifyError(result);
if(available_clients.empty()) {
if(update_whisper_error(this->speak_last_no_whisper_target)) {
command_result result{error::whisper_no_targets};
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(update_whisper_error(this->speak_last_too_many_whisper_targets)) {
command_result result{error::whisper_too_many_targets};
this->notifyError(result);
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)) {
command_result result{error::whisper_too_many_targets};
this->notifyError(result);
}
return;
}
return;
}
//Create the packet data

View File

@ -3,98 +3,96 @@
#include <json/json.h>
#include "ConnectedClient.h"
namespace ts {
namespace server {
class VirtualServer;
class SpeakingClient : public ConnectedClient {
public:
struct VoicePacketFlags {
bool encrypted : 1;
bool head : 1;
bool fragmented : 1; /* used by MONO. IDK What this is */
bool new_protocol : 1;
char _unused : 4;
namespace ts::server {
class VirtualServer;
class SpeakingClient : public ConnectedClient {
public:
struct VoicePacketFlags {
bool encrypted : 1;
bool head : 1;
bool fragmented : 1; /* used by MONO. IDK What this is */
bool new_protocol : 1;
char _unused : 4;
VoicePacketFlags() : encrypted{false}, head{false}, fragmented{false}, new_protocol{false}, _unused{0} { }
};
static_assert(sizeof(VoicePacketFlags) == 1);
VoicePacketFlags() : encrypted{false}, head{false}, fragmented{false}, new_protocol{false}, _unused{0} { }
};
static_assert(sizeof(VoicePacketFlags) == 1);
enum HandshakeState {
BEGIN,
IDENTITY_PROOF,
SUCCEEDED
};
enum IdentityType : uint8_t {
TEASPEAK_FORUM,
TEAMSPEAK,
NICKNAME,
enum HandshakeState {
BEGIN,
IDENTITY_PROOF,
SUCCEEDED
};
enum IdentityType : uint8_t {
TEASPEAK_FORUM,
TEAMSPEAK,
NICKNAME,
UNSET = 0xff
};
UNSET = 0xff
};
SpeakingClient(sql::SqlManager* a, const std::shared_ptr<VirtualServer>& b) : ConnectedClient(a, b) {
speak_begin = std::chrono::system_clock::now();
speak_last_packet = std::chrono::system_clock::now();
};
~SpeakingClient() override = default;
SpeakingClient(sql::SqlManager* a, const std::shared_ptr<VirtualServer>& b) : ConnectedClient(a, b) {
speak_begin = std::chrono::system_clock::now();
speak_last_packet = std::chrono::system_clock::now();
};
~SpeakingClient() override = default;
//Voice
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);
//Voice
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);
//Whisper
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;
//Whisper
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;
inline std::chrono::milliseconds takeSpokenTime() {
auto time = this->speak_time;
this->speak_time = std::chrono::milliseconds(0);
return time;
}
protected:
void tick(const std::chrono::system_clock::time_point &time) override;
inline std::chrono::milliseconds takeSpokenTime() {
auto time = this->speak_time;
this->speak_time = std::chrono::milliseconds(0);
return time;
}
protected:
void tick(const std::chrono::system_clock::time_point &time) override;
protected:
public:
void updateChannelClientProperties(bool channel_lock, bool notify) override;
protected:
public:
void updateChannelClientProperties(bool channel_lock, bool notify) override;
protected:
command_result handleCommand(Command &command) override;
protected:
command_result handleCommand(Command &command) override;
public:
void handlePacketVoice(const pipes::buffer_view&, bool head, bool fragmented);
virtual void handlePacketVoiceWhisper(const pipes::buffer_view&, bool /* new */);
public:
void handlePacketVoice(const pipes::buffer_view&, bool head, bool fragmented);
virtual void handlePacketVoiceWhisper(const pipes::buffer_view&, bool /* new */);
void processJoin();
void processLeave();
void processJoin();
void processLeave();
virtual command_result handleCommandHandshakeBegin(Command&);
virtual command_result handleCommandHandshakeIdentityProof(Command &);
virtual command_result handleCommandClientInit(Command&);
virtual command_result handleCommandHandshakeBegin(Command&);
virtual command_result handleCommandHandshakeIdentityProof(Command &);
virtual command_result handleCommandClientInit(Command&);
void triggerVoiceEnd();
inline void updateSpeak(bool onlyUpdate, const std::chrono::system_clock::time_point &time);
std::chrono::milliseconds speak_accuracy = std::chrono::seconds{1};
void triggerVoiceEnd();
inline void updateSpeak(bool onlyUpdate, const std::chrono::system_clock::time_point &time);
std::chrono::milliseconds speak_accuracy = std::chrono::seconds{1};
threads::Mutex speak_lock;
std::chrono::milliseconds speak_time = std::chrono::milliseconds{0};
std::chrono::system_clock::time_point speak_begin;
std::chrono::system_clock::time_point speak_last_packet;
threads::Mutex speak_lock;
std::chrono::milliseconds speak_time = std::chrono::milliseconds{0};
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_no_whisper_target;
std::chrono::system_clock::time_point speak_last_too_many_whisper_targets;
std::chrono::system_clock::time_point speak_last_no_whisper_target;
std::chrono::system_clock::time_point speak_last_too_many_whisper_targets;
permission::v2::PermissionFlaggedValue max_idle_time{permission::v2::empty_permission_flagged_value};
struct {
HandshakeState state{HandshakeState::BEGIN};
permission::v2::PermissionFlaggedValue max_idle_time{permission::v2::empty_permission_flagged_value};
struct {
HandshakeState state{HandshakeState::BEGIN};
IdentityType identityType{IdentityType::UNSET};
std::string proof_message;
//TeamSpeak
std::shared_ptr<ecc_key> identityKey;
//TeaSpeak
std::shared_ptr<Json::Value> identityData;
} handshake;
};
}
IdentityType identityType{IdentityType::UNSET};
std::string proof_message;
//TeamSpeak
std::shared_ptr<ecc_key> identityKey;
//TeaSpeak
std::shared_ptr<Json::Value> identityData;
} handshake;
};
}

View File

@ -475,6 +475,22 @@ bool QueryClient::handleMessage(const pipes::buffer_view& message) {
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) {
string commands{};

View File

@ -145,7 +145,7 @@ std::string VoiceBridge::generate_answer() {
}
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()) {
this->offer_timestamp = chrono::system_clock::time_point();
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) {
if(channel->lable() == "main") {
this->_voice_channel = channel;
if(channel->lable() == "main" || channel->lable() == "voice") {
this->voice_channel_ = channel;
debugMessage(this->server_id(), "{} Got voice channel!", CLIENT_STR_LOG_PREFIX_(this->owner()));
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) {

View File

@ -12,12 +12,14 @@ namespace ts {
class VoiceBridge {
public:
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 std::string& /* sdpMid */, int /* sdpMLineIndex */)> cb_ice_candidate_finish;
typedef std::function<void()> cb_initialized;
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>&);
virtual ~VoiceBridge();
@ -31,6 +33,7 @@ namespace ts {
cb_ice_candidate callback_ice_candidate;
cb_ice_candidate_finish callback_ice_candidate_finished;
cb_voice_data callback_voice_data;
cb_voice_whisper_data callback_voice_whisper_data;
cb_initialized callback_initialized;
cb_failed callback_failed;
@ -48,7 +51,10 @@ namespace ts {
std::weak_ptr<server::WebClient> _owner;
std::chrono::system_clock::time_point offer_timestamp;
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;
struct {
uint16_t packet_id = 0;

View File

@ -156,22 +156,29 @@ void WebClient::sendJson(const Json::Value& json) {
}
void WebClient::sendCommand(const ts::Command &command, bool low) {
Json::Value value = command.buildJson();
value["type"] = "command";
this->sendJson(value);
if(this->allow_raw_commands) {
Json::Value 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) {
#if false
Json::Value value{};
value["type"] = "command2";
value["payload"] = command.build();
this->sendJson(value);
#else
auto data = command.build();
Command parsed_command = Command::parse(pipes::buffer_view{data.data(), data.length()}, true, false);
this->sendCommand(parsed_command, low);
#endif
if(this->allow_raw_commands) {
Json::Value value{};
value["type"] = "command-raw";
value["payload"] = command.build();
this->sendJson(value);
} else {
auto data = command.build();
Command parsed_command = Command::parse(pipes::buffer_view{data.data(), data.length()}, true, false);
this->sendCommand(parsed_command, low);
}
}
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));
}
Json::CharReaderBuilder json_reader_builder = []{
Json::CharReaderBuilder json_reader_builder = []() noexcept {
Json::CharReaderBuilder 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()};
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) {
logError(this->server->getServerId(), "Could not parse web message! Message: " + string(ex.what()));
logTrace(this->server->getServerId(), "Payload: " + message);
@ -458,7 +466,7 @@ void WebClient::handleMessage(const std::string &message) {
} else if(val["type"].asString() == "WebRTC") {
auto subType = val["request"].asString();
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) {
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;
}).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) {
/* may somehow get the "real" packet size? */
this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::VOICE, buffer.length());
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 = [&](){
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;
voice_bridge_lock.unlock();
voice_bridge_lock_.unlock();
shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
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.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) {
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) {
shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
if(!this->voice_bridge || !this->voice_bridge->voice_channel()) return false;
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) {
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) {
auto channel = this->voice_bridge->voice_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());
}
@ -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) {
logError(this->server->getServerId(), "Web client got whisper packet");
//As well log the data!
std::shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
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());
}
}
}

View File

@ -14,105 +14,103 @@
#include <json/json.h>
#include <EventLoop.h>
namespace ts {
namespace server {
class WebControlServer;
namespace ts::server {
class WebControlServer;
class WebClient : public SpeakingClient {
friend class WebControlServer;
public:
WebClient(WebControlServer*, int socketFd);
~WebClient() override;
class WebClient : public SpeakingClient {
friend class WebControlServer;
public:
WebClient(WebControlServer*, int socketFd);
~WebClient() override;
void sendJson(const Json::Value&);
void sendCommand(const ts::Command &command, bool low = false) override;
void sendCommand(const ts::command_builder &command, bool low) override;
void sendJson(const Json::Value&);
void sendCommand(const ts::Command &command, bool low) override;
void sendCommand(const ts::command_builder &command, bool low) 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 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 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:
void tick(const std::chrono::system_clock::time_point&) override; /* Every 500ms */
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; }
void applySelfLock(const std::shared_ptr<WebClient> &cl){ _this = cl; }
private:
WebControlServer* handle;
std::chrono::time_point<std::chrono::system_clock> connectedTimestamp;
protected:
void tick(const std::chrono::system_clock::time_point&) override; /* Every 500ms */
std::shared_mutex voice_bridge_lock;
std::unique_ptr<web::VoiceBridge> voice_bridge;
int file_descriptor;
void applySelfLock(const std::shared_ptr<WebClient> &cl){ _this = cl; }
private:
WebControlServer* handle;
std::chrono::time_point<std::chrono::system_clock> connectedTimestamp;
std::mutex event_lock;
::event* readEvent;
::event* writeEvent;
std::shared_mutex voice_bridge_lock;
std::unique_ptr<web::VoiceBridge> voice_bridge;
int file_descriptor;
struct {
uint8_t current_id = 0;
std::chrono::system_clock::time_point last_request;
std::chrono::system_clock::time_point last_response;
std::mutex event_lock;
::event* readEvent;
::event* writeEvent;
std::chrono::nanoseconds value{};
std::chrono::nanoseconds timeout{2000};
} ping;
struct {
uint8_t current_id = 0;
std::chrono::system_clock::time_point last_request;
std::chrono::system_clock::time_point last_response;
struct {
uint8_t current_id = 0;
std::chrono::system_clock::time_point last_request;
std::chrono::system_clock::time_point last_response;
std::chrono::nanoseconds value{};
std::chrono::nanoseconds timeout{2000};
} ping;
std::chrono::nanoseconds value{};
std::chrono::nanoseconds timeout{2000};
} js_ping;
struct {
uint8_t current_id = 0;
std::chrono::system_clock::time_point last_request;
std::chrono::system_clock::time_point last_response;
std::mutex queue_lock;
std::deque<pipes::buffer> queue_read;
std::deque<pipes::buffer> queue_write;
threads::Mutex execute_lock; /* needs to be recursive! */
std::chrono::nanoseconds value{};
std::chrono::nanoseconds timeout{2000};
} js_ping;
std::thread flush_thread;
std::recursive_mutex close_lock;
private:
void initialize();
std::mutex queue_lock;
std::deque<pipes::buffer> queue_read;
std::deque<pipes::buffer> queue_write;
threads::Mutex execute_lock; /* needs to be recursive! */
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::thread flush_thread;
std::recursive_mutex close_lock;
private:
void initialize();
void processNextMessage(const std::chrono::system_clock::time_point& /* scheduled */);
void registerMessageProcess();
bool allow_raw_commands{false};
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
void onWSConnected();
void onWSDisconnected(const std::string& reason);
void onWSMessage(const pipes::WSMessage&);
protected:
void disconnectFinal();
void handleMessage(const std::string &);
std::shared_ptr<event::ProxiedEventEntry<WebClient>> event_handle_packet;
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;
//WS events
void onWSConnected();
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;
command_result handleCommandClientInit(Command &command) override;
};
}
protected:
command_result handleCommand(Command &command) override;
command_result handleCommandClientInit(Command &command) override;
};
}
#endif

View File

@ -79,9 +79,19 @@ do { \
} \
} 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>
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 auto_increment{sql->getType() == sql::TYPE_SQLITE ? "AUTOINCREMENT" : "AUTO_INCREMENT"};
for(const auto& cmd : commands) {
std::string command{cmd};
@ -89,8 +99,8 @@ inline bool execute_commands(sql::SqlManager* sql, std::string& error, const std
return !std::isspace(ch);
}));
if(command.starts_with("[INSERT_OR_IGNORE]"))
command = insert_or_ignore + command.substr(18);
command = replace_all(command, "[INSERT_OR_IGNORE]", insert_or_ignore);
command = replace_all(command, "[AUTO_INCREMENT]", auto_increment);
auto result = sql::command(sql, command).execute();
if(!result) {
@ -451,7 +461,7 @@ bool SqlDataManager::update_database(std::string &error) {
case 12: {
constexpr static std::string_view kCreateClientsV2{R"(
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_created` BIGINT,
`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_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`;",
"DROP TABLE `groups`;",
"ALTER TABLE `groups_v2` RENAME TO groups;",