From 064d54eac552250db6b5d706490ab2095ade0bb7 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Mon, 23 Mar 2020 10:57:57 +0100 Subject: [PATCH 01/18] Some fixes --- src/sql/sqlite/SqliteSQL.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sql/sqlite/SqliteSQL.cpp b/src/sql/sqlite/SqliteSQL.cpp index 4f81fd8..d098ed3 100644 --- a/src/sql/sqlite/SqliteSQL.cpp +++ b/src/sql/sqlite/SqliteSQL.cpp @@ -162,6 +162,8 @@ result SqliteManager::executeCommand(std::shared_ptr _ptr) { bindVariable(stmt, varIndex, var); int result = sqlite3_step(stmt); + + //logCritical(0, "Changes: {} SQL: {}", sqlite3_changes(this->database), ptr->sql_command); if(result == SQLITE_DONE) return {_ptr->sql_command,0, "success"}; if(result == SQLITE_ROW) return {_ptr->sql_command,-1, "query has a result"}; From 4965c4d3d05ebd92f29990f5c4364ddfe7b38cb7 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Tue, 31 Mar 2020 22:01:46 +0200 Subject: [PATCH 02/18] Some updates --- CMakeLists.txt | 4 ++-- src/PermissionManager.cpp | 3 ++- src/PermissionManager.h | 1 + src/misc/base64.cpp | 4 ++-- src/protocol/generation.h | 4 ++-- test/generationTest.cpp | 9 ++------- 6 files changed, 11 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index eb3fd58..6e0660d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -268,7 +268,7 @@ if(BUILD_TESTS) target_link_libraries(RingTest ${TEST_LIBRARIES}) if(NOT WIN32) - add_executable(CommandTest ${SOURCE_FILES} ${HEADER_FILES} test/CommandTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h ../server/MySQLLibSSLFix.c) + add_executable(CommandTest ${SOURCE_FILES} ${HEADER_FILES} test/CommandTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) target_link_libraries(CommandTest ${TEST_LIBRARIES}) add_executable(WebsocketTest ${SOURCE_FILES} ${HEADER_FILES} test/WSSTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) @@ -299,7 +299,7 @@ if(BUILD_TESTS) add_executable(PermissionTest test/PermissionTest.cpp ${SOURCE_FILES}) target_link_libraries(PermissionTest ${TEST_LIBRARIES}) - add_executable(GenerationTest test/generationTest.cpp ${SOURCE_FILES} ../server/MySQLLibSSLFix.c) + add_executable(GenerationTest test/generationTest.cpp src/protocol/generation.cpp) target_link_libraries(GenerationTest ${TEST_LIBRARIES}) add_executable(RW-Lock-Test test/rw_lock.cpp src/lock/rw_mutex.cpp) diff --git a/src/PermissionManager.cpp b/src/PermissionManager.cpp index d75c5b4..af5df2a 100644 --- a/src/PermissionManager.cpp +++ b/src/PermissionManager.cpp @@ -330,7 +330,8 @@ deque> ts::permission::availablePermissions make_shared(PermissionType::b_client_delete_dbproperties, PermissionGroup::client_modify, "b_client_delete_dbproperties", "Delete a clients properties in the sql"), make_shared(PermissionType::b_client_create_modify_serverquery_login, PermissionGroup::client_modify, "b_client_create_modify_serverquery_login", "Create or modify own ServerQuery account"), - make_shared(PermissionType::b_client_query_create, PermissionGroup::client_modify, "b_client_query_create", "Create your own ServerQuery account"), + make_shared(PermissionType::b_client_query_create, PermissionGroup::client_modify, "b_client_query_create", "Create a ServerQuery account for any user"), + make_shared(PermissionType::b_client_query_create_own, PermissionGroup::client_modify, "b_client_query_create_own", "Create your own ServerQuery account"), make_shared(PermissionType::b_client_query_list, PermissionGroup::client_modify, "b_client_query_list", "List all ServerQuery accounts"), make_shared(PermissionType::b_client_query_list_own, PermissionGroup::client_modify, "b_client_query_list_own", "List all own ServerQuery accounts"), make_shared(PermissionType::b_client_query_rename, PermissionGroup::client_modify, "b_client_query_rename", "Rename a ServerQuery account"), diff --git a/src/PermissionManager.h b/src/PermissionManager.h index b0ac10b..b1682c4 100644 --- a/src/PermissionManager.h +++ b/src/PermissionManager.h @@ -438,6 +438,7 @@ namespace ts { b_client_delete_dbproperties, b_client_create_modify_serverquery_login, b_client_query_create, + b_client_query_create_own, b_client_query_list, b_client_query_list_own, b_client_query_rename, diff --git a/src/misc/base64.cpp b/src/misc/base64.cpp index 66a402c..01f7962 100644 --- a/src/misc/base64.cpp +++ b/src/misc/base64.cpp @@ -5,7 +5,7 @@ std::string base64::decode(const char* input, size_t size) { auto out = new unsigned char[size]; if(base64_strict_decode((unsigned char*) input, (unsigned long) size, out, (unsigned long*) &size) != CRYPT_OK){ - std::cerr << "Invalid base 64 string '" << input << "'" << std::endl; + //std::cerr << "Invalid base 64 string '" << input << "'" << std::endl; return ""; } std::string ret((char*) out, size); @@ -17,7 +17,7 @@ std::string base64::encode(const char* input, const unsigned long inputSize) { auto outlen = static_cast(inputSize + (inputSize / 3.0) + 16); auto outbuf = new unsigned char[outlen]; //Reserve output memory if(base64_encode((unsigned char*) input, inputSize, outbuf, &outlen) != CRYPT_OK){ - std::cerr << "Invalid input '" << input << "'" << std::endl; + //std::cerr << "Invalid input '" << input << "'" << std::endl; return ""; } std::string ret((char*) outbuf, outlen); diff --git a/src/protocol/generation.h b/src/protocol/generation.h index 8c7429a..2391182 100644 --- a/src/protocol/generation.h +++ b/src/protocol/generation.h @@ -8,8 +8,8 @@ namespace ts::protocol { generation_estimator(); void reset(); - uint16_t visit_packet(uint16_t /* packet id */); - uint16_t generation() const; + [[nodiscard]] uint16_t visit_packet(uint16_t /* packet id */); + [[nodiscard]] uint16_t generation() const; void set_last_state(uint16_t last_packet, uint16_t generation) { this->last_packet_id = last_packet; diff --git a/test/generationTest.cpp b/test/generationTest.cpp index 2bd2374..6b27198 100644 --- a/test/generationTest.cpp +++ b/test/generationTest.cpp @@ -75,22 +75,17 @@ int main() { test_vector("25 loss", generate_test_vector(0x3000, 25)); test_vector("50 loss", generate_test_vector(0x3000, 50)); test_vector("80 loss", generate_test_vector(0x3000, 80)); + test_vector("99 loss", generate_test_vector(0x3000, 99)); } { auto base = generate_test_vector(0x3000, 0); + test_vector("swap 0:4", swap_elements(base, 0, 4)); test_vector("swap 30:20", swap_elements(base, 30, 20)); test_vector("swap 30:1000", swap_elements(base, 30, 200)); test_vector("swap 80:1000", swap_elements(base, 80, 200)); } - if(false) { - test_vector("10 loss", generate_test_vector(0x3000, 10)); - test_vector("25 loss", generate_test_vector(0x3000, 25)); - test_vector("50 loss", generate_test_vector(0x3000, 50)); - test_vector("80 loss", generate_test_vector(0x3000, 80)); - } - gen.set_last_state(0, 0); test_vector<6>(gen, {0, 1, 2, 4, 3, 5}, {0, 0, 0, 0, 0, 0}); From 889477c6461164cb1dd687b270bd629763db1ef6 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Wed, 1 Apr 2020 20:13:35 +0200 Subject: [PATCH 03/18] Updated revs --- src/protocol/generation.cpp | 14 +++++++-- src/protocol/generation.h | 2 +- test/generationTest.cpp | 58 ++++++++++++++++++++++--------------- 3 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/protocol/generation.cpp b/src/protocol/generation.cpp index 4bb8687..8c9bbbd 100644 --- a/src/protocol/generation.cpp +++ b/src/protocol/generation.cpp @@ -14,19 +14,27 @@ void generation_estimator::reset() { uint16_t generation_estimator::visit_packet(uint16_t packet_id) { if(this->last_packet_id >= generation_estimator::overflow_area_begin) { if(packet_id > this->last_packet_id) { + /* normal behaviour */ this->last_packet_id = packet_id; return this->last_generation; - } else { + } else if(packet_id < generation_estimator::overflow_area_end) { + /* we're within a new generation */ this->last_packet_id = packet_id; return ++this->last_generation; + } else { + /* old packet */ + return this->last_generation; } } else if(this->last_packet_id <= generation_estimator::overflow_area_end) { if(packet_id >= generation_estimator::overflow_area_begin) /* old packet */ return this->last_generation - 1; - this->last_packet_id = packet_id; + if(packet_id > this->last_packet_id) + this->last_packet_id = packet_id; return this->last_generation; } else { - this->last_packet_id = packet_id; + /* only update on newer packet id */ + if(packet_id > this->last_packet_id) + this->last_packet_id = packet_id; return this->last_generation; } } diff --git a/src/protocol/generation.h b/src/protocol/generation.h index 2391182..9db608c 100644 --- a/src/protocol/generation.h +++ b/src/protocol/generation.h @@ -16,7 +16,7 @@ namespace ts::protocol { this->last_generation = generation; } private: - constexpr static uint16_t overflow_window{1024}; + constexpr static uint16_t overflow_window{1024 * 8}; constexpr static uint16_t overflow_area_begin{0xFFFF - overflow_window}; constexpr static uint16_t overflow_area_end{overflow_window}; diff --git a/test/generationTest.cpp b/test/generationTest.cpp index 6b27198..71092fe 100644 --- a/test/generationTest.cpp +++ b/test/generationTest.cpp @@ -8,14 +8,14 @@ using namespace ts::protocol; typedef std::vector> test_vector_t; -test_vector_t generate_test_vector(size_t size, int loss) { +test_vector_t generate_test_vector(size_t size, int loss, int step = 1) { test_vector_t result{}; result.reserve(size); for(size_t i = 0; i < size; i++) { if ((rand() % 100) < loss) continue; - result.emplace_back(i & 0xFFFFU, i >> 16U); + result.emplace_back((i * step) & 0xFFFFU, (i * step) >> 16U); } return result; @@ -26,8 +26,6 @@ test_vector_t swap_elements(test_vector_t vector, int per, int max_distance) { if ((rand() % 100) < per) { //lets switch auto offset = rand() % max_distance; - if(!offset) offset = 1; - std::swap(vector[index], vector[index + offset]); } } @@ -38,13 +36,15 @@ test_vector_t swap_elements(test_vector_t vector, int per, int max_distance) { bool test_vector(const std::string_view& name, const test_vector_t& vector) { generation_estimator gen{}; - size_t last_value{0}; + size_t last_value{0}, last_gen{0}, index{0}; for(auto [id, exp] : vector) { if(auto val = gen.visit_packet(id); val != exp) { - std::cout << "[" << name << "] failed for " << id << " -> " << exp << " | " << val << ". Last value: " << last_value << "\n"; + std::cout << "[" << name << "] failed for " << id << " -> " << exp << " | " << val << ". Last value: " << last_value << " gen: " << last_gen << "\n"; return false; - } + } else + last_gen = val; last_value = id; + index++; } return true; } @@ -70,31 +70,43 @@ int main() { generation_estimator gen{}; { - test_vector("00 loss", generate_test_vector(0x3000, 0)); - test_vector("10 loss", generate_test_vector(0x3000, 10)); - test_vector("25 loss", generate_test_vector(0x3000, 25)); - test_vector("50 loss", generate_test_vector(0x3000, 50)); - test_vector("80 loss", generate_test_vector(0x3000, 80)); - test_vector("99 loss", generate_test_vector(0x3000, 99)); + test_vector("00 loss", generate_test_vector(0x30000, 0)); + test_vector("10 loss", generate_test_vector(0x30000, 10)); + test_vector("25 loss", generate_test_vector(0x30000, 25)); + test_vector("50 loss", generate_test_vector(0x30000, 50)); + test_vector("80 loss", generate_test_vector(0x30000, 80)); + test_vector("99 loss", generate_test_vector(0x30000, 99)); } { - auto base = generate_test_vector(0x3000, 0); - test_vector("swap 0:4", swap_elements(base, 0, 4)); + auto base = generate_test_vector(0x30000, 0); + test_vector("swap 30:4", swap_elements(base, 30, 4)); test_vector("swap 30:20", swap_elements(base, 30, 20)); test_vector("swap 30:1000", swap_elements(base, 30, 200)); test_vector("swap 80:1000", swap_elements(base, 80, 200)); } - gen.set_last_state(0, 0); - test_vector<6>(gen, {0, 1, 2, 4, 3, 5}, {0, 0, 0, 0, 0, 0}); + { + test_vector("1k step", swap_elements(generate_test_vector(0x30000, 0, 6), 100, 8)); - gen.set_last_state(0xFF00, 0); - test_vector<6>(gen, {0, 1, 2, 4, 3, 5}, {1, 1, 1, 1, 1, 1}); + } - gen.set_last_state(0xFF00, 0); - test_vector<6>(gen, {0, 1, 2, 0xFF00, 3, 5}, {1, 1, 1, 0, 1, 1}); + if(true) { +#if 0 + gen.set_last_state(0, 0); + test_vector<6>(gen, {0, 1, 2, 4, 3, 5}, {0, 0, 0, 0, 0, 0}); - gen.set_last_state(0xFF00, 0); - test_vector<6>(gen, {0xFFFE, 0xFFFF, 0, 1, 0xFFFC, 2}, {0, 0, 1, 1, 0, 1}); + gen.set_last_state(0xFF00, 0); + test_vector<6>(gen, {0, 1, 2, 4, 3, 5}, {1, 1, 1, 1, 1, 1}); + + gen.set_last_state(0xFF00, 0); + test_vector<6>(gen, {0, 1, 2, 0xFF00, 3, 5}, {1, 1, 1, 0, 1, 1}); + + gen.set_last_state(0xFF00, 0); + test_vector<6>(gen, {0xFFFE, 0xFFFF, 0, 1, 0xFFFC, 2}, {0, 0, 1, 1, 0, 1}); +#endif + + gen.set_last_state(0xFF00, 1); + test_vector<6>(gen, {0xFFFE, 0xFFFF, 0, 1, 0xFFFC, 2}, {1, 1, 2, 2, 1, 2}); + } } \ No newline at end of file From c879f3b7764e2b6a226ca80d3013bdff838a9972 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Thu, 2 Apr 2020 14:10:32 +0200 Subject: [PATCH 04/18] Fixed the permfind command --- src/query/command3.h | 119 +++++++++++++++++++++++++------------------ 1 file changed, 70 insertions(+), 49 deletions(-) diff --git a/src/query/command3.h b/src/query/command3.h index fdcf4dd..5057281 100644 --- a/src/query/command3.h +++ b/src/query/command3.h @@ -156,6 +156,66 @@ namespace ts { std::vector flags{}; }; + class command_builder_bulk { + template + friend class command_builder_impl; + public: + inline void put(const std::string_view& key, const std::string_view& value) { + size_t begin, end; + if(impl::value_raw_impl(this->bulk, key, begin, &end)) { + std::string result{}; + result.reserve(this->bulk.size()); + + result.append(this->bulk, 0, begin - key.size() - 1); /* key incl = */ + result.append(this->bulk, end + 1); /* get rid of the space */ + this->bulk = result; + } + this->impl_put_unchecked(key, value); + } + + template ::value || std::is_same::value), int> = 1> + inline void put(const std::string_view& key, const T& value) { + static_assert(converter::supported, "Target type isn't supported!"); + static_assert(!converter::supported || converter::to_string, "Target type dosn't support building"); + auto data = converter::to_string(value); + this->put(key, std::string_view{data}); + } + + /* directly puts data without checking for duplicates */ + inline void put_unchecked(const std::string_view& key, const std::string_view& value) { + this->impl_put_unchecked(key, value); + } + + inline void put_unchecked(const std::string_view& key, const std::string& value) { + this->put_unchecked(key, std::string_view{value}); + } + + template ::value || std::is_same::value), int> = 1> + inline void put_unchecked(const std::string_view& key, const T& value) { + static_assert(converter::supported, "Target type isn't supported!"); + static_assert(!converter::supported || converter::to_string, "Target type dosn't support building"); + auto data = converter::to_string(value); + this->put_unchecked(key, std::string_view{data}); + } + private: + bool& flag_changed; + std::string& bulk; + explicit command_builder_bulk(bool& change_flag, std::string& bulk) : flag_changed{change_flag}, bulk{bulk} {} + + void impl_put_unchecked(const std::string_view& key, const std::string_view& value) { + auto escaped_value = ts::query::escape(std::string{value}); + + this->bulk.reserve(this->bulk.length() + key.size() + escaped_value.size() + 2); + this->bulk.append(key); + if(!escaped_value.empty()) { + this->bulk.append("="); + this->bulk.append(escaped_value); + } + this->bulk.append(" "); + this->flag_changed = true; + } + }; + template > class command_builder_impl { public: @@ -169,7 +229,7 @@ namespace ts { } inline std::string build(bool with_empty = false) const { - if(this->builded.has_value()) + if(this->builded.has_value() && !this->flag_changed) return this->builded.value(); std::string result{}; @@ -189,74 +249,35 @@ namespace ts { this->builded = result.substr(0, result.length() - 1); else this->builded = result; + this->flag_changed = false; return this->builded.value(); } inline void reserve_bulks(size_t count) { this->bulks.reserve(count); } - inline void put(size_t index, const std::string_view& key, const std::string_view& value) { + [[nodiscard]] inline command_builder_bulk bulk(size_t index) { while(this->bulks.size() <= index) this->bulks.emplace_back("").reserve(expected_bulk_size); - auto& data = this->bulks[index]; - size_t begin, end; - if(impl::value_raw_impl(data, key, begin, &end)) { - std::string result{}; - result.reserve(data.size()); - - result.append(data, 0, begin - key.size() - 1); /* key incl = */ - result.append(data, end + 1); /* get rid of the space */ - data = result; - } - this->impl_put_unchecked(data, index, key, value); + return command_builder_bulk{this->flag_changed, this->bulks[index]}; } - template ::value || std::is_same::value), int> = 1> + template inline void put(size_t index, const std::string_view& key, const T& value) { - static_assert(converter::supported, "Target type isn't supported!"); - static_assert(!converter::supported || converter::to_string, "Target type dosn't support building"); - auto data = converter::to_string(value); - this->put(index, key, std::string_view{data}); + this->bulk(index).put(key, value); } /* directly puts data without checking for duplicates */ - inline void put_unchecked(size_t index, const std::string_view& key, const std::string_view& value) { - while(this->bulks.size() <= index) - this->bulks.emplace_back("").reserve(expected_bulk_size); - - this->impl_put_unchecked(this->bulks[index], index, key, value); - } - - inline void put_unchecked(size_t index, const std::string_view& key, const std::string& value) { - this->put_unchecked(index, key, std::string_view{value}); - } - - template ::value || std::is_same::value), int> = 1> + template inline void put_unchecked(size_t index, const std::string_view& key, const T& value) { - static_assert(converter::supported, "Target type isn't supported!"); - static_assert(!converter::supported || converter::to_string, "Target type dosn't support building"); - auto data = converter::to_string(value); - this->put_unchecked(index, key, std::string_view{data}); + this->bulk(index).put_unchecked(key, value); } private: - command_builder_impl(size_t expected, size_t identifier, typename vector_t::iterator begin, typename vector_t::iterator end) : expected_bulk_size{expected}, _identifier{identifier}, bulks{begin, end} {} - - void impl_put_unchecked(std::string& data, size_t index, const std::string_view& key, const std::string_view& value) { - auto escaped_value = ts::query::escape(std::string{value}); - - data.reserve(data.length() + key.size() + escaped_value.size() + 2); - data.append(key); - if(!escaped_value.empty()) { - data.append("="); - data.append(escaped_value); - } - data.append(" "); - - this->builded.reset(); - } + command_builder_impl(size_t expected, std::string identifier, typename vector_t::iterator begin, typename vector_t::iterator end) : expected_bulk_size{expected}, _identifier{std::move(identifier)}, bulks{begin, end} {} const size_t expected_bulk_size; const std::string _identifier; + mutable bool flag_changed{false}; mutable std::optional builded{}; vector_t bulks{}; }; From 0bfcb27002c7431d36aaa913d0f64f5a8b119d2b Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Thu, 2 Apr 2020 19:18:57 +0200 Subject: [PATCH 05/18] Added some debug info --- src/protocol/generation.cpp | 4 ---- src/protocol/generation.h | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/protocol/generation.cpp b/src/protocol/generation.cpp index 8c9bbbd..12f724f 100644 --- a/src/protocol/generation.cpp +++ b/src/protocol/generation.cpp @@ -37,8 +37,4 @@ uint16_t generation_estimator::visit_packet(uint16_t packet_id) { this->last_packet_id = packet_id; return this->last_generation; } -} - -uint16_t generation_estimator::generation() const { - return this->last_generation; } \ No newline at end of file diff --git a/src/protocol/generation.h b/src/protocol/generation.h index 9db608c..01f13f2 100644 --- a/src/protocol/generation.h +++ b/src/protocol/generation.h @@ -9,7 +9,8 @@ namespace ts::protocol { void reset(); [[nodiscard]] uint16_t visit_packet(uint16_t /* packet id */); - [[nodiscard]] uint16_t generation() const; + [[nodiscard]] inline uint16_t generation() const { return this->last_generation; } + [[nodiscard]] inline uint16_t current_packet_id() const { return this->last_packet_id; } void set_last_state(uint16_t last_packet, uint16_t generation) { this->last_packet_id = last_packet; From fab6aa90954bc85efad8a0b5a3608cb408202a17 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Fri, 3 Apr 2020 13:50:19 +0200 Subject: [PATCH 06/18] Updating some revs --- src/sql/mysql/MySQL.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sql/mysql/MySQL.cpp b/src/sql/mysql/MySQL.cpp index a4aa722..4ded0e6 100644 --- a/src/sql/mysql/MySQL.cpp +++ b/src/sql/mysql/MySQL.cpp @@ -20,7 +20,6 @@ using namespace sql::mysql; MySQLManager::MySQLManager() : SqlManager(SqlType::TYPE_MYSQL) {} MySQLManager::~MySQLManager() {} - //Property info: https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-configuration-properties.html //mysql://[host][:port]/[database][?propertyName1=propertyValue1[&propertyName2=propertyValue2]...] From e1e946ca359f65fff5e19583ff1c9711a3143460 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Fri, 3 Apr 2020 19:07:02 +0200 Subject: [PATCH 07/18] License server memory validation --- src/misc/strobf.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/misc/strobf.h b/src/misc/strobf.h index 7f8b970..13eaece 100644 --- a/src/misc/strobf.h +++ b/src/misc/strobf.h @@ -1,3 +1,5 @@ +#pragma once + #include #include #include From 9063e17efb1b1d837e771da6c8262b477b39cbb3 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 4 Apr 2020 11:59:32 +0200 Subject: [PATCH 08/18] Music bot fix --- src/Error.cpp | 2 +- src/Error.h | 2 +- src/query/command3.h | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Error.cpp b/src/Error.cpp index f630e9e..9ee8ed4 100644 --- a/src/Error.cpp +++ b/src/Error.cpp @@ -145,7 +145,7 @@ const std::vector ts::avariableErrors = { {0x0F00, "token_invalid_id" , "invalid privilege key" }, - {0x1001, "web_handshake_invalid" , "Invalid handshake" }, + {0x1000, "web_handshake_invalid" , "Invalid handshake" }, {0x1001, "web_handshake_unsupported" , "Handshake intention unsupported" }, {0x1002, "web_handshake_identity_unsupported" , "Handshake identity unsupported" }, {0x1003, "web_handshake_identity_proof_failed" , "Identity proof failed" }, diff --git a/src/Error.h b/src/Error.h index 723b649..c83bedf 100644 --- a/src/Error.h +++ b/src/Error.h @@ -147,7 +147,7 @@ namespace ts { server_connect_banned = 0xd01, ban_flooding = 0xd03, token_invalid_id = 0xf00, - web_handshake_invalid = 0x1001, + web_handshake_invalid = 0x1000, web_handshake_unsupported = 0x1001, web_handshake_identity_unsupported = 0x1002, web_handshake_identity_proof_failed = 0x1003, diff --git a/src/query/command3.h b/src/query/command3.h index 5057281..9574a8c 100644 --- a/src/query/command3.h +++ b/src/query/command3.h @@ -160,6 +160,10 @@ namespace ts { template friend class command_builder_impl; public: + inline void reserve(size_t length, bool accumulative = true) { + this->bulk.reserve(length + (accumulative ? this->bulk.size() : 0)); + } + inline void put(const std::string_view& key, const std::string_view& value) { size_t begin, end; if(impl::value_raw_impl(this->bulk, key, begin, &end)) { @@ -272,6 +276,20 @@ namespace ts { inline void put_unchecked(size_t index, const std::string_view& key, const T& value) { this->bulk(index).put_unchecked(key, value); } + + [[nodiscard]] inline size_t current_size() const { + if(this->bulks.empty()) return 0; + + size_t result{0}; + for(const auto& entry : this->bulks) + result += entry.length() + 2; + return result; + } + + inline void reset() { + for(auto& bulk : this->bulks) + bulk = " "; + } private: command_builder_impl(size_t expected, std::string identifier, typename vector_t::iterator begin, typename vector_t::iterator end) : expected_bulk_size{expected}, _identifier{std::move(identifier)}, bulks{begin, end} {} From f86c9b522a97ec6b524da3c7aa4bddffa54f7f38 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 4 Apr 2020 12:22:19 +0200 Subject: [PATCH 09/18] Fixed some small stuff --- src/Variable.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Variable.h b/src/Variable.h index 75acf72..4ab4853 100644 --- a/src/Variable.h +++ b/src/Variable.h @@ -68,7 +68,13 @@ class variable { variable&operator=(std::nullptr_t) { r_value() = ""; data->_type = VARTYPE_NULL; return *this;} template - T as() const { return typecast::variable_cast(*this); } + T as() const { + try { + return typecast::variable_cast(*this); + } catch(std::exception& ex) { + throw std::invalid_argument{"failed to parse " + this->r_key() + " as " + typeid(T).name() + " (" + ex.what() + ")"}; + } + } std::string string() const { return as(); } //fast template //TODO more secure and not just try and fail From 607ae9a3e6a64c7ac1eb8c25b9cfab50b94f1d86 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Wed, 8 Apr 2020 02:56:08 +0200 Subject: [PATCH 10/18] Updated the property system and added a packet loss calculator --- CMakeLists.txt | 7 +- src/Properties.cpp | 478 +------------------------- src/Properties.h | 384 ++++++++++++--------- src/PropertyDefinition.h | 360 +++++++++++++++++++ src/PropertyDefinitions.cpp | 84 +++++ src/converters/converter.cpp | 3 +- src/converters/converter.h | 2 + src/protocol/AcknowledgeManager.cpp | 49 +-- src/protocol/AcknowledgeManager.h | 48 ++- src/protocol/Packet.cpp | 21 +- src/protocol/Packet.h | 75 +++- src/protocol/PacketLossCalculator.cpp | 105 ++++++ src/protocol/PacketLossCalculator.h | 103 ++++++ src/query/Command.h | 19 +- test/PacketLossCalculateTest.cpp | 222 ++++++++++++ test/PropertyTest.cpp | 9 +- 16 files changed, 1257 insertions(+), 712 deletions(-) create mode 100644 src/PropertyDefinition.h create mode 100644 src/PropertyDefinitions.cpp create mode 100644 src/protocol/PacketLossCalculator.cpp create mode 100644 src/protocol/PacketLossCalculator.h create mode 100644 test/PacketLossCalculateTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e0660d..1d29498 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,12 +128,14 @@ set(SOURCE_FILES src/EventLoop.cpp src/License.cpp + src/PropertyDefinitions.cpp src/bbcode/bbcodes.cpp src/channel/TreeView.cpp src/protocol/ringbuffer.cpp src/protocol/AcknowledgeManager.cpp + src/protocol/PacketLossCalculator.cpp ) set(HEADER_FILES @@ -287,7 +289,7 @@ if(BUILD_TESTS) add_executable(CrashTest test/CrashTest.cpp ${SOURCE_FILES}) target_link_libraries(CrashTest ${TEST_LIBRARIES}) - add_executable(PorpertyTest test/PropertyTest.cpp ${SOURCE_FILES}) + add_executable(PorpertyTest test/PropertyTest.cpp src/Properties.cpp src/PropertyDefinitions.cpp) target_link_libraries(PorpertyTest ${TEST_LIBRARIES}) add_executable(BBTest test/BBTest.cpp ${SOURCE_FILES} src/query/command_unused.h) @@ -304,5 +306,8 @@ if(BUILD_TESTS) add_executable(RW-Lock-Test test/rw_lock.cpp src/lock/rw_mutex.cpp) target_link_libraries(GenerationTest ${TEST_LIBRARIES}) + + add_executable(PacketLossTest src/protocol/PacketLossCalculator.cpp test/PacketLossCalculateTest.cpp) + target_link_libraries(PacketLossTest) endif() endif() diff --git a/src/Properties.cpp b/src/Properties.cpp index d30c1df..fe3f919 100644 --- a/src/Properties.cpp +++ b/src/Properties.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "log/LogUtils.h" #include "misc/memtracker.h" #include "Properties.h" @@ -21,8 +22,6 @@ bool Properties::has(property::PropertyType type, int index) { if(!*it) continue; if(it->get()->type != type) continue; - index -= (int) it->get()->offset; - if(index < 0) return false; return index < it->get()->length; } return false; @@ -34,10 +33,6 @@ PropertyWrapper Properties::find(property::PropertyType type, int index) { if(bulk->type != type) continue; - index -= (int) bulk->offset; - if(index < 0) - break; - if(index >= bulk->length) break; @@ -72,7 +67,7 @@ std::vector Properties::all_properties() { return result; } -PropertyWrapper::PropertyWrapper(ts::Properties* handle, ts::PropertyData *data, const std::shared_ptr &bundle_lock) : handle(handle), data_ptr(data), bundle_lock(bundle_lock) { +PropertyWrapper::PropertyWrapper(ts::Properties* handle, ts::PropertyData *data, std::shared_ptr bundle_lock) : handle{handle}, data_ptr{data}, bundle_lock{std::move(bundle_lock)} { } void PropertyWrapper::trigger_update() { @@ -84,7 +79,7 @@ void PropertyWrapper::trigger_update() { } } -bool Properties::register_property_type(ts::property::PropertyType type, size_t length, size_t offset) { +bool Properties::register_property_type(ts::property::PropertyType type, size_t length) { for(auto& bulk : this->properties) if(bulk->type == type) return false; @@ -95,7 +90,6 @@ bool Properties::register_property_type(ts::property::PropertyType type, size_t for(int index = 0; index < bundle->length; index++) { auto& property = bundle->properties[index]; - property.description.~shared_ptr(); property.value.~string(); property.value_lock.~spin_lock(); property.casted_value.~any(); @@ -104,7 +98,6 @@ bool Properties::register_property_type(ts::property::PropertyType type, size_t }); ptr->type = type; - ptr->offset = offset; ptr->length = length; for(int index = 0; index < length; index++) { @@ -113,7 +106,7 @@ bool Properties::register_property_type(ts::property::PropertyType type, size_t new (&property.casted_value) any(); new (&property.value_lock) spin_lock(); new (&property.value) string(); - new (&property.description) shared_ptr(property::impl::info(type, (int) (offset + index))); + property.description = &property::describe(type, index); property.flag_modified = false; property.flag_db_reference = false; @@ -127,470 +120,7 @@ bool Properties::register_property_type(ts::property::PropertyType type, size_t namespace ts { namespace property { - PropertyDescription::PropertyDescription(int property_id, PropertyType property_type, const std::string &name, const std::string &default_value, property::ValueType type, property::flag_type flags) noexcept : name(name), default_value(default_value), - type_value(type), flags(flags), property_index(property_id), - type_property(property_type) { - std::transform(this->name.begin(), this->name.end(), this->name.begin(), ::tolower); - } - -#define FLAG_SS (FLAG_SNAPSHOT | FLAG_SAVE) -#define FLAG_SERVER_VV (FLAG_SERVER_VARIABLE | FLAG_SERVER_VIEW) -#define FLAG_SERVER_VVSS (FLAG_SERVER_VV | FLAG_SS) - -#define FLAG_CLIENT_VV (FLAG_CLIENT_VARIABLE | FLAG_CLIENT_VIEW) -#define FLAG_CLIENT_VVSS (FLAG_CLIENT_VV | FLAG_SS) - - array, VirtualServerProperties::VIRTUALSERVER_ENDMARKER> virtualserver_info = { - make_shared(VIRTUALSERVER_UNDEFINED, "VIRTUALSERVER_UNDEFINED", "", TYPE_UNKNOWN, 0), //Must be at index 0! - make_shared(VIRTUALSERVER_ID, "VIRTUALSERVER_ID", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VIEW), //available when connected - make_shared(VIRTUALSERVER_UNIQUE_IDENTIFIER, "VIRTUALSERVER_UNIQUE_IDENTIFIER", "", TYPE_STRING, FLAG_SERVER_VV | FLAG_SNAPSHOT), - make_shared(VIRTUALSERVER_NAME, "VIRTUALSERVER_NAME", "Another TeaSpeak server software user", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), - make_shared(VIRTUALSERVER_WELCOMEMESSAGE, "VIRTUALSERVER_WELCOMEMESSAGE", "Welcome on another TeaSpeak server. (Download now and a license fee is not your cup of tea! [URL]www.teaspeak.de[/URL])", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), - make_shared(VIRTUALSERVER_PLATFORM, "VIRTUALSERVER_PLATFORM", "undefined", TYPE_STRING, FLAG_SERVER_VIEW), - make_shared(VIRTUALSERVER_VERSION, "VIRTUALSERVER_VERSION", "undefined", TYPE_STRING, FLAG_SERVER_VIEW), - make_shared(VIRTUALSERVER_MAXCLIENTS, "VIRTUALSERVER_MAXCLIENTS", "120", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), - make_shared(VIRTUALSERVER_PASSWORD, "VIRTUALSERVER_PASSWORD", "", TYPE_STRING, FLAG_SS | FLAG_USER_EDITABLE), - make_shared(VIRTUALSERVER_CLIENTS_ONLINE, "virtualserver_clientsonline", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE), - make_shared(VIRTUALSERVER_CHANNELS_ONLINE, "virtualserver_channelsonline", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE), //only available on request (=> requestServerVariables), - make_shared(VIRTUALSERVER_QUERYCLIENTS_ONLINE, "virtualserver_queryclientsonline", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_CREATED, "VIRTUALSERVER_CREATED", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VIEW | FLAG_SS), //available when connected, stores the time when the server was created - make_shared(VIRTUALSERVER_UPTIME, "VIRTUALSERVER_UPTIME", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE), //only available on request (=> requestServerVariables), the time since the server was started - make_shared(VIRTUALSERVER_CODEC_ENCRYPTION_MODE, "VIRTUALSERVER_CODEC_ENCRYPTION_MODE", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //available and always up-to-date when connected - - // Rare properties - make_shared(VIRTUALSERVER_KEYPAIR, "VIRTUALSERVER_KEYPAIR", "", TYPE_STRING, FLAG_SS), //internal use - make_shared(VIRTUALSERVER_HOSTMESSAGE, "VIRTUALSERVER_HOSTMESSAGE", "Welcome", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //available when connected, not updated while connected - make_shared(VIRTUALSERVER_HOSTMESSAGE_MODE, "VIRTUALSERVER_HOSTMESSAGE_MODE", "1", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //available when connected, not updated while connected - make_shared(VIRTUALSERVER_DEFAULT_SERVER_GROUP, "VIRTUALSERVER_DEFAULT_SERVER_GROUP", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //the manager permissions server group that a new manager gets assigned - make_shared(VIRTUALSERVER_DEFAULT_MUSIC_GROUP, "VIRTUALSERVER_DEFAULT_MUSIC_GROUP", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //the manager permissions server group that a new manager gets assigned - make_shared(VIRTUALSERVER_DEFAULT_CHANNEL_GROUP, "VIRTUALSERVER_DEFAULT_CHANNEL_GROUP", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //the channel permissions group that a new manager gets assigned when joining a channel - make_shared(VIRTUALSERVER_FLAG_PASSWORD, "VIRTUALSERVER_FLAG_PASSWORD", "0", TYPE_BOOL, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP, "VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SERVER_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //the channel permissions group that a manager gets assigned when creating a channel - make_shared(VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH, "VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH", "-1", TYPE_SIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH, "VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH", "-1", TYPE_SIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_HOSTBANNER_URL, "VIRTUALSERVER_HOSTBANNER_URL", "", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //available when connected, always up-to-date - make_shared(VIRTUALSERVER_HOSTBANNER_GFX_URL, "VIRTUALSERVER_HOSTBANNER_GFX_URL", "", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //available when connected, always up-to-date - make_shared(VIRTUALSERVER_HOSTBANNER_GFX_INTERVAL, "VIRTUALSERVER_HOSTBANNER_GFX_INTERVAL", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //available when connected, always up-to-date - make_shared(VIRTUALSERVER_COMPLAIN_AUTOBAN_COUNT, "VIRTUALSERVER_COMPLAIN_AUTOBAN_COUNT", "5", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_COMPLAIN_AUTOBAN_TIME, "VIRTUALSERVER_COMPLAIN_AUTOBAN_TIME", "5", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_COMPLAIN_REMOVE_TIME, "VIRTUALSERVER_COMPLAIN_REMOVE_TIME", "5", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE, "VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE", "20", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE),//only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_PRIORITY_SPEAKER_DIMM_MODIFICATOR, "VIRTUALSERVER_PRIORITY_SPEAKER_DIMM_MODIFICATOR", "-18", TYPE_FLOAT, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //available when connected, always up-to-date - make_shared(VIRTUALSERVER_ANTIFLOOD_POINTS_TICK_REDUCE, "VIRTUALSERVER_ANTIFLOOD_POINTS_TICK_REDUCE", "25", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_ANTIFLOOD_POINTS_NEEDED_COMMAND_BLOCK, "VIRTUALSERVER_ANTIFLOOD_POINTS_NEEDED_COMMAND_BLOCK", "150", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_ANTIFLOOD_POINTS_NEEDED_IP_BLOCK, "VIRTUALSERVER_ANTIFLOOD_POINTS_NEEDED_IP_BLOCK", "300", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_CLIENT_CONNECTIONS, "VIRTUALSERVER_CLIENT_CONNECTIONS", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_QUERY_CLIENT_CONNECTIONS, "VIRTUALSERVER_QUERY_CLIENT_CONNECTIONS", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_HOSTBUTTON_TOOLTIP, "VIRTUALSERVER_HOSTBUTTON_TOOLTIP", "", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //available when connected, always up-to-date - make_shared(VIRTUALSERVER_HOSTBUTTON_URL, "VIRTUALSERVER_HOSTBUTTON_URL", "", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //available when connected, always up-to-date - make_shared(VIRTUALSERVER_HOSTBUTTON_GFX_URL, "VIRTUALSERVER_HOSTBUTTON_GFX_URL", "", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //available when connected, always up-to-date - make_shared(VIRTUALSERVER_HOSTBANNER_MODE, "VIRTUALSERVER_HOSTBANNER_MODE", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //available when connected, always up-to-date - make_shared(VIRTUALSERVER_FILEBASE, "VIRTUALSERVER_FILEBASE", "", TYPE_STRING, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //not available to clients, stores the folder used for file transfers - make_shared(VIRTUALSERVER_DOWNLOAD_QUOTA, "VIRTUALSERVER_DOWNLOAD_QUOTA", "-1", TYPE_SIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SAVE | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_UPLOAD_QUOTA, "VIRTUALSERVER_UPLOAD_QUOTA", "-1", TYPE_SIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SAVE | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_MONTH_BYTES_DOWNLOADED, "VIRTUALSERVER_MONTH_BYTES_DOWNLOADED", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SAVE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_MONTH_BYTES_UPLOADED, "VIRTUALSERVER_MONTH_BYTES_UPLOADED", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SAVE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_TOTAL_BYTES_DOWNLOADED, "VIRTUALSERVER_TOTAL_BYTES_DOWNLOADED", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SAVE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_TOTAL_BYTES_UPLOADED, "VIRTUALSERVER_TOTAL_BYTES_UPLOADED", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SAVE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_PORT, "VIRTUALSERVER_PORT", "9987", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_HOST, "VIRTUALSERVER_HOST", "0.0.0.0,::", TYPE_STRING, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //internal use | contains comma separated ip list - make_shared(VIRTUALSERVER_AUTOSTART, "VIRTUALSERVER_AUTOSTART", "1", TYPE_BOOL, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_MACHINE_ID, "VIRTUALSERVER_MACHINE_ID", "", TYPE_STRING, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_NEEDED_IDENTITY_SECURITY_LEVEL, "VIRTUALSERVER_NEEDED_IDENTITY_SECURITY_LEVEL", "8", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_LOG_CLIENT, "VIRTUALSERVER_LOG_CLIENT", "1", TYPE_BOOL, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_LOG_QUERY, "VIRTUALSERVER_LOG_QUERY", "1", TYPE_BOOL, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_LOG_CHANNEL, "VIRTUALSERVER_LOG_CHANNEL", "1", TYPE_BOOL, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_LOG_PERMISSIONS, "VIRTUALSERVER_LOG_PERMISSIONS", "1", TYPE_BOOL, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_LOG_SERVER, "VIRTUALSERVER_LOG_SERVER", "1", TYPE_BOOL, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_LOG_FILETRANSFER, "VIRTUALSERVER_LOG_FILETRANSFER", "1", TYPE_BOOL, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_NAME_PHONETIC, "VIRTUALSERVER_NAME_PHONETIC", "", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //available when connected, always up-to-date - make_shared(VIRTUALSERVER_ICON_ID, "VIRTUALSERVER_ICON_ID", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //available when connected, always up-to-date - make_shared(VIRTUALSERVER_RESERVED_SLOTS, "VIRTUALSERVER_RESERVED_SLOTS", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //available when connected, always up-to-date - make_shared(VIRTUALSERVER_TOTAL_PACKETLOSS_SPEECH, "VIRTUALSERVER_TOTAL_PACKETLOSS_SPEECH", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_TOTAL_PACKETLOSS_KEEPALIVE, "VIRTUALSERVER_TOTAL_PACKETLOSS_KEEPALIVE", "0", TYPE_FLOAT, FLAG_SERVER_VARIABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_TOTAL_PACKETLOSS_CONTROL, "VIRTUALSERVER_TOTAL_PACKETLOSS_CONTROL", "0", TYPE_FLOAT, FLAG_SERVER_VARIABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_TOTAL_PACKETLOSS_TOTAL, "VIRTUALSERVER_TOTAL_PACKETLOSS_TOTAL", "0", TYPE_FLOAT, FLAG_SERVER_VARIABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_TOTAL_PING, "VIRTUALSERVER_TOTAL_PING", "0", TYPE_FLOAT, FLAG_SERVER_VARIABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_WEBLIST_ENABLED, "VIRTUALSERVER_WEBLIST_ENABLED", "1", TYPE_BOOL, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY, "VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY", "", TYPE_STRING, FLAG_SAVE), //internal use - make_shared(VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY, "VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY", "1", TYPE_BOOL, FLAG_SERVER_VV | FLAG_SAVE | FLAG_USER_EDITABLE), //available when connected - make_shared(VIRTUALSERVER_CHANNEL_TEMP_DELETE_DELAY_DEFAULT, "VIRTUALSERVER_CHANNEL_TEMP_DELETE_DELAY_DEFAULT", "60", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE), //available when connected, always up-to-date - make_shared(VIRTUALSERVER_MIN_CLIENT_VERSION, "VIRTUALSERVER_MIN_CLIENT_VERSION", "1445512488", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_MIN_ANDROID_VERSION, "VIRTUALSERVER_MIN_ANDROID_VERSION", "1407159763", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_MIN_IOS_VERSION, "VIRTUALSERVER_MIN_IOS_VERSION", "1407159763", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_MIN_WINPHONE_VERSION, "VIRTUALSERVER_MIN_WINPHONE_VERSION", "1407159763", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_MAX_CHANNELS, "VIRTUALSERVER_MAX_CHANNELS", "1000", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - - make_shared(VIRTUALSERVER_LAST_CLIENT_CONNECT, "VIRTUALSERVER_LAST_CLIENT_CONNECT", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_LAST_QUERY_CONNECT, "VIRTUALSERVER_LAST_QUERY_CONNECT", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_LAST_CLIENT_DISCONNECT, "VIRTUALSERVER_LAST_CLIENT_DISCONNECT", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_LAST_QUERY_DISCONNECT, "VIRTUALSERVER_LAST_QUERY_DISCONNECT", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS), //only available on request (=> requestServerVariables) - - make_shared(VIRTUALSERVER_WEB_PORT, "VIRTUALSERVER_WEB_PORT", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_WEB_HOST, "VIRTUALSERVER_WEB_HOST", "0.0.0.0", TYPE_STRING, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - - make_shared(VIRTUALSERVER_DEFAULT_CLIENT_DESCRIPTION, "VIRTUALSERVER_DEFAULT_CLIENT_DESCRIPTION", "", TYPE_STRING, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_DEFAULT_CHANNEL_DESCRIPTION, "VIRTUALSERVER_DEFAULT_CHANNEL_DESCRIPTION", "", TYPE_STRING, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_DEFAULT_CHANNEL_TOPIC, "VIRTUALSERVER_DEFAULT_CHANNEL_TOPIC", "", TYPE_STRING, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - - make_shared(VIRTUALSERVER_MUSIC_BOT_LIMIT, "VIRTUALSERVER_MUSIC_BOT_LIMIT", "-1", TYPE_SIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_SPOKEN_TIME, "VIRTUALSERVER_SPOKEN_TIME", "0", TYPE_UNSIGNED_NUMBER, FLAG_INTERNAL | FLAG_NEW | FLAG_SAVE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_DISABLE_IP_SAVING, "VIRTUALSERVER_DISABLE_IP_SAVING", "0", TYPE_BOOL, FLAG_INTERNAL | FLAG_NEW | FLAG_SAVE | FLAG_USER_EDITABLE), //only available on request (=> requestServerVariables) - make_shared(VIRTUALSERVER_COUNTRY_CODE, "VIRTUALSERVER_COUNTRY_CODE", "XX", TYPE_STRING, FLAG_SERVER_VV | FLAG_SAVE | FLAG_USER_EDITABLE) //available when connected - }; - - array, ChannelProperties::CHANNEL_ENDMARKER> channel_info = { - make_shared(CHANNEL_UNDEFINED, "CHANNEL_UNDEFINED", "", TYPE_UNKNOWN, 0), //Must be at index 0! - make_shared(CHANNEL_ID, "cid", "0", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS), - make_shared(CHANNEL_PID, "cpid", "0", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS), - make_shared(CHANNEL_NAME, "CHANNEL_NAME", "undefined", TYPE_STRING, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_TOPIC, "CHANNEL_TOPIC", "", TYPE_STRING, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_DESCRIPTION, "CHANNEL_DESCRIPTION", "", TYPE_STRING, FLAG_CHANNEL_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE), //Must be requested (=> requestChannelDescription) - make_shared(CHANNEL_PASSWORD, "CHANNEL_PASSWORD", "0", TYPE_STRING, FLAG_SS | FLAG_USER_EDITABLE), //not available manager side - make_shared(CHANNEL_CODEC, "CHANNEL_CODEC", "4", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_CODEC_QUALITY, "CHANNEL_CODEC_QUALITY", "7", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_MAXCLIENTS, "CHANNEL_MAXCLIENTS", "-1", TYPE_SIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_MAXFAMILYCLIENTS, "CHANNEL_MAXFAMILYCLIENTS", "-1", TYPE_SIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_ORDER, "CHANNEL_ORDER", "0", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_FLAG_PERMANENT, "CHANNEL_FLAG_PERMANENT", "1", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_FLAG_SEMI_PERMANENT, "CHANNEL_FLAG_SEMI_PERMANENT", "0", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_FLAG_DEFAULT, "CHANNEL_FLAG_DEFAULT", "0", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_FLAG_PASSWORD, "CHANNEL_FLAG_PASSWORD", "0", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_CODEC_LATENCY_FACTOR, "CHANNEL_CODEC_LATENCY_FACTOR", "1", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_CODEC_IS_UNENCRYPTED, "CHANNEL_CODEC_IS_UNENCRYPTED", "1", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_SECURITY_SALT, "CHANNEL_SECURITY_SALT", "", TYPE_STRING, FLAG_SS), //Not available manager side, not used in teamspeak, only SDK. Sets the options+salt for security hash. - make_shared(CHANNEL_DELETE_DELAY, "CHANNEL_DELETE_DELAY", "0", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //How many seconds to wait before deleting this channel - make_shared(CHANNEL_FLAG_MAXCLIENTS_UNLIMITED, "CHANNEL_FLAG_MAXCLIENTS_UNLIMITED", "1", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED, "CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED", "1", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE),//Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED, "CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED", "0", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE),//Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_FLAG_ARE_SUBSCRIBED, "CHANNEL_FLAG_ARE_SUBSCRIBED", "1", TYPE_BOOL, FLAG_INTERNAL), //Only available manager side, stores whether we are subscribed to this channel - make_shared(CHANNEL_FILEPATH, "CHANNEL_FILEPATH", "", TYPE_STRING, FLAG_SS), //not available manager side, the folder used for file-transfers for this channel - make_shared(CHANNEL_NEEDED_TALK_POWER, "CHANNEL_NEEDED_TALK_POWER", "0", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_FORCED_SILENCE, "CHANNEL_FORCED_SILENCE", "0", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_NAME_PHONETIC, "CHANNEL_NAME_PHONETIC", "", TYPE_STRING, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_ICON_ID, "CHANNEL_ICON_ID", "0", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_FLAG_PRIVATE, "CHANNEL_FLAG_PRIVATE", "0", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_LAST_LEFT, "CHANNEL_LAST_LEFT", "0", TYPE_UNSIGNED_NUMBER, FLAG_SAVE | FLAG_CHANNEL_VIEW | FLAG_CHANNEL_VARIABLE | FLAG_NEW), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_CREATED_AT, "CHANNEL_CREATED_AT", "0", TYPE_UNSIGNED_NUMBER, FLAG_SS | FLAG_CHANNEL_VIEW | FLAG_CHANNEL_VARIABLE | FLAG_NEW), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_CREATED_BY, "CHANNEL_CREATED_BY", "0", TYPE_UNSIGNED_NUMBER, FLAG_SS | FLAG_CHANNEL_VIEW | FLAG_CHANNEL_VARIABLE | FLAG_NEW), //Available for all channels that are "in view", always up-to-date - make_shared(CHANNEL_CONVERSATION_HISTORY_LENGTH, "CHANNEL_CONVERSATION_HISTORY_LENGTH", "1500", TYPE_SIGNED_NUMBER, FLAG_SS | FLAG_CHANNEL_VIEW | FLAG_CHANNEL_VARIABLE | FLAG_NEW | FLAG_USER_EDITABLE), - make_shared(CHANNEL_FLAG_CONVERSATION_PRIVATE, "CHANNEL_FLAG_CONVERSATION_PRIVATE", "0", TYPE_BOOL, FLAG_SS | FLAG_CHANNEL_VIEW | FLAG_CHANNEL_VARIABLE | FLAG_NEW | FLAG_USER_EDITABLE) - }; - - array, GroupProperties::GROUP_ENDMARKER> group_info = { - make_shared(GROUP_UNDEFINED, "GROUP_UNDEFINED", "", TYPE_UNKNOWN, 0), - make_shared(GROUP_ID, "gid", "0", TYPE_UNSIGNED_NUMBER, FLAG_INTERNAL), - make_shared(GROUP_TYPE, "type", "0", TYPE_UNSIGNED_NUMBER, FLAG_GROUP_VIEW), - make_shared(GROUP_NAME, "name", "Undefined group", TYPE_STRING, FLAG_GROUP_VIEW), - make_shared(GROUP_SORTID, "sortid", "0", TYPE_UNSIGNED_NUMBER, FLAG_GROUP_VIEW), - make_shared(GROUP_SAVEDB, "savedb", "0", TYPE_BOOL, FLAG_GROUP_VIEW), - make_shared(GROUP_NAMEMODE, "namemode", "0", TYPE_UNSIGNED_NUMBER, FLAG_GROUP_VIEW), - make_shared(GROUP_ICONID, "iconid", "0", TYPE_UNSIGNED_NUMBER, FLAG_GROUP_VIEW) - }; - - array, ClientProperties::CLIENT_ENDMARKER> client_info = { - make_shared(CLIENT_UNDEFINED, "CLIENT_UNDEFINED", "undefined", TYPE_UNKNOWN, 0), - make_shared(CLIENT_UNIQUE_IDENTIFIER, "CLIENT_UNIQUE_IDENTIFIER", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_SNAPSHOT | FLAG_GLOBAL), //automatically up-to-date for any manager "in view", can be used to identify this particular manager installation - make_shared(CLIENT_NICKNAME, "CLIENT_NICKNAME", "undefined", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_SAVE_MUSIC | FLAG_SNAPSHOT | FLAG_GLOBAL | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view" - make_shared(CLIENT_VERSION, "CLIENT_VERSION", "unknown", TYPE_STRING, FLAG_CLIENT_VVSS | FLAG_USER_EDITABLE | FLAG_GLOBAL | FLAG_SAVE_MUSIC), //for other clients than ourself, this needs to be requested (=> requestClientVariables) - make_shared(CLIENT_PLATFORM, "CLIENT_PLATFORM", "unknown", TYPE_STRING, FLAG_CLIENT_VVSS | FLAG_USER_EDITABLE | FLAG_GLOBAL | FLAG_SAVE_MUSIC), //for other clients than ourself, this needs to be requested (=> requestClientVariables) - make_shared(CLIENT_FLAG_TALKING, "CLIENT_FLAG_TALKING", "0", TYPE_BOOL, FLAG_INTERNAL), //automatically up-to-date for any manager that can be heard (in room / whisper) - make_shared(CLIENT_INPUT_MUTED, "CLIENT_INPUT_MUTED", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view", this clients microphone mute status - make_shared(CLIENT_OUTPUT_MUTED, "CLIENT_OUTPUT_MUTED", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view", this clients headphones/speakers/mic combined mute status - make_shared(CLIENT_OUTPUTONLY_MUTED, "CLIENT_OUTPUTONLY_MUTED", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view", this clients headphones/speakers only mute status - make_shared(CLIENT_INPUT_HARDWARE, "CLIENT_INPUT_HARDWARE", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view", this clients microphone hardware status (is the capture device opened?) - make_shared(CLIENT_OUTPUT_HARDWARE, "CLIENT_OUTPUT_HARDWARE", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view", this clients headphone/speakers hardware status (is the playback device opened?) - make_shared(CLIENT_DEFAULT_CHANNEL, "CLIENT_DEFAULT_CHANNEL", "", TYPE_STRING, FLAG_INTERNAL), //only usable for ourself, the default channel we used to connect on our last connection attempt - make_shared(CLIENT_DEFAULT_CHANNEL_PASSWORD, "CLIENT_DEFAULT_CHANNEL_PASSWORD", "", TYPE_STRING, FLAG_INTERNAL), //internal use - make_shared(CLIENT_SERVER_PASSWORD, "CLIENT_SERVER_PASSWORD", "", TYPE_STRING, FLAG_INTERNAL), //internal use - make_shared(CLIENT_META_DATA, "CLIENT_META_DATA", "", TYPE_STRING, FLAG_CLIENT_VIEW| FLAG_GLOBAL|FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view", not used by TeamSpeak, free storage for sdk users - make_shared(CLIENT_IS_RECORDING, "CLIENT_IS_RECORDING", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view" - make_shared(CLIENT_VERSION_SIGN, "CLIENT_VERSION_SIGN", "", TYPE_STRING, FLAG_INTERNAL), //sign - make_shared(CLIENT_SECURITY_HASH, "CLIENT_SECURITY_HASH", "", TYPE_STRING, FLAG_INTERNAL), //SDK use, not used by teamspeak. Hash is provided by an outside source. A channel will use the security salt + other manager data to calculate a hash, which must be the same as the one provided here. - - //Rare properties - make_shared(CLIENT_KEY_OFFSET, "CLIENT_KEY_OFFSET", "0", TYPE_UNSIGNED_NUMBER, FLAG_INTERNAL), //internal use - make_shared(CLIENT_LOGIN_NAME, "CLIENT_LOGIN_NAME", "", TYPE_STRING, FLAG_CLIENT_VARIABLE| FLAG_GLOBAL), //used for serverquery clients, makes no sense on normal clients currently - make_shared(CLIENT_LOGIN_PASSWORD, "CLIENT_LOGIN_PASSWORD", "", TYPE_STRING, FLAG_INTERNAL| FLAG_GLOBAL), //used for serverquery clients, makes no sense on normal clients currently - make_shared(CLIENT_DATABASE_ID, "CLIENT_DATABASE_ID", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW | FLAG_GLOBAL), //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds database manager id - make_shared(CLIENT_ID, "clid", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VV), //clid! - make_shared(CLIENT_HARDWARE_ID, "hwid", "", TYPE_STRING, FLAG_SAVE), //hwid! - make_shared(CLIENT_CHANNEL_GROUP_ID, "CLIENT_CHANNEL_GROUP_ID", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW), //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds database manager id - make_shared(CLIENT_SERVERGROUPS, "CLIENT_SERVERGROUPS", "0", TYPE_STRING, FLAG_CLIENT_VIEW), //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds all servergroups manager belongs too - make_shared(CLIENT_CREATED, "CLIENT_CREATED", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_SAVE_MUSIC | FLAG_SNAPSHOT | FLAG_GLOBAL), //this needs to be requested (=> requestClientVariables), first time this manager connected to this server - make_shared(CLIENT_LASTCONNECTED, "CLIENT_LASTCONNECTED", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_SNAPSHOT | FLAG_GLOBAL), //this needs to be requested (=> requestClientVariables), last time this manager connected to this server - make_shared(CLIENT_TOTALCONNECTIONS, "CLIENT_TOTALCONNECTIONS", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_CLIENT_VIEW | FLAG_SNAPSHOT | FLAG_GLOBAL), //this needs to be requested (=> requestClientVariables), how many times this manager connected to this server - make_shared(CLIENT_AWAY, "CLIENT_AWAY", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view", this clients away status - make_shared(CLIENT_AWAY_MESSAGE, "CLIENT_AWAY_MESSAGE", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view", this clients away message - make_shared(CLIENT_TYPE, "CLIENT_TYPE", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW | FLAG_GLOBAL), //automatically up-to-date for any manager "in view", determines if this is a real manager or a server-query connection - make_shared(CLIENT_TYPE_EXACT, "CLIENT_TYPE_EXACT", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW | FLAG_NEW | FLAG_GLOBAL), //automatically up-to-date for any manager "in view", determines if this is a real manager or a server-query connection - make_shared(CLIENT_FLAG_AVATAR, "CLIENT_FLAG_AVATAR", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_SAVE | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view", this manager got an avatar - make_shared(CLIENT_TALK_POWER, "CLIENT_TALK_POWER", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW), //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds database manager id - make_shared(CLIENT_TALK_REQUEST, "CLIENT_TALK_REQUEST", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds timestamp where manager requested to talk - make_shared(CLIENT_TALK_REQUEST_MSG, "CLIENT_TALK_REQUEST_MSG", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds matter for the request - make_shared(CLIENT_DESCRIPTION, "CLIENT_DESCRIPTION", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view" - make_shared(CLIENT_IS_TALKER, "CLIENT_IS_TALKER", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view" - make_shared(CLIENT_MONTH_BYTES_UPLOADED, "CLIENT_MONTH_BYTES_UPLOADED", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_SAVE), //this needs to be requested (=> requestClientVariables) - make_shared(CLIENT_MONTH_BYTES_DOWNLOADED, "CLIENT_MONTH_BYTES_DOWNLOADED", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_SAVE), //this needs to be requested (=> requestClientVariables) - make_shared(CLIENT_TOTAL_BYTES_UPLOADED, "CLIENT_TOTAL_BYTES_UPLOADED", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_SAVE), //this needs to be requested (=> requestClientVariables) - make_shared(CLIENT_TOTAL_BYTES_DOWNLOADED, "CLIENT_TOTAL_BYTES_DOWNLOADED", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_SAVE), //this needs to be requested (=> requestClientVariables) - make_shared(CLIENT_TOTAL_ONLINE_TIME, "CLIENT_TOTAL_ONLINE_TIME", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_SAVE | FLAG_NEW), //this needs to be requested (=> requestClientVariables) - make_shared(CLIENT_MONTH_ONLINE_TIME, "CLIENT_MONTH_ONLINE_TIME", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_SAVE | FLAG_NEW), //this needs to be requested (=> requestClientVariables) - make_shared(CLIENT_IS_PRIORITY_SPEAKER, "CLIENT_IS_PRIORITY_SPEAKER", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view" - make_shared(CLIENT_UNREAD_MESSAGES, "CLIENT_UNREAD_MESSAGES", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW), //automatically up-to-date for any manager "in view" - make_shared(CLIENT_NICKNAME_PHONETIC, "CLIENT_NICKNAME_PHONETIC", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view" - make_shared(CLIENT_NEEDED_SERVERQUERY_VIEW_POWER, "CLIENT_NEEDED_SERVERQUERY_VIEW_POWER", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW), //automatically up-to-date for any manager "in view" - make_shared(CLIENT_DEFAULT_TOKEN, "CLIENT_DEFAULT_TOKEN", "", TYPE_STRING, FLAG_INTERNAL), //only usable for ourself, the default token we used to connect on our last connection attempt - make_shared(CLIENT_ICON_ID, "CLIENT_ICON_ID", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW | FLAG_CLIENT_VARIABLE), //automatically up-to-date for any manager "in view" - make_shared(CLIENT_IS_CHANNEL_COMMANDER, "CLIENT_IS_CHANNEL_COMMANDER", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE | FLAG_SAVE_MUSIC), //automatically up-to-date for any manager "in view" - make_shared(CLIENT_COUNTRY, "CLIENT_COUNTRY", "TS", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_CLIENT_VARIABLE | FLAG_GLOBAL | FLAG_SAVE_MUSIC | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view" - make_shared(CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID, "CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW), //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, contains channel_id where the channel_group_id is set from - make_shared(CLIENT_BADGES, "CLIENT_BADGES", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view", stores icons for partner badges - make_shared(CLIENT_MYTEAMSPEAK_ID, "CLIENT_MYTEAMSPEAK_ID", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_SS | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view", stores icons for partner badges - make_shared(CLIENT_INTEGRATIONS, "CLIENT_INTEGRATIONS", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE), //automatically up-to-date for any manager "in view", stores icons for partner badges - make_shared(CLIENT_ACTIVE_INTEGRATIONS_INFO, "CLIENT_ACTIVE_INTEGRATIONS_INFO", "", TYPE_STRING, FLAG_INTERNAL | FLAG_USER_EDITABLE), - - //Using FLAG_GLOBAL here,lse they will be overridden on clientinit - make_shared(CLIENT_TEAFORO_ID, "CLIENT_TEAFORO_ID", "0", TYPE_UNSIGNED_NUMBER, FLAG_NEW | FLAG_CLIENT_VIEW | FLAG_GLOBAL), - make_shared(CLIENT_TEAFORO_NAME, "CLIENT_TEAFORO_NAME", "", TYPE_STRING, FLAG_NEW | FLAG_CLIENT_VIEW | FLAG_GLOBAL), - make_shared(CLIENT_TEAFORO_FLAGS, "CLIENT_TEAFORO_FLAGS", "0", TYPE_UNSIGNED_NUMBER, FLAG_NEW | FLAG_CLIENT_VIEW | FLAG_GLOBAL), - - //Music bot stuff - make_shared(CLIENT_OWNER, "CLIENT_OWNER", "0", TYPE_UNSIGNED_NUMBER, FLAG_NEW | FLAG_CLIENT_VIEW), - make_shared(CLIENT_BOT_TYPE, "CLIENT_BOT_TYPE", "0", TYPE_UNSIGNED_NUMBER, FLAG_NEW | FLAG_SAVE_MUSIC | FLAG_USER_EDITABLE | FLAG_CLIENT_VIEW), - make_shared(CLIENT_LAST_CHANNEL, "CLIENT_LAST_CHANNEL", "0", TYPE_UNSIGNED_NUMBER, FLAG_NEW | FLAG_INTERNAL | FLAG_SAVE_MUSIC), - make_shared(CLIENT_PLAYER_STATE, "PLAYER_STATE", "0", TYPE_UNSIGNED_NUMBER, FLAG_NEW | FLAG_CLIENT_VIEW | FLAG_SAVE_MUSIC), - make_shared(CLIENT_PLAYER_VOLUME, "PLAYER_VOLUME", "1", TYPE_FLOAT, FLAG_NEW | FLAG_SAVE_MUSIC | FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE), - make_shared(CLIENT_PLAYLIST_ID, "CLIENT_PLAYLIST_ID", "0", TYPE_UNSIGNED_NUMBER, FLAG_NEW | FLAG_CLIENT_VARIABLE | FLAG_SAVE_MUSIC), - make_shared(CLIENT_DISABLED, "CLIENT_DISABLED", "0", TYPE_BOOL, FLAG_NEW | FLAG_CLIENT_VARIABLE), - make_shared(CLIENT_UPTIME_MODE, "CLIENT_UPTIME_MODE", "0", TYPE_UNSIGNED_NUMBER, FLAG_NEW | FLAG_CLIENT_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE_MUSIC), - make_shared(CLIENT_FLAG_NOTIFY_SONG_CHANGE, "CLIENT_FLAG_NOTIFY_SONG_CHANGE", "1", TYPE_BOOL, FLAG_NEW | FLAG_CLIENT_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE_MUSIC) - }; - - #define str(s) #s - #define V(key, flags) key, str(key), "0", TYPE_UNSIGNED_NUMBER, flags - #define F(key, flags) key, str(key), "0", TYPE_FLOAT, flags - - array, ConnectionProperties::CONNECTION_ENDMARKER> connection_info = { - make_shared(CONNECTION_UNDEFINED, "CONNECTION_UNDEFINED", "undefined", TYPE_UNKNOWN, 0), - make_shared(CONNECTION_PING, "CONNECTION_PING", "0", TYPE_UNSIGNED_NUMBER, 0), //average latency for a round trip through and back this connection - make_shared(CONNECTION_PING_DEVIATION, "CONNECTION_PING_DEVIATION", "0", TYPE_UNSIGNED_NUMBER, 0), //standard deviation of the above average latency - make_shared(CONNECTION_CONNECTED_TIME, "CONNECTION_CONNECTED_TIME", "0", TYPE_UNSIGNED_NUMBER, 0), //how long the connection exists already - make_shared(CONNECTION_IDLE_TIME, "CONNECTION_IDLE_TIME", "0", TYPE_UNSIGNED_NUMBER, 0), //how long since the last action of this manager - make_shared(CONNECTION_CLIENT_IP, "CONNECTION_CLIENT_IP", "", TYPE_STRING, FLAG_SAVE), //NEED DB SAVE! //IP of this manager (as seen from the server side) - make_shared(CONNECTION_CLIENT_PORT, "CONNECTION_CLIENT_PORT", "0", TYPE_UNSIGNED_NUMBER, 0), //Port of this manager (as seen from the server side) - make_shared(CONNECTION_SERVER_IP, "CONNECTION_SERVER_IP", "", TYPE_STRING, 0), //IP of the server (seen from the manager side) - only available on yourself, not for remote clients, not available server side - make_shared(CONNECTION_SERVER_PORT, "CONNECTION_SERVER_PORT", "0", TYPE_UNSIGNED_NUMBER, 0), //Port of the server (seen from the manager side) - only available on yourself, not for remote clients, not available server side - make_shared(V(CONNECTION_PACKETS_SENT_SPEECH, 0)), //how many Speech packets were sent through this connection - make_shared(V(CONNECTION_PACKETS_SENT_KEEPALIVE, 0)), - make_shared(V(CONNECTION_PACKETS_SENT_CONTROL,0)), - make_shared(V(CONNECTION_PACKETS_SENT_TOTAL, FLAG_CLIENT_INFO)), //how many packets were sent totally (this is PACKETS_SENT_SPEECH + PACKETS_SENT_KEEPALIVE + PACKETS_SENT_CONTROL) - make_shared(V(CONNECTION_BYTES_SENT_SPEECH, 0)), - make_shared(V(CONNECTION_BYTES_SENT_KEEPALIVE, 0)), - make_shared(V(CONNECTION_BYTES_SENT_CONTROL, 0)), - make_shared(V(CONNECTION_BYTES_SENT_TOTAL, FLAG_CLIENT_INFO)), - make_shared(V(CONNECTION_PACKETS_RECEIVED_SPEECH, 0)), - make_shared(V(CONNECTION_PACKETS_RECEIVED_KEEPALIVE, 0)), - make_shared(V(CONNECTION_PACKETS_RECEIVED_CONTROL, 0)), - make_shared(V(CONNECTION_PACKETS_RECEIVED_TOTAL, FLAG_CLIENT_INFO)), - make_shared(V(CONNECTION_BYTES_RECEIVED_SPEECH, 0)), - make_shared(V(CONNECTION_BYTES_RECEIVED_KEEPALIVE, 0)), - make_shared(V(CONNECTION_BYTES_RECEIVED_CONTROL, 0)), - make_shared(V(CONNECTION_BYTES_RECEIVED_TOTAL, FLAG_CLIENT_INFO)), - make_shared(F(CONNECTION_PACKETLOSS_SPEECH, 0)), - make_shared(F(CONNECTION_PACKETLOSS_KEEPALIVE, 0)), - make_shared(F(CONNECTION_PACKETLOSS_CONTROL, 0)), - make_shared(F(CONNECTION_PACKETLOSS_TOTAL, FLAG_CLIENT_INFO)), //the probability with which a packet round trip failed because a packet was lost - make_shared(F(CONNECTION_SERVER2CLIENT_PACKETLOSS_SPEECH, 0)), //the probability with which a speech packet failed from the server to the manager - make_shared(F(CONNECTION_SERVER2CLIENT_PACKETLOSS_KEEPALIVE, 0)), - make_shared(F(CONNECTION_SERVER2CLIENT_PACKETLOSS_CONTROL, 0)), - make_shared(F(CONNECTION_SERVER2CLIENT_PACKETLOSS_TOTAL, FLAG_CLIENT_INFO)), - make_shared(F(CONNECTION_CLIENT2SERVER_PACKETLOSS_SPEECH, 0)), - make_shared(F(CONNECTION_CLIENT2SERVER_PACKETLOSS_KEEPALIVE, 0)), - make_shared(F(CONNECTION_CLIENT2SERVER_PACKETLOSS_CONTROL, 0)), - make_shared(F(CONNECTION_CLIENT2SERVER_PACKETLOSS_TOTAL, FLAG_CLIENT_INFO)), - make_shared(V(CONNECTION_BANDWIDTH_SENT_LAST_SECOND_SPEECH, 0)), //howmany bytes of speech packets we sent during the last second - make_shared(V(CONNECTION_BANDWIDTH_SENT_LAST_SECOND_KEEPALIVE, 0)), - make_shared(V(CONNECTION_BANDWIDTH_SENT_LAST_SECOND_CONTROL, 0)), - make_shared(V(CONNECTION_BANDWIDTH_SENT_LAST_SECOND_TOTAL,FLAG_CLIENT_INFO)), - make_shared(V(CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_SPEECH,0)), //howmany bytes/s of speech packets we sent in average during the last minute - make_shared(V(CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_KEEPALIVE,0)), - make_shared(V(CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_CONTROL,0)), - make_shared(V(CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_TOTAL,FLAG_CLIENT_INFO)), - make_shared(V(CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_SPEECH, 0)), - make_shared(V(CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_KEEPALIVE,0)), - make_shared(V(CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_CONTROL,0)), - make_shared(V(CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_TOTAL,FLAG_CLIENT_INFO)), - make_shared(V(CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_SPEECH,0)), - make_shared(V(CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_KEEPALIVE,0)), - make_shared(V(CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_CONTROL,0)), - make_shared(V(CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_TOTAL,FLAG_CLIENT_INFO)), - - //Rare properties - make_shared(V(CONNECTION_FILETRANSFER_BANDWIDTH_SENT, FLAG_CLIENT_INFO)), //how many bytes per second are currently being sent by file transfers - make_shared(V(CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED, FLAG_CLIENT_INFO)), //how many bytes per second are currently being received by file transfers - make_shared(V(CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL, FLAG_CLIENT_INFO)), //how many bytes we received in total through file transfers - make_shared(V(CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL, FLAG_CLIENT_INFO)) //how many bytes we sent in total through file transfers - }; - - array, InstanceProperties::SERVERINSTANCE_ENDMARKER> instance_info = { - make_shared(SERVERINSTANCE_UNDEFINED, "SERVERINSTANCE_UNDEFINED", "undefined", TYPE_UNKNOWN, 0), - make_shared(SERVERINSTANCE_DATABASE_VERSION, "SERVERINSTANCE_DATABASE_VERSION", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE), - make_shared(SERVERINSTANCE_PERMISSIONS_VERSION, "SERVERINSTANCE_PERMISSIONS_VERSION", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE), - make_shared(SERVERINSTANCE_FILETRANSFER_HOST, "SERVERINSTANCE_FILETRANSFER_HOST", "0.0.0.0,[::]", TYPE_STRING, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_FILETRANSFER_PORT, "SERVERINSTANCE_FILETRANSFER_PORT", "30303", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_FILETRANSFER_MAX_CONNECTIONS, "SERVERINSTANCE_FILETRANSFER_MAX_CONNECTIONS", "100", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_FILETRANSFER_MAX_CONNECTIONS_PER_IP, "SERVERINSTANCE_FILETRANSFER_MAX_CONNECTIONS_PER_IP", "20", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_QUERY_HOST, "SERVERINSTANCE_QUERY_HOST", "0.0.0.0,[::]", TYPE_STRING, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_QUERY_PORT, "SERVERINSTANCE_QUERY_PORT", "10101", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_QUERY_MAX_CONNECTIONS, "SERVERINSTANCE_QUERY_MAX_CONNECTIONS", "100", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_QUERY_MAX_CONNECTIONS_PER_IP, "SERVERINSTANCE_QUERY_MAX_CONNECTIONS_PER_IP", "3", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_MONTHLY_TIMESTAMP, "SERVERINSTANCE_MONTHLY_TIMESTAMP", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_MAX_DOWNLOAD_TOTAL_BANDWIDTH, "SERVERINSTANCE_MAX_DOWNLOAD_TOTAL_BANDWIDTH", "-1", TYPE_SIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_MAX_UPLOAD_TOTAL_BANDWIDTH, "SERVERINSTANCE_MAX_UPLOAD_TOTAL_BANDWIDTH", "-1", TYPE_SIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_SERVERQUERY_FLOOD_COMMANDS, "SERVERINSTANCE_SERVERQUERY_FLOOD_COMMANDS", "3", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), //how many commands we can issue while in the SERVERINSTANCE_SERVERQUERY_FLOOD_TIME window - make_shared(SERVERINSTANCE_SERVERQUERY_FLOOD_TIME, "SERVERINSTANCE_SERVERQUERY_FLOOD_TIME", "1", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), //time window in seconds for max command execution check - make_shared(SERVERINSTANCE_SERVERQUERY_BAN_TIME, "SERVERINSTANCE_SERVERQUERY_BAN_TIME", "600", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), //how many seconds someone get banned if he floods - make_shared(SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP, "SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP, "SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP, "SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP, "SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP, "SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_GUEST_SERVERQUERY_GROUP, "SERVERINSTANCE_GUEST_SERVERQUERY_GROUP", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_ADMIN_SERVERQUERY_GROUP, "SERVERINSTANCE_ADMIN_SERVERQUERY_GROUP", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_PENDING_CONNECTIONS_PER_IP, "SERVERINSTANCE_PENDING_CONNECTIONS_PER_IP", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE), - make_shared(SERVERINSTANCE_SPOKEN_TIME_TOTAL, "SERVERINSTANCE_SPOKEN_TIME_TOTAL", "0", TYPE_UNSIGNED_NUMBER, FLAG_INTERNAL | FLAG_SAVE | FLAG_NEW), - make_shared(SERVERINSTANCE_SPOKEN_TIME_DELETED, "SERVERINSTANCE_SPOKEN_TIME_DELETED", "0", TYPE_UNSIGNED_NUMBER, FLAG_INTERNAL | FLAG_SAVE | FLAG_NEW), - make_shared(SERVERINSTANCE_SPOKEN_TIME_ALIVE, "SERVERINSTANCE_SPOKEN_TIME_ALIVE", "0", TYPE_UNSIGNED_NUMBER, FLAG_INTERNAL | FLAG_SAVE | FLAG_NEW), - make_shared(SERVERINSTANCE_SPOKEN_TIME_VARIANZ, "SERVERINSTANCE_SPOKEN_TIME_VARIANZ", "0", TYPE_UNSIGNED_NUMBER, FLAG_INTERNAL | FLAG_SAVE | FLAG_NEW), - make_shared(SERVERINSTANCE_VIRTUAL_SERVER_ID_INDEX, "SERVERINSTANCE_VIRTUAL_SERVER_ID_INDEX", "1", TYPE_UNSIGNED_NUMBER, FLAG_SAVE | FLAG_INSTANCE_VARIABLE | FLAG_NEW), - make_shared(SERVERINSTANCE_UNIQUE_ID, "SERVERINSTANCE_UNIQUE_ID", "", TYPE_STRING, FLAG_INTERNAL | FLAG_SAVE | FLAG_NEW), - }; - - - array, PlaylistProperties::PLAYLIST_ENDMARKER> playlist_info = { - make_shared(PLAYLIST_UNDEFINED, "PLAYLIST_UNDEFINED", "undefined", TYPE_UNKNOWN, 0), - make_shared(PLAYLIST_ID, "PLAYLIST_ID", "0", TYPE_UNSIGNED_NUMBER, FLAG_PLAYLIST_VARIABLE), - make_shared(PLAYLIST_TYPE, "PLAYLIST_TYPE", "0", TYPE_UNSIGNED_NUMBER, FLAG_PLAYLIST_VARIABLE | FLAG_SAVE), - make_shared(PLAYLIST_TITLE, "PLAYLIST_TITLE", "Yet another playlist", TYPE_STRING, FLAG_PLAYLIST_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE), - make_shared(PLAYLIST_DESCRIPTION, "PLAYLIST_DESCRIPTION", "", TYPE_STRING, FLAG_PLAYLIST_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE), - make_shared(PLAYLIST_OWNER_DBID, "PLAYLIST_OWNER_DBID", "0", TYPE_UNSIGNED_NUMBER, FLAG_PLAYLIST_VARIABLE | FLAG_SAVE), - make_shared(PLAYLIST_OWNER_NAME, "PLAYLIST_OWNER_NAME", "0", TYPE_STRING, FLAG_PLAYLIST_VARIABLE | FLAG_SAVE), - make_shared(PLAYLIST_FLAG_DELETE_PLAYED, "PLAYLIST_FLAG_DELETE_PLAYED", "1", TYPE_BOOL, FLAG_PLAYLIST_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE), - make_shared(PLAYLIST_REPLAY_MODE, "PLAYLIST_REPLAY_MODE", "0", TYPE_UNSIGNED_NUMBER, FLAG_PLAYLIST_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE), - make_shared(PLAYLIST_CURRENT_SONG_ID, "PLAYLIST_CURRENT_SONG_ID", "0", TYPE_UNSIGNED_NUMBER, FLAG_PLAYLIST_VARIABLE | FLAG_SAVE), - make_shared(PLAYLIST_FLAG_FINISHED, "PLAYLIST_FLAG_FINISHED", "0", TYPE_BOOL, FLAG_PLAYLIST_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE), - make_shared(PLAYLIST_MAX_SONGS, "PLAYLIST_MAX_SONGS", "-1", TYPE_SIGNED_NUMBER, FLAG_PLAYLIST_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE), - }; - - shared_ptr PropertyDescription::unknown = make_shared(UNKNOWN_UNDEFINED, "undefined", "", ValueType::TYPE_UNKNOWN, 0); namespace impl { - #define M(type, _info) \ - template <> \ - const std::shared_ptr& info(type type) { \ - for(const auto& element : (_info)) \ - if(element->property_index == (type)) \ - return element; \ - return (_info)[0]; \ - } \ - \ - template <> \ - const std::shared_ptr& info(const std::string& _type) { \ - for(const auto& element : (_info)) \ - if(element->name == _type) \ - return element; \ - return (_info)[0]; \ - } \ - \ - \ - template <> \ - std::deque> list() { \ - return {_info.begin(), _info.end()}; \ - } - - M(VirtualServerProperties, virtualserver_info); - M(ChannelProperties, channel_info); - M(GroupProperties, group_info); - M(ClientProperties, client_info); - M(ConnectionProperties, connection_info); - M(InstanceProperties, instance_info); - M(PlaylistProperties, playlist_info); - - - #define TP(info) \ - { for(const auto& prop : (info)) \ - if(prop->name == key) return prop; } - - const std::shared_ptr& info_key(const std::string& key) { - TP(virtualserver_info); - TP(channel_info); - TP(group_info); - TP(client_info); - TP(connection_info); - TP(instance_info); - TP(playlist_info); - return PropertyDescription::unknown; - } - - - const std::shared_ptr& info_key(PropertyType type, const std::string& key) { - if(type == PropertyType::PROP_TYPE_SERVER) - TP(virtualserver_info) - else if(type == PropertyType::PROP_TYPE_CHANNEL) - TP(channel_info) - else if(type == PropertyType::PROP_TYPE_CLIENT) - TP(client_info) - else if(type == PropertyType::PROP_TYPE_GROUP) - TP(group_info) - else if(type == PropertyType::PROP_TYPE_CONNECTION) - TP(connection_info) - else if(type == PropertyType::PROP_TYPE_INSTANCE) - TP(instance_info) - else if(type == PropertyType::PROP_TYPE_PLAYLIST) - TP(playlist_info); - return PropertyDescription::unknown; - } - - const std::shared_ptr& info(PropertyType type, int index) { - if(type == PropertyType::PROP_TYPE_SERVER) - return impl::info((VirtualServerProperties) index); - else if(type == PropertyType::PROP_TYPE_CHANNEL) - return impl::info((ChannelProperties) index); - else if(type == PropertyType::PROP_TYPE_CLIENT) - return impl::info((ClientProperties) index); - else if(type == PropertyType::PROP_TYPE_GROUP) - return impl::info((GroupProperties) index); - else if(type == PropertyType::PROP_TYPE_CONNECTION) - return impl::info((ConnectionProperties) index); - else if(type == PropertyType::PROP_TYPE_INSTANCE) - return impl::info((InstanceProperties) index); - else if(type == PropertyType::PROP_TYPE_PLAYLIST) - return impl::info((PlaylistProperties) index); - return PropertyDescription::unknown; - } - - #define M1(info) \ - for(const auto& e : (info)){ \ - auto found = find(used.begin(), used.end(), e->name); \ - if(found != used.end()) { \ - cerr << "[CRITICAL] Double registered property! (" << e->name << ")" << endl; \ - return false; \ - } \ - used.push_back(e->name); \ - } - - bool validateUnique() { - deque used; - M1(virtualserver_info); - M1(channel_info); - M1(group_info); - M1(client_info); - M1(connection_info); - M1(instance_info); - M1(playlist_info); - return true; - } - bool validateInput(const std::string& input, ValueType type) { if(type == ValueType::TYPE_UNKNOWN) return true; else if(type == ValueType::TYPE_UNSIGNED_NUMBER) { diff --git a/src/Properties.h b/src/Properties.h index 6744781..df34b45 100644 --- a/src/Properties.h +++ b/src/Properties.h @@ -17,9 +17,11 @@ #include "misc/spin_lock.h" #include "converters/converter.h" -#define PROPS_DEFINED -namespace ts { +#ifdef NDEBUG + #define EXTERNALIZE_PROPERTY_DEFINITIONS +#endif +namespace ts { namespace property { enum PropertyType { PROP_TYPE_SERVER = 0, @@ -29,8 +31,10 @@ namespace ts { PROP_TYPE_INSTANCE = 4, PROP_TYPE_CONNECTION = 5, PROP_TYPE_PLAYLIST = 6, - PROP_TYPE_UNKNOWN = 7 + PROP_TYPE_UNKNOWN = 7, + PROP_TYPE_MAX }; + static constexpr const char *PropertyType_Names[7] = { "SERVER", "CHANNEL", @@ -134,12 +138,12 @@ namespace ts { VIRTUALSERVER_MAXCLIENTS, //only available on request (=> requestServerVariables), stores the maximum number of clients that may currently join the server VIRTUALSERVER_PASSWORD, //not available to clients, the server password VIRTUALSERVER_CLIENTS_ONLINE, //only available on request (=> requestServerVariables), + VIRTUALSERVER_QUERYCLIENTS_ONLINE, //only available on request (=> requestServerVariables) VIRTUALSERVER_CHANNELS_ONLINE, //only available on request (=> requestServerVariables), VIRTUALSERVER_CREATED, //available when connected, stores the time when the server was created VIRTUALSERVER_UPTIME, //only available on request (=> requestServerVariables), the time since the server was started - VIRTUALSERVER_CODEC_ENCRYPTION_MODE, //available and always up-to-date when connected - //Rare properties + VIRTUALSERVER_CODEC_ENCRYPTION_MODE, //available and always up-to-date when connected VIRTUALSERVER_KEYPAIR, //internal use VIRTUALSERVER_HOSTMESSAGE, //available when connected, not updated while connected VIRTUALSERVER_HOSTMESSAGE_MODE, //available when connected, not updated while connected @@ -154,6 +158,7 @@ namespace ts { VIRTUALSERVER_HOSTBANNER_URL, //available when connected, always up-to-date VIRTUALSERVER_HOSTBANNER_GFX_URL, //available when connected, always up-to-date VIRTUALSERVER_HOSTBANNER_GFX_INTERVAL, //available when connected, always up-to-date + VIRTUALSERVER_HOSTBANNER_MODE, //available when connected, always up-to-date VIRTUALSERVER_COMPLAIN_AUTOBAN_COUNT, //only available on request (=> requestServerVariables) VIRTUALSERVER_COMPLAIN_AUTOBAN_TIME, //only available on request (=> requestServerVariables) VIRTUALSERVER_COMPLAIN_REMOVE_TIME, //only available on request (=> requestServerVariables) @@ -168,7 +173,6 @@ namespace ts { VIRTUALSERVER_HOSTBUTTON_TOOLTIP, //available when connected, always up-to-date VIRTUALSERVER_HOSTBUTTON_URL, //available when connected, always up-to-date VIRTUALSERVER_HOSTBUTTON_GFX_URL, //available when connected, always up-to-date - VIRTUALSERVER_QUERYCLIENTS_ONLINE, //only available on request (=> requestServerVariables) VIRTUALSERVER_DOWNLOAD_QUOTA, //only available on request (=> requestServerVariables) VIRTUALSERVER_UPLOAD_QUOTA, //only available on request (=> requestServerVariables) VIRTUALSERVER_MONTH_BYTES_DOWNLOADED, //only available on request (=> requestServerVariables) @@ -176,6 +180,7 @@ namespace ts { VIRTUALSERVER_TOTAL_BYTES_DOWNLOADED, //only available on request (=> requestServerVariables) VIRTUALSERVER_TOTAL_BYTES_UPLOADED, //only available on request (=> requestServerVariables) VIRTUALSERVER_PORT, //only available on request (=> requestServerVariables) + VIRTUALSERVER_HOST, //internal use | contains comma separated ip list VIRTUALSERVER_AUTOSTART, //only available on request (=> requestServerVariables) VIRTUALSERVER_MACHINE_ID, //only available on request (=> requestServerVariables) VIRTUALSERVER_NEEDED_IDENTITY_SECURITY_LEVEL, //only available on request (=> requestServerVariables) @@ -185,7 +190,6 @@ namespace ts { VIRTUALSERVER_LOG_PERMISSIONS, //only available on request (=> requestServerVariables) VIRTUALSERVER_LOG_SERVER, //only available on request (=> requestServerVariables) VIRTUALSERVER_LOG_FILETRANSFER, //only available on request (=> requestServerVariables) - VIRTUALSERVER_MIN_CLIENT_VERSION, //only available on request (=> requestServerVariables) VIRTUALSERVER_NAME_PHONETIC, //available when connected, always up-to-date VIRTUALSERVER_ICON_ID, //available when connected, always up-to-date VIRTUALSERVER_RESERVED_SLOTS, //available when connected, always up-to-date @@ -194,12 +198,11 @@ namespace ts { VIRTUALSERVER_TOTAL_PACKETLOSS_CONTROL, //only available on request (=> requestServerVariables) VIRTUALSERVER_TOTAL_PACKETLOSS_TOTAL, //only available on request (=> requestServerVariables) VIRTUALSERVER_TOTAL_PING, //only available on request (=> requestServerVariables) - VIRTUALSERVER_HOST, //internal use | contains comma separated ip list VIRTUALSERVER_WEBLIST_ENABLED, //only available on request (=> requestServerVariables) VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY, //internal use VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY, //available when connected - VIRTUALSERVER_HOSTBANNER_MODE, //available when connected, always up-to-date VIRTUALSERVER_CHANNEL_TEMP_DELETE_DELAY_DEFAULT, //available when connected, always up-to-date + VIRTUALSERVER_MIN_CLIENT_VERSION, //only available on request (=> requestServerVariables) VIRTUALSERVER_MIN_ANDROID_VERSION, //only available on request (=> requestServerVariables) VIRTUALSERVER_MIN_IOS_VERSION, //only available on request (=> requestServerVariables) VIRTUALSERVER_MIN_WINPHONE_VERSION, //only available on request (=> requestServerVariables) @@ -223,7 +226,6 @@ namespace ts { VIRTUALSERVER_DISABLE_IP_SAVING, VIRTUALSERVER_COUNTRY_CODE, - VIRTUALSERVER_ENDMARKER }; @@ -347,21 +349,21 @@ namespace ts { CLIENT_INTEGRATIONS, CLIENT_ACTIVE_INTEGRATIONS_INFO, + CLIENT_TEAFORO_ID, + CLIENT_TEAFORO_NAME, + CLIENT_TEAFORO_FLAGS, + //Music bot stuff - CLIENT_BOT_TYPE, CLIENT_OWNER, - CLIENT_PLAYER_VOLUME, + CLIENT_BOT_TYPE, CLIENT_LAST_CHANNEL, CLIENT_PLAYER_STATE, + CLIENT_PLAYER_VOLUME, CLIENT_PLAYLIST_ID, CLIENT_DISABLED, CLIENT_UPTIME_MODE, CLIENT_FLAG_NOTIFY_SONG_CHANGE, - CLIENT_TEAFORO_ID, - CLIENT_TEAFORO_NAME, - CLIENT_TEAFORO_FLAGS, - CLIENT_ENDMARKER }; @@ -462,146 +464,212 @@ namespace ts { }; */ - class PropertyDescription; namespace impl { - template - extern std::deque> list(); - extern std::deque> list_all(); - - template - extern const std::shared_ptr &info(T /* property */); - - template - extern const std::shared_ptr &info(const std::string& /* key */); - - - extern const std::shared_ptr &info_key(const std::string &); - extern const std::shared_ptr &info_key(PropertyType, const std::string &); - extern const std::shared_ptr &info(PropertyType, int); - - template - inline PropertyType type() { - std::cerr << "[CRITICAL] Invalid property type!" << std::endl; - return PropertyType::PROP_TYPE_UNKNOWN; + template + constexpr auto make_property_array(Properties&&... props) noexcept { + return std::array, sizeof...(Properties)>{std::forward(props)...}; } - template<> - constexpr inline PropertyType type() { return PropertyType::PROP_TYPE_SERVER; } - - template<> - constexpr inline PropertyType type() { return PropertyType::PROP_TYPE_CHANNEL; } - - template<> - constexpr inline PropertyType type() { return PropertyType::PROP_TYPE_CLIENT; } - - template<> - constexpr inline PropertyType type() { return PropertyType::PROP_TYPE_CONNECTION; } - - template<> - constexpr inline PropertyType type() { return PropertyType::PROP_TYPE_GROUP; } - - template<> - constexpr inline PropertyType type() { return PropertyType::PROP_TYPE_INSTANCE; } - - template<> - constexpr inline PropertyType type() { return PropertyType::PROP_TYPE_PLAYLIST; } - - template<> - constexpr inline PropertyType type() { return PropertyType::PROP_TYPE_UNKNOWN; } - - inline size_t length(PropertyType type) { - switch (type) { - case PROP_TYPE_SERVER: - return VIRTUALSERVER_ENDMARKER - VIRTUALSERVER_BEGINMARKER; - case PROP_TYPE_CHANNEL: - return CHANNEL_ENDMARKER - CHANNEL_BEGINMARKER; - case PROP_TYPE_CLIENT: - return CLIENT_ENDMARKER - CLIENT_BEGINMARKER; - case PROP_TYPE_CONNECTION: - return CONNECTION_ENDMARKER - CONNECTION_BEGINMARKER; - case PROP_TYPE_GROUP: - return GROUP_ENDMARKER - GROUP_BEGINMARKER; - case PROP_TYPE_INSTANCE: - return SERVERINSTANCE_ENDMARKER - SERVERINSTANCE_BEGINMARKER; - case PROP_TYPE_PLAYLIST: - return PLAYLIST_ENDMARKER - PLAYLIST_BEGINMARKER; - case PROP_TYPE_UNKNOWN: - return UNKNOWN_ENDMARKER - UNKNOWN_BEGINMARKER; - default: - return 0; - } - } - - inline size_t offset(PropertyType type) { - switch (type) { - case PROP_TYPE_SERVER: - return VIRTUALSERVER_BEGINMARKER; - case PROP_TYPE_CHANNEL: - return CHANNEL_BEGINMARKER; - case PROP_TYPE_CLIENT: - return CLIENT_BEGINMARKER; - case PROP_TYPE_CONNECTION: - return CONNECTION_BEGINMARKER; - case PROP_TYPE_GROUP: - return GROUP_BEGINMARKER; - case PROP_TYPE_INSTANCE: - return SERVERINSTANCE_BEGINMARKER; - case PROP_TYPE_PLAYLIST: - return PLAYLIST_BEGINMARKER; - case PROP_TYPE_UNKNOWN: - return UNKNOWN_BEGINMARKER; - default: - return 0; - } - } - - extern bool validateUnique(); - - extern bool validateInput(const std::string &, ValueType); + extern bool validateInput(const std::string& input, ValueType type); } + template + constexpr inline PropertyType type_from_enum(); - template - inline const std::shared_ptr &info(T property) { return impl::info(property); } + template<> + constexpr inline PropertyType type_from_enum() { return PropertyType::PROP_TYPE_SERVER; } - template - inline const std::shared_ptr &info(const std::string& key) { return impl::info(key); } + template<> + constexpr inline PropertyType type_from_enum() { return PropertyType::PROP_TYPE_CHANNEL; } - class PropertyDescription { - public: - static std::shared_ptr unknown; + template<> + constexpr inline PropertyType type_from_enum() { return PropertyType::PROP_TYPE_CLIENT; } - std::string name; - std::string default_value; + template<> + constexpr inline PropertyType type_from_enum() { return PropertyType::PROP_TYPE_CONNECTION; } - ValueType type_value = property::ValueType::TYPE_UNKNOWN; - PropertyType type_property = PropertyType::PROP_TYPE_UNKNOWN; + template<> + constexpr inline PropertyType type_from_enum() { return PropertyType::PROP_TYPE_GROUP; } - int property_index = 0; - flag_type flags = 0; + template<> + constexpr inline PropertyType type_from_enum() { return PropertyType::PROP_TYPE_INSTANCE; } - PropertyDescription(int property_id, PropertyType property_type, const std::string &name, const std::string &default_value, property::ValueType type, flag_type flags) noexcept; + template<> + constexpr inline PropertyType type_from_enum() { return PropertyType::PROP_TYPE_PLAYLIST; } - template - PropertyDescription(T property, const std::string &name, const std::string &default_value, property::ValueType type, flag_type flags) noexcept : PropertyDescription((int) property, impl::type(), name, default_value, type, flags) {} + template<> + constexpr inline PropertyType type_from_enum() { return PropertyType::PROP_TYPE_UNKNOWN; } - inline bool operator==(const PropertyDescription& other) const { - return other.property_index == this->property_index && other.type_property == this->type_property; - } + struct PropertyDescription { + std::string_view name{}; + std::string_view default_value{}; - inline bool operator==(const std::shared_ptr& other) const { - return this->operator==(*other); - } + //TODO: Rename these sucky variables + ValueType type_value{property::ValueType::TYPE_UNKNOWN}; + PropertyType type_property{PropertyType::PROP_TYPE_UNKNOWN}; - template ::value, int>::type = 0> - inline bool operator==(const T& type) const { - return this->property_index == type && impl::type() == this->type_property; - } + int property_index{0}; + flag_type flags{0}; - inline bool validate_input(const std::string& input) { - return impl::validateInput(input, this->type_value); - } + template ::value, int>::type = 0> + constexpr PropertyDescription(PropertyEnumType property, std::string_view name, std::string_view default_value, property::ValueType value_type, flag_type flags) noexcept + : name{name}, default_value{default_value}, type_value{value_type}, type_property{type_from_enum()}, + property_index{(int) property}, flags{flags} { } + PropertyDescription(const PropertyDescription&) = delete; + PropertyDescription(PropertyDescription&&) = default; + + constexpr inline bool operator==(const PropertyDescription& other) const { + return this->property_index == other.property_index && this->type_property == other.type_property; + } + + template ::value, int>::type = 0> + constexpr inline bool operator==(const PropertyEnumType& other) const { + return this->property_index == (int) other && this->type_property == type_from_enum(); + } + + inline bool validate_input(const std::string& value) const { return impl::validateInput(value, this->type_value); } }; + constexpr static PropertyDescription undefined_property_description{UnknownProperties::UNKNOWN_UNDEFINED, "undefined", "", ValueType::TYPE_UNKNOWN, 0}; + + struct PropertyListInfo { + std::array begin_index{}; /* inclusive */ + std::array end_index{}; /* exclusive */ + }; + +#ifdef EXTERNALIZE_PROPERTY_DEFINITIONS + constexpr inline auto property_count() { + size_t result{0}; + result += VIRTUALSERVER_ENDMARKER; + result += CHANNEL_ENDMARKER; + result += CLIENT_ENDMARKER; + result += GROUP_ENDMARKER; + result += SERVERINSTANCE_ENDMARKER; + result += CONNECTION_ENDMARKER; + result += PLAYLIST_ENDMARKER; + result += UNKNOWN_ENDMARKER; + return result; + } + extern std::array property_list; +#else + #include "./PropertyDefinition.h" + + constexpr inline auto property_count() { + return property_list.size(); + } +#endif + +#ifdef EXTERNALIZE_PROPERTY_DEFINITIONS + #define const_modifier +#else + #define const_modifier constexpr +#endif + namespace impl { + const_modifier inline size_t property_type_begin(PropertyType type) { + size_t index{0}; + for(; index < property_list.size(); index++) + if(property_list[index].type_property == type) + return index; + return property_list.size(); + } + + const_modifier inline size_t property_type_end(size_t begin, PropertyType type) { + size_t index{begin}; + for(; index < property_list.size() - 1; index++) + if(property_list[index + 1].type_property != type) + return index; + return property_list.size(); + } + + const_modifier inline PropertyListInfo list_info() noexcept { + std::array begin_index{ + /* We're using numbers here so we don't mess up the order. This would be fatal */ + property_type_begin((PropertyType) 0), + property_type_begin((PropertyType) 1), + property_type_begin((PropertyType) 2), + property_type_begin((PropertyType) 3), + property_type_begin((PropertyType) 4), + property_type_begin((PropertyType) 5), + property_type_begin((PropertyType) 6), + property_type_begin((PropertyType) 7) + }; + + return { + begin_index, + std::array{ + /* We're using numbers here so we don't mess up the order. This would be fatal */ + property_type_end(begin_index[0], (PropertyType) 0), + property_type_end(begin_index[1], (PropertyType) 1), + property_type_end(begin_index[2], (PropertyType) 2), + property_type_end(begin_index[3], (PropertyType) 3), + property_type_end(begin_index[4], (PropertyType) 4), + property_type_end(begin_index[5], (PropertyType) 5), + property_type_end(begin_index[6], (PropertyType) 6), + property_type_end(begin_index[7], (PropertyType) 7) + } + }; + } + } +#ifdef EXTERNALIZE_PROPERTY_DEFINITIONS + extern const PropertyListInfo property_list_info; +#else + constexpr auto property_list_info = impl::list_info(); +#endif + + template ::value, int>::type = 0> + const_modifier inline const auto& describe(PropertyEnumType type) { + static_assert(type_from_enum() < property_list_info.end_index.size()); + const auto begin = property_list_info.begin_index[type_from_enum()]; + const auto end = property_list_info.end_index[type_from_enum()]; + const auto idx = begin + (size_t) type; + if(idx >= end) return undefined_property_description; + return property_list[idx]; + } + + inline const auto& describe(PropertyType type, size_t index) { + if(type >= property_list_info.end_index.size()) return undefined_property_description; + const auto begin = property_list_info.begin_index[type]; + const auto end = property_list_info.end_index[type]; + const auto idx = begin + index; + if(idx >= end) return undefined_property_description; + return property_list[idx]; + } + + inline const auto& find(PropertyType type, const std::string_view& name) { + if(type >= property_list_info.end_index.size()) return undefined_property_description; + + constexpr static auto buffer_size{128}; /* no property is longer than 128 bytes */ + if(name.size() >= buffer_size) return undefined_property_description; + char buffer[buffer_size]; + for(size_t index{0}; index < name.size(); index++) + buffer[index] = (char) tolower(name[index]); + + const std::string_view lower_name{buffer, name.size()}; + const auto begin = property_list_info.begin_index[type]; + const auto end = property_list_info.end_index[type]; + for(size_t index{begin}; index < end; index++) + if(property_list[index].name == lower_name) + return property_list[index]; + return property_list[begin]; /* begin index MUST be the undefined */ + } + + template ::value, int>::type = 0> + inline const auto& find(const std::string_view& name) { + return find(type_from_enum(), name); + } + + template ::value, int>::type = 0> + const_modifier inline const std::string_view& name(PropertyEnumType type) { /* defining the return type here to help out my IDE a bit ;) */ + return describe(type).name; + } + + template ::value, int>::type = 0> + const_modifier inline size_t property_count() { + const auto begin = property_list_info.begin_index[type_from_enum()]; + const auto end = property_list_info.end_index[type_from_enum()]; + return end - begin; + } + +#undef const_modifier } class Properties; @@ -609,7 +677,7 @@ namespace ts { spin_lock value_lock; std::any casted_value; std::string value; - std::shared_ptr description; + const property::PropertyDescription* description; bool flag_db_reference; bool flag_modified; @@ -672,14 +740,14 @@ namespace ts { PropertyWrapper&operator+=(int64_t val){ return operator=(as() + val); } PropertyWrapper&operator+=(uint64_t val){ return operator=(as() + val); } - bool hasDbReference() const { return this->data_ptr->flag_db_reference; } + [[nodiscard]] bool hasDbReference() const { return this->data_ptr->flag_db_reference; } void setDbReference(bool flag){ this->data_ptr->flag_db_reference = flag; } - bool isModified() const { return this->data_ptr->flag_modified; } + [[nodiscard]] bool isModified() const { return this->data_ptr->flag_modified; } void setModified(bool flag){ this->data_ptr->flag_modified = flag; } template - T as() const { + [[nodiscard]] T as() const { static_assert(ts::converter::supported, "as isn't supported for type"); return PropertyAccess::get(this->data_ptr); @@ -687,10 +755,10 @@ namespace ts { template - operator T(){ return this->as(); } + [[nodiscard]] operator T(){ return this->as(); } template - T as_save() const { + [[nodiscard]] T as_save() const { try { std::lock_guard lock(this->data_ptr->value_lock); if(this->data_ptr->casted_value.type() == typeid(T)) @@ -703,8 +771,8 @@ namespace ts { } } - const property::PropertyDescription& type() const { return *this->data_ptr->description; } - std::string value() const { + [[nodiscard]] const property::PropertyDescription& type() const { return *this->data_ptr->description; } + [[nodiscard]] std::string value() const { std::lock_guard lock(this->data_ptr->value_lock); return this->data_ptr->value; } @@ -721,7 +789,7 @@ namespace ts { this->trigger_update(); } - std::string default_value() const { + [[nodiscard]] const std::string_view& default_value() const { return this->type().default_value; } @@ -762,9 +830,9 @@ namespace ts { void trigger_update(); - PropertyWrapper(Properties* /* handle */, PropertyData* /* ptr */, const std::shared_ptr& /* bundle */); + PropertyWrapper(Properties* /* handle */, PropertyData* /* ptr */, std::shared_ptr /* bundle */); - inline Properties* get_handle() { return this->handle; } + [[nodiscard]] inline Properties* get_handle() { return this->handle; } private: Properties* handle = nullptr; PropertyData* data_ptr = nullptr; @@ -786,23 +854,23 @@ namespace ts { template bool register_property_type() { - constexpr auto type = property::impl::type(); - return this->register_property_type(type, property::impl::length(type), property::impl::offset(type)); + constexpr auto type = property::type_from_enum(); + return this->register_property_type(type, property::property_count()); } template - bool hasProperty(T type) { return this->has(property::impl::type(), type); } + bool hasProperty(T type) { return this->has(property::type_from_enum(), type); } template ::value, int>::type = 0> PropertyWrapper operator[](T type) { - return this->find(property::impl::type(), type); + return this->find(property::type_from_enum(), type); } PropertyWrapper operator[](const property::PropertyDescription& type) { return this->find(type.type_property, type.property_index); } - PropertyWrapper operator[](const std::shared_ptr& type) { + PropertyWrapper operator[](const property::PropertyDescription* type) { return this->find(type->type_property, type->property_index); } @@ -825,14 +893,14 @@ namespace ts { bool has(property::PropertyType type, int index); template ::value, int>::type = 0> - bool has(T type) { return this->has(property::impl::type(), type); } + bool has(T type) { return this->has(property::type_from_enum(), (int) type); } private: - bool register_property_type(property::PropertyType /* type */, size_t /* length */, size_t /* offset */); + bool register_property_type(property::PropertyType /* type */, size_t /* length */); - bool save = true; + bool save{true}; std::vector> notifyFunctions{}; - size_t properties_count = 0; + size_t properties_count{0}; std::vector> properties; }; }; diff --git a/src/PropertyDefinition.h b/src/PropertyDefinition.h new file mode 100644 index 0000000..dad31c6 --- /dev/null +++ b/src/PropertyDefinition.h @@ -0,0 +1,360 @@ + +#define FLAG_SS (FLAG_SNAPSHOT | FLAG_SAVE) +#define FLAG_SERVER_VV (FLAG_SERVER_VARIABLE | FLAG_SERVER_VIEW) +#define FLAG_SERVER_VVSS (FLAG_SERVER_VV | FLAG_SS) + +#define FLAG_CLIENT_VV (FLAG_CLIENT_VARIABLE | FLAG_CLIENT_VIEW) +#define FLAG_CLIENT_VVSS (FLAG_CLIENT_VV | FLAG_SS) + +#define V(key, lkey, flags) key, lkey, "0", TYPE_UNSIGNED_NUMBER, flags +#define F(key, lkey, flags) key, lkey, "0", TYPE_FLOAT, flags + +#ifdef EXTERNALIZE_PROPERTY_DEFINITIONS +decltype(property::property_list) property:: +#else +constexpr auto +#endif +property_list = property::impl::make_property_array( + PropertyDescription{UNKNOWN_UNDEFINED, "undefined", "", TYPE_UNKNOWN, 0}, + + /* virtual server properties */ + PropertyDescription{VIRTUALSERVER_UNDEFINED, "undefined", "", TYPE_UNKNOWN, 0}, //Must be at index 0! + PropertyDescription{VIRTUALSERVER_UNIQUE_IDENTIFIER, "virtualserver_unique_identifier", "", TYPE_STRING, FLAG_SERVER_VV | FLAG_SNAPSHOT}, + PropertyDescription{VIRTUALSERVER_NAME, "virtualserver_name", "Another TeaSpeak server software user", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, + PropertyDescription{VIRTUALSERVER_WELCOMEMESSAGE, "virtualserver_welcomemessage", "Welcome on another TeaSpeak server. (Download now and a license fee is not your cup of tea! [URL]www.teaspeak.de[/URL])", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, + PropertyDescription{VIRTUALSERVER_PLATFORM, "virtualserver_platform", "undefined", TYPE_STRING, FLAG_SERVER_VIEW}, + PropertyDescription{VIRTUALSERVER_VERSION, "virtualserver_version", "undefined", TYPE_STRING, FLAG_SERVER_VIEW}, + PropertyDescription{VIRTUALSERVER_MAXCLIENTS, "virtualserver_maxclients", "120", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, + PropertyDescription{VIRTUALSERVER_PASSWORD, "virtualserver_password", "", TYPE_STRING, FLAG_SS | FLAG_USER_EDITABLE}, + PropertyDescription{VIRTUALSERVER_CLIENTS_ONLINE, "virtualserver_clientsonline", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE}, + PropertyDescription{VIRTUALSERVER_QUERYCLIENTS_ONLINE, "virtualserver_queryclientsonline", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_CHANNELS_ONLINE, "virtualserver_channelsonline", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE}, //only available on request (=> requestServerVariables), + PropertyDescription{VIRTUALSERVER_CREATED, "virtualserver_created", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VIEW | FLAG_SS}, //available when connected, stores the time when the server was created + PropertyDescription{VIRTUALSERVER_UPTIME, "virtualserver_uptime", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE}, //only available on request (=> requestServerVariables), the time since the server was started + + PropertyDescription{VIRTUALSERVER_CODEC_ENCRYPTION_MODE, "virtualserver_codec_encryption_mode", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //available and always up-to-date when connected + PropertyDescription{VIRTUALSERVER_KEYPAIR, "virtualserver_keypair", "", TYPE_STRING, FLAG_SS}, //internal use + PropertyDescription{VIRTUALSERVER_HOSTMESSAGE, "virtualserver_hostmessage", "Welcome", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //available when connected, not updated while connected + PropertyDescription{VIRTUALSERVER_HOSTMESSAGE_MODE, "virtualserver_hostmessage_mode", "1", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //available when connected, not updated while connected + PropertyDescription{VIRTUALSERVER_FILEBASE, "virtualserver_filebase", "", TYPE_STRING, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //not available to clients, stores the folder used for file transfers + PropertyDescription{VIRTUALSERVER_DEFAULT_SERVER_GROUP, "virtualserver_default_server_group", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //the manager permissions server group that a new manager gets assigned + PropertyDescription{VIRTUALSERVER_DEFAULT_MUSIC_GROUP, "virtualserver_default_music_group", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //the manager permissions server group that a new manager gets assigned + PropertyDescription{VIRTUALSERVER_DEFAULT_CHANNEL_GROUP, "virtualserver_default_channel_group", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //the channel permissions group that a new manager gets assigned when joining a channel + PropertyDescription{VIRTUALSERVER_FLAG_PASSWORD, "virtualserver_flag_password", "0", TYPE_BOOL, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP, "virtualserver_default_channel_admin_group", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SERVER_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //the channel permissions group that a manager gets assigned when creating a channel + PropertyDescription{VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH, "virtualserver_max_download_total_bandwidth", "-1", TYPE_SIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH, "virtualserver_max_upload_total_bandwidth", "-1", TYPE_SIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_HOSTBANNER_URL, "virtualserver_hostbanner_url", "", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //available when connected, always up-to-date + PropertyDescription{VIRTUALSERVER_HOSTBANNER_GFX_URL, "virtualserver_hostbanner_gfx_url", "", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //available when connected, always up-to-date + PropertyDescription{VIRTUALSERVER_HOSTBANNER_GFX_INTERVAL, "virtualserver_hostbanner_gfx_interval", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //available when connected, always up-to-date + PropertyDescription{VIRTUALSERVER_HOSTBANNER_MODE, "virtualserver_hostbanner_mode", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //available when connected, always up-to-date + PropertyDescription{VIRTUALSERVER_COMPLAIN_AUTOBAN_COUNT, "virtualserver_complain_autoban_count", "5", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_COMPLAIN_AUTOBAN_TIME, "virtualserver_complain_autoban_time", "5", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_COMPLAIN_REMOVE_TIME, "virtualserver_complain_remove_time", "5", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE, "virtualserver_min_clients_in_channel_before_forced_silence", "20", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE},//only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_PRIORITY_SPEAKER_DIMM_MODIFICATOR, "virtualserver_priority_speaker_dimm_modificator", "-18", TYPE_FLOAT, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //available when connected, always up-to-date + PropertyDescription{VIRTUALSERVER_ID, "virtualserver_id", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VIEW}, //available when connected + PropertyDescription{VIRTUALSERVER_ANTIFLOOD_POINTS_TICK_REDUCE, "virtualserver_antiflood_points_tick_reduce", "25", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_ANTIFLOOD_POINTS_NEEDED_COMMAND_BLOCK, "virtualserver_antiflood_points_needed_command_block", "150", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_ANTIFLOOD_POINTS_NEEDED_IP_BLOCK, "virtualserver_antiflood_points_needed_ip_block", "300", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_CLIENT_CONNECTIONS, "virtualserver_client_connections", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_QUERY_CLIENT_CONNECTIONS, "virtualserver_query_client_connections", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_HOSTBUTTON_TOOLTIP, "virtualserver_hostbutton_tooltip", "", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //available when connected, always up-to-date + PropertyDescription{VIRTUALSERVER_HOSTBUTTON_URL, "virtualserver_hostbutton_url", "", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //available when connected, always up-to-date + PropertyDescription{VIRTUALSERVER_HOSTBUTTON_GFX_URL, "virtualserver_hostbutton_gfx_url", "", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //available when connected, always up-to-date + PropertyDescription{VIRTUALSERVER_DOWNLOAD_QUOTA, "virtualserver_download_quota", "-1", TYPE_SIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SAVE | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_UPLOAD_QUOTA, "virtualserver_upload_quota", "-1", TYPE_SIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SAVE | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_MONTH_BYTES_DOWNLOADED, "virtualserver_month_bytes_downloaded", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SAVE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_MONTH_BYTES_UPLOADED, "virtualserver_month_bytes_uploaded", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SAVE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_TOTAL_BYTES_DOWNLOADED, "virtualserver_total_bytes_downloaded", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SAVE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_TOTAL_BYTES_UPLOADED, "virtualserver_total_bytes_uploaded", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SAVE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_PORT, "virtualserver_port", "9987", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_HOST, "virtualserver_host", "0.0.0.0,::", TYPE_STRING, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //internal use | contains comma separated ip list + PropertyDescription{VIRTUALSERVER_AUTOSTART, "virtualserver_autostart", "1", TYPE_BOOL, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_MACHINE_ID, "virtualserver_machine_id", "", TYPE_STRING, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_NEEDED_IDENTITY_SECURITY_LEVEL, "virtualserver_needed_identity_security_level", "8", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_LOG_CLIENT, "virtualserver_log_client", "1", TYPE_BOOL, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_LOG_QUERY, "virtualserver_log_query", "1", TYPE_BOOL, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_LOG_CHANNEL, "virtualserver_log_channel", "1", TYPE_BOOL, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_LOG_PERMISSIONS, "virtualserver_log_permissions", "1", TYPE_BOOL, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_LOG_SERVER, "virtualserver_log_server", "1", TYPE_BOOL, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_LOG_FILETRANSFER, "virtualserver_log_filetransfer", "1", TYPE_BOOL, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_NAME_PHONETIC, "virtualserver_name_phonetic", "", TYPE_STRING, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //available when connected, always up-to-date + PropertyDescription{VIRTUALSERVER_ICON_ID, "virtualserver_icon_id", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //available when connected, always up-to-date + PropertyDescription{VIRTUALSERVER_RESERVED_SLOTS, "virtualserver_reserved_slots", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //available when connected, always up-to-date + PropertyDescription{VIRTUALSERVER_TOTAL_PACKETLOSS_SPEECH, "virtualserver_total_packetloss_speech", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_TOTAL_PACKETLOSS_KEEPALIVE, "virtualserver_total_packetloss_keepalive", "0", TYPE_FLOAT, FLAG_SERVER_VARIABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_TOTAL_PACKETLOSS_CONTROL, "virtualserver_total_packetloss_control", "0", TYPE_FLOAT, FLAG_SERVER_VARIABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_TOTAL_PACKETLOSS_TOTAL, "virtualserver_total_packetloss_total", "0", TYPE_FLOAT, FLAG_SERVER_VARIABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_TOTAL_PING, "virtualserver_total_ping", "0", TYPE_FLOAT, FLAG_SERVER_VARIABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_WEBLIST_ENABLED, "virtualserver_weblist_enabled", "1", TYPE_BOOL, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY, "virtualserver_autogenerated_privilegekey", "", TYPE_STRING, FLAG_SAVE}, //internal use + PropertyDescription{VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY, "virtualserver_ask_for_privilegekey", "1", TYPE_BOOL, FLAG_SERVER_VV | FLAG_SAVE | FLAG_USER_EDITABLE}, //available when connected + PropertyDescription{VIRTUALSERVER_CHANNEL_TEMP_DELETE_DELAY_DEFAULT, "virtualserver_channel_temp_delete_delay_default", "60", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VVSS | FLAG_USER_EDITABLE}, //available when connected, always up-to-date + PropertyDescription{VIRTUALSERVER_MIN_CLIENT_VERSION, "virtualserver_min_client_version", "1445512488", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_MIN_ANDROID_VERSION, "virtualserver_min_android_version", "1407159763", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_MIN_IOS_VERSION, "virtualserver_min_ios_version", "1407159763", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_MIN_WINPHONE_VERSION, "virtualserver_min_winphone_version", "1407159763", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_MAX_CHANNELS, "virtualserver_max_channels", "1000", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + + PropertyDescription{VIRTUALSERVER_LAST_CLIENT_CONNECT, "virtualserver_last_client_connect", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_LAST_QUERY_CONNECT, "virtualserver_last_query_connect", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_LAST_CLIENT_DISCONNECT, "virtualserver_last_client_disconnect", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_LAST_QUERY_DISCONNECT, "virtualserver_last_query_disconnect", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS}, //only available on request (=> requestServerVariables) + + PropertyDescription{VIRTUALSERVER_WEB_HOST, "virtualserver_web_host", "0.0.0.0", TYPE_STRING, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_WEB_PORT, "virtualserver_web_port", "0", TYPE_UNSIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + + PropertyDescription{VIRTUALSERVER_DEFAULT_CLIENT_DESCRIPTION, "virtualserver_default_client_description", "", TYPE_STRING, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_DEFAULT_CHANNEL_DESCRIPTION, "virtualserver_default_channel_description", "", TYPE_STRING, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_DEFAULT_CHANNEL_TOPIC, "virtualserver_default_channel_topic", "", TYPE_STRING, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + + PropertyDescription{VIRTUALSERVER_MUSIC_BOT_LIMIT, "virtualserver_music_bot_limit", "-1", TYPE_SIGNED_NUMBER, FLAG_SERVER_VARIABLE | FLAG_NEW | FLAG_SS | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_SPOKEN_TIME, "virtualserver_spoken_time", "0", TYPE_UNSIGNED_NUMBER, FLAG_INTERNAL | FLAG_NEW | FLAG_SAVE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_DISABLE_IP_SAVING, "virtualserver_disable_ip_saving", "0", TYPE_BOOL, FLAG_INTERNAL | FLAG_NEW | FLAG_SAVE | FLAG_USER_EDITABLE}, //only available on request (=> requestServerVariables) + PropertyDescription{VIRTUALSERVER_COUNTRY_CODE, "virtualserver_country_code", "XX", TYPE_STRING, FLAG_SERVER_VV | FLAG_SAVE | FLAG_USER_EDITABLE}, //available when connected + + /* channel properties */ + PropertyDescription{CHANNEL_UNDEFINED, "undefined", "", TYPE_UNKNOWN, 0}, //Must be at index 0! + PropertyDescription{CHANNEL_ID, "cid", "0", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS}, + PropertyDescription{CHANNEL_PID, "cpid", "0", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS}, + PropertyDescription{CHANNEL_NAME, "channel_name", "undefined", TYPE_STRING, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_TOPIC, "channel_topic", "", TYPE_STRING, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_DESCRIPTION, "channel_description", "", TYPE_STRING, FLAG_CHANNEL_VARIABLE | FLAG_SS | FLAG_USER_EDITABLE}, //Must be requested (=> requestChannelDescription) + PropertyDescription{CHANNEL_PASSWORD, "channel_password", "0", TYPE_STRING, FLAG_SS | FLAG_USER_EDITABLE}, //not available manager side + PropertyDescription{CHANNEL_CODEC, "channel_codec", "4", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_CODEC_QUALITY, "channel_codec_quality", "7", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_MAXCLIENTS, "channel_maxclients", "-1", TYPE_SIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_MAXFAMILYCLIENTS, "channel_maxfamilyclients", "-1", TYPE_SIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_ORDER, "channel_order", "0", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_FLAG_PERMANENT, "channel_flag_permanent", "1", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_FLAG_SEMI_PERMANENT, "channel_flag_semi_permanent", "0", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_FLAG_DEFAULT, "channel_flag_default", "0", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_FLAG_PASSWORD, "channel_flag_password", "0", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_CODEC_LATENCY_FACTOR, "channel_codec_latency_factor", "1", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_CODEC_IS_UNENCRYPTED, "channel_codec_is_unencrypted", "1", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_SECURITY_SALT, "channel_security_salt", "", TYPE_STRING, FLAG_SS}, //Not available manager side, not used in teamspeak, only SDK. Sets the options+salt for security hash. + PropertyDescription{CHANNEL_DELETE_DELAY, "channel_delete_delay", "0", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //How many seconds to wait before deleting this channel + PropertyDescription{CHANNEL_FLAG_MAXCLIENTS_UNLIMITED, "channel_flag_maxclients_unlimited", "1", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED, "channel_flag_maxfamilyclients_unlimited", "1", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE},//Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED, "channel_flag_maxfamilyclients_inherited", "0", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE},//Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_FLAG_ARE_SUBSCRIBED, "channel_flag_are_subscribed", "1", TYPE_BOOL, FLAG_INTERNAL}, //Only available manager side, stores whether we are subscribed to this channel + PropertyDescription{CHANNEL_FILEPATH, "channel_filepath", "", TYPE_STRING, FLAG_SS}, //not available manager side, the folder used for file-transfers for this channel + PropertyDescription{CHANNEL_NEEDED_TALK_POWER, "channel_needed_talk_power", "0", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_FORCED_SILENCE, "channel_forced_silence", "0", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_NAME_PHONETIC, "channel_name_phonetic", "", TYPE_STRING, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_ICON_ID, "channel_icon_id", "0", TYPE_UNSIGNED_NUMBER, FLAG_CHANNEL_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_FLAG_PRIVATE, "channel_flag_private", "0", TYPE_BOOL, FLAG_CHANNEL_VIEW | FLAG_SS}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_LAST_LEFT, "channel_last_left", "0", TYPE_UNSIGNED_NUMBER, FLAG_SAVE | FLAG_CHANNEL_VIEW | FLAG_CHANNEL_VARIABLE | FLAG_NEW}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_CREATED_AT, "channel_created_at", "0", TYPE_UNSIGNED_NUMBER, FLAG_SS | FLAG_CHANNEL_VIEW | FLAG_CHANNEL_VARIABLE | FLAG_NEW}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_CREATED_BY, "channel_created_by", "0", TYPE_UNSIGNED_NUMBER, FLAG_SS | FLAG_CHANNEL_VIEW | FLAG_CHANNEL_VARIABLE | FLAG_NEW}, //Available for all channels that are "in view", always up-to-date + PropertyDescription{CHANNEL_CONVERSATION_HISTORY_LENGTH, "channel_conversation_history_length", "1500", TYPE_SIGNED_NUMBER, FLAG_SS | FLAG_CHANNEL_VIEW | FLAG_CHANNEL_VARIABLE | FLAG_NEW | FLAG_USER_EDITABLE}, + PropertyDescription{CHANNEL_FLAG_CONVERSATION_PRIVATE, "channel_flag_conversation_private", "0", TYPE_BOOL, FLAG_SS | FLAG_CHANNEL_VIEW | FLAG_CHANNEL_VARIABLE | FLAG_NEW | FLAG_USER_EDITABLE}, + + /* group properties, this may gets removed */ + PropertyDescription{GROUP_UNDEFINED, "undefined", "", TYPE_UNKNOWN, 0}, + PropertyDescription{GROUP_ID, "gid", "0", TYPE_UNSIGNED_NUMBER, FLAG_INTERNAL}, + PropertyDescription{GROUP_TYPE, "type", "0", TYPE_UNSIGNED_NUMBER, FLAG_GROUP_VIEW}, + PropertyDescription{GROUP_NAME, "name", "Undefined group", TYPE_STRING, FLAG_GROUP_VIEW}, + PropertyDescription{GROUP_SORTID, "sortid", "0", TYPE_UNSIGNED_NUMBER, FLAG_GROUP_VIEW}, + PropertyDescription{GROUP_SAVEDB, "savedb", "0", TYPE_BOOL, FLAG_GROUP_VIEW}, + PropertyDescription{GROUP_NAMEMODE, "namemode", "0", TYPE_UNSIGNED_NUMBER, FLAG_GROUP_VIEW}, + PropertyDescription{GROUP_ICONID, "iconid", "0", TYPE_UNSIGNED_NUMBER, FLAG_GROUP_VIEW}, + + /* client properties */ + PropertyDescription{CLIENT_UNDEFINED, "undefined", "undefined", TYPE_UNKNOWN, 0}, + PropertyDescription{CLIENT_UNIQUE_IDENTIFIER, "client_unique_identifier", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_SNAPSHOT | FLAG_GLOBAL}, //automatically up-to-date for any manager "in view", can be used to identify this particular manager installation + PropertyDescription{CLIENT_NICKNAME, "client_nickname", "undefined", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_SAVE_MUSIC | FLAG_SNAPSHOT | FLAG_GLOBAL | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view" + PropertyDescription{CLIENT_VERSION, "client_version", "unknown", TYPE_STRING, FLAG_CLIENT_VVSS | FLAG_USER_EDITABLE | FLAG_GLOBAL | FLAG_SAVE_MUSIC}, //for other clients than ourself, this needs to be requested (=> requestClientVariables) + PropertyDescription{CLIENT_PLATFORM, "client_platform", "unknown", TYPE_STRING, FLAG_CLIENT_VVSS | FLAG_USER_EDITABLE | FLAG_GLOBAL | FLAG_SAVE_MUSIC}, //for other clients than ourself, this needs to be requested (=> requestClientVariables) + PropertyDescription{CLIENT_FLAG_TALKING, "client_flag_talking", "0", TYPE_BOOL, FLAG_INTERNAL}, //automatically up-to-date for any manager that can be heard (in room / whisper) + PropertyDescription{CLIENT_INPUT_MUTED, "client_input_muted", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view", this clients microphone mute status + PropertyDescription{CLIENT_OUTPUT_MUTED, "client_output_muted", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view", this clients headphones/speakers/mic combined mute status + PropertyDescription{CLIENT_OUTPUTONLY_MUTED, "client_outputonly_muted", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view", this clients headphones/speakers only mute status + PropertyDescription{CLIENT_INPUT_HARDWARE, "client_input_hardware", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view", this clients microphone hardware status (is the capture device opened?) + PropertyDescription{CLIENT_OUTPUT_HARDWARE, "client_output_hardware", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view", this clients headphone/speakers hardware status (is the playback device opened?) + PropertyDescription{CLIENT_DEFAULT_CHANNEL, "client_default_channel", "", TYPE_STRING, FLAG_INTERNAL}, //only usable for ourself, the default channel we used to connect on our last connection attempt + PropertyDescription{CLIENT_DEFAULT_CHANNEL_PASSWORD, "client_default_channel_password", "", TYPE_STRING, FLAG_INTERNAL}, //internal use + PropertyDescription{CLIENT_SERVER_PASSWORD, "client_server_password", "", TYPE_STRING, FLAG_INTERNAL}, //internal use + PropertyDescription{CLIENT_META_DATA, "client_meta_data", "", TYPE_STRING, FLAG_CLIENT_VIEW| FLAG_GLOBAL|FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view", not used by TeamSpeak, free storage for sdk users + PropertyDescription{CLIENT_IS_RECORDING, "client_is_recording", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view" + PropertyDescription{CLIENT_VERSION_SIGN, "client_version_sign", "", TYPE_STRING, FLAG_INTERNAL}, //sign + PropertyDescription{CLIENT_SECURITY_HASH, "client_security_hash", "", TYPE_STRING, FLAG_INTERNAL}, //SDK use, not used by teamspeak. Hash is provided by an outside source. A channel will use the security salt + other manager data to calculate a hash, which must be the same as the one provided here. + + //Rare properties + PropertyDescription{CLIENT_KEY_OFFSET, "client_key_offset", "0", TYPE_UNSIGNED_NUMBER, FLAG_INTERNAL}, //internal use + PropertyDescription{CLIENT_LOGIN_NAME, "client_login_name", "", TYPE_STRING, FLAG_CLIENT_VARIABLE| FLAG_GLOBAL}, //used for serverquery clients, makes no sense on normal clients currently + PropertyDescription{CLIENT_LOGIN_PASSWORD, "client_login_password", "", TYPE_STRING, FLAG_INTERNAL| FLAG_GLOBAL}, //used for serverquery clients, makes no sense on normal clients currently + PropertyDescription{CLIENT_DATABASE_ID, "client_database_id", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW | FLAG_GLOBAL}, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds database manager id + PropertyDescription{CLIENT_ID, "clid", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VV}, //clid! + PropertyDescription{CLIENT_HARDWARE_ID, "hwid", "", TYPE_STRING, FLAG_SAVE}, //hwid! + PropertyDescription{CLIENT_CHANNEL_GROUP_ID, "client_channel_group_id", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW}, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds database manager id + PropertyDescription{CLIENT_SERVERGROUPS, "client_servergroups", "0", TYPE_STRING, FLAG_CLIENT_VIEW}, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds all servergroups manager belongs too + PropertyDescription{CLIENT_CREATED, "client_created", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_SAVE_MUSIC | FLAG_SNAPSHOT | FLAG_GLOBAL}, //this needs to be requested (=> requestClientVariables), first time this manager connected to this server + PropertyDescription{CLIENT_LASTCONNECTED, "client_lastconnected", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_SNAPSHOT | FLAG_GLOBAL}, //this needs to be requested (=> requestClientVariables), last time this manager connected to this server + PropertyDescription{CLIENT_TOTALCONNECTIONS, "client_totalconnections", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_CLIENT_VIEW | FLAG_SNAPSHOT | FLAG_GLOBAL}, //this needs to be requested (=> requestClientVariables), how many times this manager connected to this server + PropertyDescription{CLIENT_AWAY, "client_away", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view", this clients away status + PropertyDescription{CLIENT_AWAY_MESSAGE, "client_away_message", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view", this clients away message + PropertyDescription{CLIENT_TYPE, "client_type", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW | FLAG_GLOBAL}, //automatically up-to-date for any manager "in view", determines if this is a real manager or a server-query connection + PropertyDescription{CLIENT_TYPE_EXACT, "client_type_exact", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW | FLAG_NEW | FLAG_GLOBAL}, //automatically up-to-date for any manager "in view", determines if this is a real manager or a server-query connection + PropertyDescription{CLIENT_FLAG_AVATAR, "client_flag_avatar", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_SAVE | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view", this manager got an avatar + PropertyDescription{CLIENT_TALK_POWER, "client_talk_power", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW}, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds database manager id + PropertyDescription{CLIENT_TALK_REQUEST, "client_talk_request", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds timestamp where manager requested to talk + PropertyDescription{CLIENT_TALK_REQUEST_MSG, "client_talk_request_msg", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds matter for the request + PropertyDescription{CLIENT_DESCRIPTION, "client_description", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view" + PropertyDescription{CLIENT_IS_TALKER, "client_is_talker", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view" + PropertyDescription{CLIENT_MONTH_BYTES_UPLOADED, "client_month_bytes_uploaded", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_SAVE}, //this needs to be requested (=> requestClientVariables) + PropertyDescription{CLIENT_MONTH_BYTES_DOWNLOADED, "client_month_bytes_downloaded", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_SAVE}, //this needs to be requested (=> requestClientVariables) + PropertyDescription{CLIENT_TOTAL_BYTES_UPLOADED, "client_total_bytes_uploaded", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_SAVE}, //this needs to be requested (=> requestClientVariables) + PropertyDescription{CLIENT_TOTAL_BYTES_DOWNLOADED, "client_total_bytes_downloaded", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_SAVE}, //this needs to be requested (=> requestClientVariables) + PropertyDescription{CLIENT_TOTAL_ONLINE_TIME, "client_total_online_time", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_SAVE | FLAG_NEW}, //this needs to be requested (=> requestClientVariables) + PropertyDescription{CLIENT_MONTH_ONLINE_TIME, "client_month_online_time", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VARIABLE | FLAG_SAVE | FLAG_NEW}, //this needs to be requested (=> requestClientVariables) + PropertyDescription{CLIENT_IS_PRIORITY_SPEAKER, "client_is_priority_speaker", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view" + PropertyDescription{CLIENT_UNREAD_MESSAGES, "client_unread_messages", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW}, //automatically up-to-date for any manager "in view" + PropertyDescription{CLIENT_NICKNAME_PHONETIC, "client_nickname_phonetic", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view" + PropertyDescription{CLIENT_NEEDED_SERVERQUERY_VIEW_POWER, "client_needed_serverquery_view_power", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW}, //automatically up-to-date for any manager "in view" + PropertyDescription{CLIENT_DEFAULT_TOKEN, "client_default_token", "", TYPE_STRING, FLAG_INTERNAL}, //only usable for ourself, the default token we used to connect on our last connection attempt + PropertyDescription{CLIENT_ICON_ID, "client_icon_id", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW | FLAG_CLIENT_VARIABLE}, //automatically up-to-date for any manager "in view" + PropertyDescription{CLIENT_IS_CHANNEL_COMMANDER, "client_is_channel_commander", "0", TYPE_BOOL, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE | FLAG_SAVE_MUSIC}, //automatically up-to-date for any manager "in view" + PropertyDescription{CLIENT_COUNTRY, "client_country", "TS", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_CLIENT_VARIABLE | FLAG_GLOBAL | FLAG_SAVE_MUSIC | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view" + PropertyDescription{CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID, "client_channel_group_inherited_channel_id", "0", TYPE_UNSIGNED_NUMBER, FLAG_CLIENT_VIEW}, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, contains channel_id where the channel_group_id is set from + PropertyDescription{CLIENT_BADGES, "client_badges", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view", stores icons for partner badges + PropertyDescription{CLIENT_MYTEAMSPEAK_ID, "client_myteamspeak_id", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_SS | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view", stores icons for partner badges + PropertyDescription{CLIENT_INTEGRATIONS, "client_integrations", "", TYPE_STRING, FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE}, //automatically up-to-date for any manager "in view", stores icons for partner badges + PropertyDescription{CLIENT_ACTIVE_INTEGRATIONS_INFO, "client_active_integrations_info", "", TYPE_STRING, FLAG_INTERNAL | FLAG_USER_EDITABLE}, + + //Using FLAG_GLOBAL here,lse they will be overridden on clientinit + PropertyDescription{CLIENT_TEAFORO_ID, "client_teaforo_id", "0", TYPE_UNSIGNED_NUMBER, FLAG_NEW | FLAG_CLIENT_VIEW | FLAG_GLOBAL}, + PropertyDescription{CLIENT_TEAFORO_NAME, "client_teaforo_name", "", TYPE_STRING, FLAG_NEW | FLAG_CLIENT_VIEW | FLAG_GLOBAL}, + PropertyDescription{CLIENT_TEAFORO_FLAGS, "client_teaforo_flags", "0", TYPE_UNSIGNED_NUMBER, FLAG_NEW | FLAG_CLIENT_VIEW | FLAG_GLOBAL}, + + //Music bot stuff + PropertyDescription{CLIENT_OWNER, "client_owner", "0", TYPE_UNSIGNED_NUMBER, FLAG_NEW | FLAG_CLIENT_VIEW}, + PropertyDescription{CLIENT_BOT_TYPE, "client_bot_type", "0", TYPE_UNSIGNED_NUMBER, FLAG_NEW | FLAG_SAVE_MUSIC | FLAG_USER_EDITABLE | FLAG_CLIENT_VIEW}, + PropertyDescription{CLIENT_LAST_CHANNEL, "client_last_channel", "0", TYPE_UNSIGNED_NUMBER, FLAG_NEW | FLAG_INTERNAL | FLAG_SAVE_MUSIC}, + PropertyDescription{CLIENT_PLAYER_STATE, "player_state", "0", TYPE_UNSIGNED_NUMBER, FLAG_NEW | FLAG_CLIENT_VIEW | FLAG_SAVE_MUSIC}, + PropertyDescription{CLIENT_PLAYER_VOLUME, "player_volume", "1", TYPE_FLOAT, FLAG_NEW | FLAG_SAVE_MUSIC | FLAG_CLIENT_VIEW | FLAG_USER_EDITABLE}, + PropertyDescription{CLIENT_PLAYLIST_ID, "client_playlist_id", "0", TYPE_UNSIGNED_NUMBER, FLAG_NEW | FLAG_CLIENT_VARIABLE | FLAG_SAVE_MUSIC}, + PropertyDescription{CLIENT_DISABLED, "client_disabled", "0", TYPE_BOOL, FLAG_NEW | FLAG_CLIENT_VARIABLE}, + PropertyDescription{CLIENT_UPTIME_MODE, "client_uptime_mode", "0", TYPE_UNSIGNED_NUMBER, FLAG_NEW | FLAG_CLIENT_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE_MUSIC}, + PropertyDescription{CLIENT_FLAG_NOTIFY_SONG_CHANGE, "client_flag_notify_song_change", "1", TYPE_BOOL, FLAG_NEW | FLAG_CLIENT_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE_MUSIC}, + + /* connection list properties */ + PropertyDescription{CONNECTION_UNDEFINED, "undefined", "", TYPE_UNKNOWN, 0}, + PropertyDescription{CONNECTION_PING, "connection_ping", "0", TYPE_UNSIGNED_NUMBER, 0}, //average latency for a round trip through and back this connection + PropertyDescription{CONNECTION_PING_DEVIATION, "connection_ping_deviation", "0", TYPE_UNSIGNED_NUMBER, 0}, //standard deviation of the above average latency + PropertyDescription{CONNECTION_CONNECTED_TIME, "connection_connected_time", "0", TYPE_UNSIGNED_NUMBER, 0}, //how long the connection exists already + PropertyDescription{CONNECTION_IDLE_TIME, "connection_idle_time", "0", TYPE_UNSIGNED_NUMBER, 0}, //how long since the last action of this manager + PropertyDescription{CONNECTION_CLIENT_IP, "connection_client_ip", "", TYPE_STRING, FLAG_SAVE}, //NEED DB SAVE! //IP of this manager (as seen from the server side) + PropertyDescription{CONNECTION_CLIENT_PORT, "connection_client_port", "0", TYPE_UNSIGNED_NUMBER, 0}, //Port of this manager (as seen from the server side) + PropertyDescription{CONNECTION_SERVER_IP, "connection_server_ip", "", TYPE_STRING, 0}, //IP of the server (seen from the manager side) - only available on yourself, not for remote clients, not available server side + PropertyDescription{CONNECTION_SERVER_PORT, "connection_server_port", "0", TYPE_UNSIGNED_NUMBER, 0}, //Port of the server (seen from the manager side) - only available on yourself, not for remote clients, not available server side + + PropertyDescription{V(CONNECTION_PACKETS_SENT_SPEECH, "connection_packets_sent_speech", 0)}, //how many Speech packets were sent through this connection + PropertyDescription{V(CONNECTION_PACKETS_SENT_KEEPALIVE, "connection_packets_sent_keepalive", 0)}, + PropertyDescription{V(CONNECTION_PACKETS_SENT_CONTROL, "connection_packets_sent_control", 0)}, + PropertyDescription{V(CONNECTION_PACKETS_SENT_TOTAL, "connection_packets_sent_total", FLAG_CLIENT_INFO)}, //how many packets were sent totally (this is PACKETS_SENT_SPEECH + PACKETS_SENT_KEEPALIVE + PACKETS_SENT_CONTROL) + PropertyDescription{V(CONNECTION_BYTES_SENT_SPEECH, "connection_bytes_sent_speech", 0)}, + PropertyDescription{V(CONNECTION_BYTES_SENT_KEEPALIVE, "connection_bytes_sent_keepalive", 0)}, + PropertyDescription{V(CONNECTION_BYTES_SENT_CONTROL, "connection_bytes_sent_control", 0)}, + PropertyDescription{V(CONNECTION_BYTES_SENT_TOTAL, "connection_bytes_sent_total", FLAG_CLIENT_INFO)}, + PropertyDescription{V(CONNECTION_PACKETS_RECEIVED_SPEECH, "connection_packets_received_speech", 0)}, + PropertyDescription{V(CONNECTION_PACKETS_RECEIVED_KEEPALIVE, "connection_packets_received_keepalive", 0)}, + PropertyDescription{V(CONNECTION_PACKETS_RECEIVED_CONTROL, "connection_packets_received_control", 0)}, + PropertyDescription{V(CONNECTION_PACKETS_RECEIVED_TOTAL, "connection_packets_received_total", FLAG_CLIENT_INFO)}, + PropertyDescription{V(CONNECTION_BYTES_RECEIVED_SPEECH, "connection_bytes_received_speech", 0)}, + PropertyDescription{V(CONNECTION_BYTES_RECEIVED_KEEPALIVE, "connection_bytes_received_keepalive", 0)}, + PropertyDescription{V(CONNECTION_BYTES_RECEIVED_CONTROL, "connection_bytes_received_control", 0)}, + PropertyDescription{V(CONNECTION_BYTES_RECEIVED_TOTAL, "connection_bytes_received_total", FLAG_CLIENT_INFO)}, + PropertyDescription{F(CONNECTION_PACKETLOSS_SPEECH, "connection_packetloss_speech", 0)}, + PropertyDescription{F(CONNECTION_PACKETLOSS_KEEPALIVE, "connection_packetloss_keepalive", 0)}, + PropertyDescription{F(CONNECTION_PACKETLOSS_CONTROL, "connection_packetloss_control", 0)}, + PropertyDescription{F(CONNECTION_PACKETLOSS_TOTAL, "connection_packetloss_total", FLAG_CLIENT_INFO)}, //the probability with which a packet round trip failed because a packet was lost + PropertyDescription{F(CONNECTION_SERVER2CLIENT_PACKETLOSS_SPEECH, "connection_server2client_packetloss_speech", 0)}, //the probability with which a speech packet failed from the server to the manager + PropertyDescription{F(CONNECTION_SERVER2CLIENT_PACKETLOSS_KEEPALIVE, "connection_server2client_packetloss_keepalive", 0)}, + PropertyDescription{F(CONNECTION_SERVER2CLIENT_PACKETLOSS_CONTROL, "connection_server2client_packetloss_control", 0)}, + PropertyDescription{F(CONNECTION_SERVER2CLIENT_PACKETLOSS_TOTAL, "connection_server2client_packetloss_total", FLAG_CLIENT_INFO)}, + PropertyDescription{F(CONNECTION_CLIENT2SERVER_PACKETLOSS_SPEECH, "connection_client2server_packetloss_speech", 0)}, + PropertyDescription{F(CONNECTION_CLIENT2SERVER_PACKETLOSS_KEEPALIVE, "connection_client2server_packetloss_keepalive", 0)}, + PropertyDescription{F(CONNECTION_CLIENT2SERVER_PACKETLOSS_CONTROL, "connection_client2server_packetloss_control", 0)}, + PropertyDescription{F(CONNECTION_CLIENT2SERVER_PACKETLOSS_TOTAL, "connection_client2server_packetloss_total", FLAG_CLIENT_INFO)}, + PropertyDescription{V(CONNECTION_BANDWIDTH_SENT_LAST_SECOND_SPEECH, "connection_bandwidth_sent_last_second_speech", 0)}, //howmany bytes of speech packets we sent during the last second + PropertyDescription{V(CONNECTION_BANDWIDTH_SENT_LAST_SECOND_KEEPALIVE, "connection_bandwidth_sent_last_second_keepalive", 0)}, + PropertyDescription{V(CONNECTION_BANDWIDTH_SENT_LAST_SECOND_CONTROL, "connection_bandwidth_sent_last_second_control", 0)}, + PropertyDescription{V(CONNECTION_BANDWIDTH_SENT_LAST_SECOND_TOTAL, "connection_bandwidth_sent_last_second_total", FLAG_CLIENT_INFO)}, + PropertyDescription{V(CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_SPEECH, "connection_bandwidth_sent_last_minute_speech", 0)}, //howmany bytes/s of speech packets we sent in average during the last minute + PropertyDescription{V(CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_KEEPALIVE, "connection_bandwidth_sent_last_minute_keepalive", 0)}, + PropertyDescription{V(CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_CONTROL, "connection_bandwidth_sent_last_minute_control", 0)}, + PropertyDescription{V(CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_TOTAL, "connection_bandwidth_sent_last_minute_total", FLAG_CLIENT_INFO)}, + PropertyDescription{V(CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_SPEECH, "connection_bandwidth_received_last_second_speech", 0)}, + PropertyDescription{V(CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_KEEPALIVE, "connection_bandwidth_received_last_second_keepalive", 0)}, + PropertyDescription{V(CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_CONTROL, "connection_bandwidth_received_last_second_control", 0)}, + PropertyDescription{V(CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_TOTAL, "connection_bandwidth_received_last_second_total", FLAG_CLIENT_INFO)}, + PropertyDescription{V(CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_SPEECH, "connection_bandwidth_received_last_minute_speech", 0)}, + PropertyDescription{V(CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_KEEPALIVE, "connection_bandwidth_received_last_minute_keepalive", 0)}, + PropertyDescription{V(CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_CONTROL, "connection_bandwidth_received_last_minute_control", 0)}, + PropertyDescription{V(CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_TOTAL, "connection_bandwidth_received_last_minute_total", FLAG_CLIENT_INFO)}, + + //Rare properties + PropertyDescription{V(CONNECTION_FILETRANSFER_BANDWIDTH_SENT, "connection_filetransfer_bandwidth_sent", FLAG_CLIENT_INFO)}, //how many bytes per second are currently being sent by file transfers + PropertyDescription{V(CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED, "connection_filetransfer_bandwidth_received", FLAG_CLIENT_INFO)}, //how many bytes per second are currently being received by file transfers + PropertyDescription{V(CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL, "connection_filetransfer_bytes_received_total", FLAG_CLIENT_INFO)}, //how many bytes we received in total through file transfers + PropertyDescription{V(CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL, "connection_filetransfer_bytes_sent_total", FLAG_CLIENT_INFO)}, //how many bytes we sent in total through file transfers + + /* server instance properties */ + PropertyDescription{SERVERINSTANCE_UNDEFINED, "undefined", "", TYPE_UNKNOWN, 0}, + PropertyDescription{SERVERINSTANCE_DATABASE_VERSION, "serverinstance_database_version", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE}, + PropertyDescription{SERVERINSTANCE_PERMISSIONS_VERSION, "serverinstance_permissions_version", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE}, + PropertyDescription{SERVERINSTANCE_FILETRANSFER_HOST, "serverinstance_filetransfer_host", "0.0.0.0,[::]", TYPE_STRING, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_FILETRANSFER_PORT, "serverinstance_filetransfer_port", "30303", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_FILETRANSFER_MAX_CONNECTIONS, "serverinstance_filetransfer_max_connections", "100", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_FILETRANSFER_MAX_CONNECTIONS_PER_IP, "serverinstance_filetransfer_max_connections_per_ip", "20", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_QUERY_HOST, "serverinstance_query_host", "0.0.0.0,[::]", TYPE_STRING, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_QUERY_PORT, "serverinstance_query_port", "10101", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_QUERY_MAX_CONNECTIONS, "serverinstance_query_max_connections", "100", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_QUERY_MAX_CONNECTIONS_PER_IP, "serverinstance_query_max_connections_per_ip", "3", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_MONTHLY_TIMESTAMP, "serverinstance_monthly_timestamp", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_MAX_DOWNLOAD_TOTAL_BANDWIDTH, "serverinstance_max_download_total_bandwidth", "-1", TYPE_SIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_MAX_UPLOAD_TOTAL_BANDWIDTH, "serverinstance_max_upload_total_bandwidth", "-1", TYPE_SIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_SERVERQUERY_FLOOD_COMMANDS, "serverinstance_serverquery_flood_commands", "3", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, //how many commands we can issue while in the SERVERINSTANCE_SERVERQUERY_FLOOD_TIME window + PropertyDescription{SERVERINSTANCE_SERVERQUERY_FLOOD_TIME, "serverinstance_serverquery_flood_time", "1", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, //time window in seconds for max command execution check + PropertyDescription{SERVERINSTANCE_SERVERQUERY_BAN_TIME, "serverinstance_serverquery_ban_time", "600", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, //how many seconds someone get banned if he floods + PropertyDescription{SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP, "serverinstance_template_serveradmin_group", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP, "serverinstance_template_serverdefault_group", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP, "serverinstance_template_channeladmin_group", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP, "serverinstance_template_channeldefault_group", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP, "serverinstance_template_musicdefault_group", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_GUEST_SERVERQUERY_GROUP, "serverinstance_guest_serverquery_group", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_ADMIN_SERVERQUERY_GROUP, "serverinstance_admin_serverquery_group", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_PENDING_CONNECTIONS_PER_IP, "serverinstance_pending_connections_per_ip", "0", TYPE_UNSIGNED_NUMBER, FLAG_INSTANCE_VARIABLE | FLAG_SAVE}, + PropertyDescription{SERVERINSTANCE_SPOKEN_TIME_TOTAL, "serverinstance_spoken_time_total", "0", TYPE_UNSIGNED_NUMBER, FLAG_INTERNAL | FLAG_SAVE | FLAG_NEW}, + PropertyDescription{SERVERINSTANCE_SPOKEN_TIME_DELETED, "serverinstance_spoken_time_deleted", "0", TYPE_UNSIGNED_NUMBER, FLAG_INTERNAL | FLAG_SAVE | FLAG_NEW}, + PropertyDescription{SERVERINSTANCE_SPOKEN_TIME_ALIVE, "serverinstance_spoken_time_alive", "0", TYPE_UNSIGNED_NUMBER, FLAG_INTERNAL | FLAG_SAVE | FLAG_NEW}, + PropertyDescription{SERVERINSTANCE_SPOKEN_TIME_VARIANZ, "serverinstance_spoken_time_varianz", "0", TYPE_UNSIGNED_NUMBER, FLAG_INTERNAL | FLAG_SAVE | FLAG_NEW}, + PropertyDescription{SERVERINSTANCE_VIRTUAL_SERVER_ID_INDEX, "serverinstance_virtual_server_id_index", "1", TYPE_UNSIGNED_NUMBER, FLAG_SAVE | FLAG_INSTANCE_VARIABLE | FLAG_NEW}, + PropertyDescription{SERVERINSTANCE_UNIQUE_ID, "serverinstance_unique_id", "", TYPE_STRING, FLAG_INTERNAL | FLAG_SAVE | FLAG_NEW}, + + /* playlist properties */ + PropertyDescription{PLAYLIST_UNDEFINED, "undefined", "", TYPE_UNKNOWN, 0}, + PropertyDescription{PLAYLIST_ID, "playlist_id", "0", TYPE_UNSIGNED_NUMBER, FLAG_PLAYLIST_VARIABLE}, + PropertyDescription{PLAYLIST_TITLE, "playlist_title", "Yet another playlist", TYPE_STRING, FLAG_PLAYLIST_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE}, + PropertyDescription{PLAYLIST_DESCRIPTION, "playlist_description", "", TYPE_STRING, FLAG_PLAYLIST_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE}, + PropertyDescription{PLAYLIST_TYPE, "playlist_type", "0", TYPE_UNSIGNED_NUMBER, FLAG_PLAYLIST_VARIABLE | FLAG_SAVE}, + PropertyDescription{PLAYLIST_OWNER_DBID, "playlist_owner_dbid", "0", TYPE_UNSIGNED_NUMBER, FLAG_PLAYLIST_VARIABLE | FLAG_SAVE}, + PropertyDescription{PLAYLIST_OWNER_NAME, "playlist_owner_name", "0", TYPE_STRING, FLAG_PLAYLIST_VARIABLE | FLAG_SAVE}, + PropertyDescription{PLAYLIST_MAX_SONGS, "playlist_max_songs", "-1", TYPE_SIGNED_NUMBER, FLAG_PLAYLIST_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE}, + PropertyDescription{PLAYLIST_FLAG_DELETE_PLAYED, "playlist_flag_delete_played", "1", TYPE_BOOL, FLAG_PLAYLIST_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE}, + PropertyDescription{PLAYLIST_FLAG_FINISHED, "playlist_flag_finished", "0", TYPE_BOOL, FLAG_PLAYLIST_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE}, + PropertyDescription{PLAYLIST_REPLAY_MODE, "playlist_replay_mode", "0", TYPE_UNSIGNED_NUMBER, FLAG_PLAYLIST_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE}, + PropertyDescription{PLAYLIST_CURRENT_SONG_ID, "playlist_current_song_id", "0", TYPE_UNSIGNED_NUMBER, FLAG_PLAYLIST_VARIABLE | FLAG_SAVE} +); + +#undef str_ +#undef V +#undef F + +#undef FLAG_SS +#undef FLAG_SERVER_VV +#undef FLAG_SERVER_VVSS +#undef FLAG_CLIENT_VV +#undef FLAG_CLIENT_VVSS \ No newline at end of file diff --git a/src/PropertyDefinitions.cpp b/src/PropertyDefinitions.cpp new file mode 100644 index 0000000..393c93f --- /dev/null +++ b/src/PropertyDefinitions.cpp @@ -0,0 +1,84 @@ +// +// Created by WolverinDEV on 08/04/2020. +// + +#include "./Properties.h" + +using namespace ts; + +#ifdef EXTERNALIZE_PROPERTY_DEFINITIONS +using PropertyDescription = property::PropertyDescription; +#include "./PropertyDefinition.h" +const property::PropertyListInfo property::property_list_info = impl::list_info(); +#endif + +using PropertyType = property::PropertyType; +using PropertyType = property::PropertyType; + +/* verifier methods */ +constexpr bool validate_property_list_contains_all_types() { + for(const auto& entry : property::property_list_info.begin_index) + if(entry >= property::property_list.size()) + return false; + return true; +} + +constexpr bool validate_all_names_valid() { + for(const auto& entry : property::property_list) + for(const auto& c : entry.name) + if(!((c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9'))) + return false; + return true; +} + +constexpr bool validate_first_property_undefined() { + for(const auto& index : property::property_list_info.begin_index) + if(index < property::property_list.size()) + if(property::property_list[index].name != "undefined") + return false; + return true; +} + +constexpr bool property_list_ordered() { + static_assert(!property::property_list.empty()); + + /* initial setup */ + std::array visited_types{}; + PropertyType current_type{property::property_list[0].type_property}; + visited_types[current_type] = true; + size_t last_id{(size_t) property::property_list[0].property_index}; + if(last_id != 0) return false; + + /* iterate */ + for(auto index{1}; index < property::property_list.size(); index++) { + const auto& entry = property::property_list[index]; + if(last_id + 1 != entry.property_index) { + if(current_type == entry.type_property) + return false; + if(visited_types[entry.type_property]) + return false; + if(entry.property_index != 0) /* new type must start with 0 (undefined) */ + return false; + visited_types[current_type = entry.type_property] = true; + last_id = 0; + } else if(current_type != entry.type_property) + return false; + else + last_id++; + } + return true; +} + +#ifdef EXTERNALIZE_PROPERTY_DEFINITIONS +__attribute__((constructor)) void validate_properties() { + assert(property_list_ordered()); + assert(validate_first_property_undefined()); + assert(validate_property_list_contains_all_types()); + assert(validate_all_names_valid()); +} +#else +static_assert(property_list_ordered(), "Property list is unordered!"); +static_assert(validate_first_property_undefined(), "First property of each type must be the undefined property"); +static_assert(validate_property_list_contains_all_types(), "Missing property begin for a property type"); +static_assert(validate_all_names_valid(), "Property list contains name which does not match the expected names: [a-z_]+"); +#endif \ No newline at end of file diff --git a/src/converters/converter.cpp b/src/converters/converter.cpp index 7ffb256..aebbc1d 100644 --- a/src/converters/converter.cpp +++ b/src/converters/converter.cpp @@ -37,4 +37,5 @@ CONVERTER_PRIMITIVE_ST(double, std::stod(std::string{str})); CONVERTER_PRIMITIVE_ST(long_double, std::stold(std::string{str})); CONVERTER_ST(std__string, return std::string{str};, return std::any_cast(value);); -CONVERTER_ST(const_char__ , return str.data();, return std::string(std::any_cast(value));); \ No newline at end of file +CONVERTER_ST(std__string_view, return str;, return std::string{std::any_cast(value)};); +CONVERTER_ST(const_char__ , return str.data();, return std::string{std::any_cast(value)};); \ No newline at end of file diff --git a/src/converters/converter.h b/src/converters/converter.h index 58330b7..c0c0394 100644 --- a/src/converters/converter.h +++ b/src/converters/converter.h @@ -54,8 +54,10 @@ namespace ts { CONVERTER_PRIMITIVE(uint64_t); typedef std::string std__string; + typedef std::string_view std__string_view; typedef const char* const_char__; CONVERTER_PRIMITIVE(std__string); + CONVERTER_PRIMITIVE(std__string_view); CONVERTER_PRIMITIVE(const_char__); /* const expr char literal */ diff --git a/src/protocol/AcknowledgeManager.cpp b/src/protocol/AcknowledgeManager.cpp index 6d5484c..bbe0b41 100644 --- a/src/protocol/AcknowledgeManager.cpp +++ b/src/protocol/AcknowledgeManager.cpp @@ -41,12 +41,12 @@ void AcknowledgeManager::process_packet(ts::protocol::BasicPacket &packet) { entry->buffer = packet.buffer(); entry->resend_count = 0; - entry->resend_period = milliseconds((int) ceil(this->average_response * 3/2)); entry->first_send = system_clock::now(); - entry->next_resend = entry->first_send + entry->resend_period; + entry->next_resend = entry->first_send + std::chrono::milliseconds{(int64_t) ceil(this->rto)}; entry->packet_type = packet.type().type(); entry->packet_id = packet.packetId(); + entry->generation_id = packet.generationId(); entry->acknowledged = false; entry->send_count = 1; @@ -56,11 +56,8 @@ void AcknowledgeManager::process_packet(ts::protocol::BasicPacket &packet) { } } -bool AcknowledgeManager::process_acknowledge(uint8_t packet_type, const pipes::buffer_view& payload, std::string& error) { - if(payload.length() < 2) return false; - +bool AcknowledgeManager::process_acknowledge(uint8_t packet_type, uint16_t target_id, std::string& error) { PacketType target_type{packet_type == protocol::ACK_LOW ? PacketType::COMMAND_LOW : PacketType::COMMAND}; - uint16_t target_id{be2le16((char*) payload.data_ptr())}; std::shared_ptr entry; std::unique_ptr> ack_listener; @@ -72,8 +69,13 @@ bool AcknowledgeManager::process_acknowledge(uint8_t packet_type, const pipes::b ack_listener = std::move(entry->acknowledge_listener); /* move it out so nobody else could call it as well */ entry->send_count--; - if(entry->send_count == 0) + if(entry->send_count == 0) { this->entries.erase(it); + if(entry->resend_count == 0) { + auto difference = std::chrono::system_clock::now() - entry->first_send; + this->update_rto(std::chrono::duration_cast(difference).count()); + } + } break; } } @@ -83,18 +85,12 @@ bool AcknowledgeManager::process_acknowledge(uint8_t packet_type, const pipes::b return false; } - auto time = system_clock::now() - entry->next_resend + entry->resend_period; - auto ms_time = duration_cast(time).count(); - if(ms_time > 5) { - this->average_response = this->average_response * .80 + ms_time * .2; - } - entry->acknowledged = true; if(ack_listener) ack_listener->executionSucceed(true); return true; } -ssize_t AcknowledgeManager::execute_resend(const system_clock::time_point& now , std::chrono::system_clock::time_point &next_resend,std::deque& buffers, string& error) { +ssize_t AcknowledgeManager::execute_resend(const system_clock::time_point& now , std::chrono::system_clock::time_point &next_resend,std::deque>& buffers, string& error) { size_t resend_count{0}; vector> need_resend; @@ -105,19 +101,13 @@ ssize_t AcknowledgeManager::execute_resend(const system_clock::time_point& now , for (auto &entry : this->entries) { if(entry->acknowledged) { - if(entry->next_resend + entry->resend_period <= now) { // Some resends are lost. So we just drop it after time + if(entry->next_resend + std::chrono::milliseconds{(int64_t) ceil(this->rto * 4)} <= now) { // Some resends are lost. So we just drop it after time entry.reset(); cleanup = true; } } else { if(entry->next_resend <= now) { - entry->resend_period = entry->resend_period + milliseconds{(int) ceil(this->average_response * 2)}; - if(entry->resend_period.count() > 1000) - entry->resend_period = milliseconds(1000); - else if(entry->resend_period.count() < 25) - entry->resend_period = milliseconds(25); - - entry->next_resend = now + entry->resend_period; + entry->next_resend = now + std::chrono::milliseconds{(int64_t) min(ceil(this->rto), 1500.f)}; need_resend.push_back(entry); } if(next_resend > entry->next_resend) @@ -140,8 +130,21 @@ ssize_t AcknowledgeManager::execute_resend(const system_clock::time_point& now , resend_count++; packet->resend_count++; packet->send_count++; - buffers.push_back(packet->buffer); + buffers.push_back(packet); } return resend_count; +} + +/* we're not taking the clock granularity into account because its nearly 1ms and it would only add more branches */ +void AcknowledgeManager::update_rto(size_t r) { + if(srtt == -1) { + this->srtt = r; + this->rttvar = r / 2.f; + this->rto = srtt + 4 * this->rttvar; + } else { + this->rttvar = (1.f - alpha) * this->rttvar + beta * abs(this->srtt - r); + this->srtt = (1.f - alpha) * srtt + alpha * r; + this->rto = std::min(200.f, this->srtt + 4 * this->rttvar); + } } \ No newline at end of file diff --git a/src/protocol/AcknowledgeManager.h b/src/protocol/AcknowledgeManager.h index e3d3352..81d6416 100644 --- a/src/protocol/AcknowledgeManager.h +++ b/src/protocol/AcknowledgeManager.h @@ -7,22 +7,23 @@ namespace ts::connection { class VoiceClientConnection; class AcknowledgeManager { - struct Entry { - uint16_t packet_id = 0; - uint8_t packet_type = 0xFF; - uint8_t resend_count = 0; - bool acknowledged : 1; - uint8_t send_count : 7; - - - pipes::buffer buffer; - std::chrono::system_clock::time_point first_send; - std::chrono::system_clock::time_point next_resend; - std::chrono::milliseconds resend_period; - - std::unique_ptr> acknowledge_listener; - }; public: + struct Entry { + uint16_t packet_id{0}; + uint16_t generation_id{0}; + + uint8_t packet_type{0xFF}; + uint8_t resend_count{0}; + bool acknowledged : 1; + uint8_t send_count : 7; + + + pipes::buffer buffer; + std::chrono::system_clock::time_point first_send; + std::chrono::system_clock::time_point next_resend; + std::unique_ptr> acknowledge_listener; + }; + AcknowledgeManager(); virtual ~AcknowledgeManager(); @@ -30,20 +31,29 @@ namespace ts::connection { void reset(); void process_packet(ts::protocol::BasicPacket& /* packet */); - bool process_acknowledge(uint8_t packet_type, const pipes::buffer_view& /* payload */, std::string& /* error */); + bool process_acknowledge(uint8_t packet_type, uint16_t /* packet id */, std::string& /* error */); ssize_t execute_resend( const std::chrono::system_clock::time_point& /* now */, std::chrono::system_clock::time_point& /* next resend */, - std::deque& /* buffers to resend */, + std::deque>& /* buffers to resend */, std::string& /* error */ ); + + [[nodiscard]] inline auto current_rto() const { return this->rto; } + [[nodiscard]] inline auto current_srtt() const { return this->srtt; } + [[nodiscard]] inline auto current_rttvar() const { return this->rttvar; } private: std::mutex entry_lock; std::deque> entries; - std::chrono::milliseconds resend_delay{500}; + float rto{1000}; + float srtt{-1}; + float rttvar{}; - double average_response = 20; + constexpr static auto alpha{.125f}; + constexpr static auto beta{.25f}; + + void update_rto(size_t /* response time */); }; } \ No newline at end of file diff --git a/src/protocol/Packet.cpp b/src/protocol/Packet.cpp index 7cb3b31..efeb9b9 100644 --- a/src/protocol/Packet.cpp +++ b/src/protocol/Packet.cpp @@ -216,27 +216,32 @@ namespace ts { } - uint16_t ClientPacketParser::packet_id() const { return be2le16(this->_buffer.data_ptr(), ClientPacketParser::kHeaderOffset + 0); } - uint16_t ClientPacketParser::client_id() const { return be2le16(this->_buffer.data_ptr(), ClientPacketParser::kHeaderOffset + 2); } - uint8_t ClientPacketParser::type() const { return (uint8_t) this->_buffer[ClientPacketParser::kHeaderOffset + 4] & 0xFU; } - uint8_t ClientPacketParser::flags() const { return (uint8_t) this->_buffer[ClientPacketParser::kHeaderOffset + 4] & 0xF0U; } - - bool ClientPacketParser::is_encrypted() const { + /* New packet parser API */ + bool PacketParser::is_encrypted() const { if(this->decrypted) return false; return (this->flags() & PacketFlag::Unencrypted) == 0; } - bool ClientPacketParser::is_compressed() const { + bool PacketParser::is_compressed() const { if(this->uncompressed) return false; return (this->flags() & PacketFlag::Compressed) > 0; } - bool ClientPacketParser::is_fragmented() const { + bool PacketParser::is_fragmented() const { if(this->defragmented) return false; return (this->flags() & PacketFlag::Fragmented) > 0; } + + uint16_t ClientPacketParser::packet_id() const { return be2le16(this->_buffer.data_ptr(), ClientPacketParser::kHeaderOffset + 0); } + uint16_t ClientPacketParser::client_id() const { return be2le16(this->_buffer.data_ptr(), ClientPacketParser::kHeaderOffset + 2); } + uint8_t ClientPacketParser::type() const { return (uint8_t) this->_buffer[ClientPacketParser::kHeaderOffset + 4] & 0xFU; } + uint8_t ClientPacketParser::flags() const { return (uint8_t) this->_buffer[ClientPacketParser::kHeaderOffset + 4] & 0xF0U; } + + uint16_t ServerPacketParser::packet_id() const { return be2le16(this->_buffer.data_ptr(), ClientPacketParser::kHeaderOffset + 0); } + uint8_t ServerPacketParser::type() const { return (uint8_t) this->_buffer[ClientPacketParser::kHeaderOffset + 2] & 0xFU; } + uint8_t ServerPacketParser::flags() const { return (uint8_t) this->_buffer[ClientPacketParser::kHeaderOffset + 2] & 0xF0U; } } } \ No newline at end of file diff --git a/src/protocol/Packet.h b/src/protocol/Packet.h index ce456bf..97beac5 100644 --- a/src/protocol/Packet.h +++ b/src/protocol/Packet.h @@ -286,32 +286,23 @@ namespace ts { void setPacketId(uint16_t, uint16_t) override; }; - class ClientPacketParser { + class PacketParser { public: - constexpr static auto kHeaderOffset = 8; - constexpr static auto kHeaderLength = CLIENT_HEADER_SIZE; - - constexpr static auto kPayloadOffset = kHeaderOffset + CLIENT_HEADER_SIZE; - explicit ClientPacketParser(pipes::buffer_view buffer) : _buffer{std::move(buffer)} {} - ClientPacketParser(const ClientPacketParser&) = delete; - - [[nodiscard]] inline bool valid() const { - if(this->_buffer.length() < kPayloadOffset) return false; - return this->type() <= 8; - } + PacketParser(const PacketParser&) = delete; + explicit PacketParser(pipes::buffer_view buffer) : _buffer{std::move(buffer)} {} [[nodiscard]] inline const void* data_ptr() const { return this->_buffer.data_ptr(); } [[nodiscard]] inline void* mutable_data_ptr() { return (void*) this->_buffer.data_ptr(); } [[nodiscard]] inline pipes::buffer_view buffer() const { return this->_buffer; } [[nodiscard]] inline pipes::buffer_view mac() const { return this->_buffer.view(0, 8); } - [[nodiscard]] inline pipes::buffer_view payload() const { return this->_buffer.view(kPayloadOffset); } - [[nodiscard]] inline size_t payload_length() const { return this->_buffer.length() - kPayloadOffset; } + [[nodiscard]] virtual pipes::buffer_view payload() const = 0; + [[nodiscard]] virtual size_t payload_length() const = 0; - [[nodiscard]] uint16_t client_id() const; - [[nodiscard]] uint16_t packet_id() const; - [[nodiscard]] uint8_t type() const; - [[nodiscard]] uint8_t flags() const; + [[nodiscard]] inline uint32_t full_packet_id() const { return this->packet_id() | (this->estimated_generation() << 16U); } + [[nodiscard]] virtual uint16_t packet_id() const = 0; + [[nodiscard]] virtual uint8_t type() const = 0; + [[nodiscard]] virtual uint8_t flags() const = 0; [[nodiscard]] bool is_encrypted() const; [[nodiscard]] bool is_compressed() const; @@ -323,12 +314,36 @@ namespace ts { inline void set_decrypted() { this->decrypted = true; } inline void set_uncompressed() { this->uncompressed = true; } inline void set_defragmented() { this->defragmented = true; } - private: + + protected: uint16_t generation{}; bool decrypted{false}, uncompressed{false}, defragmented{false}; pipes::buffer_view _buffer{}; }; + class ClientPacketParser : public PacketParser { + public: + constexpr static auto kHeaderOffset = 8; + constexpr static auto kHeaderLength = CLIENT_HEADER_SIZE; + + constexpr static auto kPayloadOffset = kHeaderOffset + CLIENT_HEADER_SIZE; + explicit ClientPacketParser(pipes::buffer_view buffer) : PacketParser{std::move(buffer)} {} + ClientPacketParser(const ClientPacketParser&) = delete; + + [[nodiscard]] inline bool valid() const { + if(this->_buffer.length() < kPayloadOffset) return false; + return this->type() <= 8; + } + + [[nodiscard]] inline pipes::buffer_view payload() const override { return this->_buffer.view(kPayloadOffset); } + [[nodiscard]] inline size_t payload_length() const override { return this->_buffer.length() - kPayloadOffset; } + + [[nodiscard]] uint16_t client_id() const; + [[nodiscard]] uint16_t packet_id() const override; + [[nodiscard]] uint8_t type() const override; + [[nodiscard]] uint8_t flags() const override; + }; + /** * Packet from the server */ @@ -367,5 +382,27 @@ namespace ts { void setPacketId(uint16_t, uint16_t) override; }; + + class ServerPacketParser : public PacketParser { + public: + constexpr static auto kHeaderOffset = 8; + constexpr static auto kHeaderLength = SERVER_HEADER_SIZE; + + constexpr static auto kPayloadOffset = kHeaderOffset + SERVER_HEADER_SIZE; + explicit ServerPacketParser(pipes::buffer_view buffer) : PacketParser{std::move(buffer)} {} + ServerPacketParser(const ServerPacketParser&) = delete; + + [[nodiscard]] inline bool valid() const { + if(this->_buffer.length() < kPayloadOffset) return false; + return this->type() <= 8; + } + + [[nodiscard]] inline pipes::buffer_view payload() const override { return this->_buffer.view(kPayloadOffset); } + [[nodiscard]] inline size_t payload_length() const override { return this->_buffer.length() - kPayloadOffset; } + + [[nodiscard]] uint16_t packet_id() const override; + [[nodiscard]] uint8_t type() const override; + [[nodiscard]] uint8_t flags() const override; + }; } } \ No newline at end of file diff --git a/src/protocol/PacketLossCalculator.cpp b/src/protocol/PacketLossCalculator.cpp new file mode 100644 index 0000000..f0a5bc6 --- /dev/null +++ b/src/protocol/PacketLossCalculator.cpp @@ -0,0 +1,105 @@ +// +// Created by WolverinDEV on 06/04/2020. +// + +#include +#include +#include "PacketLossCalculator.h" + +using namespace ts::protocol; + +void UnorderedPacketLossCalculator::packet_received(uint32_t packet_id) { + if(packet_id > this->packet_history_offset) { + const auto age = packet_id - this->packet_history_offset; /* best case should be 1 */ + + if(age < this->packet_history.max_bits()) { + const auto received = this->packet_history.shift_in_bounds(age).count(); + this->received_packets_ += received; + this->received_packets_total_ += received; + + this->lost_packets_ += age - received; + this->lost_packets_total_ += age - received; + } else { + const auto received = this->packet_history.clear().count(); + this->received_packets_ += received; + this->received_packets_total_ += received; + this->lost_packets_ += this->packet_history.max_bits() - received; + this->lost_packets_total_ += this->packet_history.max_bits() - received; + + if(age >= this->packet_history.max_bits() * 2) { + this->packet_history.set_unchecked(0); + this->lost_packets_ += age - this->packet_history.max_bits() * 2; + this->lost_packets_total_ += age - this->packet_history.max_bits() * 2; + } else { + this->packet_history.set_unchecked(age - this->packet_history.max_bits()); + } + } + this->packet_history.set_unchecked(0); + this->packet_history_offset = packet_id; + if(packet_id < this->packet_history.max_bits()) { + this->received_packets_ = 0; + this->received_packets_total_ = 0; + this->lost_packets_ = 0; + this->lost_packets_total_ = 0; + } + } else { + /* unordered packet */ + const auto age = this->packet_history_offset - packet_id; + if(age >= this->packet_history.max_bits()) + return; /* well that packet is way too old */ + + this->packet_history.set_unchecked(age); + } +} + +void UnorderedPacketLossCalculator::short_stats() { + constexpr auto target_interval = 32; + + const auto packets_passed = this->packet_history_offset - this->last_history_offset; + if(packets_passed < target_interval) return; + + const auto factor = .5; + this->received_packets_ *= factor; + this->lost_packets_ *= factor; + this->last_history_offset = this->packet_history_offset; +} + +void CommandPacketLossCalculator::packet_send(uint32_t packet_id) { + if(packet_id > this->packet_history_offset) { + assert(packet_id - 1 == this->packet_history_offset); /* the method will only be called with an incrementing packet id */ + /* newly send packet */ + auto lost = std::exchange(this->packet_ack_counts[packet_id % CommandPacketLossCalculator::packet_ack_counts_length], 1); + this->lost_packets_ += lost; + this->lost_packets_total_ += lost; + if(lost > 0) + __asm__("nop"); + this->packet_history_offset = packet_id; + } else { + /* We're not really interested if the packet id matches the resend packet. If not we may accidentally increase the loss count. */ + this->packet_ack_counts[packet_id % CommandPacketLossCalculator::packet_ack_counts_length]++; + } + this->packets_send_unshorten++; +} + +void CommandPacketLossCalculator::ack_received(uint32_t packet_id) { + auto& count = this->packet_ack_counts[packet_id % CommandPacketLossCalculator::packet_ack_counts_length]; + if(count > 0) /* could happen if receive an acknowledge for an packet which is older than out buffer size or the client send the ack twice... */ + count--; + else + __asm__("nop"); + + this->received_packets_++; + this->received_packets_total_++; +} + +void CommandPacketLossCalculator::short_stats() { + constexpr auto target_interval = 64; + + const auto packets_passed = this->packets_send_unshorten; + if(packets_passed < target_interval) return; + this->packets_send_unshorten = 0; + + const auto factor = target_interval / packets_passed; + this->received_packets_ *= factor; + this->lost_packets_ *= factor; +} \ No newline at end of file diff --git a/src/protocol/PacketLossCalculator.h b/src/protocol/PacketLossCalculator.h new file mode 100644 index 0000000..b0c7e73 --- /dev/null +++ b/src/protocol/PacketLossCalculator.h @@ -0,0 +1,103 @@ +#pragma once + +#include +#include +#include +#include + +namespace ts::protocol { + template + struct bit_set; + + template <> + struct bit_set<32> { + constexpr static auto native_memory_bytes = sizeof(uint32_t); + constexpr static auto native_memory_bits = native_memory_bytes * 8; + public: + bit_set() : memory{0} {} + explicit bit_set(uint32_t mem) : memory{mem} {} + + [[nodiscard]] inline auto max_bits() const { return native_memory_bits; } + + inline auto shift_in_bounds(size_t offset) { + assert(offset <= this->native_memory_bits); + + auto result = bit_set{this->memory >> (native_memory_bits - offset)}; + this->memory <<= offset; + return result; + } + + inline void set_unchecked(size_t offset) { + this->memory |= 1U << offset; + } + + [[nodiscard]] inline auto count() const { + /* std::bitset will get optimized away */ + return std::bitset<32>(this->memory).count(); + } + + inline auto clear() { return bit_set{std::exchange(this->memory, 0)}; } + private: + uint32_t memory; + }; + + /* will we ever have more than 2^32 packets? Hell no. That would be over (2^32 / (50 * 60 * 60 * 24) := 994.2 days continuous speaking */ + class UnorderedPacketLossCalculator { + public: + UnorderedPacketLossCalculator() = default; + + void packet_received(uint32_t /* packet id */); + void short_stats(); + + [[nodiscard]] inline auto last_packet_id() const { return this->packet_history_offset; } + [[nodiscard]] inline bool valid_data() const { return this->packet_history_offset >= 32; } + + [[nodiscard]] inline uint32_t received_packets() const { return this->received_packets_; } + [[nodiscard]] inline uint32_t lost_packets() const { return this->lost_packets_; } + + [[nodiscard]] inline uint32_t received_packets_total() const { return this->received_packets_total_; } + [[nodiscard]] inline uint32_t lost_packets_total() const { return this->lost_packets_total_; } + + [[nodiscard]] inline uint32_t unconfirmed_received_packets() const { return this->packet_history.count(); }; + [[nodiscard]] inline uint32_t unconfirmed_lost_packets() const { return this->packet_history.max_bits() - this->packet_history.count(); }; + + inline void reset() { this->received_packets_ = 0; this->lost_packets_ = 0; } + private: + uint32_t received_packets_{0}, received_packets_total_{0}, lost_packets_{0}, lost_packets_total_{0}; + + uint32_t packet_history_offset{0}, last_history_offset{0}; + bit_set<32> packet_history{}; + }; + + class CommandPacketLossCalculator { + constexpr static auto packet_ack_counts_length{32}; + public: + CommandPacketLossCalculator() = default; + + void packet_send(uint32_t /* packet id */); + void ack_received(uint32_t /* packet id */); //Attention: This is a full ID! + + void short_stats(); + + [[nodiscard]] inline bool valid_data() const { return true; } + + [[nodiscard]] inline uint32_t received_packets() const { return this->received_packets_; } + [[nodiscard]] inline uint32_t lost_packets() const { return this->lost_packets_; } + + [[nodiscard]] inline uint32_t received_packets_total() const { return this->received_packets_total_; } + [[nodiscard]] inline uint32_t lost_packets_total() const { return this->lost_packets_total_; } + + [[nodiscard]] inline uint32_t unconfirmed_received_packets() const { return 0; }; + [[nodiscard]] inline uint32_t unconfirmed_lost_packets() const { + uint32_t result{0}; + for(auto& e : this->packet_ack_counts) + result += e; + return result; + }; + private: + uint32_t received_packets_{0}, received_packets_total_{0}, lost_packets_{0}, lost_packets_total_{0}; + + uint32_t packet_history_offset{0}, packets_send_unshorten{0}; + uint8_t packet_ack_counts[packet_ack_counts_length]{0}; + }; +} \ No newline at end of file diff --git a/src/query/Command.h b/src/query/Command.h index 52f964b..e315615 100644 --- a/src/query/Command.h +++ b/src/query/Command.h @@ -141,7 +141,7 @@ operator type(){ \ friend std::ostream& operator<<(std::ostream&, const ValueList&); private: - ValueList(std::string& key, std::deque& bulkList) : key(key), bulkList(bulkList) {} + ValueList(std::string key, std::deque& bulkList) : key{std::move(key)}, bulkList(bulkList) {} std::string key; public: std::deque& bulkList; @@ -171,9 +171,9 @@ operator type(){ \ ~Command(); inline std::string command() const { return getCommand(); } - std::string getCommand() const { return _command; } + [[nodiscard]] std::string getCommand() const { return _command; } - std::string build(bool escaped = true) const; + [[nodiscard]] std::string build(bool escaped = true) const; #ifdef HAVE_JSON Json::Value buildJson() const; @@ -184,15 +184,24 @@ operator type(){ \ return bulks[index]; } - ParameterBulk& operator[](size_t index){ + template ::value, int> = 0> + ParameterBulk& operator[](T index){ while(bulks.size() <= index) bulks.push_back(ParameterBulk{}); return bulks[index]; } - ValueList operator[](std::string key){ + ValueList operator[](const std::string& key){ return ValueList(key, bulks); } + ValueList operator[](const std::string_view& key){ + return ValueList(std::string{key}, bulks); //FIXME This should not be the normal case! + } + + ValueList operator[](const char* key){ + return ValueList(std::string{key}, bulks); //FIXME This should not be the normal case! + } + size_t bulkCount() const { return bulks.size(); } void pop_bulk(); void push_bulk_front(); diff --git a/test/PacketLossCalculateTest.cpp b/test/PacketLossCalculateTest.cpp new file mode 100644 index 0000000..50e63bc --- /dev/null +++ b/test/PacketLossCalculateTest.cpp @@ -0,0 +1,222 @@ +// +// Created by WolverinDEV on 06/04/2020. +// +#include +#include +#include +#include +#include +#include + +using UnorderedPacketLossCalculator = ts::protocol::UnorderedPacketLossCalculator; + +inline void print_unordered_stats(UnorderedPacketLossCalculator& generator) { + std::cout << "Valid data: " << generator.valid_data() << ". (Received{local: " << generator.received_packets() << ", unconfirmed: " << generator.unconfirmed_received_packets() << ", total: " << generator.received_packets_total() << "}" << + " Lost: {local: " << generator.lost_packets() << ", unconfirmed: " << generator.unconfirmed_lost_packets() << ", total: " << generator.lost_packets_total() << "} Current ID: " << generator.last_packet_id() << ")\n"; +} + +template +vector_t swap_elements(vector_t vector, int per, int max_distance) { + for(size_t index = 0; index < vector.size() - max_distance; index++) { + if ((rand() % 100) < per) { + //lets switch + auto offset = rand() % max_distance; + std::swap(vector[index], vector[index + offset]); + } + } + + return vector; +} + +inline void generate_unordered(UnorderedPacketLossCalculator& generator, int loss, size_t count) { + size_t id{generator.last_packet_id()}; + while(count--) { + if(rand() % 100 >= loss) + generator.packet_received(id); + id++; + } +} + +void test_const_unordered_0() { + UnorderedPacketLossCalculator generator{}; + for(size_t pid{0}; pid < 100; pid++) + generator.packet_received(pid); + + assert(generator.received_packets_total() == 68); + assert(generator.unconfirmed_received_packets() == 32); + assert(generator.received_packets() == 68); + + assert(generator.lost_packets_total() == 0); + assert(generator.lost_packets() == 0); + assert(generator.unconfirmed_lost_packets() == 0); + + assert(generator.valid_data()); +} + + +void test_const_unordered_1() { + UnorderedPacketLossCalculator generator{}; + generator.packet_received(100); /* we lost the first 99 packets */ + + assert(generator.received_packets_total() == 0); + assert(generator.unconfirmed_received_packets() == 1); + assert(generator.received_packets() == 0); + + assert(generator.lost_packets_total() == 68); + assert(generator.lost_packets() == 68); + assert(generator.unconfirmed_lost_packets() == 31); + + assert(generator.valid_data()); +} + +void test_const_unordered_2() { + UnorderedPacketLossCalculator generator{}; + for(size_t pid{0}; pid < 100; pid++) + if(pid % 2) + generator.packet_received(pid); + + assert(generator.received_packets_total() == 34); + assert(generator.received_packets() == 34); + assert(generator.unconfirmed_received_packets() == 16); + + assert(generator.lost_packets_total() == 34); + assert(generator.lost_packets() == 34); + assert(generator.unconfirmed_lost_packets() == 16); + + assert(generator.valid_data()); +} + + +void test_const_unordered_3() { + UnorderedPacketLossCalculator generator{}; + for(size_t pid{0}; pid < 100; pid++) + if(pid % 3) + generator.packet_received(pid); + + assert(generator.received_packets_total() == 44); + assert(generator.received_packets() == 44); + assert(generator.unconfirmed_received_packets() == 22); + + assert(generator.lost_packets_total() == 23); + assert(generator.lost_packets() == 23); + assert(generator.unconfirmed_lost_packets() == 10); + + assert(generator.valid_data()); +} + +void test_const_unordered_4() { + std::vector received_packets{}; + for(size_t pid{0}; pid < 100; pid++) + received_packets.push_back(pid); + + received_packets = swap_elements(received_packets, 50, 7); + + UnorderedPacketLossCalculator generator{}; + for(const auto& packet : received_packets) + generator.packet_received(packet); + + assert(generator.received_packets_total() == 68); + assert(generator.unconfirmed_received_packets() == 32); + assert(generator.received_packets() == 68); + + assert(generator.lost_packets_total() == 0); + assert(generator.lost_packets() == 0); + assert(generator.unconfirmed_lost_packets() == 0); + + assert(generator.valid_data()); +} + +void test_const_unordered_5() { + std::vector received_packets{}; + for(size_t pid{0}; pid < 100; pid++) + if(pid % 3) + received_packets.push_back(pid); + + received_packets = swap_elements(received_packets, 30, 7); + + UnorderedPacketLossCalculator generator{}; + for(const auto& packet : received_packets) + generator.packet_received(packet); + + assert(generator.received_packets_total() == 44); + assert(generator.received_packets() == 44); + assert(generator.unconfirmed_received_packets() == 22); + + assert(generator.lost_packets_total() == 23); + assert(generator.lost_packets() == 23); + assert(generator.unconfirmed_lost_packets() == 10); + + assert(generator.valid_data()); +} + +void test_unordered_6() { + UnorderedPacketLossCalculator generator{}; + { + const auto pid_base = generator.last_packet_id(); + for(size_t pid{0}; pid < 100; pid++) + generator.packet_received(pid_base + pid); + } + + print_unordered_stats(generator); + generator.short_stats(); + + { + const auto pid_base = generator.last_packet_id(); + generator.packet_received(pid_base + 100); + } + print_unordered_stats(generator); + generator.short_stats(); + + { + const auto pid_base = generator.last_packet_id(); + generator.packet_received(pid_base + 100); + } + print_unordered_stats(generator); + generator.short_stats(); + { + const auto pid_base = generator.last_packet_id(); + for(size_t pid{0}; pid < 100; pid++) + generator.packet_received(pid_base + pid); + } + print_unordered_stats(generator); + generator.short_stats(); + { + const auto pid_base = generator.last_packet_id(); + for(size_t pid{0}; pid < 100; pid++) + generator.packet_received(pid_base + pid); + } + print_unordered_stats(generator); +} + +int main() { +#if 0 + test_const_unordered_0(); + test_const_unordered_1(); + test_const_unordered_2(); + test_const_unordered_3(); + test_const_unordered_4(); + test_const_unordered_5(); + test_unordered_6(); +#endif + size_t value = 0; + + + std::vector bandwidth_seconds{}; + for(size_t iteration{0}; iteration < 600; iteration++) { + if(iteration >= 200 && iteration <= 270) + bandwidth_seconds.push_back(0); + else + bandwidth_seconds.push_back(500); + } + + constexpr auto cofactor = 0.915; + //constexpr auto cofactor = 0.978; + for(size_t iteration{0}; iteration < bandwidth_seconds.size(); iteration++) { + const auto next_value = value * cofactor + bandwidth_seconds[iteration] * (1 - cofactor); + if(next_value > value) + value = ceil(next_value); + else + value = floor(next_value); + std::cout << iteration << ": " << value << "\n"; + } +} diff --git a/test/PropertyTest.cpp b/test/PropertyTest.cpp index 724ff18..589dc09 100644 --- a/test/PropertyTest.cpp +++ b/test/PropertyTest.cpp @@ -7,15 +7,14 @@ using namespace std; int main() { - assert(property::impl::validateUnique()); + //assert(property::impl::validateUnique()); - cout << property::impl::info(property::VIRTUALSERVER_HOST)->name << endl; - cout << property::impl::info("virtualserver_host")->name<< endl; + cout << property::describe(property::VIRTUALSERVER_HOST).name << endl; + cout << property::find(property::PROP_TYPE_SERVER, "virtualserver_host").name<< endl; Properties props; props.register_property_type(); - cout << "X: " << property::impl::info(property::SERVERINSTANCE_QUERY_PORT)->default_value << endl; auto property = props[property::SERVERINSTANCE_QUERY_PORT]; cout << "Port: " << props[property::SERVERINSTANCE_QUERY_PORT].as() << endl; @@ -26,6 +25,7 @@ int main() { cout << "Port: " << props[property::SERVERINSTANCE_QUERY_PORT].as() << endl; + /* { assert(property::impl::validateInput("022222", property::TYPE_UNSIGNED_NUMBER) == true); assert(property::impl::validateInput("000000", property::TYPE_UNSIGNED_NUMBER) == true); @@ -54,6 +54,7 @@ int main() { assert(property::impl::validateInput("-.1-", property::TYPE_FLOAT) == false); assert(property::impl::validateInput("-2.22222.2", property::TYPE_FLOAT) == false); } + */ { TIMING_START(timings); From e0eb4c5a16b900f6ed906c88a264d07479b13c5e Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Wed, 8 Apr 2020 13:01:37 +0200 Subject: [PATCH 11/18] A lot of updates for 1.4.12 --- src/Properties.h | 21 ++++++++++++++++----- src/Variable.h | 1 + src/protocol/AcknowledgeManager.cpp | 2 +- src/query/command3.h | 22 ++++++++++++++++++---- 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/Properties.h b/src/Properties.h index df34b45..bec4d10 100644 --- a/src/Properties.h +++ b/src/Properties.h @@ -1,7 +1,6 @@ -#include - #pragma once +#include #include "misc/memtracker.h" #include #include "Variable.h" @@ -21,6 +20,8 @@ #define EXTERNALIZE_PROPERTY_DEFINITIONS #endif +#define PROPERTIES_DEFINED + namespace ts { namespace property { enum PropertyType { @@ -527,7 +528,8 @@ namespace ts { return this->property_index == (int) other && this->type_property == type_from_enum(); } - inline bool validate_input(const std::string& value) const { return impl::validateInput(value, this->type_value); } + [[nodiscard]] inline bool is_undefined() const { return property_index == 0; } + [[nodiscard]] inline bool validate_input(const std::string& value) const { return impl::validateInput(value, this->type_value); } }; constexpr static PropertyDescription undefined_property_description{UnknownProperties::UNKNOWN_UNDEFINED, "undefined", "", ValueType::TYPE_UNKNOWN, 0}; @@ -576,7 +578,7 @@ namespace ts { size_t index{begin}; for(; index < property_list.size() - 1; index++) if(property_list[index + 1].type_property != type) - return index; + return index + 1; return property_list.size(); } @@ -657,6 +659,16 @@ namespace ts { return find(type_from_enum(), name); } + template ::value, int>::type = 0> + inline std::vector list() { + constexpr auto type = type_from_enum(); + if(type >= property_list_info.end_index.size()) return {}; + + const auto begin = property_list_info.begin_index[type]; + const auto end = property_list_info.end_index[type]; + return {property_list.begin() + begin, property_list.begin() + end}; + } + template ::value, int>::type = 0> const_modifier inline const std::string_view& name(PropertyEnumType type) { /* defining the return type here to help out my IDE a bit ;) */ return describe(type).name; @@ -690,7 +702,6 @@ namespace ts { struct PropertyBundle { property::PropertyType type; size_t length; - size_t offset; PropertyData properties[0]; }; #ifdef WIN32 diff --git a/src/Variable.h b/src/Variable.h index 4ab4853..218861c 100644 --- a/src/Variable.h +++ b/src/Variable.h @@ -129,6 +129,7 @@ DEFINE_VARIABLE_TRANSFORM_TYPE(type, ntype) DEFINE_VARIABLE_TRANSFORM(class, VARTYPE_INT, std::to_string((size_type) in), static_cast(in.as())); DEFINE_VARIABLE_TRANSFORM(std::string, VARTYPE_TEXT, in, in.value()); +DEFINE_VARIABLE_TRANSFORM(std::string_view, VARTYPE_TEXT, std::string{in}, std::string_view{in}); DEFINE_VARIABLE_TRANSFORM(char*, VARTYPE_TEXT, std::string((const char*) in), (char*) in.value().c_str()); DEFINE_VARIABLE_TRANSFORM(const char*, VARTYPE_TEXT, std::string((const char*) in), in.value().c_str()); diff --git a/src/protocol/AcknowledgeManager.cpp b/src/protocol/AcknowledgeManager.cpp index bbe0b41..0edb8a6 100644 --- a/src/protocol/AcknowledgeManager.cpp +++ b/src/protocol/AcknowledgeManager.cpp @@ -145,6 +145,6 @@ void AcknowledgeManager::update_rto(size_t r) { } else { this->rttvar = (1.f - alpha) * this->rttvar + beta * abs(this->srtt - r); this->srtt = (1.f - alpha) * srtt + alpha * r; - this->rto = std::min(200.f, this->srtt + 4 * this->rttvar); + this->rto = std::max(200.f, this->srtt + 4 * this->rttvar); } } \ No newline at end of file diff --git a/src/query/command3.h b/src/query/command3.h index 9574a8c..f6e224a 100644 --- a/src/query/command3.h +++ b/src/query/command3.h @@ -185,6 +185,13 @@ namespace ts { this->put(key, std::string_view{data}); } +#ifdef PROPERTIES_DEFINED + template ::value, int> = 0> + inline void put(PropertyType key, const T& value) { + this->put(property::name(key), value); + } +#endif + /* directly puts data without checking for duplicates */ inline void put_unchecked(const std::string_view& key, const std::string_view& value) { this->impl_put_unchecked(key, value); @@ -194,6 +201,13 @@ namespace ts { this->put_unchecked(key, std::string_view{value}); } +#ifdef PROPERTIES_DEFINED + template ::value, int> = 0> + inline void put_unchecked(PropertyType key, const T& value) { + this->put_unchecked(property::name(key), value); + } +#endif + template ::value || std::is_same::value), int> = 1> inline void put_unchecked(const std::string_view& key, const T& value) { static_assert(converter::supported, "Target type isn't supported!"); @@ -266,14 +280,14 @@ namespace ts { return command_builder_bulk{this->flag_changed, this->bulks[index]}; } - template - inline void put(size_t index, const std::string_view& key, const T& value) { + template + inline void put(size_t index, const KeyT& key, const ValueT& value) { this->bulk(index).put(key, value); } /* directly puts data without checking for duplicates */ - template - inline void put_unchecked(size_t index, const std::string_view& key, const T& value) { + template + inline void put_unchecked(size_t index, const KeyT& key, const ValueT& value) { this->bulk(index).put_unchecked(key, value); } From ee7f26b7ed883027f0a81ece1c2aacd41053ce1e Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Fri, 10 Apr 2020 23:29:49 +0200 Subject: [PATCH 12/18] Some updates --- src/protocol/AcknowledgeManager.cpp | 4 ++-- src/protocol/PacketLossCalculator.cpp | 32 +++++++++++++++++++++++---- src/protocol/PacketLossCalculator.h | 5 ++++- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/protocol/AcknowledgeManager.cpp b/src/protocol/AcknowledgeManager.cpp index 0edb8a6..67f29f1 100644 --- a/src/protocol/AcknowledgeManager.cpp +++ b/src/protocol/AcknowledgeManager.cpp @@ -109,6 +109,8 @@ ssize_t AcknowledgeManager::execute_resend(const system_clock::time_point& now , if(entry->next_resend <= now) { entry->next_resend = now + std::chrono::milliseconds{(int64_t) min(ceil(this->rto), 1500.f)}; need_resend.push_back(entry); + entry->resend_count++; + entry->send_count++; } if(next_resend > entry->next_resend) next_resend = entry->next_resend; @@ -128,8 +130,6 @@ ssize_t AcknowledgeManager::execute_resend(const system_clock::time_point& now , } resend_count++; - packet->resend_count++; - packet->send_count++; buffers.push_back(packet); } diff --git a/src/protocol/PacketLossCalculator.cpp b/src/protocol/PacketLossCalculator.cpp index f0a5bc6..45e2bca 100644 --- a/src/protocol/PacketLossCalculator.cpp +++ b/src/protocol/PacketLossCalculator.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "PacketLossCalculator.h" using namespace ts::protocol; @@ -64,6 +65,19 @@ void UnorderedPacketLossCalculator::short_stats() { this->last_history_offset = this->packet_history_offset; } +void UnorderedPacketLossCalculator::reset() { + this->received_packets_ = 0; + this->received_packets_total_ = 0; + this->lost_packets_ = 0; + this->lost_packets_total_ = 0; + this->reset_offsets(); +} +void UnorderedPacketLossCalculator::reset_offsets() { + this->packet_history_offset = 0; + this->last_history_offset = 0; + this->packet_history.clear(); +} + void CommandPacketLossCalculator::packet_send(uint32_t packet_id) { if(packet_id > this->packet_history_offset) { assert(packet_id - 1 == this->packet_history_offset); /* the method will only be called with an incrementing packet id */ @@ -71,8 +85,6 @@ void CommandPacketLossCalculator::packet_send(uint32_t packet_id) { auto lost = std::exchange(this->packet_ack_counts[packet_id % CommandPacketLossCalculator::packet_ack_counts_length], 1); this->lost_packets_ += lost; this->lost_packets_total_ += lost; - if(lost > 0) - __asm__("nop"); this->packet_history_offset = packet_id; } else { /* We're not really interested if the packet id matches the resend packet. If not we may accidentally increase the loss count. */ @@ -85,8 +97,6 @@ void CommandPacketLossCalculator::ack_received(uint32_t packet_id) { auto& count = this->packet_ack_counts[packet_id % CommandPacketLossCalculator::packet_ack_counts_length]; if(count > 0) /* could happen if receive an acknowledge for an packet which is older than out buffer size or the client send the ack twice... */ count--; - else - __asm__("nop"); this->received_packets_++; this->received_packets_total_++; @@ -102,4 +112,18 @@ void CommandPacketLossCalculator::short_stats() { const auto factor = target_interval / packets_passed; this->received_packets_ *= factor; this->lost_packets_ *= factor; +} + +void CommandPacketLossCalculator::reset() { + this->received_packets_ = 0; + this->received_packets_total_ = 0; + this->lost_packets_ = 0; + this->lost_packets_total_ = 0; + this->reset_offsets(); +} + +void CommandPacketLossCalculator::reset_offsets() { + this->packet_history_offset = 0; + this->packets_send_unshorten = 0; + memset(packet_ack_counts, 0, packet_ack_counts_length); } \ No newline at end of file diff --git a/src/protocol/PacketLossCalculator.h b/src/protocol/PacketLossCalculator.h index b0c7e73..74366d2 100644 --- a/src/protocol/PacketLossCalculator.h +++ b/src/protocol/PacketLossCalculator.h @@ -48,6 +48,8 @@ namespace ts::protocol { void packet_received(uint32_t /* packet id */); void short_stats(); + void reset(); + void reset_offsets(); [[nodiscard]] inline auto last_packet_id() const { return this->packet_history_offset; } [[nodiscard]] inline bool valid_data() const { return this->packet_history_offset >= 32; } @@ -61,7 +63,6 @@ namespace ts::protocol { [[nodiscard]] inline uint32_t unconfirmed_received_packets() const { return this->packet_history.count(); }; [[nodiscard]] inline uint32_t unconfirmed_lost_packets() const { return this->packet_history.max_bits() - this->packet_history.count(); }; - inline void reset() { this->received_packets_ = 0; this->lost_packets_ = 0; } private: uint32_t received_packets_{0}, received_packets_total_{0}, lost_packets_{0}, lost_packets_total_{0}; @@ -78,6 +79,8 @@ namespace ts::protocol { void ack_received(uint32_t /* packet id */); //Attention: This is a full ID! void short_stats(); + void reset(); + void reset_offsets(); [[nodiscard]] inline bool valid_data() const { return true; } From 707736d896f46489133e6da6b7543238274c37e6 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Tue, 14 Apr 2020 11:49:05 +0200 Subject: [PATCH 13/18] Changed for new deploy algorithm --- CMakeLists.txt | 10 ++- src/PermissionManager.cpp | 25 ++++-- src/PermissionManager.h | 8 +- src/Properties.h | 102 ++++++++++----------- src/Variable.h | 6 +- src/query/Command.cpp | 1 - src/query/command3.cpp | 19 +++- src/query/command3.h | 46 +++++++--- src/sql/SqlQuery.h | 8 +- src/sql/insert.h | 153 +++++++++++++++++++++++++++++++ src/sql/mysql/MySQL.cpp | 2 +- src/sql/sqlite/SqliteSQL.cpp | 44 +++++---- test/CommandTest.cpp | 4 +- test/SQL2Test.cpp | 169 +++++++++++++++++++++++++++++++++++ 14 files changed, 483 insertions(+), 114 deletions(-) create mode 100644 src/sql/insert.h create mode 100644 test/SQL2Test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d29498..3aa8b58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -270,14 +270,16 @@ if(BUILD_TESTS) target_link_libraries(RingTest ${TEST_LIBRARIES}) if(NOT WIN32) - add_executable(CommandTest ${SOURCE_FILES} ${HEADER_FILES} test/CommandTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) - target_link_libraries(CommandTest ${TEST_LIBRARIES}) + add_executable(CommandTest test/CommandTest.cpp src/query/command3.cpp src/query/Command.cpp src/query/escape.cpp src/converters/converter.cpp src/Variable.cpp) + target_link_libraries(CommandTest DataPipes::core::shared jsoncpp_lib ${glib20_DIR}/lib/x86_64-linux-gnu/libffi.so.7 ${nice_DIR}/lib/libnice.so.10) add_executable(WebsocketTest ${SOURCE_FILES} ${HEADER_FILES} test/WSSTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) target_link_libraries(WebsocketTest ${TEST_LIBRARIES}) - add_executable(SQLTest ${SOURCE_FILES} ${HEADER_FILES} test/SQLTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) - target_link_libraries(SQLTest ${TEST_LIBRARIES}) + #add_executable(SQLTest ${SOURCE_FILES} ${HEADER_FILES} test/SQLTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) + #target_link_libraries(SQLTest ${TEST_LIBRARIES}) + add_executable(SQL2Test test/SQL2Test.cpp src/Variable.cpp) + target_link_libraries(SQL2Test sqlite3) add_executable(ChannelTest ${SOURCE_FILES} ${HEADER_FILES} test/ChannelTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) target_link_libraries(ChannelTest ${TEST_LIBRARIES}) diff --git a/src/PermissionManager.cpp b/src/PermissionManager.cpp index af5df2a..8df3448 100644 --- a/src/PermissionManager.cpp +++ b/src/PermissionManager.cpp @@ -935,6 +935,7 @@ inline void init_mapping() { if(teamspeak::unmapping.empty()) teamspeak::unmapping = build_unmapping(); } +/* template inline deque operator+(const deque& a, const deque& b) { deque result; @@ -942,6 +943,7 @@ inline deque operator+(const deque& a, const deque& b) { result.insert(result.end(), b.begin(), b.end()); return result; } +*/ inline deque mmget(teamspeak::MapType& map, teamspeak::GroupType type, const std::string& key) { return map[type].count(key) > 0 ? map[type].find(key)->second : deque{}; @@ -953,16 +955,29 @@ inline std::deque map_entry(std::string key, teamspeak::GroupType t if(key.find("_needed_modify_power_") == 1) { key = key.substr(strlen("x_needed_modify_power_")); - deque result; - if(map_table[type].count("i_" + key) > 0 || map_table[teamspeak::GroupType::GENERAL].count("i_" + key) > 0) result = mmget(map_table, type, "i_" + key) + mmget(map_table, teamspeak::GroupType::GENERAL, "i_" + key); - else if(map_table[type].count("b_" + key) > 0 || map_table[teamspeak::GroupType::GENERAL].count("b_" + key) > 0) result = mmget(map_table, type, "b_" + key) + mmget(map_table, teamspeak::GroupType::GENERAL, "b_" + key); - else result = {"x_" + key}; + + std::deque result{}; + auto mapped_type = mmget(map_table, type, "i_" + key); + result.insert(result.end(), mapped_type.begin(), mapped_type.end()); + if(type != teamspeak::GroupType::GENERAL) { + auto mapped_general = mmget(map_table, teamspeak::GroupType::GENERAL, "i_" + key); + result.insert(result.end(), mapped_general.begin(), mapped_general.end()); + } for(auto& entry : result) entry = "i_needed_modify_power_" + entry.substr(2); return result; } - if(map_table[type].count(key) > 0 || map_table[teamspeak::GroupType::GENERAL].count(key) > 0) return mmget(map_table, type, key) + mmget(map_table, teamspeak::GroupType::GENERAL, key); + if(map_table[type].count(key) > 0 || map_table[teamspeak::GroupType::GENERAL].count(key) > 0) { + std::deque result{}; + auto mapped_type = mmget(map_table, type, key); + result.insert(result.end(), mapped_type.begin(), mapped_type.end()); + if(type != teamspeak::GroupType::GENERAL) { + auto mapped_general = mmget(map_table, teamspeak::GroupType::GENERAL, key); + result.insert(result.end(), mapped_general.begin(), mapped_general.end()); + } + return result; + } return {key}; } diff --git a/src/PermissionManager.h b/src/PermissionManager.h index b1682c4..c0ffd74 100644 --- a/src/PermissionManager.h +++ b/src/PermissionManager.h @@ -813,11 +813,11 @@ namespace ts { }; struct PermissionFlaggedValue { - PermissionValue value = permNotGranted; - bool has_value = false; + PermissionValue value{permNotGranted}; + bool has_value{false}; - constexpr bool has_power() const { return this->has_value && (this->value > 0 || this->value == -1); } - constexpr bool has_infinite_power() const { return this->has_value && this->value == -1; } + [[nodiscard]] constexpr bool has_power() const { return this->has_value && (this->value > 0 || this->value == -1); } + [[nodiscard]] constexpr bool has_infinite_power() const { return this->has_value && this->value == -1; } inline bool operator==(const PermissionFlaggedValue& other) const { return other.value == this->value && other.has_value == this->has_value; } inline bool operator!=(const PermissionFlaggedValue& other) const { return !(*this == other); } diff --git a/src/Properties.h b/src/Properties.h index bec4d10..ec3a6c0 100644 --- a/src/Properties.h +++ b/src/Properties.h @@ -149,11 +149,11 @@ namespace ts { VIRTUALSERVER_HOSTMESSAGE, //available when connected, not updated while connected VIRTUALSERVER_HOSTMESSAGE_MODE, //available when connected, not updated while connected VIRTUALSERVER_FILEBASE, //not available to clients, stores the folder used for file transfers - VIRTUALSERVER_DEFAULT_SERVER_GROUP, //the manager permissions server group that a new manager gets assigned - VIRTUALSERVER_DEFAULT_MUSIC_GROUP, //the manager permissions server group that a new manager gets assigned - VIRTUALSERVER_DEFAULT_CHANNEL_GROUP, //the channel permissions group that a new manager gets assigned when joining a channel + VIRTUALSERVER_DEFAULT_SERVER_GROUP, //the client permissions server group that a new client gets assigned + VIRTUALSERVER_DEFAULT_MUSIC_GROUP, //the client permissions server group that a new client gets assigned + VIRTUALSERVER_DEFAULT_CHANNEL_GROUP, //the channel permissions group that a new client gets assigned when joining a channel VIRTUALSERVER_FLAG_PASSWORD, //only available on request (=> requestServerVariables) - VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP, //the channel permissions group that a manager gets assigned when creating a channel + VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP, //the channel permissions group that a client gets assigned when creating a channel VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH, //only available on request (=> requestServerVariables) VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH, //only available on request (=> requestServerVariables) VIRTUALSERVER_HOSTBANNER_URL, //available when connected, always up-to-date @@ -238,7 +238,7 @@ namespace ts { CHANNEL_NAME, //Available for all channels that are "in view", always up-to-date CHANNEL_TOPIC, //Available for all channels that are "in view", always up-to-date CHANNEL_DESCRIPTION, //Must be requested (=> requestChannelDescription) - CHANNEL_PASSWORD, //not available manager side + CHANNEL_PASSWORD, //not available client side CHANNEL_CODEC, //Available for all channels that are "in view", always up-to-date CHANNEL_CODEC_QUALITY, //Available for all channels that are "in view", always up-to-date CHANNEL_MAXCLIENTS, //Available for all channels that are "in view", always up-to-date @@ -250,13 +250,13 @@ namespace ts { CHANNEL_FLAG_PASSWORD, //Available for all channels that are "in view", always up-to-date CHANNEL_CODEC_LATENCY_FACTOR, //Available for all channels that are "in view", always up-to-date CHANNEL_CODEC_IS_UNENCRYPTED, //Available for all channels that are "in view", always up-to-date - CHANNEL_SECURITY_SALT, //Not available manager side, not used in teamspeak, only SDK. Sets the options+salt for security hash. + CHANNEL_SECURITY_SALT, //Not available client side, not used in teamspeak, only SDK. Sets the options+salt for security hash. CHANNEL_DELETE_DELAY, //How many seconds to wait before deleting this channel CHANNEL_FLAG_MAXCLIENTS_UNLIMITED, //Available for all channels that are "in view", always up-to-date CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED,//Available for all channels that are "in view", always up-to-date CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED,//Available for all channels that are "in view", always up-to-date - CHANNEL_FLAG_ARE_SUBSCRIBED, //Only available manager side, stores whether we are subscribed to this channel - CHANNEL_FILEPATH, //not available manager side, the folder used for file-transfers for this channel + CHANNEL_FLAG_ARE_SUBSCRIBED, //Only available client side, stores whether we are subscribed to this channel + CHANNEL_FILEPATH, //not available client side, the folder used for file-transfers for this channel CHANNEL_NEEDED_TALK_POWER, //Available for all channels that are "in view", always up-to-date CHANNEL_FORCED_SILENCE, //Available for all channels that are "in view", always up-to-date CHANNEL_NAME_PHONETIC, //Available for all channels that are "in view", always up-to-date @@ -289,62 +289,62 @@ namespace ts { enum ClientProperties { CLIENT_UNDEFINED, CLIENT_BEGINMARKER, - CLIENT_UNIQUE_IDENTIFIER = CLIENT_BEGINMARKER, //automatically up-to-date for any manager "in view", can be used to identify this particular manager installation - CLIENT_NICKNAME, //automatically up-to-date for any manager "in view" + CLIENT_UNIQUE_IDENTIFIER = CLIENT_BEGINMARKER, //automatically up-to-date for any client "in view", can be used to identify this particular client installation + CLIENT_NICKNAME, //automatically up-to-date for any client "in view" CLIENT_VERSION, //for other clients than ourself, this needs to be requested (=> requestClientVariables) CLIENT_PLATFORM, //for other clients than ourself, this needs to be requested (=> requestClientVariables) - CLIENT_FLAG_TALKING, //automatically up-to-date for any manager that can be heard (in room / whisper) - CLIENT_INPUT_MUTED, //automatically up-to-date for any manager "in view", this clients microphone mute status - CLIENT_OUTPUT_MUTED, //automatically up-to-date for any manager "in view", this clients headphones/speakers/mic combined mute status - CLIENT_OUTPUTONLY_MUTED, //automatically up-to-date for any manager "in view", this clients headphones/speakers only mute status - CLIENT_INPUT_HARDWARE, //automatically up-to-date for any manager "in view", this clients microphone hardware status (is the capture device opened?) - CLIENT_OUTPUT_HARDWARE, //automatically up-to-date for any manager "in view", this clients headphone/speakers hardware status (is the playback device opened?) + CLIENT_FLAG_TALKING, //automatically up-to-date for any client that can be heard (in room / whisper) + CLIENT_INPUT_MUTED, //automatically up-to-date for any client "in view", this clients microphone mute status + CLIENT_OUTPUT_MUTED, //automatically up-to-date for any client "in view", this clients headphones/speakers/mic combined mute status + CLIENT_OUTPUTONLY_MUTED, //automatically up-to-date for any client "in view", this clients headphones/speakers only mute status + CLIENT_INPUT_HARDWARE, //automatically up-to-date for any client "in view", this clients microphone hardware status (is the capture device opened?) + CLIENT_OUTPUT_HARDWARE, //automatically up-to-date for any client "in view", this clients headphone/speakers hardware status (is the playback device opened?) CLIENT_DEFAULT_CHANNEL, //only usable for ourself, the default channel we used to connect on our last connection attempt CLIENT_DEFAULT_CHANNEL_PASSWORD, //internal use CLIENT_SERVER_PASSWORD, //internal use - CLIENT_META_DATA, //automatically up-to-date for any manager "in view", not used by TeamSpeak, free storage for sdk users - CLIENT_IS_RECORDING, //automatically up-to-date for any manager "in view" + CLIENT_META_DATA, //automatically up-to-date for any client "in view", not used by TeamSpeak, free storage for sdk users + CLIENT_IS_RECORDING, //automatically up-to-date for any client "in view" CLIENT_VERSION_SIGN, //sign - CLIENT_SECURITY_HASH, //SDK use, not used by teamspeak. Hash is provided by an outside source. A channel will use the security salt + other manager data to calculate a hash, which must be the same as the one provided here. + CLIENT_SECURITY_HASH, //SDK use, not used by teamspeak. Hash is provided by an outside source. A channel will use the security salt + other client data to calculate a hash, which must be the same as the one provided here. //Rare properties CLIENT_KEY_OFFSET, //internal use CLIENT_LOGIN_NAME, //used for serverquery clients, makes no sense on normal clients currently CLIENT_LOGIN_PASSWORD, //used for serverquery clients, makes no sense on normal clients currently - CLIENT_DATABASE_ID, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds database manager id + CLIENT_DATABASE_ID, //automatically up-to-date for any client "in view", only valid with PERMISSION feature, holds database client id CLIENT_ID, //clid! CLIENT_HARDWARE_ID, //hwid! - CLIENT_CHANNEL_GROUP_ID, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds database manager id - CLIENT_SERVERGROUPS, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds all servergroups manager belongs too - CLIENT_CREATED, //this needs to be requested (=> requestClientVariables), first time this manager connected to this server - CLIENT_LASTCONNECTED, //this needs to be requested (=> requestClientVariables), last time this manager connected to this server - CLIENT_TOTALCONNECTIONS, //this needs to be requested (=> requestClientVariables), how many times this manager connected to this server - CLIENT_AWAY, //automatically up-to-date for any manager "in view", this clients away status - CLIENT_AWAY_MESSAGE, //automatically up-to-date for any manager "in view", this clients away message - CLIENT_TYPE, //automatically up-to-date for any manager "in view", determines if this is a real manager or a server-query connection - CLIENT_TYPE_EXACT, //automatically up-to-date for any manager "in view", determines if this is a real manager or a server-query connection - CLIENT_FLAG_AVATAR, //automatically up-to-date for any manager "in view", this manager got an avatar - CLIENT_TALK_POWER, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds database manager id - CLIENT_TALK_REQUEST, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds timestamp where manager requested to talk - CLIENT_TALK_REQUEST_MSG, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, holds matter for the request - CLIENT_DESCRIPTION, //automatically up-to-date for any manager "in view" - CLIENT_IS_TALKER, //automatically up-to-date for any manager "in view" + CLIENT_CHANNEL_GROUP_ID, //automatically up-to-date for any client "in view", only valid with PERMISSION feature, holds database client id + CLIENT_SERVERGROUPS, //automatically up-to-date for any client "in view", only valid with PERMISSION feature, holds all servergroups client belongs too + CLIENT_CREATED, //this needs to be requested (=> requestClientVariables), first time this client connected to this server + CLIENT_LASTCONNECTED, //this needs to be requested (=> requestClientVariables), last time this client connected to this server + CLIENT_TOTALCONNECTIONS, //this needs to be requested (=> requestClientVariables), how many times this client connected to this server + CLIENT_AWAY, //automatically up-to-date for any client "in view", this clients away status + CLIENT_AWAY_MESSAGE, //automatically up-to-date for any client "in view", this clients away message + CLIENT_TYPE, //automatically up-to-date for any client "in view", determines if this is a real client or a server-query connection + CLIENT_TYPE_EXACT, //automatically up-to-date for any client "in view", determines if this is a real client or a server-query connection + CLIENT_FLAG_AVATAR, //automatically up-to-date for any client "in view", this client got an avatar + CLIENT_TALK_POWER, //automatically up-to-date for any client "in view", only valid with PERMISSION feature, holds database client id + CLIENT_TALK_REQUEST, //automatically up-to-date for any client "in view", only valid with PERMISSION feature, holds timestamp where client requested to talk + CLIENT_TALK_REQUEST_MSG, //automatically up-to-date for any client "in view", only valid with PERMISSION feature, holds matter for the request + CLIENT_DESCRIPTION, //automatically up-to-date for any client "in view" + CLIENT_IS_TALKER, //automatically up-to-date for any client "in view" CLIENT_MONTH_BYTES_UPLOADED, //this needs to be requested (=> requestClientVariables) CLIENT_MONTH_BYTES_DOWNLOADED, //this needs to be requested (=> requestClientVariables) CLIENT_TOTAL_BYTES_UPLOADED, //this needs to be requested (=> requestClientVariables) CLIENT_TOTAL_BYTES_DOWNLOADED, //this needs to be requested (=> requestClientVariables) CLIENT_TOTAL_ONLINE_TIME, CLIENT_MONTH_ONLINE_TIME, - CLIENT_IS_PRIORITY_SPEAKER, //automatically up-to-date for any manager "in view" - CLIENT_UNREAD_MESSAGES, //automatically up-to-date for any manager "in view" - CLIENT_NICKNAME_PHONETIC, //automatically up-to-date for any manager "in view" - CLIENT_NEEDED_SERVERQUERY_VIEW_POWER, //automatically up-to-date for any manager "in view" + CLIENT_IS_PRIORITY_SPEAKER, //automatically up-to-date for any client "in view" + CLIENT_UNREAD_MESSAGES, //automatically up-to-date for any client "in view" + CLIENT_NICKNAME_PHONETIC, //automatically up-to-date for any client "in view" + CLIENT_NEEDED_SERVERQUERY_VIEW_POWER, //automatically up-to-date for any client "in view" CLIENT_DEFAULT_TOKEN, //only usable for ourself, the default token we used to connect on our last connection attempt - CLIENT_ICON_ID, //automatically up-to-date for any manager "in view" - CLIENT_IS_CHANNEL_COMMANDER, //automatically up-to-date for any manager "in view" - CLIENT_COUNTRY, //automatically up-to-date for any manager "in view" - CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID, //automatically up-to-date for any manager "in view", only valid with PERMISSION feature, contains channel_id where the channel_group_id is set from - CLIENT_BADGES, //automatically up-to-date for any manager "in view", stores icons for partner badges + CLIENT_ICON_ID, //automatically up-to-date for any client "in view" + CLIENT_IS_CHANNEL_COMMANDER, //automatically up-to-date for any client "in view" + CLIENT_COUNTRY, //automatically up-to-date for any client "in view" + CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID, //automatically up-to-date for any client "in view", only valid with PERMISSION feature, contains channel_id where the channel_group_id is set from + CLIENT_BADGES, //automatically up-to-date for any client "in view", stores icons for partner badges CLIENT_MYTEAMSPEAK_ID, CLIENT_INTEGRATIONS, @@ -374,11 +374,11 @@ namespace ts { CONNECTION_PING = CONNECTION_BEGINMARKER, //average latency for a round trip through and back this connection CONNECTION_PING_DEVIATION, //standard deviation of the above average latency CONNECTION_CONNECTED_TIME, //how long the connection exists already - CONNECTION_IDLE_TIME, //how long since the last action of this manager - CONNECTION_CLIENT_IP, //NEED DB SAVE! //IP of this manager (as seen from the server side) - CONNECTION_CLIENT_PORT, //Port of this manager (as seen from the server side) - CONNECTION_SERVER_IP, //IP of the server (seen from the manager side) - only available on yourself, not for remote clients, not available server side - CONNECTION_SERVER_PORT, //Port of the server (seen from the manager side) - only available on yourself, not for remote clients, not available server side + CONNECTION_IDLE_TIME, //how long since the last action of this client + CONNECTION_CLIENT_IP, //NEED DB SAVE! //IP of this client (as seen from the server side) + CONNECTION_CLIENT_PORT, //Port of this client (as seen from the server side) + CONNECTION_SERVER_IP, //IP of the server (seen from the client side) - only available on yourself, not for remote clients, not available server side + CONNECTION_SERVER_PORT, //Port of the server (seen from the client side) - only available on yourself, not for remote clients, not available server side CONNECTION_PACKETS_SENT_SPEECH, //how many Speech packets were sent through this connection CONNECTION_PACKETS_SENT_KEEPALIVE, CONNECTION_PACKETS_SENT_CONTROL, @@ -399,7 +399,7 @@ namespace ts { CONNECTION_PACKETLOSS_KEEPALIVE, CONNECTION_PACKETLOSS_CONTROL, CONNECTION_PACKETLOSS_TOTAL, //the probability with which a packet round trip failed because a packet was lost - CONNECTION_SERVER2CLIENT_PACKETLOSS_SPEECH, //the probability with which a speech packet failed from the server to the manager + CONNECTION_SERVER2CLIENT_PACKETLOSS_SPEECH, //the probability with which a speech packet failed from the server to the client CONNECTION_SERVER2CLIENT_PACKETLOSS_KEEPALIVE, CONNECTION_SERVER2CLIENT_PACKETLOSS_CONTROL, CONNECTION_SERVER2CLIENT_PACKETLOSS_TOTAL, @@ -858,7 +858,7 @@ namespace ts { Properties(); ~Properties(); Properties(const Properties&) = delete; - Properties(Properties&&) = delete; + Properties(Properties&&) = default; std::vector list_properties(property::flag_type flagMask = (property::flag_type) ~0UL, property::flag_type negatedFlagMask = 0); std::vector all_properties(); diff --git a/src/Variable.h b/src/Variable.h index 218861c..acdf406 100644 --- a/src/Variable.h +++ b/src/Variable.h @@ -58,8 +58,10 @@ class variable { variable& operator=(const variable& ref); variable& operator=(variable&& ref); - std::string key() const { return data->pair.first; } - std::string value() const { return data->pair.second; } + [[nodiscard]] std::string key() const { return this->r_key(); } + void set_key(const std::string_view& key) const { this->r_key() = key; } + + std::string value() const { return this->r_value(); } VariableType type() const { return data->_type; } variable clone(){ return variable(key(), value(), type()); } diff --git a/src/query/Command.cpp b/src/query/Command.cpp index 7b14cde..00353e2 100644 --- a/src/query/Command.cpp +++ b/src/query/Command.cpp @@ -5,7 +5,6 @@ #include #include #include -#include "log/LogUtils.h" #include "escape.h" using namespace std; diff --git a/src/query/command3.cpp b/src/query/command3.cpp index e297fcf..dba1b30 100644 --- a/src/query/command3.cpp +++ b/src/query/command3.cpp @@ -2,11 +2,12 @@ // Created by wolverindev on 25.01.20. // +#include #include "command3.h" using namespace ts; -command_bulk command_parser::empty_bulk{std::string::npos, ""}; +command_bulk command_parser::empty_bulk{std::string::npos, 0, ""}; bool command_parser::parse(bool command) { this->data = this->_command; @@ -31,8 +32,22 @@ bool command_parser::parse(bool command) { if(findex == std::string::npos) findex = this->_command.size(); - this->_bulks.emplace_back(this->_bulks.size() - 1, this->data.substr(index, findex - index)); + this->_bulks.emplace_back(this->_bulks.size() - 1, index, this->data.substr(index, findex - index)); index = findex + 1; } return true; +} + +std::optional command_parser::next_bulk_containing(const std::string_view &key, size_t start) const { + if(start >= this->bulk_count()) return std::nullopt; + + auto index = this->bulk(start).command_character_index(); + auto next = this->data.find(key, index); + if(next == std::string::npos) return std::nullopt; + + size_t upper_bulk{start + 1}; + for(; upper_bulk < this->bulk_count(); upper_bulk++) + if(this->bulk(upper_bulk).command_character_index() > next) + break; + return upper_bulk - 1; } \ No newline at end of file diff --git a/src/query/command3.h b/src/query/command3.h index f6e224a..79f7e49 100644 --- a/src/query/command3.h +++ b/src/query/command3.h @@ -26,13 +26,16 @@ namespace ts { findex += key_size; if(findex < max) { - if(findex < max && data[findex] != '=') { + if(data[findex] == '=') + begin = findex + 1; + else if(data[findex] == ' ') + begin = findex; /* empty value */ + else { index = findex + key_size; continue; } - begin = findex + 1; - if(end) *end = data.find(' ', findex + 1); + if(end) *end = data.find(' ', findex); return true; } else { begin = max; @@ -46,6 +49,11 @@ namespace ts { public: [[nodiscard]] inline bool is_empty() const noexcept { return this->data.empty(); } + [[nodiscard]] inline bool has_key(const std::string_view& key) const { + size_t begin{0}; + return value_raw_impl(this->data, key, begin, nullptr); + } + [[nodiscard]] inline std::string_view value_raw(const std::string_view& key) const { bool tmp; auto result = this->value_raw(key, tmp); @@ -54,7 +62,7 @@ namespace ts { } [[nodiscard]] inline std::string_view value_raw(const std::string_view& key, bool& has_been_found) const noexcept { - size_t begin, end; + size_t begin{0}, end; has_been_found = value_raw_impl(this->data, key, begin, &end); if(!has_been_found) return {}; @@ -68,6 +76,13 @@ namespace ts { return query::unescape(std::string{value}, false); } + [[nodiscard]] inline std::string value(const std::string_view& key, bool& has_been_found) const noexcept { + const auto value = this->value_raw(key, has_been_found); + if(value.empty()) return std::string{}; + + return query::unescape(std::string{value}, false); + } + template [[nodiscard]] inline T value_as(const std::string_view& key) const { static_assert(converter::supported, "Target type isn't supported!"); @@ -76,16 +91,23 @@ namespace ts { return converter::from_string_view(this->value(key)); } + [[nodiscard]] inline size_t command_character_index() const { return this->abs_index; } + [[nodiscard]] inline size_t key_command_character_index(const std::string_view& key) const { + size_t begin{0}; + if(!value_raw_impl(this->data, key, begin, nullptr)) return this->abs_index; + return this->abs_index + begin; + } protected: - command_string_parser(size_t index, std::string_view data) : index{index}, data{std::move(data)} {} + command_string_parser(size_t index, size_t abs_index, std::string_view data) : index{index}, abs_index{abs_index}, data{data} {} + size_t abs_index{}; size_t index{}; std::string_view data{}; }; } struct command_bulk : public impl::command_string_parser { - command_bulk(size_t index, std::string_view data) : command_string_parser{index, std::move(data)} {} + command_bulk(size_t index, size_t abs_index, std::string_view data) : command_string_parser{index, abs_index, data} {} inline bool next_entry(size_t& index, std::string_view& key, std::string& value) const { auto next_key = this->data.find_first_not_of(' ', index); @@ -119,7 +141,7 @@ namespace ts { failed }; - explicit command_parser(std::string command) : _command{std::move(command)}, impl::command_string_parser{std::string::npos, this->_command} { } + explicit command_parser(std::string command) : impl::command_string_parser{std::string::npos, 0, command}, _command{std::move(command)} { } bool parse(bool /* contains identifier */); @@ -135,25 +157,23 @@ namespace ts { [[nodiscard]] inline bool has_switch(const std::string_view& key) const noexcept { size_t index{0}; do { - index = this->_command_view.find(key, index); + index = this->data.find(key, index); index -= 1; if(index > key.size()) return false; - if(this->_command_view[index] == '-') return index == 0 || this->_command_view[index - 1] == ' '; + if(this->data[index] == '-') return index == 0 || this->data[index - 1] == ' '; index += 2; } while(true); } [[nodiscard]] const std::vector& bulks() const { return this->_bulks; } + + [[nodiscard]] std::optional next_bulk_containing(const std::string_view& /* key */, size_t /* bulk offset */) const; private: static command_bulk empty_bulk; const std::string _command{}; - const std::string_view _command_view{}; - std::string_view command_type{}; - std::vector _bulks{}; - std::vector flags{}; }; class command_builder_bulk { diff --git a/src/sql/SqlQuery.h b/src/sql/SqlQuery.h index eceac7c..f78f9c6 100644 --- a/src/sql/SqlQuery.h +++ b/src/sql/SqlQuery.h @@ -53,7 +53,7 @@ namespace sql { return *this; } - std::string fmtStr(){ + std::string fmtStr() const { std::stringstream s; operator<<(s, *this); return s.str(); @@ -285,12 +285,6 @@ namespace sql { return *(SelfType*) this; } - template - SelfType& value(const std::initializer_list& val) { - //this->_data->variables.push_back(val.begin()); - return *(SelfType*) this; - } - template SelfType& value(const std::string& key, T&& value) { this->_data->variables.push_back(variable{key, value}); diff --git a/src/sql/insert.h b/src/sql/insert.h new file mode 100644 index 0000000..d68edea --- /dev/null +++ b/src/sql/insert.h @@ -0,0 +1,153 @@ +#pragma once + +namespace sql { + + template + struct Column { + constexpr explicit Column(const std::string_view& name) : name{name} { } + + const std::string name; + }; + + struct BindingNameGenerator { + public: + [[nodiscard]] static std::string generate(size_t index) { + assert(index > 0); + std::string result{}; + result.resize(std::max(index >> 4U, 1UL)); + for(auto it = result.begin(); index > 0; index >>= 4U) + *(it++) = number_map[index & 0xFU]; + return result; + } + + private: + constexpr static std::array number_map{ + 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p' + }; + }; + + template + struct InsertQueryGenerator { + struct GenerateOptions { + bool enable_ignore{false}; + sql::SqlType target{sql::SqlType::TYPE_SQLITE}; + }; + + explicit InsertQueryGenerator(std::string target_table, Column... columns) : table_name{std::move(target_table)}, columns{std::move(columns.name)...} { } + + [[nodiscard]] constexpr inline auto column_count() const { return sizeof...(ColTypes); }; + + [[nodiscard]] inline std::string generate_query(size_t entry_count, sql::SqlType target = sql::SqlType::TYPE_SQLITE, bool enable_ignore = false) const { + if(entry_count == 0) return "-- No entries given"; + + std::string result{"INSERT "}; + if(enable_ignore) { + if(target == sql::TYPE_MYSQL) + result += "IGNORE "; + else + result += "OR IGNORE "; + } + + result += this->table_name + " ("; + //"INSERT INTO " + this->table_name + " (" + for(auto it = this->columns.begin();;) { + result += "`" + *it + "`"; + if(++it != this->columns.end()) { + result += ", "; + } else { + break; + } + } + result += ") VALUES ("; + for(size_t index{1}; index <= this->column_count() * entry_count; index++) + result += ":" + BindingNameGenerator::generate(index) + ", "; + result = result.substr(0, result.length() - 2) + ");"; + return result; + } + + const std::string table_name{}; + const std::array columns{}; + }; + + template + struct InsertQuery : public InsertQueryGenerator { + struct ExecuteResult { + std::vector> failed_entries{}; + + [[nodiscard]] inline auto has_succeeded() const { return this->failed_entries.empty(); } + }; + + explicit InsertQuery(const std::string& target_table, Column... columns) : InsertQueryGenerator{target_table, std::move(columns)...} { } + + inline void reserve_entries(size_t amount) { + this->entries.reserve(amount); + } + + inline void add_entry(const ColTypes&... values) { + this->entries.push_back(std::array{variable{"", values}...}); + } + + inline ExecuteResult execute(sql::SqlManager* sql, bool ignore_fails = false) { + if(this->entries.empty()) return ExecuteResult{}; + + ExecuteResult result{}; + + const auto chunk_size = std::min(2UL, this->entries.size()); + sql::model chunk_base{sql, this->generate_query(chunk_size, sql->getType(), ignore_fails)}; + sql::model entry_model{sql, this->generate_query(1, sql->getType(), ignore_fails)}; + + for(size_t chunk{0}; chunk < this->entries.size() / chunk_size; chunk++) { + auto command = chunk_base.command(); + size_t parameter_index{1}; + for(size_t index{chunk * chunk_size}; index < (chunk + 1) * chunk_size; index++) { + for(auto& var : this->entries[index]) { + var.set_key(":" + BindingNameGenerator::generate(parameter_index++)); + command.value(var); + } + } + + auto exec_result = command.execute(); + if(!exec_result) { + /* try every entry 'till we've found the error one */ + for(size_t index{chunk * chunk_size}; index < (chunk + 1) * chunk_size; index++) { + parameter_index = 1; + + auto entry_command = entry_model.command(); + for(auto& var : this->entries[index]) { + var.set_key(":" + BindingNameGenerator::generate(parameter_index++)); + entry_command.value(var); + } + exec_result = entry_command.execute(); + if(!exec_result) { + result.failed_entries.emplace_back(index, std::move(exec_result)); + if(!ignore_fails) + return result; + } + } + } + } + + for(size_t index{(this->entries.size() / chunk_size) * chunk_size}; index < this->entries.size(); index++) { + size_t parameter_index{1}; + auto entry_command = entry_model.command(); + for(auto& var : this->entries[index]) { + var.set_key(":" + BindingNameGenerator::generate(parameter_index++)); + entry_command.value(var); + } + auto exec_result = entry_command.execute(); + if(!exec_result) { + result.failed_entries.emplace_back(index, std::move(exec_result)); + if(!ignore_fails) + return result; + } + } + + return result; + } + + std::vector> entries{}; + }; + +} \ No newline at end of file diff --git a/src/sql/mysql/MySQL.cpp b/src/sql/mysql/MySQL.cpp index 4ded0e6..42a70c2 100644 --- a/src/sql/mysql/MySQL.cpp +++ b/src/sql/mysql/MySQL.cpp @@ -49,7 +49,7 @@ inline result parse_url(const string& url, std::map& c index = idx + 1; } while(index != 0); } - + //TODO: Set CLIENT_MULTI_STATEMENTS //if(!connect_map["hostName"].get() || strcmp(*connect_map["hostName"].get(), "")) connect_map["hostName"] = target_url; logTrace(LOG_GENERAL, "Got mysql property {}. Value: {}", "hostName", target_url); diff --git a/src/sql/sqlite/SqliteSQL.cpp b/src/sql/sqlite/SqliteSQL.cpp index d098ed3..3fac9db 100644 --- a/src/sql/sqlite/SqliteSQL.cpp +++ b/src/sql/sqlite/SqliteSQL.cpp @@ -54,32 +54,30 @@ std::shared_ptr SqliteManager::copyCommandData(std::shared_ptr '" + val.value() + "' in query '" + sqlite3_sql(stmt) + "'" << endl; - return; - } +namespace sql::sqlite { + inline void bindVariable(sqlite3_stmt* stmt, int& valueIndex, const variable& val){ + valueIndex = sqlite3_bind_parameter_index(stmt, val.key().c_str()); + if(valueIndex == 0){ //TODO maybe throw an exception + //cerr << "Cant find variable '" + val.key() + "' -> '" + val.value() + "' in query '" + sqlite3_sql(stmt) + "'" << endl; + return; + } - int resultState = 0; - if(val.type() == VARTYPE_NULL) - resultState = sqlite3_bind_null(stmt, valueIndex); - else if(val.type() == VARTYPE_TEXT) - resultState = sqlite3_bind_text(stmt, valueIndex, val.value().c_str(), val.value().length(), SQLITE_TRANSIENT); - else if(val.type() == VARTYPE_INT || val.type() == VARTYPE_BOOLEAN) - resultState = sqlite3_bind_int(stmt, valueIndex, val.as()); - else if(val.type() == VARTYPE_LONG) - resultState = sqlite3_bind_int64(stmt, valueIndex, val.as()); - else if(val.type() == VARTYPE_DOUBLE || val.type() == VARTYPE_FLOAT) - resultState = sqlite3_bind_double(stmt, valueIndex, val.as()); - else cerr << "Invalid value type!" << endl; //TODO throw exception + int resultState = 0; + if(val.type() == VARTYPE_NULL) + resultState = sqlite3_bind_null(stmt, valueIndex); + else if(val.type() == VARTYPE_TEXT) + resultState = sqlite3_bind_text(stmt, valueIndex, val.value().c_str(), val.value().length(), SQLITE_TRANSIENT); + else if(val.type() == VARTYPE_INT || val.type() == VARTYPE_BOOLEAN) + resultState = sqlite3_bind_int(stmt, valueIndex, val.as()); + else if(val.type() == VARTYPE_LONG) + resultState = sqlite3_bind_int64(stmt, valueIndex, val.as()); + else if(val.type() == VARTYPE_DOUBLE || val.type() == VARTYPE_FLOAT) + resultState = sqlite3_bind_double(stmt, valueIndex, val.as()); + else cerr << "Invalid value type!" << endl; //TODO throw exception - if(resultState != SQLITE_OK){ - cerr << "Invalid bind. " << sqlite3_errmsg(sqlite3_db_handle(stmt)) << " Index: " << valueIndex << endl; //TODO throw exception - } + if(resultState != SQLITE_OK){ + cerr << "Invalid bind. " << sqlite3_errmsg(sqlite3_db_handle(stmt)) << " Index: " << valueIndex << endl; //TODO throw exception } } } diff --git a/test/CommandTest.cpp b/test/CommandTest.cpp index a0b8d83..f425b87 100644 --- a/test/CommandTest.cpp +++ b/test/CommandTest.cpp @@ -132,10 +132,12 @@ int main() { ts::command cmd("notify"); */ - auto command = ts::command_parser{"a a=c a=c2 -z |-? a=2 key_c=c"}; + auto command = ts::command_parser{"a a=x |x b=x | c=x | a=x"}; if(!command.parse(true)) return 1; print_entries(command); + auto next = command.next_bulk_containing("a", 0); + std::cout << (next.has_value() ? *next : -1) << "\n"; return 0; std::cout << "Command v3:\n"; diff --git a/test/SQL2Test.cpp b/test/SQL2Test.cpp new file mode 100644 index 0000000..e7285a4 --- /dev/null +++ b/test/SQL2Test.cpp @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +template +struct Column { + constexpr explicit Column(const std::string_view& name) : name{name} { } + + const std::string name; +}; + +struct BindingNameGenerator { + public: + [[nodiscard]] static std::string generate(size_t index) { + assert(index > 0); + std::string result{}; + result.resize(std::max(index >> 4U, 1UL)); + for(auto it = result.begin(); index > 0; index >>= 4U) + *(it++) = number_map[index & 0xFU]; + return result; + } + + private: + constexpr static std::array number_map{ + 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p' + }; +}; + +template +struct InsertQueryGenerator { + struct GenerateOptions { + bool enable_ignore{false}; + sql::SqlType target{sql::SqlType::TYPE_SQLITE}; + }; + + explicit InsertQueryGenerator(std::string target_table, Column... columns) : table_name{std::move(target_table)}, columns{std::move(columns.name)...} { } + + [[nodiscard]] constexpr inline auto column_count() const { return sizeof...(ColTypes); }; + + [[nodiscard]] inline std::string generate_query(size_t entry_count, sql::SqlType target = sql::SqlType::TYPE_SQLITE, bool enable_ignore = false) const { + if(entry_count == 0) return "-- No entries given"; + + std::string result{"INSERT "}; + if(enable_ignore) { + if(target == sql::TYPE_MYSQL) + result += "IGNORE "; + else + result += "OR IGNORE "; + } + + result += this->table_name + " ("; + //"INSERT INTO " + this->table_name + " (" + for(auto it = this->columns.begin();;) { + result += "`" + *it + "`"; + if(++it != this->columns.end()) { + result += ", "; + } else { + break; + } + } + result += ") VALUES ("; + for(size_t index{1}; index <= this->column_count() * entry_count; index++) + result += ":" + BindingNameGenerator::generate(index) + ", "; + result = result.substr(0, result.length() - 2) + ");"; + return result; + } + + const std::string table_name{}; + const std::array columns{}; +}; + +template +struct InsertQuery : public InsertQueryGenerator { + struct ExecuteResult { + std::vector> failed_entries{}; + + [[nodiscard]] inline auto has_succeeded() const { return this->failed_entries.empty(); } + }; + + explicit InsertQuery(const std::string& target_table, Column... columns) : InsertQueryGenerator{target_table, std::move(columns)...} { } + + inline void reserve_entries(size_t amount) { + this->entries.reserve(amount); + } + + inline void add_entry(const ColTypes&... values) { + this->entries.push_back(std::array{variable{"", values}...}); + } + + inline ExecuteResult execute(sql::SqlManager* sql, bool ignore_fails = false) { + if(this->entries.empty()) return ExecuteResult{}; + + ExecuteResult result{}; + + const auto chunk_size = std::min(2UL, this->entries.size()); + sql::model chunk_base{sql, this->generate_query(chunk_size, sql->getType(), ignore_fails)}; + sql::model entry_model{sql, this->generate_query(1, sql->getType(), ignore_fails)}; + + for(size_t chunk{0}; chunk < this->entries.size() / chunk_size; chunk++) { + auto command = chunk_base.command(); + size_t parameter_index{1}; + for(size_t index{chunk * chunk_size}; index < (chunk + 1) * chunk_size; index++) { + for(auto& var : this->entries[index]) { + var.set_key(":" + BindingNameGenerator::generate(parameter_index++)); + command.value(var); + } + } + + auto exec_result = command.execute(); + if(!exec_result) { + /* try every entry 'till we've found the error one */ + for(size_t index{chunk * chunk_size}; index < (chunk + 1) * chunk_size; index++) { + parameter_index = 1; + + auto entry_command = entry_model.command(); + for(auto& var : this->entries[index]) { + var.set_key(":" + BindingNameGenerator::generate(parameter_index++)); + entry_command.value(var); + } + exec_result = entry_command.execute(); + if(!exec_result) { + result.failed_entries.emplace_back(index, std::move(exec_result)); + if(!ignore_fails) + return result; + } + } + } + } + + for(size_t index{(this->entries.size() / chunk_size) * chunk_size}; index < this->entries.size(); index++) { + size_t parameter_index{1}; + auto entry_command = entry_model.command(); + for(auto& var : this->entries[index]) { + var.set_key(":" + BindingNameGenerator::generate(parameter_index++)); + entry_command.value(var); + } + auto exec_result = entry_command.execute(); + if(!exec_result) { + result.failed_entries.emplace_back(index, std::move(exec_result)); + if(!ignore_fails) + return result; + } + } + + return result; + } + + std::vector> entries{}; +}; + +int main() { + InsertQuery insert{"hello", Column{"column_a"}, Column{"column_ab"}}; + + for(int i{0}; i < 100; i++) + insert.add_entry(i, "X"); + + std::cout << "Result: " << insert.generate_query(2) << "\n"; + + std::string value{}; + insert.add_entry(2, value); + insert.execute(nullptr); +} \ No newline at end of file From 16c2272fe4b479c55e6db6642d039a42b0774325 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Wed, 15 Apr 2020 15:02:18 +0200 Subject: [PATCH 14/18] Fixed packet compression --- src/protocol/CompressionHandler.cpp | 35 ++++++++++++++++++++++++----- src/protocol/CompressionHandler.h | 1 + test/SQL2Test.cpp | 20 +++++++++++++++++ 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/protocol/CompressionHandler.cpp b/src/protocol/CompressionHandler.cpp index 0b1f900..321296a 100644 --- a/src/protocol/CompressionHandler.cpp +++ b/src/protocol/CompressionHandler.cpp @@ -1,4 +1,3 @@ -#include #include #include "CompressionHandler.h" #define QLZ_COMPRESSION_LEVEL 1 @@ -39,17 +38,38 @@ namespace ts::compression { void* buffer_ptr{nullptr}; size_t buffer_length{0}; }; + + class qlz_states { + public: + qlz_states() noexcept { + this->state_compress = (qlz_state_compress*) malloc(sizeof(qlz_state_compress)); + this->state_decompress = (qlz_state_decompress*) malloc(sizeof(qlz_state_decompress)); + } + + ~qlz_states() { + ::free(this->state_compress); + ::free(this->state_decompress); + } + + qlz_state_compress* state_compress{nullptr}; + qlz_state_decompress* state_decompress{nullptr}; + private: + }; + thread_local thread_buffer qlz_buffer{}; + thread_local qlz_states qlz_states{}; size_t qlz_decompressed_size(const void* payload, size_t payload_length) { + if(payload_length < 9) return 0; /* payload too small */ + return qlz_size_decompressed((char*) payload) + 400; } bool qlz_decompress_payload(const void* payload, void* buffer, size_t* buffer_size) { + if(!qlz_states.state_decompress) return false; assert(payload != buffer); - qlz_state_decompress state{}; - size_t data_length = qlz_decompress((char*) payload, (char*) buffer, &state); + size_t data_length = qlz_decompress((char*) payload, (char*) buffer, qlz_states.state_decompress); if(data_length <= 0) return false; @@ -60,16 +80,18 @@ namespace ts::compression { } size_t qlz_compressed_size(const void* payload, size_t payload_length) { + assert(payload_length >= 9); //// "Always allocate size + 400 bytes for the destination buffer when compressing." <= http://www.quicklz.com/manual.html return max(min(payload_length * 2, (size_t) (payload_length + 400ULL)), (size_t) 24ULL); /* at least 12 bytes (QLZ header) */ } bool qlz_compress_payload(const void* payload, size_t payload_length, void* buffer, size_t* buffer_length) { + if(!qlz_states.state_compress) return false; + assert(payload != buffer); assert(*buffer_length >= qlz_compressed_size(payload, payload_length)); - qlz_state_compress state{}; - size_t compressed_length = qlz_compress(payload, (char*) buffer, payload_length, &state); + size_t compressed_length = qlz_compress(payload, (char*) buffer, payload_length, qlz_states.state_compress); if(compressed_length > *buffer_length) terminate(); if(compressed_length <= 0) @@ -99,6 +121,9 @@ bool CompressionHandler::decompress(protocol::BasicPacket* packet, std::string & if(expected_length > this->max_packet_size){ //Max 16MB. (97% Compression!) error = "Invalid packet size. (Calculated target length of " + to_string(expected_length) + ". Max length: " + to_string(this->max_packet_size) + ")"; return false; + } else if(expected_length == 0) { + error = "Failed to calculate decompressed packet length"; + return false; } auto header_length = packet->header().length() + packet->mac().length(); auto buffer = buffer::allocate_buffer(expected_length + header_length); diff --git a/src/protocol/CompressionHandler.h b/src/protocol/CompressionHandler.h index b76d655..cd8c541 100644 --- a/src/protocol/CompressionHandler.h +++ b/src/protocol/CompressionHandler.h @@ -4,6 +4,7 @@ namespace ts { namespace compression { + /* Attention: These methods does not validate the data! */ size_t qlz_decompressed_size(const void* payload, size_t payload_length); bool qlz_decompress_payload(const void* payload, void* buffer, size_t* buffer_size); //Attention: payload & buffer must be differen! diff --git a/test/SQL2Test.cpp b/test/SQL2Test.cpp index e7285a4..55fdd42 100644 --- a/test/SQL2Test.cpp +++ b/test/SQL2Test.cpp @@ -7,6 +7,7 @@ #include #include +#if 0 template struct Column { constexpr explicit Column(const std::string_view& name) : name{name} { } @@ -166,4 +167,23 @@ int main() { std::string value{}; insert.add_entry(2, value); insert.execute(nullptr); +} +#endif + +struct A { + A() {} + ~A() = default; +}; + +#include +int main() { + std::set elements{}; + if(!elements.empty()) { + auto it = elements.begin(); + const int* last_element{&*(it++)}; /* if elements would be empty this would be undefined behaviour */ + while(it != elements.end()) { + const auto now = *it++; + const auto diff = last_element - now; + } + } } \ No newline at end of file From c31cc9d0ee8fbcd04ff3a76e8fc4ab8723127a84 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Thu, 16 Apr 2020 14:05:56 +0200 Subject: [PATCH 15/18] Updated to 1.4.13 ;) --- CMakeLists.txt | 3 +- src/misc/net.cpp | 92 +++++++++++++++++++++++++++++++++++++++++++++++ src/misc/net.h | 78 ++++++++++++---------------------------- test/SQL2Test.cpp | 13 ++++++- 4 files changed, 129 insertions(+), 57 deletions(-) create mode 100644 src/misc/net.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3aa8b58..8c8e98f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,7 @@ set(SOURCE_FILES src/misc/memtracker.cpp src/misc/digest.cpp src/misc/base64.cpp + src/misc/net.cpp src/lock/rw_mutex.cpp @@ -278,7 +279,7 @@ if(BUILD_TESTS) #add_executable(SQLTest ${SOURCE_FILES} ${HEADER_FILES} test/SQLTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) #target_link_libraries(SQLTest ${TEST_LIBRARIES}) - add_executable(SQL2Test test/SQL2Test.cpp src/Variable.cpp) + add_executable(SQL2Test test/SQL2Test.cpp src/Variable.cpp src/misc/net.cpp) target_link_libraries(SQL2Test sqlite3) add_executable(ChannelTest ${SOURCE_FILES} ${HEADER_FILES} test/ChannelTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) diff --git a/src/misc/net.cpp b/src/misc/net.cpp new file mode 100644 index 0000000..626774f --- /dev/null +++ b/src/misc/net.cpp @@ -0,0 +1,92 @@ +// +// Created by WolverinDEV on 16/04/2020. +// + +#ifndef WIN32 +#include +#include +#endif + +#include "./net.h" + +namespace helpers { + inline void strip(std::string& message) { + while(!message.empty()) { + if(message[0] == ' ') + message = message.substr(1); + else if(message[message.length() - 1] == ' ') + message = message.substr(0, message.length() - 1); + else break; + } + } + + inline std::deque split(const std::string& message, char delimiter) { + std::deque result{}; + size_t found, index = 0; + do { + found = message.find(delimiter, index); + result.push_back(message.substr(index, found - index)); + index = found + 1; + } while(index != 0); + return result; + } +} + +std::vector> net::resolve_bindings(const std::string& bindings, uint16_t port) { + auto binding_list = helpers::split(bindings, ','); + std::vector> result; + result.reserve(binding_list.size()); + + for(auto& address : binding_list) { + helpers::strip(address); + + sockaddr_storage element{}; + memset(&element, 0, sizeof(element)); + if(!resolve_address(address, element)) { + result.emplace_back(address, element, "address resolve failed"); + continue; + } + + if(element.ss_family == AF_INET) { + ((sockaddr_in*) &element)->sin_port = htons(port); + } else if(element.ss_family == AF_INET6) { + ((sockaddr_in6*) &element)->sin6_port = htons(port); + } + result.emplace_back(address, element, ""); + } + return result; +} + +#ifndef WIN32 +net::binding_result net::address_available(const sockaddr_storage& address, binding_type type) { + int file_descriptor{0}; + net::binding_result result{binding_result::INTERNAL_ERROR}; + int disable{0}, enable{1}; + + file_descriptor = socket(address.ss_family, type == binding_type::TCP ? SOCK_STREAM : SOCK_DGRAM, 0); + if(file_descriptor <= 0) + goto cleanup_exit; + + fcntl(file_descriptor, F_SETFD, FD_CLOEXEC); /* just to ensure */ + setsockopt(file_descriptor, SOL_SOCKET, SO_REUSEADDR, &disable, sizeof(int)); + setsockopt(file_descriptor, SOL_SOCKET, SO_REUSEPORT, &disable, sizeof(int)); + if(type == binding_type::UDP && address.ss_family == AF_INET6) + setsockopt(file_descriptor, IPPROTO_IPV6, IPV6_V6ONLY, &enable, sizeof(int)); + + if(::bind(file_descriptor, (const sockaddr*) &address, net::address_size(address)) != 0) { + result = binding_result::ADDRESS_USED; + } else { + result = binding_result::ADDRESS_FREE; + } + + if(type == binding_type::TCP) { + if(::listen(file_descriptor, 1) != 0) + result = binding_result::ADDRESS_USED; + } + + cleanup_exit: + if(file_descriptor > 0) + ::close(file_descriptor); + return result; +} +#endif \ No newline at end of file diff --git a/src/misc/net.h b/src/misc/net.h index c46eba5..6d00544 100644 --- a/src/misc/net.h +++ b/src/misc/net.h @@ -4,6 +4,7 @@ #include #include #include +#include #ifdef WIN32 #include @@ -18,19 +19,19 @@ #endif namespace net { - inline std::string to_string(const in6_addr& address) { + [[nodiscard]] inline std::string to_string(const in6_addr& address) { char buffer[INET6_ADDRSTRLEN]; if(!inet_ntop(AF_INET6, (void*) &address, buffer, INET6_ADDRSTRLEN)) return ""; return std::string(buffer); } - inline std::string to_string(const in_addr& address) { + [[nodiscard]] inline std::string to_string(const in_addr& address) { char buffer[INET_ADDRSTRLEN]; if(!inet_ntop(AF_INET, (void*) &address, buffer, INET_ADDRSTRLEN)) return ""; return std::string(buffer); } - inline std::string to_string(const sockaddr_storage& address, bool port = true) { + [[nodiscard]] inline std::string to_string(const sockaddr_storage& address, bool port = true) { switch(address.ss_family) { case AF_INET: return to_string(((sockaddr_in*) &address)->sin_addr) + (port ? ":" + std::to_string(htons(((sockaddr_in*) &address)->sin_port)) : ""); @@ -41,7 +42,7 @@ namespace net { } } - inline uint16_t port(const sockaddr_storage& address) { + [[nodiscard]] inline uint16_t port(const sockaddr_storage& address) { switch(address.ss_family) { case AF_INET: return htons(((sockaddr_in*) &address)->sin_port); @@ -52,7 +53,7 @@ namespace net { } } - inline socklen_t address_size(const sockaddr_storage& address) { + [[nodiscard]] inline socklen_t address_size(const sockaddr_storage& address) { switch (address.ss_family) { case AF_INET: return sizeof(sockaddr_in); case AF_INET6: return sizeof(sockaddr_in6); @@ -60,7 +61,7 @@ namespace net { } } - inline bool address_equal(const sockaddr_storage& a, const sockaddr_storage& b) { + [[nodiscard]] inline bool address_equal(const sockaddr_storage& a, const sockaddr_storage& b) { if(a.ss_family != b.ss_family) return false; if(a.ss_family == AF_INET) return ((sockaddr_in*) &a)->sin_addr.s_addr == ((sockaddr_in*) &b)->sin_addr.s_addr; else if(a.ss_family == AF_INET6) { @@ -73,7 +74,7 @@ namespace net { return false; } - inline bool address_equal_ranged(const sockaddr_storage& a, const sockaddr_storage& b, uint8_t range) { + [[nodiscard]] inline bool address_equal_ranged(const sockaddr_storage& a, const sockaddr_storage& b, uint8_t range) { if(a.ss_family != b.ss_family) return false; if(a.ss_family == AF_INET) { auto address_a = ((sockaddr_in*) &a)->sin_addr.s_addr; @@ -130,17 +131,17 @@ namespace net { } - inline bool is_ipv6(const std::string& str) { + [[nodiscard]] inline bool is_ipv6(const std::string& str) { sockaddr_in6 sa{}; return inet_pton(AF_INET6, str.c_str(), &(sa.sin6_addr)) != 0; } - inline bool is_ipv4(const std::string& str) { + [[nodiscard]] inline bool is_ipv4(const std::string& str) { sockaddr_in sa{}; return inet_pton(AF_INET, str.c_str(), &(sa.sin_addr)) != 0; } - inline bool is_anybind(sockaddr_storage& storage) { + [[nodiscard]] inline bool is_anybind(sockaddr_storage& storage) { if(storage.ss_family == AF_INET) { auto data = (sockaddr_in*) &storage; return data->sin_addr.s_addr == 0; @@ -165,7 +166,7 @@ namespace net { return false; } - inline bool resolve_address(const std::string& address, sockaddr_storage& result) { + [[nodiscard]] inline bool resolve_address(const std::string& address, sockaddr_storage& result) { if(is_ipv4(address)) { sockaddr_in s{}; s.sin_port = 0; @@ -210,51 +211,18 @@ namespace net { return false; } - namespace helpers { - inline void strip(std::string& message) { - while(!message.empty()) { - if(message[0] == ' ') - message = message.substr(1); - else if(message[message.length() - 1] == ' ') - message = message.substr(0, message.length() - 1); - else break; - } - } + [[nodiscard]] std::vector> resolve_bindings(const std::string& bindings, uint16_t port); - inline std::deque split(const std::string& message, char delimiter) { - std::deque result{}; - size_t found, index = 0; - do { - found = message.find(delimiter, index); - result.push_back(message.substr(index, found - index)); - index = found + 1; - } while(index != 0); - return result; - } - } + enum struct binding_result { + ADDRESS_FREE, + ADDRESS_USED, + INTERNAL_ERROR + }; - inline std::vector> resolve_bindings(const std::string& bindings, uint16_t port) { - auto binding_list = helpers::split(bindings, ','); - std::vector> result; - result.reserve(binding_list.size()); + enum struct binding_type { + TCP, + UDP + }; - for(auto& address : binding_list) { - helpers::strip(address); - - sockaddr_storage element{}; - memset(&element, 0, sizeof(element)); - if(!resolve_address(address, element)) { - result.emplace_back(address, element, "address resolve failed"); - continue; - } - - if(element.ss_family == AF_INET) { - ((sockaddr_in*) &element)->sin_port = htons(port); - } else if(element.ss_family == AF_INET6) { - ((sockaddr_in6*) &element)->sin6_port = htons(port); - } - result.emplace_back(address, element, ""); - } - return result; - } + [[nodiscard]] binding_result address_available(const sockaddr_storage& address, binding_type type); } \ No newline at end of file diff --git a/test/SQL2Test.cpp b/test/SQL2Test.cpp index 55fdd42..0b621a3 100644 --- a/test/SQL2Test.cpp +++ b/test/SQL2Test.cpp @@ -175,8 +175,11 @@ struct A { ~A() = default; }; -#include +#include +#include + int main() { +#if 0 std::set elements{}; if(!elements.empty()) { auto it = elements.begin(); @@ -186,4 +189,12 @@ int main() { const auto diff = last_element - now; } } +#endif + sockaddr_in addr{}; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_family = AF_INET; + addr.sin_port = htons(9987); + + std::cout << "Result: " << (int) net::address_available(*(sockaddr_storage*) &addr, net::binding_type::TCP) << "\n"; + std::cout << strerror(errno) << "\n"; } \ No newline at end of file From 17d2d7ae56d9c4f96a8ccd27d17eddcb6806743c Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 18 Apr 2020 00:14:52 +0200 Subject: [PATCH 16/18] Updated some stuff --- CMakeLists.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c8e98f..ab30865 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -199,11 +199,10 @@ if(HAVE_OPEN_SSL) openssl::crypto::static) endif() -find_package(mysql REQUIRED) +find_package(mysql) add_library(TeaSpeak STATIC ${SOURCE_FILES} ${HEADER_FILES}) target_link_libraries(TeaSpeak PUBLIC - mysql::client::static threadpool::static jsoncpp_lib ${OPENSSL_LIBRARIES} tomcrypt::static @@ -211,8 +210,13 @@ target_link_libraries(TeaSpeak PUBLIC dl ) -target_compile_options(TeaSpeak PRIVATE "-Wall" "-DHAVE_MYSQL_H") - +if(mysql_FOUND) + message("Found MySQL") + target_link_libraries(TeaSpeak PUBLIC + mysql::client::static + ) + target_compile_options(TeaSpeak PRIVATE "-Wall" "-DHAVE_MYSQL_H") +endif() if (TEASPEAK_SERVER) target_link_libraries(TeaSpeak PUBLIC CXXTerminal::static) endif () From 65dad8e9cb0e198688f68e49ea0f5def2113b4ae Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 18 Apr 2020 00:46:45 +0200 Subject: [PATCH 17/18] Fixed windows build --- src/Properties.h | 34 +++++++++++++-------------- src/PropertyDefinition.h | 4 ++-- src/misc/digest.cpp | 3 +++ src/misc/net.h | 2 ++ src/misc/strobf.h | 4 ++-- src/protocol/AcknowledgeManager.cpp | 5 ++-- src/protocol/PacketLossCalculator.cpp | 24 +++++++++---------- src/protocol/PacketLossCalculator.h | 4 ++-- 8 files changed, 42 insertions(+), 38 deletions(-) diff --git a/src/Properties.h b/src/Properties.h index ec3a6c0..897d878 100644 --- a/src/Properties.h +++ b/src/Properties.h @@ -8,10 +8,12 @@ #include #include #include +#include #include #include -#include +#include #include +#include #include "misc/spin_lock.h" #include "converters/converter.h" @@ -466,9 +468,17 @@ namespace ts { */ namespace impl { - template - constexpr auto make_property_array(Properties&&... props) noexcept { - return std::array, sizeof...(Properties)>{std::forward(props)...}; + constexpr inline auto property_count() { + size_t result{0}; + result += VIRTUALSERVER_ENDMARKER; + result += CHANNEL_ENDMARKER; + result += CLIENT_ENDMARKER; + result += GROUP_ENDMARKER; + result += SERVERINSTANCE_ENDMARKER; + result += CONNECTION_ENDMARKER; + result += PLAYLIST_ENDMARKER; + result += UNKNOWN_ENDMARKER; + return result; } extern bool validateInput(const std::string& input, ValueType type); @@ -539,19 +549,7 @@ namespace ts { }; #ifdef EXTERNALIZE_PROPERTY_DEFINITIONS - constexpr inline auto property_count() { - size_t result{0}; - result += VIRTUALSERVER_ENDMARKER; - result += CHANNEL_ENDMARKER; - result += CLIENT_ENDMARKER; - result += GROUP_ENDMARKER; - result += SERVERINSTANCE_ENDMARKER; - result += CONNECTION_ENDMARKER; - result += PLAYLIST_ENDMARKER; - result += UNKNOWN_ENDMARKER; - return result; - } - extern std::array property_list; + extern std::array property_list; #else #include "./PropertyDefinition.h" @@ -784,7 +782,7 @@ namespace ts { [[nodiscard]] const property::PropertyDescription& type() const { return *this->data_ptr->description; } [[nodiscard]] std::string value() const { - std::lock_guard lock(this->data_ptr->value_lock); + std::lock_guard lock{this->data_ptr->value_lock}; return this->data_ptr->value; } diff --git a/src/PropertyDefinition.h b/src/PropertyDefinition.h index dad31c6..4c42bb5 100644 --- a/src/PropertyDefinition.h +++ b/src/PropertyDefinition.h @@ -14,7 +14,7 @@ decltype(property::property_list) property:: #else constexpr auto #endif -property_list = property::impl::make_property_array( +property_list = std::array{ PropertyDescription{UNKNOWN_UNDEFINED, "undefined", "", TYPE_UNKNOWN, 0}, /* virtual server properties */ @@ -347,7 +347,7 @@ property_list = property::impl::make_property_array( PropertyDescription{PLAYLIST_FLAG_FINISHED, "playlist_flag_finished", "0", TYPE_BOOL, FLAG_PLAYLIST_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE}, PropertyDescription{PLAYLIST_REPLAY_MODE, "playlist_replay_mode", "0", TYPE_UNSIGNED_NUMBER, FLAG_PLAYLIST_VARIABLE | FLAG_USER_EDITABLE | FLAG_SAVE}, PropertyDescription{PLAYLIST_CURRENT_SONG_ID, "playlist_current_song_id", "0", TYPE_UNSIGNED_NUMBER, FLAG_PLAYLIST_VARIABLE | FLAG_SAVE} -); +}; #undef str_ #undef V diff --git a/src/misc/digest.cpp b/src/misc/digest.cpp index 9176278..9f21b6e 100644 --- a/src/misc/digest.cpp +++ b/src/misc/digest.cpp @@ -1,4 +1,7 @@ +#ifndef NO_OPEN_SSL #define NO_OPEN_SSL +#endif + #include "./digest.h" #include diff --git a/src/misc/net.h b/src/misc/net.h index 6d00544..f8997df 100644 --- a/src/misc/net.h +++ b/src/misc/net.h @@ -5,8 +5,10 @@ #include #include #include +#include #ifdef WIN32 + #define _WINSOCK_DEPRECATED_NO_WARNINGS /* gethostbyname is deprecated for windows */ #include #include #include diff --git a/src/misc/strobf.h b/src/misc/strobf.h index 13eaece..f834330 100644 --- a/src/misc/strobf.h +++ b/src/misc/strobf.h @@ -72,7 +72,7 @@ namespace str_obf { static_assert(sizeof(char_t) == 1, "Currently only 8 bit supported"); if(length == 0) return; if(key.size() == 0) _invalid_key_size(); - if(key.size() & (key.size() - 1)) _invalid_key_size(); /* key must be an power of 2 */ + if(key.size() & (key.size() - 1UL)) _invalid_key_size(); /* key must be an power of 2 */ auto left = length; size_t key_index{0}; @@ -140,7 +140,7 @@ namespace str_obf { length = (std::uint64_t) ((internal::rng32_next(rng_base, (uint32_t) seed) >> 12UL) & 0xFFUL); } while(length == 0 || length > power2); - return 1U << length; + return uint64_t{1} << length; } template diff --git a/src/protocol/AcknowledgeManager.cpp b/src/protocol/AcknowledgeManager.cpp index 67f29f1..7dfd683 100644 --- a/src/protocol/AcknowledgeManager.cpp +++ b/src/protocol/AcknowledgeManager.cpp @@ -1,6 +1,7 @@ #include "AcknowledgeManager.h" #include #include +#include using namespace ts; using namespace ts::connection; @@ -107,7 +108,7 @@ ssize_t AcknowledgeManager::execute_resend(const system_clock::time_point& now , } } else { if(entry->next_resend <= now) { - entry->next_resend = now + std::chrono::milliseconds{(int64_t) min(ceil(this->rto), 1500.f)}; + entry->next_resend = now + std::chrono::milliseconds{(int64_t) std::min(ceil(this->rto), 1500.f)}; need_resend.push_back(entry); entry->resend_count++; entry->send_count++; @@ -139,7 +140,7 @@ ssize_t AcknowledgeManager::execute_resend(const system_clock::time_point& now , /* we're not taking the clock granularity into account because its nearly 1ms and it would only add more branches */ void AcknowledgeManager::update_rto(size_t r) { if(srtt == -1) { - this->srtt = r; + this->srtt = (float) r; this->rttvar = r / 2.f; this->rto = srtt + 4 * this->rttvar; } else { diff --git a/src/protocol/PacketLossCalculator.cpp b/src/protocol/PacketLossCalculator.cpp index 45e2bca..6a1907a 100644 --- a/src/protocol/PacketLossCalculator.cpp +++ b/src/protocol/PacketLossCalculator.cpp @@ -15,22 +15,22 @@ void UnorderedPacketLossCalculator::packet_received(uint32_t packet_id) { if(age < this->packet_history.max_bits()) { const auto received = this->packet_history.shift_in_bounds(age).count(); - this->received_packets_ += received; - this->received_packets_total_ += received; + this->received_packets_ += (uint32_t) received; + this->received_packets_total_ += (uint32_t) received; - this->lost_packets_ += age - received; - this->lost_packets_total_ += age - received; + this->lost_packets_ += (uint32_t) (age - received); + this->lost_packets_total_ += (uint32_t) (age - received); } else { const auto received = this->packet_history.clear().count(); - this->received_packets_ += received; - this->received_packets_total_ += received; - this->lost_packets_ += this->packet_history.max_bits() - received; - this->lost_packets_total_ += this->packet_history.max_bits() - received; + this->received_packets_ += (uint32_t) received; + this->received_packets_total_ += (uint32_t) received; + this->lost_packets_ += (uint32_t) (this->packet_history.max_bits() - received); + this->lost_packets_total_ += (uint32_t) (this->packet_history.max_bits() - received); if(age >= this->packet_history.max_bits() * 2) { this->packet_history.set_unchecked(0); - this->lost_packets_ += age - this->packet_history.max_bits() * 2; - this->lost_packets_total_ += age - this->packet_history.max_bits() * 2; + this->lost_packets_ += (uint32_t) (age - this->packet_history.max_bits() * 2); + this->lost_packets_total_ += (uint32_t) (age - this->packet_history.max_bits() * 2); } else { this->packet_history.set_unchecked(age - this->packet_history.max_bits()); } @@ -60,8 +60,8 @@ void UnorderedPacketLossCalculator::short_stats() { if(packets_passed < target_interval) return; const auto factor = .5; - this->received_packets_ *= factor; - this->lost_packets_ *= factor; + this->received_packets_ = (uint32_t) (this->received_packets_ * factor); + this->lost_packets_ = (uint32_t) (this->lost_packets_ * factor); this->last_history_offset = this->packet_history_offset; } diff --git a/src/protocol/PacketLossCalculator.h b/src/protocol/PacketLossCalculator.h index 74366d2..18a4a70 100644 --- a/src/protocol/PacketLossCalculator.h +++ b/src/protocol/PacketLossCalculator.h @@ -60,8 +60,8 @@ namespace ts::protocol { [[nodiscard]] inline uint32_t received_packets_total() const { return this->received_packets_total_; } [[nodiscard]] inline uint32_t lost_packets_total() const { return this->lost_packets_total_; } - [[nodiscard]] inline uint32_t unconfirmed_received_packets() const { return this->packet_history.count(); }; - [[nodiscard]] inline uint32_t unconfirmed_lost_packets() const { return this->packet_history.max_bits() - this->packet_history.count(); }; + [[nodiscard]] inline uint32_t unconfirmed_received_packets() const { return (uint32_t) this->packet_history.count(); }; + [[nodiscard]] inline uint32_t unconfirmed_lost_packets() const { return (uint32_t) (this->packet_history.max_bits() - this->packet_history.count()); }; private: uint32_t received_packets_{0}, received_packets_total_{0}, lost_packets_{0}, lost_packets_total_{0}; From edc0a9db2b564f1967dbf1955daccac43e7f9e1a Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 18 Apr 2020 11:20:15 +0200 Subject: [PATCH 18/18] Force enable MySQL --- CMakeLists.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ab30865..d9609e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -199,8 +199,6 @@ if(HAVE_OPEN_SSL) openssl::crypto::static) endif() -find_package(mysql) - add_library(TeaSpeak STATIC ${SOURCE_FILES} ${HEADER_FILES}) target_link_libraries(TeaSpeak PUBLIC threadpool::static jsoncpp_lib @@ -210,13 +208,19 @@ target_link_libraries(TeaSpeak PUBLIC dl ) +find_package(mysql REQUIRED) +set(mysql_FOUND ON) +message("${mysql_FOUND}") if(mysql_FOUND) message("Found MySQL") target_link_libraries(TeaSpeak PUBLIC mysql::client::static ) target_compile_options(TeaSpeak PRIVATE "-Wall" "-DHAVE_MYSQL_H") +else() + message("Building without MySQL Support") endif() + if (TEASPEAK_SERVER) target_link_libraries(TeaSpeak PUBLIC CXXTerminal::static) endif ()