From 61726282471f63911be6903b7dc47d95f82a9eb1 Mon Sep 17 00:00:00 2001
From: WolverinDEV <git@teaspeak.de>
Date: Thu, 5 Mar 2020 14:10:22 +0100
Subject: [PATCH] Some more changes

---
 server/CMakeLists.txt                         |   2 +-
 server/lock_concept                           | 107 +--
 server/src/DatabaseHelper.cpp                 |  23 +-
 server/src/DatabaseHelper.h                   |   4 +-
 server/src/Group.h                            |   4 +-
 server/src/client/ConnectedClient.cpp         |   4 +
 server/src/client/DataClient.h                |   3 +-
 server/src/groups/Group.cpp                   |  11 +-
 server/src/groups/Group.h                     |  24 +-
 server/src/groups/GroupManager.cpp            | 609 +++++++++++++++++-
 server/src/groups/GroupManager.h              | 101 ++-
 server/src/services/ClientChannelService.cpp  |   5 +
 server/src/services/PermissionsService.cpp    |  57 +-
 server/src/services/PermissionsService.h      |   6 +
 .../src/services/VirtualServerInformation.cpp |  15 +
 .../src/services/VirtualServerInformation.h   |  18 +
 server/src/vserver/VirtualServerBase.cpp      | 136 ++++
 server/src/vserver/VirtualServerBase.h        |  21 +-
 server/src/vserver/VirtualServerManager.cpp   |   5 +
 server/src/vserver/VirtualServerManager.h     |  21 +
 shared                                        |   2 +-
 21 files changed, 1072 insertions(+), 106 deletions(-)
 create mode 100644 server/src/services/VirtualServerInformation.cpp
 create mode 100644 server/src/services/VirtualServerInformation.h
 create mode 100644 server/src/vserver/VirtualServerManager.cpp
 create mode 100644 server/src/vserver/VirtualServerManager.h

diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt
index b3c5a83..038dde8 100644
--- a/server/CMakeLists.txt
+++ b/server/CMakeLists.txt
@@ -160,7 +160,7 @@ if (COMPILE_WEB_CLIENT)
             src/client/web/WSWebClient.cpp
             src/client/web/SampleHandler.cpp
             src/client/web/VoiceBridge.cpp
-            src/client/command_handler/helpers.h src/music/PlaylistPermissions.cpp src/music/PlaylistPermissions.h src/lincense/LicenseService.cpp src/lincense/LicenseService.h src/groups/GroupManager.cpp src/groups/GroupManager.h src/groups/GroupAssignmentManager.cpp src/groups/GroupAssignmentManager.h src/groups/Group.cpp src/groups/Group.h)
+            src/client/command_handler/helpers.h src/music/PlaylistPermissions.cpp src/music/PlaylistPermissions.h src/lincense/LicenseService.cpp src/lincense/LicenseService.h src/groups/GroupManager.cpp src/groups/GroupManager.h src/groups/GroupAssignmentManager.cpp src/groups/GroupAssignmentManager.h src/groups/Group.cpp src/groups/Group.h src/services/VirtualServerInformation.cpp src/services/VirtualServerInformation.h src/vserver/VirtualServerManager.cpp src/vserver/VirtualServerManager.h)
 endif ()
 
 add_executable(PermHelper helpers/permgen.cpp)
diff --git a/server/lock_concept b/server/lock_concept
index f258142..2492a96 100644
--- a/server/lock_concept
+++ b/server/lock_concept
@@ -1,58 +1,85 @@
-CommandResult handleCommandClientUpdate(Command&);
-CommandResult handleCommandClientEdit(Command&);
-CommandResult handleCommandClientEdit(Command&, const std::shared_ptr<ConnectedClient>& /* target */);
-CommandResult handleCommandClientMove(Command&);
-CommandResult handleCommandClientGetVariables(Command&);
-CommandResult handleCommandClientKick(Command&);
-CommandResult handleCommandClientPoke(Command&);
+General lock order:
+- Client execute lock
+- Server state lock
+- Server channel tree
+- Client channel tree
 
-CommandResult handleCommandChannelSubscribe(Command&); read lock: server channel tree => client lock write
-CommandResult handleCommandChannelSubscribeAll(Command&); read lock: server channel tree => client lock write
-CommandResult handleCommandChannelUnsubscribe(Command&); read lock: server channel tree => client lock write
-CommandResult handleCommandChannelUnsubscribeAll(Command&); read lock: server channel tree => client lock write
-CommandResult handleCommandChannelCreate(Command&); write lock server channel tree => iterate clients lock (write)
-CommandResult handleCommandChannelDelete(Command&); write lock server channel tree => iterate clients lock (write)
-CommandResult handleCommandChannelEdit(Command&); write lock server channel tree
-CommandResult handleCommandChannelGetDescription(Command&); read lock: server channel tree
-CommandResult handleCommandChannelMove(Command&); write lock server channel tree
+When executing a command:
+    Lock order:
+    - Client execute lock
+    - Server state lock (Server should not try to change state while a client is executing something)
+    Notes:
+    The server might be null or the default server.
+    This must be checked via the given macro!
 
-CommandResult handleCommandChannelAddPerm(Command&); read lock: server channel tree => all clients write lock
-CommandResult handleCommandChannelDelPerm(Command&); read lock: server channel tree => all clients write lock
+Disconnect/unregister a client:
+- Lock client execute lock
+- Lock server state
+- Unregister client from server and set server variable to nullptr
+- Underlying server (UDP, Web, etc. disconnect)
 
-CommandResult handleCommandChannelGroupDel(Command&); read lock: server channel tree => all clients in the group should not be allowed to switch a channel
-CommandResult handleCommandSetClientChannelGroup(Command&); read lock: server channel tree => client should not be allowed to switch channel
+Client channel tree connect/mode/disconnect:
+- Lock client execute lock
+- Lock server channel tree in write mode
+- Lock client channel tree in write mode
+- Move client & notify (notify required all clients to have their channel tree locked in shared mode)
 
-CommandResult handleCommandPluginCmd(Command&);
+Command lock overview:
+    CommandResult handleCommandClientUpdate(Command&);
+    CommandResult handleCommandClientEdit(Command&);
+    CommandResult handleCommandClientEdit(Command&, const std::shared_ptr<ConnectedClient>& /* target */);
+    CommandResult handleCommandClientMove(Command&);
+    CommandResult handleCommandClientGetVariables(Command&);
+    CommandResult handleCommandClientKick(Command&);
+    CommandResult handleCommandClientPoke(Command&);
 
-CommandResult handleCommandClientMute(Command&);
-CommandResult handleCommandClientUnmute(Command&);
+    CommandResult handleCommandChannelSubscribe(Command&); read lock: server channel tree => client lock write
+    CommandResult handleCommandChannelSubscribeAll(Command&); read lock: server channel tree => client lock write
+    CommandResult handleCommandChannelUnsubscribe(Command&); read lock: server channel tree => client lock write
+    CommandResult handleCommandChannelUnsubscribeAll(Command&); read lock: server channel tree => client lock write
+    CommandResult handleCommandChannelCreate(Command&); write lock server channel tree => iterate clients lock (write)
+    CommandResult handleCommandChannelDelete(Command&); write lock server channel tree => iterate clients lock (write)
+    CommandResult handleCommandChannelEdit(Command&); write lock server channel tree
+    CommandResult handleCommandChannelGetDescription(Command&); read lock: server channel tree
+    CommandResult handleCommandChannelMove(Command&); write lock server channel tree
 
-//Original from query but still reachable for all
-CommandResult handleCommandClientList(Command&);
+    CommandResult handleCommandChannelAddPerm(Command&); read lock: server channel tree => all clients write lock
+    CommandResult handleCommandChannelDelPerm(Command&); read lock: server channel tree => all clients write lock
 
-CommandResult handleCommandClientFind(Command&);
-CommandResult handleCommandClientInfo(Command&);
+    CommandResult handleCommandChannelGroupDel(Command&); read lock: server channel tree => all clients in the group should not be allowed to switch a channel
+    CommandResult handleCommandSetClientChannelGroup(Command&); read lock: server channel tree => client should not be allowed to switch channel
 
-CommandResult handleCommandVerifyChannelPassword(Command&); read lock: server channel tree
+    CommandResult handleCommandPluginCmd(Command&);
 
-handleCommandChannelFind read lock: server channel tree
-handleCommandChannelInfo read lock: server channel tree
+    CommandResult handleCommandClientMute(Command&);
+    CommandResult handleCommandClientUnmute(Command&);
 
-General command handling: client_command_lock
-    Ensure that only one command at time will be handeled
+    //Original from query but still reachable for all
+    CommandResult handleCommandClientList(Command&);
+
+    CommandResult handleCommandClientFind(Command&);
+    CommandResult handleCommandClientInfo(Command&);
+
+    CommandResult handleCommandVerifyChannelPassword(Command&); read lock: server channel tree
+
+    handleCommandChannelFind read lock: server channel tree
+    handleCommandChannelInfo read lock: server channel tree
+
+    General command handling: client_command_lock
+        Ensure that only one command at time will be handeled
 
 
-Read access server channel tree: read lock channel_tree_lock
-Write access server channel tree: lock channel_tree_lock
-Write access client channel tree: read lock channel_tree_lock => lock client channel tree
-    if we write to the server channel tree no client should have their channel tree updated
-Read access client channel tree: no lock required
-    Note: the server channel tree should not be accessed!
+    Read access server channel tree: read lock channel_tree_lock
+    Write access server channel tree: lock channel_tree_lock
+    Write access client channel tree: read lock channel_tree_lock => lock client channel tree
+        if we write to the server channel tree no client should have their channel tree updated
+    Read access client channel tree: no lock required
+        Note: the server channel tree should not be accessed!
 
-Move client acts like access server channel tree: write lock channel_tree_lock => for each client write lock their tree
+    Move client acts like access server channel tree: write lock channel_tree_lock => for each client write lock their tree
 
 
 TODO: Some kind of perm channel lock
 TODO: Fix handleCommandChannelEdit
 
-Test: Channel hide & show with clients! Multible clients as well!
\ No newline at end of file
+Test: Channel hide & show with clients! Multiple clients as well!
\ No newline at end of file
diff --git a/server/src/DatabaseHelper.cpp b/server/src/DatabaseHelper.cpp
index 794ef24..61b3e01 100644
--- a/server/src/DatabaseHelper.cpp
+++ b/server/src/DatabaseHelper.cpp
@@ -159,10 +159,9 @@ void DatabaseHelper::deleteClient(const std::shared_ptr<VirtualServer>& server,
     //TODO delete complains
 }
 
-inline sql::result load_permissions_v2(const std::shared_ptr<VirtualServer>& server, v2::PermissionRegister* manager, sql::command& command, bool test_channel /* only used for client permissions (client channel permissions) */) {
+inline sql::result load_permissions_v2(VirtualServerId server_id, v2::PermissionRegister* manager, sql::command& command, bool test_channel /* only used for client permissions (client channel permissions) */) {
     auto start = system_clock::now();
 
-    auto server_id = server ? server->getServerId() : 0;
     return command.query([&](int length, char** values, char** names){
         permission::PermissionType key = permission::PermissionType::undefined;
         permission::PermissionValue value = permNotGranted, granted = permNotGranted;
@@ -198,6 +197,8 @@ inline sql::result load_permissions_v2(const std::shared_ptr<VirtualServer>& ser
             return 0;
         }
         if(channel_id > 0 && test_channel) {
+            //TODO: Test for invalid channels
+            /*
             if(!server)
                 logError(server_id, "[SQL] Cant find channel for channel bound permission (No server given)! Command: {} ChannelID: {}", command.sqlCommand(), values[index]);
             else {
@@ -205,6 +206,7 @@ inline sql::result load_permissions_v2(const std::shared_ptr<VirtualServer>& ser
                 if(!channel)
                     logError(server_id, "[SQL] Cant find channel for channel bound permission! Command: {} ChannelID: {}", command.sqlCommand(), values[index]);
             }
+             */
         }
 
 
@@ -282,7 +284,7 @@ std::shared_ptr<v2::PermissionRegister> DatabaseHelper::loadClientPermissionMana
                                     variable{":serverId", server ? server->getServerId() : 0},
                                     variable{":type", permission::SQL_PERM_USER},
                                     variable{":id", cldbid});
-        LOG_SQL_CMD(load_permissions_v2(server, permission_manager.get(), command, true));
+        LOG_SQL_CMD(load_permissions_v2(server ? server->getServerId() : 0, permission_manager.get(), command, true));
     }
 
 
@@ -336,14 +338,14 @@ void DatabaseHelper::saveClientPermissions(const std::shared_ptr<ts::server::Vir
 }
 
 
-std::shared_ptr<permission::v2::PermissionRegister> DatabaseHelper::loadGroupPermissions(const std::shared_ptr<VirtualServer>& server, ts::GroupId group_id) {
+std::shared_ptr<permission::v2::PermissionRegister> DatabaseHelper::loadGroupPermissions(VirtualServerId server, ts::GroupId group_id, uint8_t type) {
     auto result = std::make_shared<v2::PermissionRegister>();
     if(this->use_startup_cache && server) {
         shared_ptr<StartupCacheEntry> entry;
         {
             threads::MutexLock lock(this->startup_lock);
             for(const auto& entries : this->startup_entries) {
-                if(entries->sid == server->getServerId()) {
+                if(entries->sid == server) {
                     entry = entries;
                     break;
                 }
@@ -361,19 +363,18 @@ std::shared_ptr<permission::v2::PermissionRegister> DatabaseHelper::loadGroupPer
 
     //7931
     auto command = sql::command(this->sql, "SELECT `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id",
-                                variable{":serverId", server ? server->getServerId() : 0},
+                                variable{":serverId", server},
                                 variable{":type", permission::SQL_PERM_GROUP},
                                 variable{":id", group_id});
     LOG_SQL_CMD(load_permissions_v2(server, result.get(), command, false));
     return result;
 }
 
-void DatabaseHelper::saveGroupPermissions(const std::shared_ptr<ts::server::VirtualServer> &server, ts::GroupId group_id, const std::shared_ptr<ts::permission::v2::PermissionRegister> &permissions) {
+void DatabaseHelper::saveGroupPermissions(VirtualServerId server_id, ts::GroupId group_id, const std::shared_ptr<ts::permission::v2::PermissionRegister> &permissions) {
     const auto updates = permissions->flush_db_updates();
     if(updates.empty())
         return;
 
-    auto server_id = server ? server->getServerId() : 0;
     for(auto& update : updates) {
         std::string query = update.flag_delete ? DELETE_COMMAND : (update.flag_db ? UPDATE_COMMAND : INSERT_COMMAND);
 
@@ -388,7 +389,7 @@ void DatabaseHelper::saveGroupPermissions(const std::shared_ptr<ts::server::Virt
                  query
         );
         sql::command(this->sql, query,
-                     variable{":serverId", server ? server->getServerId() : 0},
+                     variable{":serverId", server_id},
                      variable{":id", group_id},
                      variable{":chId", 0},
                      variable{":type", permission::SQL_PERM_GROUP},
@@ -436,7 +437,7 @@ std::shared_ptr<permission::v2::PermissionRegister> DatabaseHelper::loadPlaylist
                                 variable{":serverId", server ? server->getServerId() : 0},
                                 variable{":type", permission::SQL_PERM_PLAYLIST},
                                 variable{":id", playlist_id});
-    LOG_SQL_CMD(load_permissions_v2(server, result.get(), command, false));
+    LOG_SQL_CMD(load_permissions_v2(server ? server->getServerId() : 0, result.get(), command, false));
     return result;
 }
 
@@ -502,7 +503,7 @@ std::shared_ptr<permission::v2::PermissionRegister> DatabaseHelper::loadChannelP
                                 variable{":chid", channel},
                                 variable{":id", 0},
                                 variable{":type", permission::SQL_PERM_CHANNEL});
-    LOG_SQL_CMD(load_permissions_v2(server, result.get(), command, false));
+    LOG_SQL_CMD(load_permissions_v2(server ? server->getServerId() : 0, result.get(), command, false));
     return result;
 }
 
diff --git a/server/src/DatabaseHelper.h b/server/src/DatabaseHelper.h
index 60af788..4078086 100644
--- a/server/src/DatabaseHelper.h
+++ b/server/src/DatabaseHelper.h
@@ -97,8 +97,8 @@ namespace ts {
                 std::shared_ptr<permission::v2::PermissionRegister> loadChannelPermissions(const std::shared_ptr<VirtualServer>&, ChannelId);
                 void saveChannelPermissions(const std::shared_ptr<VirtualServer>&, ChannelId, const std::shared_ptr<permission::v2::PermissionRegister>& /* permission manager */);
 
-                std::shared_ptr<permission::v2::PermissionRegister> loadGroupPermissions(const std::shared_ptr<VirtualServer>&, GroupId);
-                void saveGroupPermissions(const std::shared_ptr<VirtualServer>&, GroupId, const std::shared_ptr<permission::v2::PermissionRegister>& /* permission manager */);
+                std::shared_ptr<permission::v2::PermissionRegister> loadGroupPermissions(VirtualServerId, GroupId, uint8_t /* group type */);
+                void saveGroupPermissions(VirtualServerId, GroupId, const std::shared_ptr<permission::v2::PermissionRegister>& /* permission manager */);
 
                 std::shared_ptr<permission::v2::PermissionRegister> loadPlaylistPermissions(const std::shared_ptr<VirtualServer>&, PlaylistId /* playlist id */);
                 void savePlaylistPermissions(const std::shared_ptr<VirtualServer>&, PlaylistId, const std::shared_ptr<permission::v2::PermissionRegister>& /* permission manager */);
diff --git a/server/src/Group.h b/server/src/Group.h
index ef1cc47..28804de 100644
--- a/server/src/Group.h
+++ b/server/src/Group.h
@@ -29,7 +29,9 @@ namespace ts {
 
     enum GroupTarget {
         GROUPTARGET_SERVER,
-        GROUPTARGET_CHANNEL
+        GROUPTARGET_CHANNEL,
+
+        GROUPTARGET_UNKNOWN
     };
 
     enum GroupNameMode {
diff --git a/server/src/client/ConnectedClient.cpp b/server/src/client/ConnectedClient.cpp
index 54856ae..a2dc31f 100644
--- a/server/src/client/ConnectedClient.cpp
+++ b/server/src/client/ConnectedClient.cpp
@@ -560,6 +560,10 @@ bool ConnectedClient::notifyClientNeededPermissions() {
         cmd[index]["permid"] = value.first;
         cmd[index++]["permvalue"] = value.second.has_value ? value.second.value : 0;
     }
+    if(index == 0) {
+        cmd[index]["permid"] = permission::i_client_talk_power;
+        cmd[index++]["permvalue"] = 0;
+    }
 
     this->sendCommand(cmd);
     return true;
diff --git a/server/src/client/DataClient.h b/server/src/client/DataClient.h
index 9af04e8..1761a7c 100644
--- a/server/src/client/DataClient.h
+++ b/server/src/client/DataClient.h
@@ -105,7 +105,8 @@ namespace ts {
                 std::shared_ptr<permission::v2::PermissionRegister> clientPermissions = nullptr;
                 std::shared_ptr<Properties> _properties;
 
-                std::shared_ptr<BasicChannel> currentChannel = nullptr;
+                /* the current channel is save to access when the server channel tree has been locked (shared or exclusively) */
+                std::shared_ptr<BasicChannel> currentChannel{nullptr};
         };
     }
 }
\ No newline at end of file
diff --git a/server/src/groups/Group.cpp b/server/src/groups/Group.cpp
index 09ee3e8..0c3b792 100644
--- a/server/src/groups/Group.cpp
+++ b/server/src/groups/Group.cpp
@@ -7,15 +7,8 @@
 
 using namespace ts::server::groups;
 
-Group::Group(const std::shared_ptr<GroupManager> &manager, ts::GroupId id, ts::server::groups::GroupType type, std::string name,
-             std::shared_ptr<permission::v2::PermissionRegister> permissions) : manager_{manager}, group_id_{id}, type_{type}, name_{std::move(name)}, permissions_{std::move(permissions)} { }
-
-void Group::set_display_name(const std::string &name) {
-    this->name_ = name;
-
-    auto handle = this->manager_.lock();
-    if(handle) handle->update_group_name(this);
-}
+Group::Group(VirtualServerId sid, ts::GroupId id, ts::server::groups::GroupType type, std::string name,
+             std::shared_ptr<permission::v2::PermissionRegister> permissions) : virtual_server_id_{sid}, group_id_{id}, type_{type}, name_{std::move(name)}, permissions_{std::move(permissions)} { }
 
 void Group::set_permissions(const std::shared_ptr<permission::v2::PermissionRegister> &permissions) {
     assert(permissions);
diff --git a/server/src/groups/Group.h b/server/src/groups/Group.h
index c113078..1239360 100644
--- a/server/src/groups/Group.h
+++ b/server/src/groups/Group.h
@@ -7,7 +7,9 @@ namespace ts::server::groups {
     enum GroupType {
         GROUP_TYPE_QUERY,
         GROUP_TYPE_TEMPLATE,
-        GROUP_TYPE_NORMAL
+        GROUP_TYPE_NORMAL,
+
+        GROUP_TYPE_UNKNOWN = 0xFF
     };
 
     enum GroupNameMode {
@@ -23,13 +25,15 @@ namespace ts::server::groups {
     class Group {
             friend class GroupManager;
         public:
-            Group(const std::shared_ptr<GroupManager>& /* manager */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionRegister> /* permissions */);
+            Group(VirtualServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionRegister> /* permissions */);
             ~Group() = default;
 
             /* information getters */
+            [[nodiscard]] inline VirtualServerId virtual_server_id() const { return this->virtual_server_id_; }
             [[nodiscard]] inline GroupId group_id() const { return this->group_id_; }
             [[nodiscard]] inline GroupType group_type() const { return this->type_; }
             [[nodiscard]] inline const std::string& display_name() const { return this->name_; }
+            /* we're not returning a cr here because the permissions might get reloaded */
             [[nodiscard]] inline std::shared_ptr<permission::v2::PermissionRegister> permissions() { return this->permissions_; }
 
             [[nodiscard]] inline bool save_assignments() const {
@@ -56,11 +60,8 @@ namespace ts::server::groups {
                 return value.has_value ? value.value : 0;
             }
 
-            /* information setters */
-            void set_display_name(const std::string& name);
         private:
-            std::weak_ptr<GroupManager> manager_;
-
+            const VirtualServerId virtual_server_id_;
             const GroupId group_id_;
             const GroupType type_;
             std::string name_;
@@ -70,6 +71,13 @@ namespace ts::server::groups {
             void set_permissions(const std::shared_ptr<permission::v2::PermissionRegister>& /* permissions */);
     };
 
-    class ServerGroup : public Group { };
-    class ChannelGroup : public Group { };
+    class ServerGroup : public Group {
+        public:
+            ServerGroup(VirtualServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionRegister> /* permissions */);
+    };
+
+    class ChannelGroup : public Group {
+        public:
+            ChannelGroup(VirtualServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionRegister> /* permissions */);
+    };
 }
\ No newline at end of file
diff --git a/server/src/groups/GroupManager.cpp b/server/src/groups/GroupManager.cpp
index a851a25..405dd3c 100644
--- a/server/src/groups/GroupManager.cpp
+++ b/server/src/groups/GroupManager.cpp
@@ -1,5 +1,612 @@
 //
 // Created by WolverinDEV on 03/03/2020.
 //
-
+#include <log/LogUtils.h>
 #include "GroupManager.h"
+#include "../vserver/VirtualServerBase.h"
+#include "../InstanceHandler.h"
+
+using namespace ts::server::groups;
+
+GroupManager::GroupManager(ts::server::vserver::VirtualServerBase *server, std::shared_ptr<GroupManager> parent)
+    : virtual_server_{server}, parent_manager_{std::move(parent)}, assignment_manager_{this} {
+    assert(server);
+}
+
+GroupManager::~GroupManager() = default;
+
+ts::ServerId GroupManager::server_id() {
+    return this->virtual_server_->server_id();
+}
+
+sql::SqlManager* GroupManager::sql_manager() {
+    return this->virtual_server_->sql_manager();
+}
+
+bool GroupManager::initialize(const std::shared_ptr<GroupManager>& self, std::string &error) {
+    assert(&*self == this);
+    this->weak_ref_ = self;
+    return this->assignment_manager_.initialize(error);
+}
+
+GroupLoadResult GroupManager::load_data(bool initialize) {
+    std::lock_guard manage_lock{this->group_manage_lock_};
+    {
+        std::lock_guard list_lock{this->group_lock_};
+        this->server_groups_.clear();
+        this->channel_groups_.clear();
+    }
+
+    auto res = sql::command(this->sql_manager(), "SELECT * FROM `groups` WHERE `serverId` = :sid", variable{":sid", this->server_id()}).query(&GroupManager::insert_group_from_sql, this);
+    if(!res) {
+        LOG_SQL_CMD(res);
+        return GroupLoadResult::DATABASE_ERROR;
+    }
+
+    if(this->server_groups_.empty() || this->channel_groups_.empty())
+        return GroupLoadResult::NO_GROUPS;
+
+    this->ensure_valid_default_groups(initialize);
+    return GroupLoadResult::SUCCESS;
+}
+
+void GroupManager::unload_data() {
+    std::lock_guard manage_lock{this->group_manage_lock_};
+    {
+        std::lock_guard list_lock{this->group_lock_};
+        this->server_groups_.clear();
+        this->channel_groups_.clear();
+    }
+}
+
+void GroupManager::reset_groups(bool db_cleanup) {
+    std::lock_guard manage_lock{this->group_manage_lock_};
+    this->unload_data();
+
+    if(db_cleanup) {
+        LOG_SQL_CMD(sql::command(this->virtual_server_->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type",
+                                 variable{":serverId", this->server_id()},
+                                 variable{":type", ts::permission::SQL_PERM_GROUP}).execute());
+        LOG_SQL_CMD(sql::command(this->virtual_server_->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :serverId", variable{":serverId", this->server_id()}).execute());
+        LOG_SQL_CMD(sql::command(this->virtual_server_->sql_manager(), "DELETE FROM `groups` WHERE `serverId` = :serverId", variable{":serverId", this->server_id()}).execute());
+    }
+
+    if(auto error = this->load_data(true); error != GroupLoadResult::SUCCESS)
+        logCritical(this->server_id(), "Failed to load groups after group unload ({}). There might be no groups loaded now!", (int) error);
+}
+
+int GroupManager::insert_group_from_sql(int length, std::string *values, std::string *names) {
+    GroupId group_id{0};
+    GroupType group_type{GroupType::GROUP_TYPE_UNKNOWN};
+    std::string group_name{};
+    GroupTarget group_target{GroupTarget::GROUPTARGET_UNKNOWN};
+
+    for(size_t index = 0; index < length; index++) {
+        try {
+            if(names[index] == "groupId")
+                group_id = std::stoull(values[index]);
+            else if(names[index] == "target")
+                group_target = (GroupTarget) std::stoull(values[index]);
+            else if(names[index] == "type")
+                group_type = (GroupType) std::stoull(values[index]);
+            else if(names[index] == "displayName")
+                group_name = names[index];
+        } catch(std::exception& ex) {
+            logWarning(this->server_id(), "Failed to parse group from database. Failed to parse column {} (value: {})", names[index], values[index]);
+            return 0;
+        }
+    }
+
+    if(!group_id || group_type == GroupType::GROUP_TYPE_UNKNOWN || group_target == GroupTarget::GROUPTARGET_UNKNOWN) {
+        logWarning(this->server_id(), "Failed to query group from database. Invalid values found.");
+        return 0;
+    }
+
+    auto database_target = (uint8_t) (group_target == GroupTarget::GROUPTARGET_SERVER ? DatabaseGroupTarget::SERVER : DatabaseGroupTarget::CHANNEL);
+    auto permissions = serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), group_id, database_target);
+
+    if(group_target == GroupTarget::GROUPTARGET_SERVER) {
+        auto group = std::make_shared<ServerGroup>(this->server_id(), group_id, group_type, group_name, permissions);
+
+        std::lock_guard lock{this->group_lock_};
+        this->server_groups_.push_back(group);
+    } else {
+        auto group = std::make_shared<ChannelGroup>(this->server_id(), group_id, group_type, group_name, permissions);
+
+        std::lock_guard lock{this->group_lock_};
+        this->channel_groups_.push_back(group);
+    }
+
+    return 0;
+}
+
+void GroupManager::ensure_valid_default_groups(bool initialize) {
+    assert(!this->channel_groups_.empty() && !this->server_groups_.empty());
+
+    auto fallback_server_group = this->server_groups_.front();
+    auto fallback_channel_group = this->channel_groups_.front();
+
+    auto properties = this->virtual_server_->server_properties();
+    /* default server music group */
+    {
+        auto group_id = properties->find(property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP).as_save<GroupId>();
+        auto group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, group_id);
+        if(!group) {
+            if(!initialize)
+                logWarning(this->server_id(), "Missing default music bot group ({}). Try to reinitialize from serverinstance_template_musicdefault_group.", group_id);
+
+            group_id = serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as<GroupId>();
+            group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, group_id);
+            if(!group) {
+                group = fallback_server_group;
+                logCritical(LOG_INSTANCE, "Failed to find default music bot group ({}). Using first available group ({}).", group_id, fallback_server_group->group_id());
+            }
+
+            auto group_name = group->display_name();
+            group = this->find_server_group_by_name(GroupCalculateMode::LOCAL, group_name);
+            if(!group) {
+                group = fallback_server_group;
+                logError(this->server_id(), "Missing default music bot group and could not find group from template name ({}). Using {} as fallback.", group_name, group);
+            }
+
+            properties->find(property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP) = group->group_id();
+        }
+    }
+
+    /* default server guest group */
+    {
+        auto group_id = properties->find(property::VIRTUALSERVER_DEFAULT_SERVER_GROUP).as_save<GroupId>();
+        auto group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, group_id);
+        if(!group) {
+            if(!initialize)
+                logWarning(this->server_id(), "Missing default server guest group ({}). Try to reinitialize from serverinstance_template_serverdefault_group.", group_id);
+
+            group_id = serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP].as<GroupId>();
+            group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, group_id);
+            if(!group) {
+                group = fallback_server_group;
+                logCritical(LOG_INSTANCE, "Failed to find default server guest group ({}). Using first available group ({}).", group_id, fallback_server_group->group_id());
+            }
+
+            auto group_name = group->display_name();
+            group = this->find_server_group_by_name(GroupCalculateMode::LOCAL, group_name);
+            if(!group) {
+                group = fallback_server_group;
+                logError(this->server_id(), "Missing default server guest group and could not find group from template name ({}). Using {} as fallback.", group_name, group);
+            }
+
+            properties->find(property::VIRTUALSERVER_DEFAULT_SERVER_GROUP) = group->group_id();
+        }
+    }
+
+    /* default channel admin group */
+    {
+        auto group_id = properties->find(property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP).as_save<GroupId>();
+        auto group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
+        if(!group) {
+            if(!initialize)
+                logWarning(this->server_id(), "Missing default channel admin group ({}). Try to reinitialize from serverinstance_template_channeladmin_group.", group_id);
+
+            group_id = serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP].as<GroupId>();
+            group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
+            if(!group) {
+                group = fallback_channel_group;
+                logCritical(LOG_INSTANCE, "Failed to find default channel admin group ({}). Using first available group ({}).", group_id, fallback_channel_group->group_id());
+            }
+
+            auto group_name = group->display_name();
+            group = this->find_channel_group_by_name(GroupCalculateMode::LOCAL, group_name);
+            if(!group) {
+                group = fallback_channel_group;
+                logError(this->server_id(), "Missing default channel admin group and could not find group from template name ({}). Using {} as fallback.", group_name, group);
+            }
+
+            properties->find(property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP) = group->group_id();
+        }
+    }
+
+    /* default channel group */
+    {
+        auto group_id = properties->find(property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP).as_save<GroupId>();
+        auto group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
+        if(!group) {
+            if(!initialize)
+                logWarning(this->server_id(), "Missing default channel guest group ({}). Try to reinitialize from serverinstance_template_channeldefault_group.", group_id);
+
+            group_id = serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP].as<GroupId>();
+            group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
+            if(!group) {
+                group = fallback_channel_group;
+                logCritical(LOG_INSTANCE, "Failed to find default channel guest group ({}). Using first available group ({}).", group_id, fallback_channel_group->group_id());
+            }
+
+            auto group_name = group->display_name();
+            group = this->find_channel_group_by_name(GroupCalculateMode::LOCAL, group_name);
+            if(!group) {
+                group = fallback_channel_group;
+                logError(this->server_id(), "Missing default channel guest group and could not find group from template name ({}). Using {} as fallback.", group_name, group);
+            }
+
+            properties->find(property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP) = group->group_id();
+        }
+    }
+}
+
+std::shared_ptr<ServerGroup> GroupManager::default_server_group(ClientType client_type, bool enforce_id) {
+    auto id =
+            client_type != ClientType::CLIENT_QUERY ?
+            this->virtual_server_->server_properties()->find(property::VIRTUALSERVER_DEFAULT_SERVER_GROUP).as_save<GroupId>() :
+            serverInstance->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as_save<GroupId>();
+    auto group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, id);
+    if(group || enforce_id) return group;
+
+    std::lock_guard glock{this->group_lock_};
+    return this->server_groups_.empty() ? nullptr : this->server_groups_.front();
+}
+
+std::shared_ptr<ChannelGroup> GroupManager::default_channel_group(ClientType client_type, bool enforce_id) {
+    auto id = this->virtual_server_->server_properties()->find(property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP).as_save<GroupId>();
+    auto group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, id);
+    if(group || enforce_id) return group;
+
+    std::lock_guard glock{this->group_lock_};
+    return this->channel_groups_.empty() ? nullptr : this->channel_groups_.front();
+}
+
+std::shared_ptr<ServerGroup> GroupManager::find_server_group(GroupCalculateMode mode, GroupId group_id) {
+    {
+        std::lock_guard glock{this->group_lock_};
+        auto it = std::find_if(this->server_groups_.begin(), this->server_groups_.end(), [&](const std::shared_ptr<ServerGroup>& group) {
+            return group->group_id() == group_id;
+        });
+
+        if(it != this->server_groups_.end())
+            return *it;
+    }
+    if(mode == GroupCalculateMode::GLOBAL && this->parent_manager_)
+        return this->parent_manager_->find_server_group(mode, group_id);
+    return nullptr;
+}
+
+std::shared_ptr<ServerGroup> GroupManager::find_server_group_by_name(GroupCalculateMode mode, const std::string &name) {
+    {
+        std::string lname{name};
+        std::transform(lname.begin(), lname.end(), lname.begin(), ::tolower);
+
+        std::lock_guard glock{this->group_lock_};
+        auto it = std::find_if(this->server_groups_.begin(), this->server_groups_.end(),
+                               [&](const std::shared_ptr<ServerGroup> &group) {
+                                   std::string lgroup_name{group->name_};
+                                   std::transform(lgroup_name.begin(), lgroup_name.end(), lgroup_name.begin(),
+                                                  ::tolower);
+                                   return lname == lgroup_name;
+                               });
+
+        if(it != this->server_groups_.end())
+            return *it;
+    }
+
+    if(mode == GroupCalculateMode::GLOBAL && this->parent_manager_)
+        return this->parent_manager_->find_server_group_by_name(mode, name);
+    return nullptr;
+}
+
+std::shared_ptr<ChannelGroup> GroupManager::find_channel_group(GroupCalculateMode mode, GroupId group_id) {
+    {
+        std::lock_guard glock{this->group_lock_};
+        auto it = std::find_if(this->channel_groups_.begin(), this->channel_groups_.end(), [&](const std::shared_ptr<ChannelGroup>& group) {
+            return group->group_id() == group_id;
+        });
+
+        if(it != this->channel_groups_.end())
+            return *it;
+    }
+
+    if(mode == GroupCalculateMode::GLOBAL && this->parent_manager_)
+        return this->parent_manager_->find_channel_group(mode, group_id);
+    return nullptr;
+}
+
+std::shared_ptr<ChannelGroup> GroupManager::find_channel_group_by_name(GroupCalculateMode mode, const std::string &name) {
+    {
+        std::string lname{name};
+        std::transform(lname.begin(), lname.end(), lname.begin(), ::tolower);
+
+        std::lock_guard glock{this->group_lock_};
+        auto it = std::find_if(this->channel_groups_.begin(), this->channel_groups_.end(),
+                               [&](const std::shared_ptr<ChannelGroup> &group) {
+                                   std::string lgroup_name{group->name_};
+                                   std::transform(lgroup_name.begin(), lgroup_name.end(), lgroup_name.begin(),
+                                                  ::tolower);
+                                   return lname == lgroup_name;
+                               });
+
+        if(it != this->channel_groups_.end())
+            return *it;
+    }
+
+    if(mode == GroupCalculateMode::GLOBAL && this->parent_manager_)
+        return this->parent_manager_->find_channel_group_by_name(mode, name);
+    return nullptr;
+}
+
+GroupCreateResult GroupManager::create_server_group(GroupType type, const std::string &name, std::shared_ptr<ServerGroup>& result) {
+    std::lock_guard manage_lock{this->group_manage_lock_};
+    auto self = this->weak_ref_.lock();
+    if(!self) return GroupCreateResult::DATABASE_ERROR;
+
+    if(this->find_server_group_by_name(GroupCalculateMode::LOCAL, name))
+        return GroupCreateResult::NAME_ALREADY_IN_USED;
+
+    auto group_id = this->generate_server_group_id();
+    if(!group_id)
+        return GroupCreateResult::FAILED_TO_GENERATE_ID;
+
+    auto res = sql::command(this->sql_manager(), "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)",
+                            variable{":sid", this->server_id()}, variable{":gid", group_id}, variable{":target", DatabaseGroupTarget::SERVER}, variable{":type", type}, variable{":name", name}).execute();
+
+    if(!res) {
+        LOG_SQL_CMD(res);
+        return GroupCreateResult::DATABASE_ERROR;
+    }
+
+    auto permissions = serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), group_id, (uint8_t) DatabaseGroupTarget::SERVER);
+    auto group = std::make_shared<ServerGroup>(self->server_id(), group_id, type, name, permissions);
+    {
+        std::lock_guard glock{this->group_lock_};
+        this->server_groups_.push_back(group);
+    }
+    result = group;
+    return GroupCreateResult::SUCCESS;
+}
+
+GroupCreateResult GroupManager::create_channel_group(GroupType type, const std::string &name, std::shared_ptr<ChannelGroup> &result) {
+    std::lock_guard manage_lock{this->group_manage_lock_};
+    auto self = this->weak_ref_.lock();
+    if(!self) return GroupCreateResult::DATABASE_ERROR;
+
+    if(this->find_channel_group_by_name(GroupCalculateMode::LOCAL, name))
+        return GroupCreateResult::NAME_ALREADY_IN_USED;
+
+    auto group_id = this->generate_channel_group_id();
+    if(!group_id)
+        return GroupCreateResult::FAILED_TO_GENERATE_ID;
+
+    auto res = sql::command(this->sql_manager(), "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)",
+                            variable{":sid", this->server_id()}, variable{":gid", group_id}, variable{":target", DatabaseGroupTarget::CHANNEL}, variable{":type", type}, variable{":name", name}).execute();
+
+    if(!res) {
+        LOG_SQL_CMD(res);
+        return GroupCreateResult::DATABASE_ERROR;
+    }
+
+    auto permissions = serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), group_id, (uint8_t) DatabaseGroupTarget::CHANNEL);
+    auto group = std::make_shared<ChannelGroup>(this->server_id(), group_id, type, name, permissions);
+    {
+        std::lock_guard glock{this->group_lock_};
+        this->channel_groups_.push_back(group);
+    }
+    result = group;
+    return GroupCreateResult::SUCCESS;
+}
+
+GroupCopyResult GroupManager::copy_server_group(GroupId source, GroupType target_type, const std::string &display_name, std::shared_ptr<ServerGroup>& result) {
+    std::lock_guard manage_lock{this->group_manage_lock_};
+    auto self = this->weak_ref_.lock();
+    if(!self) return GroupCopyResult::DATABASE_ERROR;
+
+    std::shared_ptr<ServerGroup> group{};
+    auto create_result = this->create_server_group(target_type, display_name, group);
+    switch(create_result) {
+        case GroupCreateResult::SUCCESS:
+            break;
+
+        case GroupCreateResult::DATABASE_ERROR:
+            return GroupCopyResult::DATABASE_ERROR;
+
+        case GroupCreateResult::NAME_ALREADY_IN_USED:
+            return GroupCopyResult::NAME_ALREADY_IN_USE;
+
+        case GroupCreateResult::FAILED_TO_GENERATE_ID:
+            return GroupCopyResult::FAILED_TO_GENERATE_ID;
+    }
+
+    assert(group);
+    return this->copy_server_group_permissions(source, group->group_id());
+}
+
+GroupCopyResult GroupManager::copy_channel_group(GroupId source, GroupType target_type, const std::string &display_name, std::shared_ptr<ChannelGroup> &result) {
+    std::lock_guard manage_lock{this->group_manage_lock_};
+    auto self = this->weak_ref_.lock();
+    if(!self) return GroupCopyResult::DATABASE_ERROR;
+
+    std::shared_ptr<ChannelGroup> group{};
+    auto create_result = this->create_channel_group(target_type, display_name, group);
+    switch(create_result) {
+        case GroupCreateResult::SUCCESS:
+            break;
+
+        case GroupCreateResult::DATABASE_ERROR:
+            return GroupCopyResult::DATABASE_ERROR;
+
+        case GroupCreateResult::NAME_ALREADY_IN_USED:
+            return GroupCopyResult::NAME_ALREADY_IN_USE;
+
+        case GroupCreateResult::FAILED_TO_GENERATE_ID:
+            return GroupCopyResult::FAILED_TO_GENERATE_ID;
+    }
+
+    assert(group);
+    return this->copy_channel_group_permissions(source, group->group_id());
+}
+
+GroupCopyResult GroupManager::copy_server_group_permissions(GroupId source, GroupId target) {
+    std::lock_guard manage_lock{this->group_manage_lock_};
+    auto self = this->weak_ref_.lock();
+    if(!self) return GroupCopyResult::DATABASE_ERROR;
+
+    auto source_group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, source);
+    if(!source_group)
+        return GroupCopyResult::UNKNOWN_SOURCE_GROUP;
+
+    auto target_group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, target);
+    if(!target_group)
+        return GroupCopyResult::UNKNOWN_TARGET_GROUP;
+
+    auto res = sql::command(this->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `id` = :id",
+            variable{":sid", this->server_id()}, variable{":type", ts::permission::SQL_PERM_GROUP}, variable{":id", target}).execute();
+    if(!res) {
+        LOG_SQL_CMD(res);
+        return GroupCopyResult::DATABASE_ERROR;
+    }
+
+    res = sql::command(this->sql_manager(), "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) SELECT :tsid AS `serverId`, `type`, :target AS `id`, 0 AS `channelId`, `permId`, `value`,`grant`,`flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :ssid AND `type` = :type AND `id` = :source",
+                       variable{":ssid", source_group->virtual_server_id()},
+                       variable{":tsid", target_group->virtual_server_id()},
+                       variable{":type", ts::permission::SQL_PERM_GROUP},
+                       variable{":source", source},
+                       variable{":target", target}).execute();
+    target_group->set_permissions(serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), target, (uint8_t) DatabaseGroupTarget::SERVER));
+    LOG_SQL_CMD(res);
+
+    return GroupCopyResult::SUCCESS;
+}
+
+GroupCopyResult GroupManager::copy_channel_group_permissions(GroupId source, GroupId target) {
+    std::lock_guard manage_lock{this->group_manage_lock_};
+    auto self = this->weak_ref_.lock();
+    if(!self) return GroupCopyResult::DATABASE_ERROR;
+
+    auto source_group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, source);
+    if(!source_group)
+        return GroupCopyResult::UNKNOWN_SOURCE_GROUP;
+
+    auto target_group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, target);
+    if(!target_group)
+        return GroupCopyResult::UNKNOWN_TARGET_GROUP;
+
+    auto res = sql::command(this->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `id` = :id",
+                            variable{":sid", this->server_id()}, variable{":type", ts::permission::SQL_PERM_GROUP}, variable{":id", target}).execute();
+    if(!res) {
+        LOG_SQL_CMD(res);
+        return GroupCopyResult::DATABASE_ERROR;
+    }
+
+    res = sql::command(this->sql_manager(), "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) SELECT :tsid AS `serverId`, `type`, :target AS `id`, 0 AS `channelId`, `permId`, `value`,`grant`,`flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :ssid AND `type` = :type AND `id` = :source",
+                       variable{":ssid", source_group->virtual_server_id()},
+                       variable{":tsid", target_group->virtual_server_id()},
+                       variable{":type", ts::permission::SQL_PERM_GROUP},
+                       variable{":source", source},
+                       variable{":target", target}).execute();
+    target_group->set_permissions(serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), target, (uint8_t) DatabaseGroupTarget::CHANNEL));
+    LOG_SQL_CMD(res);
+
+    return GroupCopyResult::SUCCESS;
+}
+
+
+GroupRenameResult GroupManager::rename_server_group(GroupId group_id, const std::string &name) {
+    std::lock_guard manage_lock{this->group_manage_lock_};
+    auto self = this->weak_ref_.lock();
+    if(!self) return GroupRenameResult::DATABASE_ERROR;
+
+    if(name.empty() || name.length() > 40)
+        return GroupRenameResult::NAME_INVALID;
+
+    auto group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, group_id);
+    if(!group) return GroupRenameResult::INVALID_GROUP_ID;
+
+    if(this->find_server_group_by_name(GroupCalculateMode::LOCAL, name))
+        return GroupRenameResult::NAME_ALREADY_USED;
+
+    sql::command(this->sql_manager(), "UPDATE `groups` SET `displayName` = :name WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target",
+            variable{":server", this->server_id()},
+            variable{":target", DatabaseGroupTarget::SERVER},
+            variable{":group_id", group_id}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future failed"});
+    group->name_ = name;
+    return GroupRenameResult::SUCCESS;
+}
+
+GroupRenameResult GroupManager::rename_channel_group(GroupId group_id, const std::string &name) {
+    std::lock_guard manage_lock{this->group_manage_lock_};
+    auto self = this->weak_ref_.lock();
+    if(!self) return GroupRenameResult::DATABASE_ERROR;
+
+    if(name.empty() || name.length() > 40)
+        return GroupRenameResult::NAME_INVALID;
+
+    auto group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
+    if(!group) return GroupRenameResult::INVALID_GROUP_ID;
+
+    if(this->find_channel_group_by_name(GroupCalculateMode::LOCAL, name))
+        return GroupRenameResult::NAME_ALREADY_USED;
+
+    sql::command(this->sql_manager(), "UPDATE `groups` SET `displayName` = :name WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target",
+                 variable{":server", this->server_id()},
+                 variable{":target", DatabaseGroupTarget::CHANNEL},
+                 variable{":group_id", group_id}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future failed"});
+    group->name_ = name;
+    return GroupRenameResult::SUCCESS;
+}
+
+GroupDeleteResult GroupManager::delete_server_group(GroupId group_id) {
+    std::lock_guard manage_lock{this->group_manage_lock_};
+    auto self = this->weak_ref_.lock();
+    if(!self) return GroupDeleteResult::DATABASE_ERROR;
+
+    {
+        std::lock_guard glock{this->group_lock_};
+        auto it = std::find_if(this->server_groups_.begin(), this->server_groups_.begin(), [&](const std::shared_ptr<ServerGroup>& group) {
+            return group->group_id() == group_id;
+        });
+        if(it == this->server_groups_.end())
+            return GroupDeleteResult::INVALID_GROUP_ID;
+        this->server_groups_.erase(it);
+    }
+
+    sql::command(this->sql_manager(), "DELETE FROM WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target",
+                 variable{":server", this->server_id()},
+                 variable{":target", DatabaseGroupTarget::SERVER},
+                 variable{":group_id", group_id}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future failed"});
+    return GroupDeleteResult::SUCCESS;
+}
+
+GroupDeleteResult GroupManager::delete_channel_group(GroupId group_id) {
+    std::lock_guard manage_lock{this->group_manage_lock_};
+    auto self = this->weak_ref_.lock();
+    if(!self) return GroupDeleteResult::DATABASE_ERROR;
+
+    {
+        std::lock_guard glock{this->group_lock_};
+        auto it = std::find_if(this->channel_groups_.begin(), this->channel_groups_.begin(), [&](const std::shared_ptr<ChannelGroup>& group) {
+            return group->group_id() == group_id;
+        });
+        if(it == this->channel_groups_.end())
+            return GroupDeleteResult::INVALID_GROUP_ID;
+        this->channel_groups_.erase(it);
+    }
+
+    sql::command(this->sql_manager(), "DELETE FROM WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target",
+                 variable{":server", this->server_id()},
+                 variable{":target", DatabaseGroupTarget::CHANNEL},
+                 variable{":group_id", group_id}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future failed"});
+    return GroupDeleteResult::SUCCESS;
+}
+
+ts::GroupId GroupManager::generate_server_group_id() {
+    std::lock_guard glock{this->group_lock_};
+    ts::GroupId highest_group_id{0};
+    for(auto& group : this->channel_groups_)
+        highest_group_id = std::max(group->group_id(), highest_group_id);
+
+    for(auto& group : this->server_groups_)
+        highest_group_id = std::max(group->group_id(), highest_group_id);
+
+    if(this->parent_manager_)
+        highest_group_id = std::max(this->parent_manager_->generate_server_group_id(), highest_group_id);
+    return highest_group_id + 1;
+}
+
+ts::GroupId GroupManager::generate_channel_group_id() {
+    return this->generate_server_group_id();
+}
\ No newline at end of file
diff --git a/server/src/groups/GroupManager.h b/server/src/groups/GroupManager.h
index 5471f43..cfaf8ba 100644
--- a/server/src/groups/GroupManager.h
+++ b/server/src/groups/GroupManager.h
@@ -20,63 +20,114 @@ namespace ts::server {
             GLOBAL /* use the parent group manager as well, if existing */
         };
 
-        enum struct ActionReturnCode {
-            RENAME_NAME_ALREADY_USED,
-            RENAME_GROUP_INVALID_ID,
+        enum struct GroupLoadResult {
+            SUCCESS,
+            NO_GROUPS,
+            DATABASE_ERROR
+        };
 
-            DELETE_GROUP_INVALID_ID,
+        enum struct GroupCreateResult {
+            SUCCESS,
+            NAME_ALREADY_IN_USED,
+            FAILED_TO_GENERATE_ID,
+            DATABASE_ERROR
+        };
 
-            CUSTOM_ERROR
+        enum struct GroupCopyResult {
+            SUCCESS,
+            UNKNOWN_SOURCE_GROUP,
+            UNKNOWN_TARGET_GROUP,
+            NAME_ALREADY_IN_USE,
+            FAILED_TO_GENERATE_ID,
+            DATABASE_ERROR
+        };
+
+        enum struct GroupRenameResult {
+            SUCCESS,
+            INVALID_GROUP_ID,
+            NAME_ALREADY_USED,
+            NAME_INVALID,
+            DATABASE_ERROR
+        };
+
+        enum struct GroupDeleteResult {
+            SUCCESS,
+            INVALID_GROUP_ID,
+            DATABASE_ERROR
         };
 
         class GroupManager {
                 friend class Group;
             public:
+                enum struct DatabaseGroupTarget {
+                    SERVER,
+                    CHANNEL
+                };
+
                 GroupManager(vserver::VirtualServerBase* /* virtual server */, std::shared_ptr<GroupManager> /* parent */);
                 ~GroupManager();
 
-                bool initialize(std::string& /* error */);
-                bool load_data(std::string& /* error */);
+                bool initialize(const std::shared_ptr<GroupManager>& /* self ref, */, std::string& /* error */);
+                GroupLoadResult load_data(bool /* initialize */ = false);
                 void unload_data();
+                void reset_groups(bool /* cleanup database */);
 
                 [[nodiscard]] inline vserver::VirtualServerBase* virtual_server() { return this->virtual_server_; }
 
                 [[nodiscard]] inline const std::shared_ptr<GroupManager>& parent_manager() { return this->parent_manager_; }
                 [[nodiscard]] inline GroupAssignmentManager& assignments() { return this->assignment_manager_; }
 
-                [[nodiscard]] std::deque<std::shared_ptr<ServerGroup>> server_groups(GroupCalculateMode /* mode */);
-                [[nodiscard]] std::deque<std::shared_ptr<ChannelGroup>> channel_groups(GroupCalculateMode /* mode */);
+                [[nodiscard]] std::vector<std::shared_ptr<ServerGroup>> server_groups(GroupCalculateMode /* mode */);
+                [[nodiscard]] std::vector<std::shared_ptr<ChannelGroup>> channel_groups(GroupCalculateMode /* mode */);
 
-                [[nodiscard]] std::shared_ptr<ServerGroup> default_server_group(ClientType /* client type */);
-                [[nodiscard]] std::shared_ptr<ChannelGroup> default_channel_group(ClientType /* client type */);
+                [[nodiscard]] std::shared_ptr<ServerGroup> default_server_group(ClientType /* client type */, bool /* enforce id */ = false);
+                [[nodiscard]] std::shared_ptr<ChannelGroup> default_channel_group(ClientType /* client type */, bool /* enforce id */ = false);
 
-                [[nodiscard]] std::shared_ptr<ServerGroup> find_server_group(GroupId /* group id */);
-                [[nodiscard]] std::shared_ptr<ChannelGroup> find_channel_group(GroupId /* group id */);
+                [[nodiscard]] std::shared_ptr<ServerGroup> find_server_group(GroupCalculateMode /* mode */, GroupId /* group id */);
+                [[nodiscard]] std::shared_ptr<ChannelGroup> find_channel_group(GroupCalculateMode /* mode */, GroupId /* group id */);
 
-                [[nodiscard]] std::shared_ptr<ServerGroup> create_server_group(GroupType type, const std::string& /* group name */);
-                [[nodiscard]] std::shared_ptr<ServerGroup> create_channel_group(GroupType type, const std::string& /* group name */);
+                [[nodiscard]] std::shared_ptr<ServerGroup> find_server_group_by_name(GroupCalculateMode /* mode */, const std::string& /* group name */);
+                [[nodiscard]] std::shared_ptr<ChannelGroup> find_channel_group_by_name(GroupCalculateMode /* mode */, const std::string& /* group name */);
 
-                [[nodiscard]] ActionReturnCode copy_server_group(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */);
-                [[nodiscard]] ActionReturnCode copy_channel_group(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */);
+                [[nodiscard]] GroupCreateResult create_server_group(GroupType type, const std::string& /* group name */, std::shared_ptr<ServerGroup>& /* result */);
+                [[nodiscard]] GroupCreateResult create_channel_group(GroupType type, const std::string& /* group name */, std::shared_ptr<ChannelGroup>& /* result */);
 
-                [[nodiscard]] ActionReturnCode copy_server_group_permissions(GroupId /* group id */, GroupId /* target group */);
-                [[nodiscard]] ActionReturnCode copy_channel_group_permissions(GroupId /* group id */, GroupId /* target group */);
+                [[nodiscard]] GroupCopyResult copy_server_group(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */, std::shared_ptr<ServerGroup>& /* result */);
+                [[nodiscard]] GroupCopyResult copy_channel_group(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */, std::shared_ptr<ChannelGroup>& /* result */);
 
-                [[nodiscard]] ActionReturnCode rename_server_group(GroupId /* group id */, const std::string& /* target group name */);
-                [[nodiscard]] ActionReturnCode rename_channel_group(GroupId /* group id */, const std::string& /* target group name */);
+                [[nodiscard]] GroupCopyResult copy_server_group_permissions(GroupId /* group id */, GroupId /* target group */);
+                [[nodiscard]] GroupCopyResult copy_channel_group_permissions(GroupId /* group id */, GroupId /* target group */);
 
-                [[nodiscard]] ActionReturnCode delete_server_group(GroupId /* group id */);
-                [[nodiscard]] ActionReturnCode delete_channel_group(GroupId /* group id */);
+                [[nodiscard]] GroupRenameResult rename_server_group(GroupId /* group id */, const std::string& /* target group name */);
+                [[nodiscard]] GroupRenameResult rename_channel_group(GroupId /* group id */, const std::string& /* target group name */);
+
+                [[nodiscard]] GroupDeleteResult delete_server_group(GroupId /* group id */);
+                [[nodiscard]] GroupDeleteResult delete_channel_group(GroupId /* group id */);
             private:
                 vserver::VirtualServerBase* virtual_server_;
 
+                std::weak_ptr<GroupManager> weak_ref_;
                 std::shared_ptr<GroupManager> parent_manager_;
                 GroupAssignmentManager assignment_manager_;
 
+                /* recursive_mutex due to the copy server/channel group methods */
+                std::recursive_mutex group_manage_lock_{};
                 std::mutex group_lock_{};
 
+                /* I think std::vector is better here because we will iterate more often than add groups */
+                std::vector<std::shared_ptr<ServerGroup>> server_groups_{};
+                std::vector<std::shared_ptr<ChannelGroup>> channel_groups_{};
+
                 [[nodiscard]] sql::SqlManager* sql_manager();
-                void update_group_name(Group* /* group */);
+                [[nodiscard]] ServerId server_id();
+
+                void ensure_valid_default_groups(bool /* initialize */);
+
+                int insert_group_from_sql(int /* length */, std::string* /* values */, std::string* /* columns */);
+                [[nodiscard]] GroupId generate_server_group_id();
+                [[nodiscard]] GroupId generate_channel_group_id();
         };
     }
-}
\ No newline at end of file
+}
+
+DEFINE_TRANSFORMS(ts::server::groups::GroupManager::DatabaseGroupTarget, uint8_t);
\ No newline at end of file
diff --git a/server/src/services/ClientChannelService.cpp b/server/src/services/ClientChannelService.cpp
index 31eb8a7..aff84ae 100644
--- a/server/src/services/ClientChannelService.cpp
+++ b/server/src/services/ClientChannelService.cpp
@@ -19,6 +19,7 @@ ts::ServerId ClientChannelService::get_server_id() const {
 }
 
 ChannelGroupInheritance ClientChannelService::calculate_channel_group(ChannelId channel_id, ClientDbId client_dbid, ClientType client_type) {
+    auto server_state_lock = this->virtual_server_->lock_server_status();
     auto tree_lock = this->virtual_server_->lock_channel_clients();
     tree_lock.auto_lock_shared();
 
@@ -67,6 +68,7 @@ ClientMoveResult ClientChannelService::client_ban(const std::shared_ptr<Connecte
     if(auto err = tree_lock.auto_lock_exclusive(); err)
         return ClientMoveResult::TREE_LOCK_FAILED;
 
+    auto server_state_lock = this->virtual_server_->lock_server_status();
     std::unique_lock client_chan_tree_lock{target->get_channel_lock()};
     auto old_channel = target->getChannel();
     target->setChannel(nullptr);
@@ -105,6 +107,7 @@ ClientMoveResult ClientChannelService::client_kick(const std::shared_ptr<Connect
         if(auto err = tree_lock.auto_lock_exclusive(); err)
             return ClientMoveResult::TREE_LOCK_FAILED;
 
+        auto server_state_lock = this->virtual_server_->lock_server_status();
         std::unique_lock client_chan_tree_lock{target->get_channel_lock()};
         auto old_channel = target->getChannel();
         target->setChannel(nullptr);
@@ -147,6 +150,8 @@ ClientMoveResult ClientChannelService::client_move(const std::shared_ptr<Connect
     if(auto err = tree_lock.auto_lock_exclusive(); err)
         return ClientMoveResult::TREE_LOCK_FAILED;
 
+    auto server_state_lock = this->virtual_server_->lock_server_status();
+
     TIMING_STEP(timings, "tree lock setup");
     if(target->getChannel() == s_target_channel)
         return ClientMoveResult::SUCCESS;
diff --git a/server/src/services/PermissionsService.cpp b/server/src/services/PermissionsService.cpp
index 1095efe..575e232 100644
--- a/server/src/services/PermissionsService.cpp
+++ b/server/src/services/PermissionsService.cpp
@@ -6,9 +6,11 @@
 #include "./PermissionsService.h"
 #include "../InstanceHandler.h"
 #include "../vserver/VirtualServerBase.h"
+#include "../vserver/VirtualServer.h"
 #include "../groups/Group.h"
 #include "../groups/GroupManager.h"
 #include "../groups/GroupAssignmentManager.h"
+#include "../client/ConnectedClient.h"
 
 using namespace ts::permission;
 using namespace ts::permission::v2;
@@ -274,7 +276,7 @@ void PermissionService::load_group_assignments(const std::shared_ptr<CalculateCa
     cache->assignment_server_groups.reserve(assignments.size());
 
     for(auto& entry : assignments) {
-        auto group = group_manager->find_server_group(entry);
+        auto group = group_manager->find_server_group(groups::GroupCalculateMode::GLOBAL, entry);
         if(!group) continue;
 
         cache->assignment_server_groups.push_back(group);
@@ -304,7 +306,7 @@ void PermissionService::load_channel_assignment(const std::shared_ptr<CalculateC
         group_id = assignment.group_id;
     }
 
-    cache->assignment_channel_group = group_manager->find_channel_group(group_id);
+    cache->assignment_channel_group = group_manager->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
     if(!cache->assignment_channel_group)
         cache->assignment_channel_group = group_manager->default_channel_group(cache->client_type);
 }
@@ -316,4 +318,55 @@ void PermissionService::load_server_channel(const std::shared_ptr<CalculateCache
     auto channel_tree = this->virtual_server_->server_channel_tree();
     auto tree_lock = this->virtual_server_->lock_channel_clients();
     cache->server_channel = channel_tree->findChannel(channel_id);
+}
+
+PermissionResetResult PermissionService::reset_server_permissions() {
+    //TODO: Delete all tokens?
+
+    LOG_SQL_CMD(sql::command(this->virtual_server_->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :serverId", variable{":serverId", this->get_server_id()}).execute());
+    LOG_SQL_CMD(sql::command(this->virtual_server_->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :serverId", variable{":serverId", this->get_server_id()}).execute());
+    LOG_SQL_CMD(sql::command(this->virtual_server_->sql_manager(), "DELETE FROM `groups` WHERE `serverId` = :serverId", variable{":serverId", this->get_server_id()}).execute());
+
+    auto group_manager = this->virtual_server_->group_manager();
+    group_manager->reset_groups(false);
+
+    auto properties = this->virtual_server_->server_properties();
+    properties->find(property::VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY) = "";
+    properties->find(property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY) = true;
+    if(auto vs = dynamic_cast<vserver::VirtualServer*>(this->virtual_server_); vs){
+        auto default_admin_group_id = serverInstance->properties().find(property::SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP).as_save<GroupId>();
+        auto group = this->virtual_server_->group_manager()->find_server_group(groups::GroupCalculateMode::GLOBAL, default_admin_group_id);
+        if(!group) {
+            logCritical(LOG_INSTANCE, "Missing default template server admin group ({}). We're not generating an admin token.", default_admin_group_id);
+        } else {
+            auto group_name = group->display_name();
+            group = this->virtual_server_->group_manager()->find_server_group_by_name(groups::GroupCalculateMode::LOCAL, group_name);
+            if(!group) {
+                logError(this->get_server_id(), "Could not find server admin group from template name ({}). We're not generating an admin token.", group_name, group);
+            } else {
+                auto token = vs->token_manager().createToken(server::tokens::TOKEN_SERVER, group->group_id(), "Default server token for the server admin.");
+                if(!token) {
+                    logError(this->get_server_id(), "Failed to generate default server admin token.");
+                } else {
+                    properties->find(property::VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY) = token->token;
+                    properties->find(property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY) = true;
+                }
+            }
+        }
+    }
+
+    for(const auto& client : this->virtual_server_->connected_clients(false)) {
+        if(client->getType() != ClientType::CLIENT_QUERY) {
+            client->notifyServerGroupList();
+            client->notifyChannelGroupList();
+        }
+
+        if(this->notifyClientPropertyUpdates(client, group_manager->assignments().update_client_group_properties(client, client->getChannelId()))) {
+            if(client->update_cached_permissions()) /* update cached calculated permissions */
+                client->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
+        }
+        client->updateChannelClientProperties(true, true);
+    }
+
+    return PermissionResetResult::SUCCESS;
 }
\ No newline at end of file
diff --git a/server/src/services/PermissionsService.h b/server/src/services/PermissionsService.h
index 3270849..0b17b5a 100644
--- a/server/src/services/PermissionsService.h
+++ b/server/src/services/PermissionsService.h
@@ -29,6 +29,10 @@ namespace ts::server::permissions {
         ChannelId last_server_channel{0};
     };
 
+    enum struct PermissionResetResult {
+        SUCCESS
+    };
+
     class PermissionService {
         public:
             explicit PermissionService(vserver::VirtualServerBase*);
@@ -39,6 +43,8 @@ namespace ts::server::permissions {
             bool load_data(std::string& /* error */);
             void unload_data();
 
+            PermissionResetResult reset_server_permissions();
+
             permission::v2::PermissionFlaggedValue calculate_client_permission(
                     permission::PermissionType,
                     ClientDbId,
diff --git a/server/src/services/VirtualServerInformation.cpp b/server/src/services/VirtualServerInformation.cpp
new file mode 100644
index 0000000..77ebf67
--- /dev/null
+++ b/server/src/services/VirtualServerInformation.cpp
@@ -0,0 +1,15 @@
+//
+// Created by WolverinDEV on 04/03/2020.
+//
+
+#include "./VirtualServerInformation.h"
+#include "../vserver/VirtualServerBase.h"
+
+using namespace ts::server::vserver;
+
+InformationService::InformationService(ts::server::vserver::VirtualServerBase *handle) : virtual_server_{handle} {}
+InformationService::~InformationService() {}
+
+float InformationService::averagePing() {
+
+}
\ No newline at end of file
diff --git a/server/src/services/VirtualServerInformation.h b/server/src/services/VirtualServerInformation.h
new file mode 100644
index 0000000..5699c18
--- /dev/null
+++ b/server/src/services/VirtualServerInformation.h
@@ -0,0 +1,18 @@
+#pragma once
+
+namespace ts::server::vserver {
+    class VirtualServerBase;
+
+    class InformationService {
+        public:
+            explicit InformationService(VirtualServerBase*);
+            ~InformationService();
+
+            [[nodiscard]] float averagePing();
+            [[nodiscard]] float averagePacketLoss();
+
+            [[nodiscard]] bool could_default_create_channel();
+        private:
+            VirtualServerBase* virtual_server_;
+    };
+}
\ No newline at end of file
diff --git a/server/src/vserver/VirtualServerBase.cpp b/server/src/vserver/VirtualServerBase.cpp
index 33bffe5..53bdd89 100644
--- a/server/src/vserver/VirtualServerBase.cpp
+++ b/server/src/vserver/VirtualServerBase.cpp
@@ -3,6 +3,9 @@
 //
 
 #include "./VirtualServerBase.h"
+#include "./VirtualServerBase.h"
+#include "../client/ConnectedClient.h"
+#include "VirtualServerBase.h"
 #include <log/LogUtils.h>
 
 using namespace ts::server::vserver;
@@ -75,4 +78,137 @@ VirtualServerStopResult VirtualServerBase::stop_server() {
 
 VirtualServerStartResult VirtualServerBase::start_server_(ts::rwshared_lock<ts::rw_mutex> &slock, std::string &error) {
     //TODO: Load server permissions, etc
+}
+
+VirtualServerClientRegisterResult VirtualServerBase::register_client(const std::shared_ptr<ConnectedClient> &client) {
+    assert(client);
+
+    ts::rwshared_lock status_lock{this->status_mutex_};
+    if(this->server_status() != VirtualServerStatus::VIRTUAL && this->server_status() != VirtualServerStatus::RUNNING)
+    return VirtualServerClientRegisterResult::SERVER_NOT_ONLINE;
+
+    {
+        std::lock_guard client_lock{this->registered_clients.mutex};
+        if(client->getClientId() > 0) {
+            logCritical(this->server_id(), "Client {} ({}|{}) has been already registered somewhere!", client->getDisplayName(), client->getClientId(), client->getUid());
+            return VirtualServerClientRegisterResult::CLIENT_ALREADY_KNOWN;
+        }
+
+        ClientId client_id = 0;
+        ClientId max_client_id = this->registered_clients.clients.size();
+
+        while(client_id < max_client_id && this->registered_clients.clients[client_id])
+            client_id++;
+
+        if(client_id > 1024 * 8)
+            return VirtualServerClientRegisterResult::PROTOCOL_LIMIT_REACHED;
+
+        if(client_id == max_client_id)
+            this->registered_clients.clients.push_back(client);
+        else
+            this->registered_clients.clients[client_id] = client;
+        this->registered_clients.count++;
+        client->setClientId(client_id);
+    }
+
+    {
+        std::lock_guard nlock{this->client_nickname_lock_};
+
+        auto login_name = client->getDisplayName();
+        while(login_name.length() < 3)
+            login_name += ".";
+
+        if(client->getExternalType() == ClientType::CLIENT_TEAMSPEAK)
+            client->properties()[property::CLIENT_LOGIN_NAME] = login_name;
+
+        std::shared_ptr<ConnectedClient> found_client = nullptr;
+
+        auto client_name = login_name;
+        size_t counter = 0;
+
+        {
+            std::lock_guard clients_lock(this->registered_clients.mutex);
+            while(true) {
+                for(auto& _client : this->registered_clients.clients) {
+                    if(!_client) continue;
+
+                    if(_client->getDisplayName() == client_name && _client != client)
+                        goto increase_name;
+                }
+                goto nickname_valid;
+
+                increase_name:
+                client_name = login_name + std::to_string(++counter);
+            }
+        }
+        nickname_valid:
+        client->setDisplayName(client_name);
+    }
+
+    {
+        auto properties = this->server_properties();
+        if(client->getType() == ClientType::CLIENT_TEAMSPEAK || client->getType() == ClientType::CLIENT_WEB) {
+            properties->find(property::VIRTUALSERVER_CLIENT_CONNECTIONS) ++; //increase client connections
+            properties->find(property::VIRTUALSERVER_LAST_CLIENT_CONNECT) = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
+        } else if(client->getType() == ClientType::CLIENT_QUERY) {
+            properties->find(property::VIRTUALSERVER_LAST_QUERY_CONNECT) ++; //increase query connections
+            properties->find(property::VIRTUALSERVER_QUERY_CLIENT_CONNECTIONS) = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
+        }
+    }
+
+    return VirtualServerClientRegisterResult::SUCCESS;
+}
+
+void VirtualServerBase::unregister_client(const std::shared_ptr<ConnectedClient> &client) {
+    ts::rwshared_lock status_lock{this->status_mutex_, ts::rw_lock_defered};
+    {
+        auto execute_lock = client->lock_command_handling();
+        if(client->getChannel()) {
+            auto tree_lock = this->lock_channel_clients();
+            auto result = this->channel_service().client_move(client, nullptr, nullptr, "", ViewReasonId::VREASON_SERVER_LEFT, false, tree_lock);
+            if(result != channels::ClientMoveResult::SUCCESS)
+                logCritical(this->server_id(), "Failed to unregister client from channel tree on client unregister ({}).", (int) result);
+        }
+
+        status_lock.auto_lock_shared();
+
+        auto client_id = client->getClientId();
+        if(client_id == 0) return; /* client hasn't been registered */
+
+        {
+            std::lock_guard lock(this->registered_clients.mutex);
+            if(client_id >= this->registered_clients.clients.size()) {
+                logCritical(this->server_id(), "Client {} ({}|{}) has been registered, but client id exceed client id! Failed to unregister client.", client->getDisplayName(), client_id, client->getUid());
+            } else {
+                auto& client_container = this->registered_clients.clients[client_id];
+                if(client_container != client) {
+                    logCritical(this->server_id(), "Client {} ({}|{}) has been registered, but container hasn't client set! Failed to unregister client.", client->getDisplayName(), client_id, client->getUid());
+                } else {
+                    client_container.reset();
+                    this->registered_clients.count--;
+                }
+            }
+        }
+    }
+
+    {
+        auto properties = this->server_properties();
+        if(client->getType() == ClientType::CLIENT_TEAMSPEAK || client->getType() == ClientType::CLIENT_WEB) {
+            properties->find(property::VIRTUALSERVER_LAST_CLIENT_DISCONNECT) = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
+        } else if(client->getType() == ClientType::CLIENT_QUERY) {
+            properties->find(property::VIRTUALSERVER_LAST_QUERY_DISCONNECT) = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
+        }
+    }
+
+    {
+        if(!chan_tree_lock.owns_lock())
+            chan_tree_lock.lock();
+
+        if(cl->currentChannel) //We dont have to make him invisible if he hasnt even a channel
+            this->client_move(cl, nullptr, nullptr, reason, ViewReasonId::VREASON_SERVER_LEFT, false, chan_tree_lock);
+    }
+
+    serverInstance->databaseHelper()->saveClientPermissions(this->ref(), cl->getClientDatabaseId(), cl->clientPermissions);
+    cl->setClientId(0);
+    return true;
 }
\ No newline at end of file
diff --git a/server/src/vserver/VirtualServerBase.h b/server/src/vserver/VirtualServerBase.h
index 5efa564..19b3847 100644
--- a/server/src/vserver/VirtualServerBase.h
+++ b/server/src/vserver/VirtualServerBase.h
@@ -57,6 +57,19 @@ namespace ts::server::vserver {
         STATE_LOCK_FAILED
     };
 
+    enum struct VirtualServerClientRegisterResult {
+        SUCCESS,
+        PROTOCOL_LIMIT_REACHED,
+        CLIENT_ALREADY_KNOWN,
+        SERVER_NOT_ONLINE
+    };
+
+#define var_access_requires_server_loaded(variable)         \
+do {                                                        \
+    assert(this->status_ != VirtualServerStatus::STOPPED);  \
+    return variable;                                        \
+} while(0);
+
     class VirtualServerBase {
         public:
             //TODO: Constructor
@@ -85,15 +98,15 @@ namespace ts::server::vserver {
             [[nodiscard]] inline ts::rwshared_lock<ts::rw_mutex> lock_channel_clients() { return ts::rwshared_lock{this->channel_clients_lock_, ts::rw_lock_shared}; }
             [[nodiscard]] inline std::mutex& client_nickname_lock() { return this->client_nickname_lock_; }
 
-            [[nodiscard]] inline const std::shared_ptr<Properties>& server_properties() { return this->server_properties_; }
-            [[nodiscard]] inline const std::shared_ptr<ServerChannelTree>& server_channel_tree() { return this->server_channel_tree_; }
+            [[nodiscard]] inline const std::shared_ptr<Properties>& server_properties() { var_access_requires_server_loaded(this->server_properties_); }
+            [[nodiscard]] inline const std::shared_ptr<ServerChannelTree>& server_channel_tree() { var_access_requires_server_loaded(this->server_channel_tree_); }
 
-            [[nodiscard]] inline const std::shared_ptr<groups::GroupManager>& group_manager() { return this->group_manager_; }
+            [[nodiscard]] inline const std::shared_ptr<groups::GroupManager>& group_manager() { var_access_requires_server_loaded(this->group_manager_); }
 
             /* client management */
             /* this may block until the current command of the client has been finished executed */
             void unregister_client(const std::shared_ptr<ConnectedClient>& /* client */);
-            void register_client(const std::shared_ptr<ConnectedClient>& /* client */);
+            VirtualServerClientRegisterResult register_client(const std::shared_ptr<ConnectedClient>& /* client */);
 
             /* ATTENTION: lock_channel_clients should be acquired for the following methods. Else the client might be disconnected while working with him.   */
             [[nodiscard]] std::vector<std::shared_ptr<ConnectedClient>> connected_clients(bool /* must be registered within the channel tree */);
diff --git a/server/src/vserver/VirtualServerManager.cpp b/server/src/vserver/VirtualServerManager.cpp
new file mode 100644
index 0000000..21a4f1c
--- /dev/null
+++ b/server/src/vserver/VirtualServerManager.cpp
@@ -0,0 +1,5 @@
+//
+// Created by WolverinDEV on 04/03/2020.
+//
+
+#include "VirtualServerManager.h"
diff --git a/server/src/vserver/VirtualServerManager.h b/server/src/vserver/VirtualServerManager.h
new file mode 100644
index 0000000..16f483a
--- /dev/null
+++ b/server/src/vserver/VirtualServerManager.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <string>
+
+namespace ts::server::vserver {
+    enum struct VirtualServerStartResult {
+
+    };
+
+    class VirtualServerManager {
+        public:
+            explicit VirtualServerManager();
+            ~VirtualServerManager();
+
+            bool initialize(std::string& /* error */);
+
+
+
+        private:
+    };
+}
\ No newline at end of file
diff --git a/shared b/shared
index ae4751e..0d0d7dd 160000
--- a/shared
+++ b/shared
@@ -1 +1 @@
-Subproject commit ae4751ee6fcb857771ff5f4e57459a75638e2b1f
+Subproject commit 0d0d7dd1924ee93bcbe4875c3cf007117d05e4d7