Added new files
This commit is contained in:
parent
be4a99dcd7
commit
3a5c152b3c
@ -1,23 +1,23 @@
|
||||
//
|
||||
// Created by WolverinDEV on 03/03/2020.
|
||||
//
|
||||
|
||||
#include "Group.h"
|
||||
#include "./GroupManager.h"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void Group::set_permissions(const std::shared_ptr<permission::v2::PermissionRegister> &permissions) {
|
||||
assert(permissions);
|
||||
this->permissions_ = permissions;
|
||||
//
|
||||
// Created by WolverinDEV on 03/03/2020.
|
||||
//
|
||||
|
||||
#include "Group.h"
|
||||
#include "./GroupManager.h"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void Group::set_permissions(const std::shared_ptr<permission::v2::PermissionRegister> &permissions) {
|
||||
assert(permissions);
|
||||
this->permissions_ = permissions;
|
||||
}
|
@ -1,75 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <PermissionRegister.h>
|
||||
|
||||
namespace ts::server::groups {
|
||||
enum GroupType {
|
||||
GROUP_TYPE_QUERY,
|
||||
GROUP_TYPE_TEMPLATE,
|
||||
GROUP_TYPE_NORMAL
|
||||
};
|
||||
|
||||
enum GroupNameMode {
|
||||
GROUP_NAME_MODE_HIDDEN,
|
||||
GROUP_NAME_MODE_BEFORE,
|
||||
GROUP_NAME_MODE_BEHIND
|
||||
};
|
||||
|
||||
typedef uint32_t GroupSortId;
|
||||
typedef uint32_t GroupIconId;
|
||||
|
||||
class GroupManager;
|
||||
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() = default;
|
||||
|
||||
/* information getters */
|
||||
[[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_; }
|
||||
[[nodiscard]] inline std::shared_ptr<permission::v2::PermissionRegister> permissions() { return this->permissions_; }
|
||||
|
||||
[[nodiscard]] inline bool save_assignments() const {
|
||||
assert(this->permissions_);
|
||||
auto value = this->permissions_->permission_value_flagged(permission::b_group_is_permanent);
|
||||
return value.has_value ? permission::v2::permission_granted(1, value) : true;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline GroupNameMode name_mode() const {
|
||||
assert(this->permissions_);
|
||||
auto value = this->permissions_->permission_value_flagged(permission::i_group_show_name_in_tree);
|
||||
return value.has_value ? (GroupNameMode) value.value : GroupNameMode::GROUP_NAME_MODE_HIDDEN;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline GroupSortId sort_id() const {
|
||||
assert(this->permissions_);
|
||||
auto value = this->permissions_->permission_value_flagged(permission::i_group_sort_id);
|
||||
return value.has_value ? value.value : 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline GroupIconId icon_id() const {
|
||||
assert(this->permissions_);
|
||||
auto value = this->permissions_->permission_value_flagged(permission::i_icon_id);
|
||||
return value.has_value ? value.value : 0;
|
||||
}
|
||||
|
||||
/* information setters */
|
||||
void set_display_name(const std::string& name);
|
||||
private:
|
||||
std::weak_ptr<GroupManager> manager_;
|
||||
|
||||
const GroupId group_id_;
|
||||
const GroupType type_;
|
||||
std::string name_;
|
||||
|
||||
std::shared_ptr<permission::v2::PermissionRegister> permissions_;
|
||||
|
||||
void set_permissions(const std::shared_ptr<permission::v2::PermissionRegister>& /* permissions */);
|
||||
};
|
||||
|
||||
class ServerGroup : public Group { };
|
||||
class ChannelGroup : public Group { };
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <PermissionRegister.h>
|
||||
|
||||
namespace ts::server::groups {
|
||||
enum GroupType {
|
||||
GROUP_TYPE_QUERY,
|
||||
GROUP_TYPE_TEMPLATE,
|
||||
GROUP_TYPE_NORMAL
|
||||
};
|
||||
|
||||
enum GroupNameMode {
|
||||
GROUP_NAME_MODE_HIDDEN,
|
||||
GROUP_NAME_MODE_BEFORE,
|
||||
GROUP_NAME_MODE_BEHIND
|
||||
};
|
||||
|
||||
typedef uint32_t GroupSortId;
|
||||
typedef uint32_t GroupIconId;
|
||||
|
||||
class GroupManager;
|
||||
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() = default;
|
||||
|
||||
/* information getters */
|
||||
[[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_; }
|
||||
[[nodiscard]] inline std::shared_ptr<permission::v2::PermissionRegister> permissions() { return this->permissions_; }
|
||||
|
||||
[[nodiscard]] inline bool save_assignments() const {
|
||||
assert(this->permissions_);
|
||||
auto value = this->permissions_->permission_value_flagged(permission::b_group_is_permanent);
|
||||
return value.has_value ? permission::v2::permission_granted(1, value) : true;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline GroupNameMode name_mode() const {
|
||||
assert(this->permissions_);
|
||||
auto value = this->permissions_->permission_value_flagged(permission::i_group_show_name_in_tree);
|
||||
return value.has_value ? (GroupNameMode) value.value : GroupNameMode::GROUP_NAME_MODE_HIDDEN;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline GroupSortId sort_id() const {
|
||||
assert(this->permissions_);
|
||||
auto value = this->permissions_->permission_value_flagged(permission::i_group_sort_id);
|
||||
return value.has_value ? value.value : 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline GroupIconId icon_id() const {
|
||||
assert(this->permissions_);
|
||||
auto value = this->permissions_->permission_value_flagged(permission::i_icon_id);
|
||||
return value.has_value ? value.value : 0;
|
||||
}
|
||||
|
||||
/* information setters */
|
||||
void set_display_name(const std::string& name);
|
||||
private:
|
||||
std::weak_ptr<GroupManager> manager_;
|
||||
|
||||
const GroupId group_id_;
|
||||
const GroupType type_;
|
||||
std::string name_;
|
||||
|
||||
std::shared_ptr<permission::v2::PermissionRegister> permissions_;
|
||||
|
||||
void set_permissions(const std::shared_ptr<permission::v2::PermissionRegister>& /* permissions */);
|
||||
};
|
||||
|
||||
class ServerGroup : public Group { };
|
||||
class ChannelGroup : public Group { };
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,110 +1,110 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <Definitions.h>
|
||||
#include <Properties.h>
|
||||
|
||||
namespace sql {
|
||||
class SqlManager;
|
||||
}
|
||||
|
||||
namespace ts::server {
|
||||
class ConnectedClient;
|
||||
namespace vserver {
|
||||
class VirtualServerBase;
|
||||
}
|
||||
|
||||
namespace groups {
|
||||
enum struct GroupAssignmentCalculateMode {
|
||||
LOCAL, /* only calculate clients groups for the local server */
|
||||
GLOBAL /* use the parent group manager as well, if existing */
|
||||
};
|
||||
|
||||
class ServerGroup;
|
||||
class ChannelGroup;
|
||||
class GroupManager;
|
||||
|
||||
struct ChannelGroupAssignment {
|
||||
ChannelGroupAssignment(ChannelId channel_id, GroupId group_id, bool t) : channel_id{channel_id}, group_id{group_id}, temporary_assignment{t} { }
|
||||
ChannelGroupAssignment(const ChannelGroupAssignment& other) = default;
|
||||
ChannelGroupAssignment(ChannelGroupAssignment&&) = default;
|
||||
|
||||
ChannelId channel_id;
|
||||
GroupId group_id;
|
||||
bool temporary_assignment;
|
||||
};
|
||||
|
||||
struct ServerGroupAssignment {
|
||||
explicit ServerGroupAssignment(GroupId group_id) : group_id{group_id} { }
|
||||
ServerGroupAssignment(const ServerGroupAssignment& other) = default;
|
||||
ServerGroupAssignment(ServerGroupAssignment&&) = default;
|
||||
|
||||
GroupId group_id;
|
||||
};
|
||||
|
||||
enum struct GroupAssignmentResult {
|
||||
SUCCESS,
|
||||
ADD_ALREADY_MEMBER_OF_GROUP,
|
||||
REMOVE_NOT_MEMBER_OF_GROUP,
|
||||
SET_ALREADY_MEMBER_OF_GROUP
|
||||
};
|
||||
|
||||
class GroupAssignmentManager {
|
||||
constexpr static bool kCacheAllClients{true};
|
||||
public:
|
||||
explicit GroupAssignmentManager(GroupManager* /* manager */);
|
||||
~GroupAssignmentManager();
|
||||
|
||||
/* general load/initialize methods */
|
||||
bool initialize(std::string& /* error */);
|
||||
bool load_data(std::string& /* error */);
|
||||
void unload_data();
|
||||
|
||||
/* client specific cache methods */
|
||||
void enable_cache_for_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */);
|
||||
void disable_cache_for_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */);
|
||||
|
||||
/* info/query methods */
|
||||
[[nodiscard]] std::vector<GroupId> server_groups_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */);
|
||||
[[nodiscard]] std::vector<ChannelGroupAssignment> channel_groups_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */);
|
||||
|
||||
[[nodiscard]] std::deque<ClientDbId> server_group_clients(GroupId /* group id */);
|
||||
//[[nodiscard]] std::deque<ClientDbId> channel_group_clients(GroupId /* group id */, ChannelId /* channel id */);
|
||||
|
||||
/* change methods */
|
||||
GroupAssignmentResult add_server_group(ClientDbId /* client database id */, GroupId /* group id */);
|
||||
GroupAssignmentResult remove_server_group(ClientDbId /* client database id */, GroupId /* group id */);
|
||||
|
||||
/* Use channel group id 0 to delete any assignment */
|
||||
GroupAssignmentResult set_channel_group(ClientDbId /* client database id */, GroupId /* group id */, ChannelId /* channel id */, bool /* temporary assignment */);
|
||||
|
||||
std::deque<property::ClientProperties> update_client_group_properties(const std::shared_ptr<server::ConnectedClient> &client, ChannelId /* target channel */);
|
||||
|
||||
void cleanup_assignments();
|
||||
void cleanup_channel_assignments(ChannelId /* channel */);
|
||||
void cleanup_channel_temporary_assignment(ClientDbId /* client database id */, ChannelId /* channel */);
|
||||
private:
|
||||
struct ClientCache {
|
||||
ClientDbId client_database_id{0};
|
||||
size_t use_count{0};
|
||||
|
||||
std::deque<ChannelGroupAssignment> channel_group_assignments{};
|
||||
std::deque<ServerGroupAssignment> server_group_assignments{};
|
||||
};
|
||||
|
||||
GroupManager* manager_;
|
||||
|
||||
std::mutex client_cache_lock;
|
||||
std::deque<std::unique_ptr<ClientCache>> client_cache{};
|
||||
|
||||
|
||||
[[nodiscard]] sql::SqlManager* sql_manager();
|
||||
[[nodiscard]] ServerId server_id();
|
||||
};
|
||||
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <Definitions.h>
|
||||
#include <Properties.h>
|
||||
|
||||
namespace sql {
|
||||
class SqlManager;
|
||||
}
|
||||
|
||||
namespace ts::server {
|
||||
class ConnectedClient;
|
||||
namespace vserver {
|
||||
class VirtualServerBase;
|
||||
}
|
||||
|
||||
namespace groups {
|
||||
enum struct GroupAssignmentCalculateMode {
|
||||
LOCAL, /* only calculate clients groups for the local server */
|
||||
GLOBAL /* use the parent group manager as well, if existing */
|
||||
};
|
||||
|
||||
class ServerGroup;
|
||||
class ChannelGroup;
|
||||
class GroupManager;
|
||||
|
||||
struct ChannelGroupAssignment {
|
||||
ChannelGroupAssignment(ChannelId channel_id, GroupId group_id, bool t) : channel_id{channel_id}, group_id{group_id}, temporary_assignment{t} { }
|
||||
ChannelGroupAssignment(const ChannelGroupAssignment& other) = default;
|
||||
ChannelGroupAssignment(ChannelGroupAssignment&&) = default;
|
||||
|
||||
ChannelId channel_id;
|
||||
GroupId group_id;
|
||||
bool temporary_assignment;
|
||||
};
|
||||
|
||||
struct ServerGroupAssignment {
|
||||
explicit ServerGroupAssignment(GroupId group_id) : group_id{group_id} { }
|
||||
ServerGroupAssignment(const ServerGroupAssignment& other) = default;
|
||||
ServerGroupAssignment(ServerGroupAssignment&&) = default;
|
||||
|
||||
GroupId group_id;
|
||||
};
|
||||
|
||||
enum struct GroupAssignmentResult {
|
||||
SUCCESS,
|
||||
ADD_ALREADY_MEMBER_OF_GROUP,
|
||||
REMOVE_NOT_MEMBER_OF_GROUP,
|
||||
SET_ALREADY_MEMBER_OF_GROUP
|
||||
};
|
||||
|
||||
class GroupAssignmentManager {
|
||||
constexpr static bool kCacheAllClients{true};
|
||||
public:
|
||||
explicit GroupAssignmentManager(GroupManager* /* manager */);
|
||||
~GroupAssignmentManager();
|
||||
|
||||
/* general load/initialize methods */
|
||||
bool initialize(std::string& /* error */);
|
||||
bool load_data(std::string& /* error */);
|
||||
void unload_data();
|
||||
|
||||
/* client specific cache methods */
|
||||
void enable_cache_for_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */);
|
||||
void disable_cache_for_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */);
|
||||
|
||||
/* info/query methods */
|
||||
[[nodiscard]] std::vector<GroupId> server_groups_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */);
|
||||
[[nodiscard]] std::vector<ChannelGroupAssignment> channel_groups_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */);
|
||||
|
||||
[[nodiscard]] std::deque<ClientDbId> server_group_clients(GroupId /* group id */);
|
||||
//[[nodiscard]] std::deque<ClientDbId> channel_group_clients(GroupId /* group id */, ChannelId /* channel id */);
|
||||
|
||||
/* change methods */
|
||||
GroupAssignmentResult add_server_group(ClientDbId /* client database id */, GroupId /* group id */);
|
||||
GroupAssignmentResult remove_server_group(ClientDbId /* client database id */, GroupId /* group id */);
|
||||
|
||||
/* Use channel group id 0 to delete any assignment */
|
||||
GroupAssignmentResult set_channel_group(ClientDbId /* client database id */, GroupId /* group id */, ChannelId /* channel id */, bool /* temporary assignment */);
|
||||
|
||||
std::deque<property::ClientProperties> update_client_group_properties(const std::shared_ptr<server::ConnectedClient> &client, ChannelId /* target channel */);
|
||||
|
||||
void cleanup_assignments();
|
||||
void cleanup_channel_assignments(ChannelId /* channel */);
|
||||
void cleanup_channel_temporary_assignment(ClientDbId /* client database id */, ChannelId /* channel */);
|
||||
private:
|
||||
struct ClientCache {
|
||||
ClientDbId client_database_id{0};
|
||||
size_t use_count{0};
|
||||
|
||||
std::deque<ChannelGroupAssignment> channel_group_assignments{};
|
||||
std::deque<ServerGroupAssignment> server_group_assignments{};
|
||||
};
|
||||
|
||||
GroupManager* manager_;
|
||||
|
||||
std::mutex client_cache_lock;
|
||||
std::deque<std::unique_ptr<ClientCache>> client_cache{};
|
||||
|
||||
|
||||
[[nodiscard]] sql::SqlManager* sql_manager();
|
||||
[[nodiscard]] ServerId server_id();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Created by WolverinDEV on 03/03/2020.
|
||||
//
|
||||
|
||||
#include "GroupManager.h"
|
||||
//
|
||||
// Created by WolverinDEV on 03/03/2020.
|
||||
//
|
||||
|
||||
#include "GroupManager.h"
|
||||
|
@ -1,82 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <Definitions.h>
|
||||
#include <sql/SqlQuery.h>
|
||||
#include "./GroupAssignmentManager.h"
|
||||
#include "./Group.h"
|
||||
|
||||
namespace ts::server {
|
||||
namespace vserver {
|
||||
class VirtualServerBase;
|
||||
}
|
||||
|
||||
namespace groups {
|
||||
enum struct GroupCalculateMode {
|
||||
LOCAL, /* only calculate clients groups for the local server */
|
||||
GLOBAL /* use the parent group manager as well, if existing */
|
||||
};
|
||||
|
||||
enum struct ActionReturnCode {
|
||||
RENAME_NAME_ALREADY_USED,
|
||||
RENAME_GROUP_INVALID_ID,
|
||||
|
||||
DELETE_GROUP_INVALID_ID,
|
||||
|
||||
CUSTOM_ERROR
|
||||
};
|
||||
|
||||
class GroupManager {
|
||||
friend class Group;
|
||||
public:
|
||||
GroupManager(vserver::VirtualServerBase* /* virtual server */, std::shared_ptr<GroupManager> /* parent */);
|
||||
~GroupManager();
|
||||
|
||||
bool initialize(std::string& /* error */);
|
||||
bool load_data(std::string& /* error */);
|
||||
void unload_data();
|
||||
|
||||
[[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::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> find_server_group(GroupId /* group id */);
|
||||
[[nodiscard]] std::shared_ptr<ChannelGroup> find_channel_group(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]] 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]] ActionReturnCode copy_server_group_permissions(GroupId /* group id */, GroupId /* target group */);
|
||||
[[nodiscard]] ActionReturnCode copy_channel_group_permissions(GroupId /* group id */, GroupId /* target group */);
|
||||
|
||||
[[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]] ActionReturnCode delete_server_group(GroupId /* group id */);
|
||||
[[nodiscard]] ActionReturnCode delete_channel_group(GroupId /* group id */);
|
||||
private:
|
||||
vserver::VirtualServerBase* virtual_server_;
|
||||
|
||||
std::shared_ptr<GroupManager> parent_manager_;
|
||||
GroupAssignmentManager assignment_manager_;
|
||||
|
||||
std::mutex group_lock_{};
|
||||
|
||||
[[nodiscard]] sql::SqlManager* sql_manager();
|
||||
void update_group_name(Group* /* group */);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <Definitions.h>
|
||||
#include <sql/SqlQuery.h>
|
||||
#include "./GroupAssignmentManager.h"
|
||||
#include "./Group.h"
|
||||
|
||||
namespace ts::server {
|
||||
namespace vserver {
|
||||
class VirtualServerBase;
|
||||
}
|
||||
|
||||
namespace groups {
|
||||
enum struct GroupCalculateMode {
|
||||
LOCAL, /* only calculate clients groups for the local server */
|
||||
GLOBAL /* use the parent group manager as well, if existing */
|
||||
};
|
||||
|
||||
enum struct ActionReturnCode {
|
||||
RENAME_NAME_ALREADY_USED,
|
||||
RENAME_GROUP_INVALID_ID,
|
||||
|
||||
DELETE_GROUP_INVALID_ID,
|
||||
|
||||
CUSTOM_ERROR
|
||||
};
|
||||
|
||||
class GroupManager {
|
||||
friend class Group;
|
||||
public:
|
||||
GroupManager(vserver::VirtualServerBase* /* virtual server */, std::shared_ptr<GroupManager> /* parent */);
|
||||
~GroupManager();
|
||||
|
||||
bool initialize(std::string& /* error */);
|
||||
bool load_data(std::string& /* error */);
|
||||
void unload_data();
|
||||
|
||||
[[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::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> find_server_group(GroupId /* group id */);
|
||||
[[nodiscard]] std::shared_ptr<ChannelGroup> find_channel_group(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]] 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]] ActionReturnCode copy_server_group_permissions(GroupId /* group id */, GroupId /* target group */);
|
||||
[[nodiscard]] ActionReturnCode copy_channel_group_permissions(GroupId /* group id */, GroupId /* target group */);
|
||||
|
||||
[[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]] ActionReturnCode delete_server_group(GroupId /* group id */);
|
||||
[[nodiscard]] ActionReturnCode delete_channel_group(GroupId /* group id */);
|
||||
private:
|
||||
vserver::VirtualServerBase* virtual_server_;
|
||||
|
||||
std::shared_ptr<GroupManager> parent_manager_;
|
||||
GroupAssignmentManager assignment_manager_;
|
||||
|
||||
std::mutex group_lock_{};
|
||||
|
||||
[[nodiscard]] sql::SqlManager* sql_manager();
|
||||
void update_group_name(Group* /* group */);
|
||||
};
|
||||
}
|
||||
}
|
@ -1,379 +1,379 @@
|
||||
//
|
||||
// Created by WolverinDEV on 03/03/2020.
|
||||
//
|
||||
|
||||
#include <deque>
|
||||
#include "./ClientChannelService.h"
|
||||
#include "../client/ConnectedClient.h"
|
||||
#include "../vserver/VirtualServer.h"
|
||||
#include "../groups/GroupManager.h"
|
||||
#include "../groups/GroupAssignmentManager.h"
|
||||
#include <misc/timer.h>
|
||||
#include <misc/sassert.h>
|
||||
#include <log/LogUtils.h>
|
||||
|
||||
using namespace ts::server::channels;
|
||||
|
||||
ts::ServerId ClientChannelService::get_server_id() const {
|
||||
return this->virtual_server_->server_id();
|
||||
}
|
||||
|
||||
ChannelGroupInheritance ClientChannelService::calculate_channel_group(ChannelId channel_id, ClientDbId client_dbid, ClientType client_type) {
|
||||
auto tree_lock = this->virtual_server_->lock_channel_clients();
|
||||
tree_lock.auto_lock_shared();
|
||||
|
||||
auto assignments = this->virtual_server_->group_manager()->assignments().channel_groups_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, client_dbid);
|
||||
|
||||
auto inheritance_channel = this->virtual_server_->server_channel_tree()->findChannel(channel_id);
|
||||
while(inheritance_channel) {
|
||||
auto inheritance_channel_id = inheritance_channel->channelId();
|
||||
auto assignment = std::find_if(assignments.begin(), assignments.end(), [&](const groups::ChannelGroupAssignment& assignment) {
|
||||
return assignment.channel_id == inheritance_channel_id;
|
||||
});
|
||||
|
||||
if(assignment == assignments.end() || !assignment->group_id) {
|
||||
auto permission = inheritance_channel->permissions()->permission_value_flagged(permission::b_channel_group_inheritance_end);
|
||||
if(permission::v2::permission_granted(1, permission))
|
||||
break;
|
||||
|
||||
inheritance_channel = inheritance_channel->parent();
|
||||
} else {
|
||||
return {
|
||||
assignment->group_id,
|
||||
inheritance_channel_id
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
auto default_group = this->virtual_server_->group_manager()->default_channel_group(client_type);
|
||||
assert(default_group);
|
||||
return {
|
||||
default_group->group_id(),
|
||||
channel_id
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* General notice:
|
||||
* We're locking the clients command_lock, so when changing the currentChannel variable nothing gets messed up.
|
||||
* As a little note: This must be done before locking the channel tree at all.
|
||||
*/
|
||||
|
||||
ClientMoveResult ClientChannelService::client_ban(const std::shared_ptr<ConnectedClient> &target,
|
||||
const std::shared_ptr<ConnectedClient> &invoker, const std::string &reason, size_t time,
|
||||
ts::rwshared_lock<ts::rw_mutex> &tree_lock) {
|
||||
tree_lock.auto_unlock();
|
||||
auto command_lock = target->lock_command_handling();
|
||||
if(auto err = tree_lock.auto_lock_exclusive(); err)
|
||||
return ClientMoveResult::TREE_LOCK_FAILED;
|
||||
|
||||
std::unique_lock client_chan_tree_lock{target->get_channel_lock()};
|
||||
auto old_channel = target->getChannel();
|
||||
target->setChannel(nullptr);
|
||||
client_chan_tree_lock.unlock();
|
||||
|
||||
ChannelId old_channel_id{0};
|
||||
if(old_channel) {
|
||||
old_channel_id = old_channel->channelId();
|
||||
for(const auto& client : this->virtual_server_->connected_clients(false)) {
|
||||
if(!client || client == target)
|
||||
continue;
|
||||
|
||||
std::unique_lock client_channel_lock(client->get_channel_lock());
|
||||
if(client->isClientVisible(target, false))
|
||||
client->notifyClientLeftViewBanned(target, old_channel_id, invoker, time, false, reason);
|
||||
}
|
||||
|
||||
auto s_channel = dynamic_pointer_cast<ServerChannel>(old_channel);
|
||||
assert(s_channel);
|
||||
s_channel->unregister_client(target);
|
||||
}
|
||||
|
||||
target->notifyClientLeftViewBanned(target, old_channel_id, invoker, time, false, reason);
|
||||
return ClientMoveResult::SUCCESS;
|
||||
}
|
||||
|
||||
ClientMoveResult ClientChannelService::client_kick(const std::shared_ptr<ConnectedClient> &target,
|
||||
const std::shared_ptr<ConnectedClient> &invoker, const std::string &reason,
|
||||
const std::shared_ptr<ServerChannel> &target_channel,
|
||||
ts::rwshared_lock<ts::rw_mutex> &tree_lock) {
|
||||
if(target_channel) {
|
||||
return this->client_move(target, target_channel, invoker, reason, ViewReasonId::VREASON_CHANNEL_KICK, true, tree_lock);
|
||||
} else {
|
||||
tree_lock.auto_unlock();
|
||||
auto command_lock = target->lock_command_handling();
|
||||
if(auto err = tree_lock.auto_lock_exclusive(); err)
|
||||
return ClientMoveResult::TREE_LOCK_FAILED;
|
||||
|
||||
std::unique_lock client_chan_tree_lock{target->get_channel_lock()};
|
||||
auto old_channel = target->getChannel();
|
||||
target->setChannel(nullptr);
|
||||
client_chan_tree_lock.unlock();
|
||||
|
||||
ChannelId old_channel_id{0};
|
||||
if(old_channel) {
|
||||
old_channel_id = old_channel->channelId();
|
||||
for(const auto& client : this->virtual_server_->connected_clients(false)) {
|
||||
if(!client || client == target)
|
||||
continue;
|
||||
|
||||
std::unique_lock client_channel_lock(client->get_channel_lock());
|
||||
if(client->isClientVisible(target, false))
|
||||
client->notifyClientLeftViewKicked(target, old_channel_id, reason, invoker, false, nullptr);
|
||||
}
|
||||
|
||||
auto s_channel = dynamic_pointer_cast<ServerChannel>(old_channel);
|
||||
assert(s_channel);
|
||||
s_channel->unregister_client(target);
|
||||
}
|
||||
|
||||
target->notifyClientLeftViewKicked(target, old_channel_id, reason, invoker, false, nullptr);
|
||||
}
|
||||
|
||||
return ClientMoveResult::SUCCESS;
|
||||
}
|
||||
|
||||
ClientMoveResult ClientChannelService::client_move(const std::shared_ptr<ConnectedClient> &target,
|
||||
std::shared_ptr<ServerChannel> s_target_channel,
|
||||
const std::shared_ptr<ConnectedClient> &invoker,
|
||||
const std::string &reason_message,
|
||||
ViewReasonId reason_id,
|
||||
bool notify_client,
|
||||
ts::rwshared_lock<ts::rw_mutex> &tree_lock) {
|
||||
TIMING_START(timings);
|
||||
tree_lock.auto_unlock();
|
||||
|
||||
auto command_lock = target->lock_command_handling();
|
||||
if(auto err = tree_lock.auto_lock_exclusive(); err)
|
||||
return ClientMoveResult::TREE_LOCK_FAILED;
|
||||
|
||||
TIMING_STEP(timings, "tree lock setup");
|
||||
if(target->getChannel() == s_target_channel)
|
||||
return ClientMoveResult::SUCCESS;
|
||||
|
||||
/* first step: resolve the target channel / or fix missing */
|
||||
auto s_source_channel = dynamic_pointer_cast<ServerChannel>(target->getChannel());
|
||||
assert(!target->getChannel() || s_source_channel != nullptr);
|
||||
|
||||
auto server_channel_tree = this->virtual_server_->server_channel_tree();
|
||||
std::deque<property::ClientProperties> client_updates;
|
||||
std::deque<property::ClientProperties> changed_groups{};
|
||||
if(s_target_channel) {
|
||||
/* don't let the client join a deleted channel */
|
||||
if(s_target_channel->deleted) {
|
||||
s_target_channel = dynamic_pointer_cast<ServerChannel>(server_channel_tree->getDefaultChannel());
|
||||
assert(s_target_channel);
|
||||
}
|
||||
|
||||
/* update the group properties here already, so for all enter views we could just send the new props directly */
|
||||
changed_groups = this->virtual_server_->group_manager()->assignments().update_client_group_properties(target, s_target_channel->channelId());
|
||||
client_updates.insert(client_updates.end(), changed_groups.begin(), changed_groups.end()); //TODO: Only update for clients which have no enter?
|
||||
}
|
||||
|
||||
auto l_target_channel = s_target_channel ? server_channel_tree->findLinkedChannel(s_target_channel->channelId()) : nullptr;
|
||||
auto l_source_channel = s_source_channel ? server_channel_tree->findLinkedChannel(s_source_channel->channelId()) : nullptr;
|
||||
TIMING_STEP(timings, "channel lookup ");
|
||||
|
||||
|
||||
/* second step: show the target channel to the client if its not shown and let him subscibe to the channel */
|
||||
if(s_target_channel && notify_client) {
|
||||
std::unique_lock client_channel_lock(target->get_channel_lock());
|
||||
|
||||
bool success = false;
|
||||
/* TODO: Use a bunk here and not a notify for every single */
|
||||
for(const auto& channel : target->channel_view()->show_channel(l_target_channel, success))
|
||||
target->notifyChannelShow(channel->channel(), channel->previous_channel);
|
||||
sassert(success);
|
||||
if(!success)
|
||||
return ClientMoveResult::VIEW_INSERT_FAILED;
|
||||
|
||||
target->subscribeChannel({s_target_channel}, false, true);
|
||||
}
|
||||
TIMING_STEP(timings, "target show chan");
|
||||
|
||||
if(s_target_channel) {
|
||||
for(const auto& client : this->virtual_server_->connected_clients(false)) {
|
||||
if (!notify_client && client == target) continue;
|
||||
|
||||
std::unique_lock client_channel_lock{client->get_channel_lock()};
|
||||
auto chan_target = client->channel_view()->find_channel(s_target_channel);
|
||||
|
||||
if(chan_target) {
|
||||
auto chan_source = client->channel_view()->find_channel(s_source_channel);
|
||||
if(chan_source) {
|
||||
if (chan_target->subscribed || client == target) {
|
||||
if (client == target || client->isClientVisible(target, false)) {
|
||||
client->notifyClientMoved(target, s_target_channel, reason_id, reason_message, invoker, false);
|
||||
} else {
|
||||
client->notifyClientEnterView(target, invoker, reason_message, s_target_channel, reason_id, s_source_channel, false);
|
||||
}
|
||||
} else if(client->isClientVisible(target, false)){
|
||||
//Client got out of view
|
||||
client->notifyClientLeftView(target, s_target_channel, reason_id, reason_message.empty() ? std::string{"view left"} : reason_message, invoker, false);
|
||||
}
|
||||
} else {
|
||||
if(client == target && client->getType() != ClientType::CLIENT_INTERNAL && client->getType() != ClientType::CLIENT_MUSIC)
|
||||
logCritical(this->get_server_id(), "{} Client enters visibility twice!", CLIENT_STR_LOG_PREFIX_(client));
|
||||
|
||||
//Client entered view
|
||||
if(chan_target->subscribed)
|
||||
client->notifyClientEnterView(target, invoker, reason_message, s_target_channel, ViewReasonId::VREASON_USER_ACTION, nullptr, false);
|
||||
}
|
||||
} else {
|
||||
/* target channel isn't visible => so client gone out of view */
|
||||
if(client == target && client->getType() != ClientType::CLIENT_INTERNAL && client->getType() != ClientType::CLIENT_MUSIC)
|
||||
logCritical(this->get_server_id(), "{} Moving own client into a not visible channel! This shall not happen!", CLIENT_STR_LOG_PREFIX_(client));
|
||||
//Test for in view? (Notify already does but nvm)
|
||||
|
||||
if(client->isClientVisible(target, false)){
|
||||
//Client got out of view
|
||||
if(reason_id == ViewReasonId::VREASON_USER_ACTION)
|
||||
client->notifyClientLeftView(target, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "joined a hidden channel" : reason_message, invoker, false);
|
||||
else
|
||||
client->notifyClientLeftView(target, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "moved to a hidden channel" : reason_message, invoker, false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(s_source_channel)
|
||||
s_source_channel->unregister_client(target);
|
||||
s_target_channel->register_client(target);
|
||||
} else {
|
||||
/* client left the server */
|
||||
if(target->getChannel()) {
|
||||
for(const auto& client : this->virtual_server_->connected_clients(false)) {
|
||||
if(!client || client == target)
|
||||
continue;
|
||||
|
||||
std::unique_lock client_channel_lock(client->get_channel_lock());
|
||||
if(client->isClientVisible(target, false))
|
||||
client->notifyClientLeftView(target, nullptr, reason_id, reason_message, invoker, false);
|
||||
}
|
||||
|
||||
s_source_channel->unregister_client(target);
|
||||
}
|
||||
}
|
||||
TIMING_STEP(timings, "notify viewers ");
|
||||
|
||||
target->setChannel(s_target_channel);
|
||||
tree_lock.downgrade_lock();
|
||||
|
||||
std::unique_lock client_channel_lock(target->get_channel_lock());
|
||||
TIMING_STEP(timings, "lock own ch tree");
|
||||
|
||||
if (s_source_channel) {
|
||||
s_source_channel->properties()[property::CHANNEL_LAST_LEFT] = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
||||
this->virtual_server_->group_manager()->assignments().cleanup_channel_temporary_assignment(target->getClientDatabaseId(), s_source_channel->channelId());
|
||||
auto update = target->properties()[property::CLIENT_IS_TALKER].as<bool>() || target->properties()[property::CLIENT_TALK_REQUEST].as<int64_t>() > 0;
|
||||
|
||||
if(update) {
|
||||
target->properties()[property::CLIENT_IS_TALKER] = 0;
|
||||
target->properties()[property::CLIENT_TALK_REQUEST] = 0;
|
||||
target->properties()[property::CLIENT_TALK_REQUEST_MSG] = "";
|
||||
client_updates.push_back(property::CLIENT_IS_TALKER);
|
||||
client_updates.push_back(property::CLIENT_TALK_REQUEST);
|
||||
client_updates.push_back(property::CLIENT_TALK_REQUEST_MSG);
|
||||
}
|
||||
TIMING_STEP(timings, "src chan up");
|
||||
}
|
||||
|
||||
if (s_target_channel) {
|
||||
if(target->update_cached_permissions()) /* update cached calculated permissions */
|
||||
target->sendNeededPermissions(false);
|
||||
TIMING_STEP(timings, "perm gr upd");
|
||||
|
||||
if(s_source_channel) {
|
||||
std::deque<ChannelId> deleted;
|
||||
for(const auto& channel : target->channel_view()->test_channel(l_source_channel, l_target_channel))
|
||||
deleted.push_back(channel->channelId());
|
||||
|
||||
if(!deleted.empty())
|
||||
target->notifyChannelHide(deleted, false);
|
||||
|
||||
/* force unsubscribe from old channel */
|
||||
auto i_source_channel = s_source_channel->channelId();
|
||||
if(std::find(deleted.begin(), deleted.end(), i_source_channel) == deleted.end()) {
|
||||
auto source_channel_sub_power = target->calculate_permission(permission::i_channel_subscribe_power, i_source_channel);
|
||||
if(!s_source_channel->permission_granted(permission::i_channel_needed_subscribe_power, source_channel_sub_power, false)) {
|
||||
auto source_channel_sub_power_ignore = target->calculate_permission(permission::b_channel_ignore_subscribe_power, i_source_channel);
|
||||
if(!permission::v2::permission_granted(1, source_channel_sub_power_ignore, true)) {
|
||||
logTrace(this->get_server_id(), "Force unsubscribing of client {} for channel {}/{}. (Channel switch and no permissions)",
|
||||
CLIENT_STR_LOG_PREFIX_(target), s_source_channel->name(),
|
||||
i_source_channel
|
||||
);
|
||||
target->unsubscribeChannel({s_source_channel}, false); //Unsubscribe last channel (hasn't permissions)
|
||||
}
|
||||
}
|
||||
}
|
||||
TIMING_STEP(timings, "src hide ts");
|
||||
}
|
||||
}
|
||||
client_channel_lock.unlock();
|
||||
/* both methods lock if they require stuff */
|
||||
this->notifyClientPropertyUpdates(target, client_updates, s_source_channel ? true : false);
|
||||
TIMING_STEP(timings, "notify cpro");
|
||||
if(s_target_channel) {
|
||||
target->updateChannelClientProperties(false, s_source_channel ? true : false);
|
||||
TIMING_STEP(timings, "notify_t_pr");
|
||||
}
|
||||
debugMessage(this->get_server_id(), "{} Client move timings: {}", CLIENT_STR_LOG_PREFIX_(target), TIMING_FINISH(timings));
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. flag channel as deleted (lock channel tree so no moves)
|
||||
* 2. Gather all clients within the channel (lock their execute lock)
|
||||
* 3. Unlock channel tree and lock client locks
|
||||
* 4. lock channel tree again and move the clients (No new clients should be joined because channel is flagged as deleted!)
|
||||
*
|
||||
* Note: channel cant be a ref because the channel itself gets deleted!
|
||||
*/
|
||||
ChannelDeleteResult ClientChannelService::delete_channel(std::shared_ptr<ServerChannel> channel, const std::shared_ptr<ConnectedClient> &invoker, const std::string &kick_message, ts::rwshared_lock<ts::rw_mutex> &tree_lock) {
|
||||
if(auto err = tree_lock.auto_lock_exclusive(); err)
|
||||
return ChannelDeleteResult::TREE_LOCK_FAILED;
|
||||
|
||||
if(channel->deleted)
|
||||
return ChannelDeleteResult::SUCCESS;
|
||||
|
||||
std::deque<std::shared_ptr<ConnectedClient>> clients;
|
||||
{
|
||||
for(const auto& sub_channel : this->virtual_server_->server_channel_tree()->channels(channel)) {
|
||||
auto s_channel = dynamic_pointer_cast<ServerChannel>(sub_channel);
|
||||
assert(s_channel);
|
||||
|
||||
auto chan_clients = this->virtual_server_->find_clients_by_channel(s_channel);
|
||||
clients.insert(clients.end(), chan_clients.begin(), chan_clients.end());
|
||||
s_channel->deleted = true;
|
||||
}
|
||||
auto chan_clients = this->virtual_server_->find_clients_by_channel(channel);
|
||||
clients.insert(clients.end(), chan_clients.begin(), chan_clients.end());
|
||||
channel->deleted = true;
|
||||
}
|
||||
auto default_channel = dynamic_pointer_cast<ServerChannel>(this->virtual_server_->server_channel_tree()->getDefaultChannel());
|
||||
assert(default_channel);
|
||||
tree_lock.auto_unlock();
|
||||
|
||||
std::deque<std::unique_lock<threads::Mutex>> command_locks;
|
||||
for(const auto& client : clients)
|
||||
command_locks.push_back(std::move(std::unique_lock(client->get_channel_lock())));
|
||||
|
||||
for(const auto& client : clients) {
|
||||
auto result = this->client_move(client, default_channel, invoker, kick_message, ViewReasonId::VREASON_CHANNEL_KICK, true, tree_lock);
|
||||
if(result != ClientMoveResult::SUCCESS)
|
||||
return ChannelDeleteResult::CLIENT_MOVE_FAILED;
|
||||
}
|
||||
|
||||
if(auto err = tree_lock.auto_lock_exclusive(); err)
|
||||
return ChannelDeleteResult::TREE_LOCK_FAILED;
|
||||
command_locks.clear();
|
||||
|
||||
auto channel_ids = this->virtual_server_->server_channel_tree()->delete_channel_root(channel);
|
||||
tree_lock.auto_lock_shared();
|
||||
|
||||
for(auto& client : this->virtual_server_->connected_clients(false)) {
|
||||
std::unique_lock client_channel_lock(client->get_channel_lock());
|
||||
client->notifyChannelDeleted(client->channel_view()->delete_channel_root(channel), invoker);
|
||||
}
|
||||
return ChannelDeleteResult::SUCCESS;
|
||||
//
|
||||
// Created by WolverinDEV on 03/03/2020.
|
||||
//
|
||||
|
||||
#include <deque>
|
||||
#include "./ClientChannelService.h"
|
||||
#include "../client/ConnectedClient.h"
|
||||
#include "../vserver/VirtualServer.h"
|
||||
#include "../groups/GroupManager.h"
|
||||
#include "../groups/GroupAssignmentManager.h"
|
||||
#include <misc/timer.h>
|
||||
#include <misc/sassert.h>
|
||||
#include <log/LogUtils.h>
|
||||
|
||||
using namespace ts::server::channels;
|
||||
|
||||
ts::ServerId ClientChannelService::get_server_id() const {
|
||||
return this->virtual_server_->server_id();
|
||||
}
|
||||
|
||||
ChannelGroupInheritance ClientChannelService::calculate_channel_group(ChannelId channel_id, ClientDbId client_dbid, ClientType client_type) {
|
||||
auto tree_lock = this->virtual_server_->lock_channel_clients();
|
||||
tree_lock.auto_lock_shared();
|
||||
|
||||
auto assignments = this->virtual_server_->group_manager()->assignments().channel_groups_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, client_dbid);
|
||||
|
||||
auto inheritance_channel = this->virtual_server_->server_channel_tree()->findChannel(channel_id);
|
||||
while(inheritance_channel) {
|
||||
auto inheritance_channel_id = inheritance_channel->channelId();
|
||||
auto assignment = std::find_if(assignments.begin(), assignments.end(), [&](const groups::ChannelGroupAssignment& assignment) {
|
||||
return assignment.channel_id == inheritance_channel_id;
|
||||
});
|
||||
|
||||
if(assignment == assignments.end() || !assignment->group_id) {
|
||||
auto permission = inheritance_channel->permissions()->permission_value_flagged(permission::b_channel_group_inheritance_end);
|
||||
if(permission::v2::permission_granted(1, permission))
|
||||
break;
|
||||
|
||||
inheritance_channel = inheritance_channel->parent();
|
||||
} else {
|
||||
return {
|
||||
assignment->group_id,
|
||||
inheritance_channel_id
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
auto default_group = this->virtual_server_->group_manager()->default_channel_group(client_type);
|
||||
assert(default_group);
|
||||
return {
|
||||
default_group->group_id(),
|
||||
channel_id
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* General notice:
|
||||
* We're locking the clients command_lock, so when changing the currentChannel variable nothing gets messed up.
|
||||
* As a little note: This must be done before locking the channel tree at all.
|
||||
*/
|
||||
|
||||
ClientMoveResult ClientChannelService::client_ban(const std::shared_ptr<ConnectedClient> &target,
|
||||
const std::shared_ptr<ConnectedClient> &invoker, const std::string &reason, size_t time,
|
||||
ts::rwshared_lock<ts::rw_mutex> &tree_lock) {
|
||||
tree_lock.auto_unlock();
|
||||
auto command_lock = target->lock_command_handling();
|
||||
if(auto err = tree_lock.auto_lock_exclusive(); err)
|
||||
return ClientMoveResult::TREE_LOCK_FAILED;
|
||||
|
||||
std::unique_lock client_chan_tree_lock{target->get_channel_lock()};
|
||||
auto old_channel = target->getChannel();
|
||||
target->setChannel(nullptr);
|
||||
client_chan_tree_lock.unlock();
|
||||
|
||||
ChannelId old_channel_id{0};
|
||||
if(old_channel) {
|
||||
old_channel_id = old_channel->channelId();
|
||||
for(const auto& client : this->virtual_server_->connected_clients(false)) {
|
||||
if(!client || client == target)
|
||||
continue;
|
||||
|
||||
std::unique_lock client_channel_lock(client->get_channel_lock());
|
||||
if(client->isClientVisible(target, false))
|
||||
client->notifyClientLeftViewBanned(target, old_channel_id, invoker, time, false, reason);
|
||||
}
|
||||
|
||||
auto s_channel = dynamic_pointer_cast<ServerChannel>(old_channel);
|
||||
assert(s_channel);
|
||||
s_channel->unregister_client(target);
|
||||
}
|
||||
|
||||
target->notifyClientLeftViewBanned(target, old_channel_id, invoker, time, false, reason);
|
||||
return ClientMoveResult::SUCCESS;
|
||||
}
|
||||
|
||||
ClientMoveResult ClientChannelService::client_kick(const std::shared_ptr<ConnectedClient> &target,
|
||||
const std::shared_ptr<ConnectedClient> &invoker, const std::string &reason,
|
||||
const std::shared_ptr<ServerChannel> &target_channel,
|
||||
ts::rwshared_lock<ts::rw_mutex> &tree_lock) {
|
||||
if(target_channel) {
|
||||
return this->client_move(target, target_channel, invoker, reason, ViewReasonId::VREASON_CHANNEL_KICK, true, tree_lock);
|
||||
} else {
|
||||
tree_lock.auto_unlock();
|
||||
auto command_lock = target->lock_command_handling();
|
||||
if(auto err = tree_lock.auto_lock_exclusive(); err)
|
||||
return ClientMoveResult::TREE_LOCK_FAILED;
|
||||
|
||||
std::unique_lock client_chan_tree_lock{target->get_channel_lock()};
|
||||
auto old_channel = target->getChannel();
|
||||
target->setChannel(nullptr);
|
||||
client_chan_tree_lock.unlock();
|
||||
|
||||
ChannelId old_channel_id{0};
|
||||
if(old_channel) {
|
||||
old_channel_id = old_channel->channelId();
|
||||
for(const auto& client : this->virtual_server_->connected_clients(false)) {
|
||||
if(!client || client == target)
|
||||
continue;
|
||||
|
||||
std::unique_lock client_channel_lock(client->get_channel_lock());
|
||||
if(client->isClientVisible(target, false))
|
||||
client->notifyClientLeftViewKicked(target, old_channel_id, reason, invoker, false, nullptr);
|
||||
}
|
||||
|
||||
auto s_channel = dynamic_pointer_cast<ServerChannel>(old_channel);
|
||||
assert(s_channel);
|
||||
s_channel->unregister_client(target);
|
||||
}
|
||||
|
||||
target->notifyClientLeftViewKicked(target, old_channel_id, reason, invoker, false, nullptr);
|
||||
}
|
||||
|
||||
return ClientMoveResult::SUCCESS;
|
||||
}
|
||||
|
||||
ClientMoveResult ClientChannelService::client_move(const std::shared_ptr<ConnectedClient> &target,
|
||||
std::shared_ptr<ServerChannel> s_target_channel,
|
||||
const std::shared_ptr<ConnectedClient> &invoker,
|
||||
const std::string &reason_message,
|
||||
ViewReasonId reason_id,
|
||||
bool notify_client,
|
||||
ts::rwshared_lock<ts::rw_mutex> &tree_lock) {
|
||||
TIMING_START(timings);
|
||||
tree_lock.auto_unlock();
|
||||
|
||||
auto command_lock = target->lock_command_handling();
|
||||
if(auto err = tree_lock.auto_lock_exclusive(); err)
|
||||
return ClientMoveResult::TREE_LOCK_FAILED;
|
||||
|
||||
TIMING_STEP(timings, "tree lock setup");
|
||||
if(target->getChannel() == s_target_channel)
|
||||
return ClientMoveResult::SUCCESS;
|
||||
|
||||
/* first step: resolve the target channel / or fix missing */
|
||||
auto s_source_channel = dynamic_pointer_cast<ServerChannel>(target->getChannel());
|
||||
assert(!target->getChannel() || s_source_channel != nullptr);
|
||||
|
||||
auto server_channel_tree = this->virtual_server_->server_channel_tree();
|
||||
std::deque<property::ClientProperties> client_updates;
|
||||
std::deque<property::ClientProperties> changed_groups{};
|
||||
if(s_target_channel) {
|
||||
/* don't let the client join a deleted channel */
|
||||
if(s_target_channel->deleted) {
|
||||
s_target_channel = dynamic_pointer_cast<ServerChannel>(server_channel_tree->getDefaultChannel());
|
||||
assert(s_target_channel);
|
||||
}
|
||||
|
||||
/* update the group properties here already, so for all enter views we could just send the new props directly */
|
||||
changed_groups = this->virtual_server_->group_manager()->assignments().update_client_group_properties(target, s_target_channel->channelId());
|
||||
client_updates.insert(client_updates.end(), changed_groups.begin(), changed_groups.end()); //TODO: Only update for clients which have no enter?
|
||||
}
|
||||
|
||||
auto l_target_channel = s_target_channel ? server_channel_tree->findLinkedChannel(s_target_channel->channelId()) : nullptr;
|
||||
auto l_source_channel = s_source_channel ? server_channel_tree->findLinkedChannel(s_source_channel->channelId()) : nullptr;
|
||||
TIMING_STEP(timings, "channel lookup ");
|
||||
|
||||
|
||||
/* second step: show the target channel to the client if its not shown and let him subscibe to the channel */
|
||||
if(s_target_channel && notify_client) {
|
||||
std::unique_lock client_channel_lock(target->get_channel_lock());
|
||||
|
||||
bool success = false;
|
||||
/* TODO: Use a bunk here and not a notify for every single */
|
||||
for(const auto& channel : target->channel_view()->show_channel(l_target_channel, success))
|
||||
target->notifyChannelShow(channel->channel(), channel->previous_channel);
|
||||
sassert(success);
|
||||
if(!success)
|
||||
return ClientMoveResult::VIEW_INSERT_FAILED;
|
||||
|
||||
target->subscribeChannel({s_target_channel}, false, true);
|
||||
}
|
||||
TIMING_STEP(timings, "target show chan");
|
||||
|
||||
if(s_target_channel) {
|
||||
for(const auto& client : this->virtual_server_->connected_clients(false)) {
|
||||
if (!notify_client && client == target) continue;
|
||||
|
||||
std::unique_lock client_channel_lock{client->get_channel_lock()};
|
||||
auto chan_target = client->channel_view()->find_channel(s_target_channel);
|
||||
|
||||
if(chan_target) {
|
||||
auto chan_source = client->channel_view()->find_channel(s_source_channel);
|
||||
if(chan_source) {
|
||||
if (chan_target->subscribed || client == target) {
|
||||
if (client == target || client->isClientVisible(target, false)) {
|
||||
client->notifyClientMoved(target, s_target_channel, reason_id, reason_message, invoker, false);
|
||||
} else {
|
||||
client->notifyClientEnterView(target, invoker, reason_message, s_target_channel, reason_id, s_source_channel, false);
|
||||
}
|
||||
} else if(client->isClientVisible(target, false)){
|
||||
//Client got out of view
|
||||
client->notifyClientLeftView(target, s_target_channel, reason_id, reason_message.empty() ? std::string{"view left"} : reason_message, invoker, false);
|
||||
}
|
||||
} else {
|
||||
if(client == target && client->getType() != ClientType::CLIENT_INTERNAL && client->getType() != ClientType::CLIENT_MUSIC)
|
||||
logCritical(this->get_server_id(), "{} Client enters visibility twice!", CLIENT_STR_LOG_PREFIX_(client));
|
||||
|
||||
//Client entered view
|
||||
if(chan_target->subscribed)
|
||||
client->notifyClientEnterView(target, invoker, reason_message, s_target_channel, ViewReasonId::VREASON_USER_ACTION, nullptr, false);
|
||||
}
|
||||
} else {
|
||||
/* target channel isn't visible => so client gone out of view */
|
||||
if(client == target && client->getType() != ClientType::CLIENT_INTERNAL && client->getType() != ClientType::CLIENT_MUSIC)
|
||||
logCritical(this->get_server_id(), "{} Moving own client into a not visible channel! This shall not happen!", CLIENT_STR_LOG_PREFIX_(client));
|
||||
//Test for in view? (Notify already does but nvm)
|
||||
|
||||
if(client->isClientVisible(target, false)){
|
||||
//Client got out of view
|
||||
if(reason_id == ViewReasonId::VREASON_USER_ACTION)
|
||||
client->notifyClientLeftView(target, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "joined a hidden channel" : reason_message, invoker, false);
|
||||
else
|
||||
client->notifyClientLeftView(target, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "moved to a hidden channel" : reason_message, invoker, false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(s_source_channel)
|
||||
s_source_channel->unregister_client(target);
|
||||
s_target_channel->register_client(target);
|
||||
} else {
|
||||
/* client left the server */
|
||||
if(target->getChannel()) {
|
||||
for(const auto& client : this->virtual_server_->connected_clients(false)) {
|
||||
if(!client || client == target)
|
||||
continue;
|
||||
|
||||
std::unique_lock client_channel_lock(client->get_channel_lock());
|
||||
if(client->isClientVisible(target, false))
|
||||
client->notifyClientLeftView(target, nullptr, reason_id, reason_message, invoker, false);
|
||||
}
|
||||
|
||||
s_source_channel->unregister_client(target);
|
||||
}
|
||||
}
|
||||
TIMING_STEP(timings, "notify viewers ");
|
||||
|
||||
target->setChannel(s_target_channel);
|
||||
tree_lock.downgrade_lock();
|
||||
|
||||
std::unique_lock client_channel_lock(target->get_channel_lock());
|
||||
TIMING_STEP(timings, "lock own ch tree");
|
||||
|
||||
if (s_source_channel) {
|
||||
s_source_channel->properties()[property::CHANNEL_LAST_LEFT] = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
||||
this->virtual_server_->group_manager()->assignments().cleanup_channel_temporary_assignment(target->getClientDatabaseId(), s_source_channel->channelId());
|
||||
auto update = target->properties()[property::CLIENT_IS_TALKER].as<bool>() || target->properties()[property::CLIENT_TALK_REQUEST].as<int64_t>() > 0;
|
||||
|
||||
if(update) {
|
||||
target->properties()[property::CLIENT_IS_TALKER] = 0;
|
||||
target->properties()[property::CLIENT_TALK_REQUEST] = 0;
|
||||
target->properties()[property::CLIENT_TALK_REQUEST_MSG] = "";
|
||||
client_updates.push_back(property::CLIENT_IS_TALKER);
|
||||
client_updates.push_back(property::CLIENT_TALK_REQUEST);
|
||||
client_updates.push_back(property::CLIENT_TALK_REQUEST_MSG);
|
||||
}
|
||||
TIMING_STEP(timings, "src chan up");
|
||||
}
|
||||
|
||||
if (s_target_channel) {
|
||||
if(target->update_cached_permissions()) /* update cached calculated permissions */
|
||||
target->sendNeededPermissions(false);
|
||||
TIMING_STEP(timings, "perm gr upd");
|
||||
|
||||
if(s_source_channel) {
|
||||
std::deque<ChannelId> deleted;
|
||||
for(const auto& channel : target->channel_view()->test_channel(l_source_channel, l_target_channel))
|
||||
deleted.push_back(channel->channelId());
|
||||
|
||||
if(!deleted.empty())
|
||||
target->notifyChannelHide(deleted, false);
|
||||
|
||||
/* force unsubscribe from old channel */
|
||||
auto i_source_channel = s_source_channel->channelId();
|
||||
if(std::find(deleted.begin(), deleted.end(), i_source_channel) == deleted.end()) {
|
||||
auto source_channel_sub_power = target->calculate_permission(permission::i_channel_subscribe_power, i_source_channel);
|
||||
if(!s_source_channel->permission_granted(permission::i_channel_needed_subscribe_power, source_channel_sub_power, false)) {
|
||||
auto source_channel_sub_power_ignore = target->calculate_permission(permission::b_channel_ignore_subscribe_power, i_source_channel);
|
||||
if(!permission::v2::permission_granted(1, source_channel_sub_power_ignore, true)) {
|
||||
logTrace(this->get_server_id(), "Force unsubscribing of client {} for channel {}/{}. (Channel switch and no permissions)",
|
||||
CLIENT_STR_LOG_PREFIX_(target), s_source_channel->name(),
|
||||
i_source_channel
|
||||
);
|
||||
target->unsubscribeChannel({s_source_channel}, false); //Unsubscribe last channel (hasn't permissions)
|
||||
}
|
||||
}
|
||||
}
|
||||
TIMING_STEP(timings, "src hide ts");
|
||||
}
|
||||
}
|
||||
client_channel_lock.unlock();
|
||||
/* both methods lock if they require stuff */
|
||||
this->notifyClientPropertyUpdates(target, client_updates, s_source_channel ? true : false);
|
||||
TIMING_STEP(timings, "notify cpro");
|
||||
if(s_target_channel) {
|
||||
target->updateChannelClientProperties(false, s_source_channel ? true : false);
|
||||
TIMING_STEP(timings, "notify_t_pr");
|
||||
}
|
||||
debugMessage(this->get_server_id(), "{} Client move timings: {}", CLIENT_STR_LOG_PREFIX_(target), TIMING_FINISH(timings));
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. flag channel as deleted (lock channel tree so no moves)
|
||||
* 2. Gather all clients within the channel (lock their execute lock)
|
||||
* 3. Unlock channel tree and lock client locks
|
||||
* 4. lock channel tree again and move the clients (No new clients should be joined because channel is flagged as deleted!)
|
||||
*
|
||||
* Note: channel cant be a ref because the channel itself gets deleted!
|
||||
*/
|
||||
ChannelDeleteResult ClientChannelService::delete_channel(std::shared_ptr<ServerChannel> channel, const std::shared_ptr<ConnectedClient> &invoker, const std::string &kick_message, ts::rwshared_lock<ts::rw_mutex> &tree_lock) {
|
||||
if(auto err = tree_lock.auto_lock_exclusive(); err)
|
||||
return ChannelDeleteResult::TREE_LOCK_FAILED;
|
||||
|
||||
if(channel->deleted)
|
||||
return ChannelDeleteResult::SUCCESS;
|
||||
|
||||
std::deque<std::shared_ptr<ConnectedClient>> clients;
|
||||
{
|
||||
for(const auto& sub_channel : this->virtual_server_->server_channel_tree()->channels(channel)) {
|
||||
auto s_channel = dynamic_pointer_cast<ServerChannel>(sub_channel);
|
||||
assert(s_channel);
|
||||
|
||||
auto chan_clients = this->virtual_server_->find_clients_by_channel(s_channel);
|
||||
clients.insert(clients.end(), chan_clients.begin(), chan_clients.end());
|
||||
s_channel->deleted = true;
|
||||
}
|
||||
auto chan_clients = this->virtual_server_->find_clients_by_channel(channel);
|
||||
clients.insert(clients.end(), chan_clients.begin(), chan_clients.end());
|
||||
channel->deleted = true;
|
||||
}
|
||||
auto default_channel = dynamic_pointer_cast<ServerChannel>(this->virtual_server_->server_channel_tree()->getDefaultChannel());
|
||||
assert(default_channel);
|
||||
tree_lock.auto_unlock();
|
||||
|
||||
std::deque<std::unique_lock<threads::Mutex>> command_locks;
|
||||
for(const auto& client : clients)
|
||||
command_locks.push_back(std::move(std::unique_lock(client->get_channel_lock())));
|
||||
|
||||
for(const auto& client : clients) {
|
||||
auto result = this->client_move(client, default_channel, invoker, kick_message, ViewReasonId::VREASON_CHANNEL_KICK, true, tree_lock);
|
||||
if(result != ClientMoveResult::SUCCESS)
|
||||
return ChannelDeleteResult::CLIENT_MOVE_FAILED;
|
||||
}
|
||||
|
||||
if(auto err = tree_lock.auto_lock_exclusive(); err)
|
||||
return ChannelDeleteResult::TREE_LOCK_FAILED;
|
||||
command_locks.clear();
|
||||
|
||||
auto channel_ids = this->virtual_server_->server_channel_tree()->delete_channel_root(channel);
|
||||
tree_lock.auto_lock_shared();
|
||||
|
||||
for(auto& client : this->virtual_server_->connected_clients(false)) {
|
||||
std::unique_lock client_channel_lock(client->get_channel_lock());
|
||||
client->notifyChannelDeleted(client->channel_view()->delete_channel_root(channel), invoker);
|
||||
}
|
||||
return ChannelDeleteResult::SUCCESS;
|
||||
}
|
@ -1,94 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <Definitions.h>
|
||||
#include <lock/rw_mutex.h>
|
||||
|
||||
namespace ts {
|
||||
class ServerChannel;
|
||||
}
|
||||
|
||||
namespace ts::server {
|
||||
class ConnectedClient;
|
||||
|
||||
namespace vserver {
|
||||
class VirtualServerBase;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ts::server::channels {
|
||||
enum struct ClientMoveResult {
|
||||
SUCCESS,
|
||||
TREE_LOCK_FAILED,
|
||||
VIEW_INSERT_FAILED
|
||||
};
|
||||
|
||||
enum struct ChannelDeleteResult {
|
||||
SUCCESS,
|
||||
TREE_LOCK_FAILED,
|
||||
CLIENT_MOVE_FAILED
|
||||
};
|
||||
|
||||
struct ChannelGroupInheritance {
|
||||
GroupId group_id{};
|
||||
ChannelId inherited_channel_id{};
|
||||
};
|
||||
|
||||
class ClientChannelService {
|
||||
public:
|
||||
explicit ClientChannelService(vserver::VirtualServerBase*);
|
||||
~ClientChannelService();
|
||||
|
||||
bool initialize(std::string& /* error */);
|
||||
|
||||
/* channel group related stuff */
|
||||
/* this will lock the channel tree in read mode */
|
||||
[[nodiscard]] ChannelGroupInheritance calculate_channel_group(ChannelId /* channel id */, ClientDbId /* client db id */, ClientType /* fallback type */);
|
||||
|
||||
/*
|
||||
* ATTENTION: The methods client_ban, client_kick, client_move unlock the channel tree completely.
|
||||
* All channel tree related variable must be checked again before using.
|
||||
*/
|
||||
/* Note: Use only this method to disconnect the client and notify everybody else that he has been banned! */
|
||||
/* remove a client from the channel tree because he's banned */
|
||||
[[nodiscard]] ClientMoveResult client_ban(
|
||||
const std::shared_ptr<ConnectedClient>& /* client */,
|
||||
const std::shared_ptr<ConnectedClient>& /* invoker */,
|
||||
const std::string& /* reason */,
|
||||
size_t /* length */,
|
||||
ts::rwshared_lock<ts::rw_mutex>& /* tree lock */);
|
||||
|
||||
/* remove/move a client from/within the channel tree because he has been kicked */
|
||||
[[nodiscard]] ClientMoveResult client_kick(
|
||||
const std::shared_ptr<ConnectedClient>& /* client */,
|
||||
const std::shared_ptr<ConnectedClient>& /* invoker */,
|
||||
const std::string& /* reason */,
|
||||
const std::shared_ptr<ServerChannel>& /* target channel */,
|
||||
ts::rwshared_lock<ts::rw_mutex>& /* tree lock */
|
||||
);
|
||||
|
||||
/* move a client within the channel tree */
|
||||
[[nodiscard]] ClientMoveResult client_move(
|
||||
const std::shared_ptr<ConnectedClient>& /* client */,
|
||||
std::shared_ptr<ServerChannel> /* target channel */,
|
||||
const std::shared_ptr<ConnectedClient>& /* invoker */,
|
||||
const std::string& /* reason */,
|
||||
ViewReasonId /* reason id */,
|
||||
bool /* notify the client */,
|
||||
ts::rwshared_lock<ts::rw_mutex>& /* tree lock */
|
||||
);
|
||||
|
||||
[[nodiscard]] ChannelDeleteResult delete_channel(
|
||||
std::shared_ptr<ServerChannel> /* target channel */,
|
||||
const std::shared_ptr<ConnectedClient>& /* invoker */,
|
||||
const std::string& /* kick message */,
|
||||
ts::rwshared_lock<ts::rw_mutex>& /* tree lock */
|
||||
);
|
||||
private:
|
||||
vserver::VirtualServerBase* virtual_server_{nullptr};
|
||||
[[nodiscard]] ServerId get_server_id() const;
|
||||
};
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <Definitions.h>
|
||||
#include <lock/rw_mutex.h>
|
||||
|
||||
namespace ts {
|
||||
class ServerChannel;
|
||||
}
|
||||
|
||||
namespace ts::server {
|
||||
class ConnectedClient;
|
||||
|
||||
namespace vserver {
|
||||
class VirtualServerBase;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ts::server::channels {
|
||||
enum struct ClientMoveResult {
|
||||
SUCCESS,
|
||||
TREE_LOCK_FAILED,
|
||||
VIEW_INSERT_FAILED
|
||||
};
|
||||
|
||||
enum struct ChannelDeleteResult {
|
||||
SUCCESS,
|
||||
TREE_LOCK_FAILED,
|
||||
CLIENT_MOVE_FAILED
|
||||
};
|
||||
|
||||
struct ChannelGroupInheritance {
|
||||
GroupId group_id{};
|
||||
ChannelId inherited_channel_id{};
|
||||
};
|
||||
|
||||
class ClientChannelService {
|
||||
public:
|
||||
explicit ClientChannelService(vserver::VirtualServerBase*);
|
||||
~ClientChannelService();
|
||||
|
||||
bool initialize(std::string& /* error */);
|
||||
|
||||
/* channel group related stuff */
|
||||
/* this will lock the channel tree in read mode */
|
||||
[[nodiscard]] ChannelGroupInheritance calculate_channel_group(ChannelId /* channel id */, ClientDbId /* client db id */, ClientType /* fallback type */);
|
||||
|
||||
/*
|
||||
* ATTENTION: The methods client_ban, client_kick, client_move unlock the channel tree completely.
|
||||
* All channel tree related variable must be checked again before using.
|
||||
*/
|
||||
/* Note: Use only this method to disconnect the client and notify everybody else that he has been banned! */
|
||||
/* remove a client from the channel tree because he's banned */
|
||||
[[nodiscard]] ClientMoveResult client_ban(
|
||||
const std::shared_ptr<ConnectedClient>& /* client */,
|
||||
const std::shared_ptr<ConnectedClient>& /* invoker */,
|
||||
const std::string& /* reason */,
|
||||
size_t /* length */,
|
||||
ts::rwshared_lock<ts::rw_mutex>& /* tree lock */);
|
||||
|
||||
/* remove/move a client from/within the channel tree because he has been kicked */
|
||||
[[nodiscard]] ClientMoveResult client_kick(
|
||||
const std::shared_ptr<ConnectedClient>& /* client */,
|
||||
const std::shared_ptr<ConnectedClient>& /* invoker */,
|
||||
const std::string& /* reason */,
|
||||
const std::shared_ptr<ServerChannel>& /* target channel */,
|
||||
ts::rwshared_lock<ts::rw_mutex>& /* tree lock */
|
||||
);
|
||||
|
||||
/* move a client within the channel tree */
|
||||
[[nodiscard]] ClientMoveResult client_move(
|
||||
const std::shared_ptr<ConnectedClient>& /* client */,
|
||||
std::shared_ptr<ServerChannel> /* target channel */,
|
||||
const std::shared_ptr<ConnectedClient>& /* invoker */,
|
||||
const std::string& /* reason */,
|
||||
ViewReasonId /* reason id */,
|
||||
bool /* notify the client */,
|
||||
ts::rwshared_lock<ts::rw_mutex>& /* tree lock */
|
||||
);
|
||||
|
||||
[[nodiscard]] ChannelDeleteResult delete_channel(
|
||||
std::shared_ptr<ServerChannel> /* target channel */,
|
||||
const std::shared_ptr<ConnectedClient>& /* invoker */,
|
||||
const std::string& /* kick message */,
|
||||
ts::rwshared_lock<ts::rw_mutex>& /* tree lock */
|
||||
);
|
||||
private:
|
||||
vserver::VirtualServerBase* virtual_server_{nullptr};
|
||||
[[nodiscard]] ServerId get_server_id() const;
|
||||
};
|
||||
}
|
@ -1,319 +1,319 @@
|
||||
//
|
||||
// Created by WolverinDEV on 03/03/2020.
|
||||
//
|
||||
|
||||
#include <log/LogUtils.h>
|
||||
#include "./PermissionsService.h"
|
||||
#include "../InstanceHandler.h"
|
||||
#include "../vserver/VirtualServerBase.h"
|
||||
#include "../groups/Group.h"
|
||||
#include "../groups/GroupManager.h"
|
||||
#include "../groups/GroupAssignmentManager.h"
|
||||
|
||||
using namespace ts::permission;
|
||||
using namespace ts::permission::v2;
|
||||
using namespace ts::server::permissions;
|
||||
|
||||
PermissionService::PermissionService(ts::server::vserver::VirtualServerBase *handle) : virtual_server_{handle} {}
|
||||
|
||||
bool PermissionService::initialize(std::string &) { return true; }
|
||||
|
||||
bool PermissionService::load_data(std::string &) { return true; }
|
||||
void PermissionService::unload_data() {}
|
||||
|
||||
ts::ServerId PermissionService::get_server_id() const {
|
||||
return this->virtual_server_->server_id();
|
||||
}
|
||||
|
||||
PermissionFlaggedValue PermissionService::calculate_client_permission(
|
||||
PermissionType permission,
|
||||
ClientDbId cldbid,
|
||||
ClientType type,
|
||||
ChannelId channel,
|
||||
bool granted,
|
||||
std::shared_ptr<CalculateCache> cache) {
|
||||
auto result = this->calculate_client_permissions({permission}, cldbid, type, channel, granted, std::move(cache));
|
||||
if(result.empty()) return {0, false};
|
||||
|
||||
return result.front().second;
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::pair<ts::permission::PermissionType, ts::permission::v2::PermissionFlaggedValue>> PermissionService::calculate_client_permissions(
|
||||
const std::deque<permission::PermissionType>& permissions,
|
||||
ClientDbId client_dbid,
|
||||
ClientType client_type,
|
||||
ChannelId channel_id,
|
||||
bool calculate_granted,
|
||||
std::shared_ptr<CalculateCache> cache) {
|
||||
if(permissions.empty()) return {};
|
||||
|
||||
std::vector<std::pair<ts::permission::PermissionType, ts::permission::v2::PermissionFlaggedValue>> result{};
|
||||
result.reserve(permissions.size());
|
||||
|
||||
if(cache->client_database_id && cache->client_database_id != client_dbid)
|
||||
cache = nullptr;
|
||||
|
||||
if(!cache)
|
||||
cache = std::make_shared<CalculateCache>();
|
||||
cache->client_type = client_type;
|
||||
cache->client_database_id = client_dbid;
|
||||
|
||||
if(!cache->client_permissions)
|
||||
cache->client_permissions = serverInstance->databaseHelper()->loadClientPermissionManager(this->virtual_server_->server_ref(), client_dbid);
|
||||
|
||||
bool have_skip_permission = false;
|
||||
int skip_permission_type = -1; /* -1 := unset | 0 := skip, not explicit | 1 := skip, explicit */
|
||||
|
||||
bool have_skip;
|
||||
|
||||
/*
|
||||
* server_group_data[0] := Server group id
|
||||
* server_group_data[1] := Skip flag
|
||||
* server_group_data[2] := Negate flag
|
||||
* server_group_data[3] := Permission value
|
||||
*/
|
||||
typedef std::tuple<GroupId, bool, bool, permission::PermissionValue> GroupData;
|
||||
bool server_group_data_initialized = false;
|
||||
std::vector<GroupData> server_group_data;
|
||||
GroupData* active_server_group;
|
||||
|
||||
/* function to calculate skip permission */
|
||||
auto calculate_skip = [&]{
|
||||
skip_permission_type = 0;
|
||||
/* test for skip permission within the client permission manager */
|
||||
{
|
||||
auto skip_value = cache->client_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions);
|
||||
if(skip_value.has_value) {
|
||||
have_skip_permission = skip_value.value == 1;
|
||||
skip_permission_type = 1;
|
||||
logTrace(this->get_server_id(), "[Permission] Found skip permission in client permissions. Value: {}", have_skip_permission);
|
||||
}
|
||||
}
|
||||
/* test for skip permission within all server groups */
|
||||
if(skip_permission_type != 1) {
|
||||
this->load_group_assignments(cache);
|
||||
assert(cache->assignment_server_groups_set);
|
||||
for(const auto& assignment : cache->assignment_server_groups) {
|
||||
auto group_permissions = assignment->permissions();
|
||||
auto flagged_value = group_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions);
|
||||
if(flagged_value.has_value) {
|
||||
have_skip_permission |= flagged_value.value == 1;
|
||||
if(have_skip_permission) {
|
||||
logTrace(this->get_server_id(), "[Permission] Found skip permission in client server group. Group: {} ({}), Value: {}", assignment->group_id(), assignment->display_name(), have_skip_permission);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto initialize_group_data = [&](const permission::PermissionType& permission_type) {
|
||||
server_group_data_initialized = true;
|
||||
active_server_group = nullptr;
|
||||
|
||||
this->load_group_assignments(cache);
|
||||
assert(cache->assignment_server_groups_set);
|
||||
|
||||
server_group_data.resize(cache->assignment_server_groups.size());
|
||||
auto it = server_group_data.begin();
|
||||
for(auto& group : cache->assignment_server_groups) {
|
||||
auto group_permissions = group->permissions();
|
||||
auto permission_flags = group_permissions->permission_flags(permission_type);
|
||||
|
||||
auto flag_set = calculate_granted ? permission_flags.grant_set : permission_flags.value_set;
|
||||
if(!flag_set)
|
||||
continue;
|
||||
|
||||
//TODO: Test if there is may a group channel permissions
|
||||
auto value = group_permissions->permission_values(permission_type);
|
||||
*it = std::make_tuple(group->group_id(), (bool) permission_flags.skip, (bool) permission_flags.negate, calculate_granted ? value.grant : value.value);
|
||||
it++;
|
||||
}
|
||||
if(it == server_group_data.begin())
|
||||
return; /* no server group has that permission */
|
||||
|
||||
server_group_data.erase(it, server_group_data.end()); /* remove unneeded */
|
||||
|
||||
auto found_negate = false;
|
||||
for(auto& group : server_group_data) {
|
||||
if(std::get<2>(group)) {
|
||||
found_negate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(found_negate) {
|
||||
server_group_data.erase(remove_if(server_group_data.begin(), server_group_data.end(), [](auto data) { return !std::get<2>(data); }), server_group_data.end());
|
||||
logTrace(this->get_server_id(), "[Permission] Found negate flag within server groups. Groups left: {}", server_group_data.size());
|
||||
if(server_group_data.empty())
|
||||
logTrace(this->get_server_id(), "[Permission] After non negated groups have been kicked out the negated groups are empty! This should not happen! Permission: {}, Client ID: {}", permission_type, client_dbid);
|
||||
permission::PermissionValue current_lowest = 0;
|
||||
for(auto& group : server_group_data) {
|
||||
if(!active_server_group || (std::get<3>(group) < current_lowest && std::get<3>(group) != -1)) {
|
||||
current_lowest = std::get<3>(group);
|
||||
active_server_group = &group;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
permission::PermissionValue current_highest = 0;
|
||||
for(auto& group : server_group_data) {
|
||||
if(!active_server_group || (std::get<3>(group) > current_highest || std::get<3>(group) == -1)) {
|
||||
current_highest = std::get<3>(group);
|
||||
active_server_group = &group;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for(const auto& permission : permissions) {
|
||||
if(permission == permission::b_client_skip_channelgroup_permissions) {
|
||||
if(skip_permission_type == -1) /* initialize skip flag */
|
||||
calculate_skip();
|
||||
result.push_back({permission, {have_skip_permission, skip_permission_type == 1}});
|
||||
continue;
|
||||
}
|
||||
|
||||
server_group_data_initialized = false; /* reset all group data */
|
||||
auto client_permission_flags = cache->client_permissions->permission_flags(permission);
|
||||
/* lets try to resolve the channel specific permission */
|
||||
if(channel_id > 0 && client_permission_flags.channel_specific) {
|
||||
auto data = cache->client_permissions->channel_permission(permission, channel_id);
|
||||
if(calculate_granted ? data.flags.grant_set : data.flags.value_set) {
|
||||
result.push_back({permission, {calculate_granted ? data.values.grant : data.values.value, true}});
|
||||
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Client channel permission)", client_dbid, permission::resolvePermissionData(permission)->name, data.values.value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
have_skip = channel_id == 0;
|
||||
if(!have_skip) {
|
||||
/* look if somewhere is the skip permission flag set */
|
||||
if(skip_permission_type == -1) /* initialize skip flag */
|
||||
calculate_skip();
|
||||
have_skip = have_skip_permission;
|
||||
}
|
||||
if(!have_skip) {
|
||||
/* okey we've no global skip. Then now lookup the groups and the client permissions */
|
||||
if(calculate_granted ? client_permission_flags.grant_set : client_permission_flags.value_set) {
|
||||
/* okey the client has the permission, this counts */
|
||||
have_skip = client_permission_flags.skip;
|
||||
} else {
|
||||
if(!server_group_data_initialized)
|
||||
initialize_group_data(permission);
|
||||
if(active_server_group)
|
||||
have_skip = std::get<1>(*active_server_group);
|
||||
}
|
||||
}
|
||||
|
||||
if(!have_skip) {
|
||||
/* lookup the channel group */
|
||||
{
|
||||
this->load_channel_assignment(cache, channel_id);
|
||||
assert(cache->assignment_channel_group_channel == channel_id);
|
||||
|
||||
auto channel_assignment = cache->assignment_channel_group;
|
||||
if(channel_assignment) {
|
||||
auto group_permissions = channel_assignment->permissions();
|
||||
auto permission_flags = group_permissions->permission_flags(permission);
|
||||
|
||||
auto flag_set = calculate_granted ? permission_flags.grant_set : permission_flags.value_set;
|
||||
if(flag_set) {
|
||||
auto value = group_permissions->permission_values(permission);
|
||||
result.push_back({permission, {calculate_granted ? value.grant : value.value, true}});
|
||||
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Channel group permission)", client_dbid, permission::resolvePermissionData(permission)->name, calculate_granted ? value.grant : value.value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* lookup the channel permissions. Whyever? */
|
||||
{
|
||||
this->load_server_channel(cache, channel_id);
|
||||
if(cache->server_channel) {
|
||||
auto channel_permissions = cache->server_channel->permissions();
|
||||
auto data = calculate_granted ? channel_permissions->permission_granted_flagged(permission) : channel_permissions->permission_value_flagged(permission);
|
||||
if(data.has_value) {
|
||||
result.push_back({permission, {data.value, true}});
|
||||
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Channel permission)", client_dbid, permission::resolvePermissionData(permission)->name, data.value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(calculate_granted ? client_permission_flags.grant_set : client_permission_flags.value_set) {
|
||||
auto client_value = cache->client_permissions->permission_values(permission);
|
||||
result.push_back({permission, {calculate_granted ? client_value.grant : client_value.value, true}});
|
||||
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Client permission)", client_dbid, permission::resolvePermissionData(permission)->name, client_value.value);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!server_group_data_initialized)
|
||||
initialize_group_data(permission);
|
||||
if(active_server_group) {
|
||||
result.push_back({permission, {get<3>(*active_server_group), true}});
|
||||
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Server group permission of group {})", client_dbid, permission::resolvePermissionData(permission)->name, get<3>(*active_server_group), get<0>(*active_server_group));
|
||||
continue;
|
||||
}
|
||||
|
||||
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned in no permission.", client_dbid, permission::resolvePermissionData(permission)->name);
|
||||
result.push_back({permission, {permNotGranted, false}});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void PermissionService::load_group_assignments(const std::shared_ptr<CalculateCache> &cache) {
|
||||
if(cache->assignment_server_groups_set) return;
|
||||
cache->assignment_server_groups_set = true;
|
||||
|
||||
auto group_manager = this->virtual_server_->group_manager();
|
||||
auto assignments = group_manager->assignments().server_groups_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, cache->client_database_id);
|
||||
cache->assignment_server_groups.reserve(assignments.size());
|
||||
|
||||
for(auto& entry : assignments) {
|
||||
auto group = group_manager->find_server_group(entry);
|
||||
if(!group) continue;
|
||||
|
||||
cache->assignment_server_groups.push_back(group);
|
||||
}
|
||||
|
||||
if(cache->assignment_server_groups.empty())
|
||||
cache->assignment_server_groups.push_back(group_manager->default_server_group(cache->client_type));
|
||||
}
|
||||
|
||||
void PermissionService::load_channel_assignment(const std::shared_ptr<CalculateCache> &cache, ts::ChannelId channel_id) {
|
||||
if(cache->assignment_channel_group_channel == channel_id) return;
|
||||
cache->assignment_channel_group_channel = channel_id;
|
||||
|
||||
auto group_manager = this->virtual_server_->group_manager();
|
||||
|
||||
auto channel_groups = group_manager->assignments().channel_groups_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, cache->client_database_id);
|
||||
auto it = std::find_if(channel_groups.begin(), channel_groups.end(), [&](const groups::ChannelGroupAssignment& assignment) {
|
||||
return assignment.channel_id == channel_id;
|
||||
});
|
||||
|
||||
GroupId group_id{0};
|
||||
if(it != channel_groups.end())
|
||||
group_id = it->group_id;
|
||||
|
||||
if(!group_id) {
|
||||
auto assignment = this->virtual_server_->channel_service().calculate_channel_group(channel_id, cache->client_database_id, cache->client_type);
|
||||
group_id = assignment.group_id;
|
||||
}
|
||||
|
||||
cache->assignment_channel_group = group_manager->find_channel_group(group_id);
|
||||
if(!cache->assignment_channel_group)
|
||||
cache->assignment_channel_group = group_manager->default_channel_group(cache->client_type);
|
||||
}
|
||||
|
||||
void PermissionService::load_server_channel(const std::shared_ptr<CalculateCache> &cache, ts::ChannelId channel_id) {
|
||||
if(cache->last_server_channel == channel_id) return;
|
||||
cache->last_server_channel = channel_id;
|
||||
|
||||
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);
|
||||
//
|
||||
// Created by WolverinDEV on 03/03/2020.
|
||||
//
|
||||
|
||||
#include <log/LogUtils.h>
|
||||
#include "./PermissionsService.h"
|
||||
#include "../InstanceHandler.h"
|
||||
#include "../vserver/VirtualServerBase.h"
|
||||
#include "../groups/Group.h"
|
||||
#include "../groups/GroupManager.h"
|
||||
#include "../groups/GroupAssignmentManager.h"
|
||||
|
||||
using namespace ts::permission;
|
||||
using namespace ts::permission::v2;
|
||||
using namespace ts::server::permissions;
|
||||
|
||||
PermissionService::PermissionService(ts::server::vserver::VirtualServerBase *handle) : virtual_server_{handle} {}
|
||||
|
||||
bool PermissionService::initialize(std::string &) { return true; }
|
||||
|
||||
bool PermissionService::load_data(std::string &) { return true; }
|
||||
void PermissionService::unload_data() {}
|
||||
|
||||
ts::ServerId PermissionService::get_server_id() const {
|
||||
return this->virtual_server_->server_id();
|
||||
}
|
||||
|
||||
PermissionFlaggedValue PermissionService::calculate_client_permission(
|
||||
PermissionType permission,
|
||||
ClientDbId cldbid,
|
||||
ClientType type,
|
||||
ChannelId channel,
|
||||
bool granted,
|
||||
std::shared_ptr<CalculateCache> cache) {
|
||||
auto result = this->calculate_client_permissions({permission}, cldbid, type, channel, granted, std::move(cache));
|
||||
if(result.empty()) return {0, false};
|
||||
|
||||
return result.front().second;
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::pair<ts::permission::PermissionType, ts::permission::v2::PermissionFlaggedValue>> PermissionService::calculate_client_permissions(
|
||||
const std::deque<permission::PermissionType>& permissions,
|
||||
ClientDbId client_dbid,
|
||||
ClientType client_type,
|
||||
ChannelId channel_id,
|
||||
bool calculate_granted,
|
||||
std::shared_ptr<CalculateCache> cache) {
|
||||
if(permissions.empty()) return {};
|
||||
|
||||
std::vector<std::pair<ts::permission::PermissionType, ts::permission::v2::PermissionFlaggedValue>> result{};
|
||||
result.reserve(permissions.size());
|
||||
|
||||
if(cache->client_database_id && cache->client_database_id != client_dbid)
|
||||
cache = nullptr;
|
||||
|
||||
if(!cache)
|
||||
cache = std::make_shared<CalculateCache>();
|
||||
cache->client_type = client_type;
|
||||
cache->client_database_id = client_dbid;
|
||||
|
||||
if(!cache->client_permissions)
|
||||
cache->client_permissions = serverInstance->databaseHelper()->loadClientPermissionManager(this->virtual_server_->server_ref(), client_dbid);
|
||||
|
||||
bool have_skip_permission = false;
|
||||
int skip_permission_type = -1; /* -1 := unset | 0 := skip, not explicit | 1 := skip, explicit */
|
||||
|
||||
bool have_skip;
|
||||
|
||||
/*
|
||||
* server_group_data[0] := Server group id
|
||||
* server_group_data[1] := Skip flag
|
||||
* server_group_data[2] := Negate flag
|
||||
* server_group_data[3] := Permission value
|
||||
*/
|
||||
typedef std::tuple<GroupId, bool, bool, permission::PermissionValue> GroupData;
|
||||
bool server_group_data_initialized = false;
|
||||
std::vector<GroupData> server_group_data;
|
||||
GroupData* active_server_group;
|
||||
|
||||
/* function to calculate skip permission */
|
||||
auto calculate_skip = [&]{
|
||||
skip_permission_type = 0;
|
||||
/* test for skip permission within the client permission manager */
|
||||
{
|
||||
auto skip_value = cache->client_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions);
|
||||
if(skip_value.has_value) {
|
||||
have_skip_permission = skip_value.value == 1;
|
||||
skip_permission_type = 1;
|
||||
logTrace(this->get_server_id(), "[Permission] Found skip permission in client permissions. Value: {}", have_skip_permission);
|
||||
}
|
||||
}
|
||||
/* test for skip permission within all server groups */
|
||||
if(skip_permission_type != 1) {
|
||||
this->load_group_assignments(cache);
|
||||
assert(cache->assignment_server_groups_set);
|
||||
for(const auto& assignment : cache->assignment_server_groups) {
|
||||
auto group_permissions = assignment->permissions();
|
||||
auto flagged_value = group_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions);
|
||||
if(flagged_value.has_value) {
|
||||
have_skip_permission |= flagged_value.value == 1;
|
||||
if(have_skip_permission) {
|
||||
logTrace(this->get_server_id(), "[Permission] Found skip permission in client server group. Group: {} ({}), Value: {}", assignment->group_id(), assignment->display_name(), have_skip_permission);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto initialize_group_data = [&](const permission::PermissionType& permission_type) {
|
||||
server_group_data_initialized = true;
|
||||
active_server_group = nullptr;
|
||||
|
||||
this->load_group_assignments(cache);
|
||||
assert(cache->assignment_server_groups_set);
|
||||
|
||||
server_group_data.resize(cache->assignment_server_groups.size());
|
||||
auto it = server_group_data.begin();
|
||||
for(auto& group : cache->assignment_server_groups) {
|
||||
auto group_permissions = group->permissions();
|
||||
auto permission_flags = group_permissions->permission_flags(permission_type);
|
||||
|
||||
auto flag_set = calculate_granted ? permission_flags.grant_set : permission_flags.value_set;
|
||||
if(!flag_set)
|
||||
continue;
|
||||
|
||||
//TODO: Test if there is may a group channel permissions
|
||||
auto value = group_permissions->permission_values(permission_type);
|
||||
*it = std::make_tuple(group->group_id(), (bool) permission_flags.skip, (bool) permission_flags.negate, calculate_granted ? value.grant : value.value);
|
||||
it++;
|
||||
}
|
||||
if(it == server_group_data.begin())
|
||||
return; /* no server group has that permission */
|
||||
|
||||
server_group_data.erase(it, server_group_data.end()); /* remove unneeded */
|
||||
|
||||
auto found_negate = false;
|
||||
for(auto& group : server_group_data) {
|
||||
if(std::get<2>(group)) {
|
||||
found_negate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(found_negate) {
|
||||
server_group_data.erase(remove_if(server_group_data.begin(), server_group_data.end(), [](auto data) { return !std::get<2>(data); }), server_group_data.end());
|
||||
logTrace(this->get_server_id(), "[Permission] Found negate flag within server groups. Groups left: {}", server_group_data.size());
|
||||
if(server_group_data.empty())
|
||||
logTrace(this->get_server_id(), "[Permission] After non negated groups have been kicked out the negated groups are empty! This should not happen! Permission: {}, Client ID: {}", permission_type, client_dbid);
|
||||
permission::PermissionValue current_lowest = 0;
|
||||
for(auto& group : server_group_data) {
|
||||
if(!active_server_group || (std::get<3>(group) < current_lowest && std::get<3>(group) != -1)) {
|
||||
current_lowest = std::get<3>(group);
|
||||
active_server_group = &group;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
permission::PermissionValue current_highest = 0;
|
||||
for(auto& group : server_group_data) {
|
||||
if(!active_server_group || (std::get<3>(group) > current_highest || std::get<3>(group) == -1)) {
|
||||
current_highest = std::get<3>(group);
|
||||
active_server_group = &group;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for(const auto& permission : permissions) {
|
||||
if(permission == permission::b_client_skip_channelgroup_permissions) {
|
||||
if(skip_permission_type == -1) /* initialize skip flag */
|
||||
calculate_skip();
|
||||
result.push_back({permission, {have_skip_permission, skip_permission_type == 1}});
|
||||
continue;
|
||||
}
|
||||
|
||||
server_group_data_initialized = false; /* reset all group data */
|
||||
auto client_permission_flags = cache->client_permissions->permission_flags(permission);
|
||||
/* lets try to resolve the channel specific permission */
|
||||
if(channel_id > 0 && client_permission_flags.channel_specific) {
|
||||
auto data = cache->client_permissions->channel_permission(permission, channel_id);
|
||||
if(calculate_granted ? data.flags.grant_set : data.flags.value_set) {
|
||||
result.push_back({permission, {calculate_granted ? data.values.grant : data.values.value, true}});
|
||||
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Client channel permission)", client_dbid, permission::resolvePermissionData(permission)->name, data.values.value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
have_skip = channel_id == 0;
|
||||
if(!have_skip) {
|
||||
/* look if somewhere is the skip permission flag set */
|
||||
if(skip_permission_type == -1) /* initialize skip flag */
|
||||
calculate_skip();
|
||||
have_skip = have_skip_permission;
|
||||
}
|
||||
if(!have_skip) {
|
||||
/* okey we've no global skip. Then now lookup the groups and the client permissions */
|
||||
if(calculate_granted ? client_permission_flags.grant_set : client_permission_flags.value_set) {
|
||||
/* okey the client has the permission, this counts */
|
||||
have_skip = client_permission_flags.skip;
|
||||
} else {
|
||||
if(!server_group_data_initialized)
|
||||
initialize_group_data(permission);
|
||||
if(active_server_group)
|
||||
have_skip = std::get<1>(*active_server_group);
|
||||
}
|
||||
}
|
||||
|
||||
if(!have_skip) {
|
||||
/* lookup the channel group */
|
||||
{
|
||||
this->load_channel_assignment(cache, channel_id);
|
||||
assert(cache->assignment_channel_group_channel == channel_id);
|
||||
|
||||
auto channel_assignment = cache->assignment_channel_group;
|
||||
if(channel_assignment) {
|
||||
auto group_permissions = channel_assignment->permissions();
|
||||
auto permission_flags = group_permissions->permission_flags(permission);
|
||||
|
||||
auto flag_set = calculate_granted ? permission_flags.grant_set : permission_flags.value_set;
|
||||
if(flag_set) {
|
||||
auto value = group_permissions->permission_values(permission);
|
||||
result.push_back({permission, {calculate_granted ? value.grant : value.value, true}});
|
||||
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Channel group permission)", client_dbid, permission::resolvePermissionData(permission)->name, calculate_granted ? value.grant : value.value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* lookup the channel permissions. Whyever? */
|
||||
{
|
||||
this->load_server_channel(cache, channel_id);
|
||||
if(cache->server_channel) {
|
||||
auto channel_permissions = cache->server_channel->permissions();
|
||||
auto data = calculate_granted ? channel_permissions->permission_granted_flagged(permission) : channel_permissions->permission_value_flagged(permission);
|
||||
if(data.has_value) {
|
||||
result.push_back({permission, {data.value, true}});
|
||||
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Channel permission)", client_dbid, permission::resolvePermissionData(permission)->name, data.value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(calculate_granted ? client_permission_flags.grant_set : client_permission_flags.value_set) {
|
||||
auto client_value = cache->client_permissions->permission_values(permission);
|
||||
result.push_back({permission, {calculate_granted ? client_value.grant : client_value.value, true}});
|
||||
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Client permission)", client_dbid, permission::resolvePermissionData(permission)->name, client_value.value);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!server_group_data_initialized)
|
||||
initialize_group_data(permission);
|
||||
if(active_server_group) {
|
||||
result.push_back({permission, {get<3>(*active_server_group), true}});
|
||||
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Server group permission of group {})", client_dbid, permission::resolvePermissionData(permission)->name, get<3>(*active_server_group), get<0>(*active_server_group));
|
||||
continue;
|
||||
}
|
||||
|
||||
logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned in no permission.", client_dbid, permission::resolvePermissionData(permission)->name);
|
||||
result.push_back({permission, {permNotGranted, false}});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void PermissionService::load_group_assignments(const std::shared_ptr<CalculateCache> &cache) {
|
||||
if(cache->assignment_server_groups_set) return;
|
||||
cache->assignment_server_groups_set = true;
|
||||
|
||||
auto group_manager = this->virtual_server_->group_manager();
|
||||
auto assignments = group_manager->assignments().server_groups_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, cache->client_database_id);
|
||||
cache->assignment_server_groups.reserve(assignments.size());
|
||||
|
||||
for(auto& entry : assignments) {
|
||||
auto group = group_manager->find_server_group(entry);
|
||||
if(!group) continue;
|
||||
|
||||
cache->assignment_server_groups.push_back(group);
|
||||
}
|
||||
|
||||
if(cache->assignment_server_groups.empty())
|
||||
cache->assignment_server_groups.push_back(group_manager->default_server_group(cache->client_type));
|
||||
}
|
||||
|
||||
void PermissionService::load_channel_assignment(const std::shared_ptr<CalculateCache> &cache, ts::ChannelId channel_id) {
|
||||
if(cache->assignment_channel_group_channel == channel_id) return;
|
||||
cache->assignment_channel_group_channel = channel_id;
|
||||
|
||||
auto group_manager = this->virtual_server_->group_manager();
|
||||
|
||||
auto channel_groups = group_manager->assignments().channel_groups_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, cache->client_database_id);
|
||||
auto it = std::find_if(channel_groups.begin(), channel_groups.end(), [&](const groups::ChannelGroupAssignment& assignment) {
|
||||
return assignment.channel_id == channel_id;
|
||||
});
|
||||
|
||||
GroupId group_id{0};
|
||||
if(it != channel_groups.end())
|
||||
group_id = it->group_id;
|
||||
|
||||
if(!group_id) {
|
||||
auto assignment = this->virtual_server_->channel_service().calculate_channel_group(channel_id, cache->client_database_id, cache->client_type);
|
||||
group_id = assignment.group_id;
|
||||
}
|
||||
|
||||
cache->assignment_channel_group = group_manager->find_channel_group(group_id);
|
||||
if(!cache->assignment_channel_group)
|
||||
cache->assignment_channel_group = group_manager->default_channel_group(cache->client_type);
|
||||
}
|
||||
|
||||
void PermissionService::load_server_channel(const std::shared_ptr<CalculateCache> &cache, ts::ChannelId channel_id) {
|
||||
if(cache->last_server_channel == channel_id) return;
|
||||
cache->last_server_channel = channel_id;
|
||||
|
||||
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);
|
||||
}
|
@ -1,68 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <PermissionRegister.h>
|
||||
|
||||
namespace ts::server {
|
||||
namespace groups {
|
||||
class ServerGroup;
|
||||
class ChannelGroup;
|
||||
}
|
||||
namespace vserver {
|
||||
class VirtualServerBase;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ts::server::permissions {
|
||||
struct CalculateCache {
|
||||
ClientDbId client_database_id{0};
|
||||
ClientType client_type{ClientType::UNKNOWN};
|
||||
|
||||
std::shared_ptr<permission::v2::PermissionRegister> client_permissions;
|
||||
std::vector<std::shared_ptr<groups::ServerGroup>> assignment_server_groups;
|
||||
bool assignment_server_groups_set{false};
|
||||
|
||||
ChannelId assignment_channel_group_channel{0};
|
||||
std::shared_ptr<groups::ChannelGroup> assignment_channel_group{};
|
||||
|
||||
std::shared_ptr<BasicChannel> server_channel;
|
||||
ChannelId last_server_channel{0};
|
||||
};
|
||||
|
||||
class PermissionService {
|
||||
public:
|
||||
explicit PermissionService(vserver::VirtualServerBase*);
|
||||
~PermissionService() = default;
|
||||
|
||||
bool initialize(std::string& /* error */);
|
||||
|
||||
bool load_data(std::string& /* error */);
|
||||
void unload_data();
|
||||
|
||||
permission::v2::PermissionFlaggedValue calculate_client_permission(
|
||||
permission::PermissionType,
|
||||
ClientDbId,
|
||||
ClientType type,
|
||||
ChannelId channel,
|
||||
bool granted,
|
||||
std::shared_ptr<CalculateCache> cache{nullptr}
|
||||
);
|
||||
|
||||
std::vector<std::pair<permission::PermissionType, permission::v2::PermissionFlaggedValue>> calculate_client_permissions(
|
||||
const std::deque<permission::PermissionType>&,
|
||||
ClientDbId,
|
||||
ClientType type,
|
||||
ChannelId channel,
|
||||
bool granted,
|
||||
std::shared_ptr<CalculateCache> cache{nullptr}
|
||||
);
|
||||
private:
|
||||
vserver::VirtualServerBase* virtual_server_{nullptr};
|
||||
|
||||
[[nodiscard]] ServerId get_server_id() const;
|
||||
|
||||
void load_group_assignments(const std::shared_ptr<CalculateCache>&);
|
||||
void load_channel_assignment(const std::shared_ptr<CalculateCache>&, ChannelId /* channel id */);
|
||||
void load_server_channel(const std::shared_ptr<CalculateCache>&, ChannelId /* channel id */);
|
||||
};
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <PermissionRegister.h>
|
||||
|
||||
namespace ts::server {
|
||||
namespace groups {
|
||||
class ServerGroup;
|
||||
class ChannelGroup;
|
||||
}
|
||||
namespace vserver {
|
||||
class VirtualServerBase;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ts::server::permissions {
|
||||
struct CalculateCache {
|
||||
ClientDbId client_database_id{0};
|
||||
ClientType client_type{ClientType::UNKNOWN};
|
||||
|
||||
std::shared_ptr<permission::v2::PermissionRegister> client_permissions;
|
||||
std::vector<std::shared_ptr<groups::ServerGroup>> assignment_server_groups;
|
||||
bool assignment_server_groups_set{false};
|
||||
|
||||
ChannelId assignment_channel_group_channel{0};
|
||||
std::shared_ptr<groups::ChannelGroup> assignment_channel_group{};
|
||||
|
||||
std::shared_ptr<BasicChannel> server_channel;
|
||||
ChannelId last_server_channel{0};
|
||||
};
|
||||
|
||||
class PermissionService {
|
||||
public:
|
||||
explicit PermissionService(vserver::VirtualServerBase*);
|
||||
~PermissionService() = default;
|
||||
|
||||
bool initialize(std::string& /* error */);
|
||||
|
||||
bool load_data(std::string& /* error */);
|
||||
void unload_data();
|
||||
|
||||
permission::v2::PermissionFlaggedValue calculate_client_permission(
|
||||
permission::PermissionType,
|
||||
ClientDbId,
|
||||
ClientType type,
|
||||
ChannelId channel,
|
||||
bool granted,
|
||||
std::shared_ptr<CalculateCache> cache{nullptr}
|
||||
);
|
||||
|
||||
std::vector<std::pair<permission::PermissionType, permission::v2::PermissionFlaggedValue>> calculate_client_permissions(
|
||||
const std::deque<permission::PermissionType>&,
|
||||
ClientDbId,
|
||||
ClientType type,
|
||||
ChannelId channel,
|
||||
bool granted,
|
||||
std::shared_ptr<CalculateCache> cache{nullptr}
|
||||
);
|
||||
private:
|
||||
vserver::VirtualServerBase* virtual_server_{nullptr};
|
||||
|
||||
[[nodiscard]] ServerId get_server_id() const;
|
||||
|
||||
void load_group_assignments(const std::shared_ptr<CalculateCache>&);
|
||||
void load_channel_assignment(const std::shared_ptr<CalculateCache>&, ChannelId /* channel id */);
|
||||
void load_server_channel(const std::shared_ptr<CalculateCache>&, ChannelId /* channel id */);
|
||||
};
|
||||
}
|
@ -1,33 +1,33 @@
|
||||
//
|
||||
// Created by WolverinDEV on 03/03/2020.
|
||||
//
|
||||
|
||||
#include <Error.h>
|
||||
#include "DefaultServer.h"
|
||||
|
||||
using namespace ts::server::vserver;
|
||||
|
||||
VirtualServerStartResult DefaultServer::start_server_virtual_(ts::rwshared_lock<ts::rw_mutex> &lock, std::string &error) {
|
||||
if(!lock.auto_lock_exclusive()) {
|
||||
error = "failed to lock server state";
|
||||
return VirtualServerStartResult::CUSTOM;
|
||||
}
|
||||
|
||||
if(auto result = DefaultServer::start_server_virtual_(lock, error); result != VirtualServerStartResult::SUCCESS)
|
||||
return result;
|
||||
|
||||
//TODO: Load anything
|
||||
|
||||
this->status_ = VirtualServerStatus::VIRTUAL;
|
||||
return VirtualServerStartResult::SUCCESS;
|
||||
}
|
||||
|
||||
VirtualServerStartResult DefaultServer::start_server_(ts::rwshared_lock<ts::rw_mutex> &lock, std::string &string) {
|
||||
return VirtualServerStartResult::SERVER_IS_DEFAULT;
|
||||
}
|
||||
|
||||
void DefaultServer::stop_server_(ts::rwshared_lock<ts::rw_mutex> &lock) {
|
||||
VirtualServerBase::stop_server_(lock);
|
||||
|
||||
/* TODO: Save server states? Because we can't really shut down. */
|
||||
}
|
||||
//
|
||||
// Created by WolverinDEV on 03/03/2020.
|
||||
//
|
||||
|
||||
#include <Error.h>
|
||||
#include "DefaultServer.h"
|
||||
|
||||
using namespace ts::server::vserver;
|
||||
|
||||
VirtualServerStartResult DefaultServer::start_server_virtual_(ts::rwshared_lock<ts::rw_mutex> &lock, std::string &error) {
|
||||
if(!lock.auto_lock_exclusive()) {
|
||||
error = "failed to lock server state";
|
||||
return VirtualServerStartResult::CUSTOM;
|
||||
}
|
||||
|
||||
if(auto result = DefaultServer::start_server_virtual_(lock, error); result != VirtualServerStartResult::SUCCESS)
|
||||
return result;
|
||||
|
||||
//TODO: Load anything
|
||||
|
||||
this->status_ = VirtualServerStatus::VIRTUAL;
|
||||
return VirtualServerStartResult::SUCCESS;
|
||||
}
|
||||
|
||||
VirtualServerStartResult DefaultServer::start_server_(ts::rwshared_lock<ts::rw_mutex> &lock, std::string &string) {
|
||||
return VirtualServerStartResult::SERVER_IS_DEFAULT;
|
||||
}
|
||||
|
||||
void DefaultServer::stop_server_(ts::rwshared_lock<ts::rw_mutex> &lock) {
|
||||
VirtualServerBase::stop_server_(lock);
|
||||
|
||||
/* TODO: Save server states? Because we can't really shut down. */
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "./VirtualServerBase.h"
|
||||
|
||||
namespace ts::server::vserver {
|
||||
class DefaultServer : public VirtualServerBase {
|
||||
public:
|
||||
|
||||
private:
|
||||
protected:
|
||||
VirtualServerStartResult start_server_virtual_(ts::rwshared_lock<ts::rw_mutex> &lock, std::string &string) override;
|
||||
VirtualServerStartResult start_server_(ts::rwshared_lock<ts::rw_mutex> &lock, std::string &string) override;
|
||||
void stop_server_(ts::rwshared_lock<ts::rw_mutex> &lock) override;
|
||||
};
|
||||
#pragma once
|
||||
|
||||
#include "./VirtualServerBase.h"
|
||||
|
||||
namespace ts::server::vserver {
|
||||
class DefaultServer : public VirtualServerBase {
|
||||
public:
|
||||
|
||||
private:
|
||||
protected:
|
||||
VirtualServerStartResult start_server_virtual_(ts::rwshared_lock<ts::rw_mutex> &lock, std::string &string) override;
|
||||
VirtualServerStartResult start_server_(ts::rwshared_lock<ts::rw_mutex> &lock, std::string &string) override;
|
||||
void stop_server_(ts::rwshared_lock<ts::rw_mutex> &lock) override;
|
||||
};
|
||||
}
|
@ -1,26 +1,26 @@
|
||||
//
|
||||
// Created by WolverinDEV on 03/03/2020.
|
||||
//
|
||||
|
||||
#include "VirtualServer.h"
|
||||
|
||||
using namespace ts::server::vserver;
|
||||
|
||||
//This method gets called when server is OFFLINE or DEPLOYING
|
||||
VirtualServerStartResult VirtualServer::start_server_virtual_(ts::rwshared_lock<ts::rw_mutex> &lock,
|
||||
std::string &error) {
|
||||
//TOOD: load bans, public key, etc
|
||||
|
||||
return VirtualServerStartResult::SUCCESS;
|
||||
}
|
||||
|
||||
VirtualServerStartResult VirtualServer::start_server_(ts::rwshared_lock<ts::rw_mutex> &lock, std::string &error) {
|
||||
//TODO: Start voice binding etc
|
||||
|
||||
return VirtualServerStartResult::SUCCESS;
|
||||
}
|
||||
|
||||
void VirtualServer::stop_server_(ts::rwshared_lock<ts::rw_mutex> &lock) {
|
||||
//TODO: Stop bindings (if set) and unload everything
|
||||
|
||||
//
|
||||
// Created by WolverinDEV on 03/03/2020.
|
||||
//
|
||||
|
||||
#include "VirtualServer.h"
|
||||
|
||||
using namespace ts::server::vserver;
|
||||
|
||||
//This method gets called when server is OFFLINE or DEPLOYING
|
||||
VirtualServerStartResult VirtualServer::start_server_virtual_(ts::rwshared_lock<ts::rw_mutex> &lock,
|
||||
std::string &error) {
|
||||
//TOOD: load bans, public key, etc
|
||||
|
||||
return VirtualServerStartResult::SUCCESS;
|
||||
}
|
||||
|
||||
VirtualServerStartResult VirtualServer::start_server_(ts::rwshared_lock<ts::rw_mutex> &lock, std::string &error) {
|
||||
//TODO: Start voice binding etc
|
||||
|
||||
return VirtualServerStartResult::SUCCESS;
|
||||
}
|
||||
|
||||
void VirtualServer::stop_server_(ts::rwshared_lock<ts::rw_mutex> &lock) {
|
||||
//TODO: Stop bindings (if set) and unload everything
|
||||
|
||||
}
|
@ -1,53 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include "./VirtualServerBase.h"
|
||||
|
||||
namespace ts {
|
||||
namespace music {
|
||||
class MusicBotManager;
|
||||
}
|
||||
|
||||
namespace stats {
|
||||
class ConnectionStatistics;
|
||||
}
|
||||
|
||||
namespace server {
|
||||
namespace tokens {
|
||||
class TokenManager;
|
||||
}
|
||||
|
||||
namespace bans {
|
||||
class BanManager;
|
||||
}
|
||||
|
||||
namespace conversation {
|
||||
class ConversationManager;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace ts::server::vserver {
|
||||
class VirtualServer : public VirtualServerBase {
|
||||
public:
|
||||
bool initialize(std::string &string) override;
|
||||
|
||||
/* managers */
|
||||
[[nodiscard]] inline tokens::TokenManager& token_manager() { return *this->token_manager_; }
|
||||
[[nodiscard]] inline bans::BanManager& ban_manager() { return *this->ban_manager_; }
|
||||
[[nodiscard]] inline const std::shared_ptr<music::MusicBotManager>& music_bot_manager() { return this->music_bots_; }
|
||||
[[nodiscard]] inline const std::shared_ptr<conversation::ConversationManager>& conversation_manager() { return this->conversation_manager_; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<tokens::TokenManager> token_manager_;
|
||||
std::unique_ptr<bans::BanManager> ban_manager_;
|
||||
|
||||
std::shared_ptr<music::MusicBotManager> music_bots_;
|
||||
std::shared_ptr<conversation::ConversationManager> conversation_manager_;
|
||||
|
||||
protected:
|
||||
VirtualServerStartResult start_server_virtual_(ts::rwshared_lock<ts::rw_mutex> &lock, std::string &string) override;
|
||||
VirtualServerStartResult start_server_(ts::rwshared_lock<ts::rw_mutex> &lock, std::string &string) override;
|
||||
void stop_server_(ts::rwshared_lock<ts::rw_mutex> &lock) override;
|
||||
void execute_tick(const std::chrono::system_clock::time_point &point) override;
|
||||
};
|
||||
#pragma once
|
||||
|
||||
#include "./VirtualServerBase.h"
|
||||
|
||||
namespace ts {
|
||||
namespace music {
|
||||
class MusicBotManager;
|
||||
}
|
||||
|
||||
namespace stats {
|
||||
class ConnectionStatistics;
|
||||
}
|
||||
|
||||
namespace server {
|
||||
namespace tokens {
|
||||
class TokenManager;
|
||||
}
|
||||
|
||||
namespace bans {
|
||||
class BanManager;
|
||||
}
|
||||
|
||||
namespace conversation {
|
||||
class ConversationManager;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace ts::server::vserver {
|
||||
class VirtualServer : public VirtualServerBase {
|
||||
public:
|
||||
bool initialize(std::string &string) override;
|
||||
|
||||
/* managers */
|
||||
[[nodiscard]] inline tokens::TokenManager& token_manager() { return *this->token_manager_; }
|
||||
[[nodiscard]] inline bans::BanManager& ban_manager() { return *this->ban_manager_; }
|
||||
[[nodiscard]] inline const std::shared_ptr<music::MusicBotManager>& music_bot_manager() { return this->music_bots_; }
|
||||
[[nodiscard]] inline const std::shared_ptr<conversation::ConversationManager>& conversation_manager() { return this->conversation_manager_; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<tokens::TokenManager> token_manager_;
|
||||
std::unique_ptr<bans::BanManager> ban_manager_;
|
||||
|
||||
std::shared_ptr<music::MusicBotManager> music_bots_;
|
||||
std::shared_ptr<conversation::ConversationManager> conversation_manager_;
|
||||
|
||||
protected:
|
||||
VirtualServerStartResult start_server_virtual_(ts::rwshared_lock<ts::rw_mutex> &lock, std::string &string) override;
|
||||
VirtualServerStartResult start_server_(ts::rwshared_lock<ts::rw_mutex> &lock, std::string &string) override;
|
||||
void stop_server_(ts::rwshared_lock<ts::rw_mutex> &lock) override;
|
||||
void execute_tick(const std::chrono::system_clock::time_point &point) override;
|
||||
};
|
||||
}
|
@ -1,78 +1,78 @@
|
||||
//
|
||||
// Created by WolverinDEV on 03/03/2020.
|
||||
//
|
||||
|
||||
#include "./VirtualServerBase.h"
|
||||
#include <log/LogUtils.h>
|
||||
|
||||
using namespace ts::server::vserver;
|
||||
|
||||
bool VirtualServerBase::initialize(std::string &error) {
|
||||
//TODO?
|
||||
return true;
|
||||
}
|
||||
|
||||
VirtualServerStartResult VirtualServerBase::start_server_virtual(std::string &error) {
|
||||
ts::rwshared_lock slock{this->status_mutex_, ts::rw_lock_exclusive};
|
||||
if(this->status_ != VirtualServerStatus::STOPPED)
|
||||
return VirtualServerStartResult::SERVER_IS_NOT_OFFLINE;
|
||||
slock.downgrade_lock();
|
||||
|
||||
if(auto err = this->start_server_virtual_(slock, error); err != VirtualServerStartResult::SUCCESS)
|
||||
return err;
|
||||
|
||||
if(auto err = slock.upgrade_lock(); err)
|
||||
logCritical(this->server_id(), "Failed to lock status lock after server virtual start. Ignoring it and still setting state to virtual!");
|
||||
|
||||
this->status_ = VirtualServerStatus::VIRTUAL;
|
||||
return VirtualServerStartResult::SUCCESS;
|
||||
}
|
||||
|
||||
VirtualServerStartResult VirtualServerBase::start_server(std::string &error) {
|
||||
ts::rwshared_lock slock{this->status_mutex_, ts::rw_lock_exclusive};
|
||||
|
||||
if(this->status_ != VirtualServerStatus::VIRTUAL && this->status_ == VirtualServerStatus::STOPPED)
|
||||
return VirtualServerStartResult::SERVER_IS_NOT_OFFLINE;
|
||||
|
||||
auto full_start = this->status_ == VirtualServerStatus::STOPPED;
|
||||
this->status_ = VirtualServerStatus::BOOTING;
|
||||
|
||||
/* even if queries could be connected, a start from virtual should not lead to a crash! */
|
||||
slock.downgrade_lock();
|
||||
|
||||
if(full_start)
|
||||
if(auto err = this->start_server_virtual_(slock, error); err != VirtualServerStartResult::SUCCESS)
|
||||
return err;
|
||||
|
||||
if(auto err = this->start_server_(slock, error); err != VirtualServerStartResult::SUCCESS)
|
||||
return err;
|
||||
|
||||
if(auto err = slock.upgrade_lock(); err)
|
||||
logCritical(this->server_id(), "Failed to lock status lock after server start. Ignoring it and still setting state to running!");
|
||||
|
||||
this->status_ = VirtualServerStatus::RUNNING;
|
||||
return VirtualServerStartResult::SUCCESS;
|
||||
}
|
||||
|
||||
VirtualServerStopResult VirtualServerBase::stop_server() {
|
||||
ts::rwshared_lock slock{this->status_mutex_, ts::rw_lock_shared};
|
||||
if(this->status_ != VirtualServerStatus::RUNNING && this->status_ != VirtualServerStatus::VIRTUAL)
|
||||
return VirtualServerStopResult::SERVER_IS_NOT_RUNNING;
|
||||
|
||||
if(auto err = slock.upgrade_lock(); err)
|
||||
return VirtualServerStopResult::STATE_LOCK_FAILED;
|
||||
|
||||
this->status_ = VirtualServerStatus::STOPPING;
|
||||
slock.downgrade_lock();
|
||||
this->stop_server_(slock);
|
||||
|
||||
if(auto err = slock.upgrade_lock(); err)
|
||||
logCritical(this->server_id(), "Failed to lock status lock after server stop. Ignoring it and still setting state to stopped!");
|
||||
|
||||
this->status_ = VirtualServerStatus::STOPPED;
|
||||
return VirtualServerStopResult::SUCCESS;
|
||||
}
|
||||
|
||||
VirtualServerStartResult VirtualServerBase::start_server_(ts::rwshared_lock<ts::rw_mutex> &slock, std::string &error) {
|
||||
//TODO: Load server permissions, etc
|
||||
//
|
||||
// Created by WolverinDEV on 03/03/2020.
|
||||
//
|
||||
|
||||
#include "./VirtualServerBase.h"
|
||||
#include <log/LogUtils.h>
|
||||
|
||||
using namespace ts::server::vserver;
|
||||
|
||||
bool VirtualServerBase::initialize(std::string &error) {
|
||||
//TODO?
|
||||
return true;
|
||||
}
|
||||
|
||||
VirtualServerStartResult VirtualServerBase::start_server_virtual(std::string &error) {
|
||||
ts::rwshared_lock slock{this->status_mutex_, ts::rw_lock_exclusive};
|
||||
if(this->status_ != VirtualServerStatus::STOPPED)
|
||||
return VirtualServerStartResult::SERVER_IS_NOT_OFFLINE;
|
||||
slock.downgrade_lock();
|
||||
|
||||
if(auto err = this->start_server_virtual_(slock, error); err != VirtualServerStartResult::SUCCESS)
|
||||
return err;
|
||||
|
||||
if(auto err = slock.upgrade_lock(); err)
|
||||
logCritical(this->server_id(), "Failed to lock status lock after server virtual start. Ignoring it and still setting state to virtual!");
|
||||
|
||||
this->status_ = VirtualServerStatus::VIRTUAL;
|
||||
return VirtualServerStartResult::SUCCESS;
|
||||
}
|
||||
|
||||
VirtualServerStartResult VirtualServerBase::start_server(std::string &error) {
|
||||
ts::rwshared_lock slock{this->status_mutex_, ts::rw_lock_exclusive};
|
||||
|
||||
if(this->status_ != VirtualServerStatus::VIRTUAL && this->status_ == VirtualServerStatus::STOPPED)
|
||||
return VirtualServerStartResult::SERVER_IS_NOT_OFFLINE;
|
||||
|
||||
auto full_start = this->status_ == VirtualServerStatus::STOPPED;
|
||||
this->status_ = VirtualServerStatus::BOOTING;
|
||||
|
||||
/* even if queries could be connected, a start from virtual should not lead to a crash! */
|
||||
slock.downgrade_lock();
|
||||
|
||||
if(full_start)
|
||||
if(auto err = this->start_server_virtual_(slock, error); err != VirtualServerStartResult::SUCCESS)
|
||||
return err;
|
||||
|
||||
if(auto err = this->start_server_(slock, error); err != VirtualServerStartResult::SUCCESS)
|
||||
return err;
|
||||
|
||||
if(auto err = slock.upgrade_lock(); err)
|
||||
logCritical(this->server_id(), "Failed to lock status lock after server start. Ignoring it and still setting state to running!");
|
||||
|
||||
this->status_ = VirtualServerStatus::RUNNING;
|
||||
return VirtualServerStartResult::SUCCESS;
|
||||
}
|
||||
|
||||
VirtualServerStopResult VirtualServerBase::stop_server() {
|
||||
ts::rwshared_lock slock{this->status_mutex_, ts::rw_lock_shared};
|
||||
if(this->status_ != VirtualServerStatus::RUNNING && this->status_ != VirtualServerStatus::VIRTUAL)
|
||||
return VirtualServerStopResult::SERVER_IS_NOT_RUNNING;
|
||||
|
||||
if(auto err = slock.upgrade_lock(); err)
|
||||
return VirtualServerStopResult::STATE_LOCK_FAILED;
|
||||
|
||||
this->status_ = VirtualServerStatus::STOPPING;
|
||||
slock.downgrade_lock();
|
||||
this->stop_server_(slock);
|
||||
|
||||
if(auto err = slock.upgrade_lock(); err)
|
||||
logCritical(this->server_id(), "Failed to lock status lock after server stop. Ignoring it and still setting state to stopped!");
|
||||
|
||||
this->status_ = VirtualServerStatus::STOPPED;
|
||||
return VirtualServerStopResult::SUCCESS;
|
||||
}
|
||||
|
||||
VirtualServerStartResult VirtualServerBase::start_server_(ts::rwshared_lock<ts::rw_mutex> &slock, std::string &error) {
|
||||
//TODO: Load server permissions, etc
|
||||
}
|
@ -1,143 +1,143 @@
|
||||
#pragma once
|
||||
|
||||
#include <Definitions.h>
|
||||
#include <lock/rw_mutex.h>
|
||||
#include <PermissionRegister.h>
|
||||
#include <sql/SqlQuery.h>
|
||||
#include "../services/PermissionsService.h"
|
||||
#include "../services/ClientChannelService.h"
|
||||
|
||||
namespace ts::server {
|
||||
class ConnectedClient;
|
||||
|
||||
namespace groups {
|
||||
class GroupManager;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ts {
|
||||
class Properties;
|
||||
class ServerChannelTree;
|
||||
|
||||
namespace stats {
|
||||
class ConnectionStatistics;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace ts::server::vserver {
|
||||
struct VirtualServerStatus {
|
||||
enum value {
|
||||
STOPPED, /* no data has been loaded */
|
||||
|
||||
BOOTING, /* loading & initializing data */
|
||||
VIRTUAL, /* only available for query clients */
|
||||
|
||||
STARTING, /* start bindings */
|
||||
RUNNING, /* server is running */
|
||||
|
||||
STOPPING, /* server is stopping and unloading any data */
|
||||
|
||||
DEPLOYING /* server is in deploy modus */
|
||||
};
|
||||
};
|
||||
|
||||
enum struct VirtualServerStartResult {
|
||||
SUCCESS,
|
||||
BIND_FAILED,
|
||||
SERVER_IS_DEFAULT,
|
||||
CUSTOM,
|
||||
|
||||
SERVER_IS_NOT_OFFLINE
|
||||
};
|
||||
|
||||
enum struct VirtualServerStopResult {
|
||||
SUCCESS,
|
||||
SERVER_IS_NOT_RUNNING,
|
||||
STATE_LOCK_FAILED
|
||||
};
|
||||
|
||||
class VirtualServerBase {
|
||||
public:
|
||||
//TODO: Constructor
|
||||
//VirtualServerBase();
|
||||
~VirtualServerBase();
|
||||
|
||||
[[nodiscard]] inline std::shared_ptr<VirtualServerBase> server_ref() { return this->server_ref_.lock(); }
|
||||
|
||||
/* basic initialize to the STOPPED state */
|
||||
virtual bool initialize(std::string& /* error */);
|
||||
VirtualServerStartResult start_server_virtual(std::string& /* error */);
|
||||
VirtualServerStartResult start_server(std::string& /* error */);
|
||||
VirtualServerStopResult stop_server();
|
||||
|
||||
/* basic information */
|
||||
[[nodiscard]] inline ServerId server_id() const { return this->server_id_; }
|
||||
[[nodiscard]] inline VirtualServerStatus::value server_status() const { return this->status_; }
|
||||
[[nodiscard]] inline ts::rwshared_lock<ts::rw_mutex> lock_server_status() { return ts::rwshared_lock{this->status_mutex_, ts::rw_lock_shared}; }
|
||||
|
||||
[[nodiscard]] inline sql::SqlManager* sql_manager() { return this->sql_manager_; }
|
||||
[[nodiscard]] inline const std::shared_ptr<stats::ConnectionStatistics>& server_connection_statistics() { return this->server_connection_statistics_; }
|
||||
|
||||
[[nodiscard]] inline permissions::PermissionService& permission_service() { return this->permission_service_; }
|
||||
|
||||
[[nodiscard]] inline channels::ClientChannelService& channel_service() { return this->channel_service_; }
|
||||
[[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<groups::GroupManager>& group_manager() { return 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 */);
|
||||
|
||||
/* 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 */);
|
||||
[[nodiscard]] std::shared_ptr<ConnectedClient> find_client_by_id(ClientId /* id */);
|
||||
[[nodiscard]] std::vector<std::shared_ptr<ConnectedClient>> find_clients_by_uid(ClientId /* id */);
|
||||
[[nodiscard]] std::vector<std::shared_ptr<ConnectedClient>> find_clients_by_channel(const std::shared_ptr<ServerChannel>& /* channel */);
|
||||
protected:
|
||||
std::weak_ptr<VirtualServerBase> server_ref_{};
|
||||
|
||||
sql::SqlManager* sql_manager_{nullptr}; //TODO!
|
||||
|
||||
ServerId server_id_{0};
|
||||
ts::rw_mutex status_mutex_{};
|
||||
VirtualServerStatus::value status_{VirtualServerStatus::STOPPED};
|
||||
|
||||
std::shared_ptr<Properties> server_properties_{nullptr};
|
||||
std::shared_ptr<ServerChannelTree> server_channel_tree_{nullptr};
|
||||
|
||||
/*
|
||||
* Lock whenever we want to read/write from the channel tree or the clients within
|
||||
* Attention: We're not allowed to do permission calculations while having the channel tree in write lock!
|
||||
*/
|
||||
ts::rw_mutex channel_clients_lock_{};
|
||||
std::mutex client_nickname_lock_{};
|
||||
|
||||
/* some services */
|
||||
permissions::PermissionService permission_service_;
|
||||
channels::ClientChannelService channel_service_;
|
||||
|
||||
struct {
|
||||
size_t count{0};
|
||||
std::mutex mutex{};
|
||||
std::vector<std::shared_ptr<ConnectedClient>> clients{};
|
||||
} registered_clients;
|
||||
|
||||
std::shared_ptr<groups::GroupManager> group_manager_;
|
||||
|
||||
std::shared_ptr<stats::ConnectionStatistics> server_connection_statistics_;
|
||||
|
||||
/* private methods */
|
||||
virtual VirtualServerStartResult start_server_virtual_(ts::rwshared_lock<ts::rw_mutex>& /* state lock */, std::string& /* error */);
|
||||
virtual VirtualServerStartResult start_server_(ts::rwshared_lock<ts::rw_mutex>& /* state lock */, std::string& /* error */);
|
||||
virtual void stop_server_(ts::rwshared_lock<ts::rw_mutex>& /* state lock */);
|
||||
|
||||
virtual void execute_tick(const std::chrono::system_clock::time_point& /* now */) = 0;
|
||||
};
|
||||
#pragma once
|
||||
|
||||
#include <Definitions.h>
|
||||
#include <lock/rw_mutex.h>
|
||||
#include <PermissionRegister.h>
|
||||
#include <sql/SqlQuery.h>
|
||||
#include "../services/PermissionsService.h"
|
||||
#include "../services/ClientChannelService.h"
|
||||
|
||||
namespace ts::server {
|
||||
class ConnectedClient;
|
||||
|
||||
namespace groups {
|
||||
class GroupManager;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ts {
|
||||
class Properties;
|
||||
class ServerChannelTree;
|
||||
|
||||
namespace stats {
|
||||
class ConnectionStatistics;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace ts::server::vserver {
|
||||
struct VirtualServerStatus {
|
||||
enum value {
|
||||
STOPPED, /* no data has been loaded */
|
||||
|
||||
BOOTING, /* loading & initializing data */
|
||||
VIRTUAL, /* only available for query clients */
|
||||
|
||||
STARTING, /* start bindings */
|
||||
RUNNING, /* server is running */
|
||||
|
||||
STOPPING, /* server is stopping and unloading any data */
|
||||
|
||||
DEPLOYING /* server is in deploy modus */
|
||||
};
|
||||
};
|
||||
|
||||
enum struct VirtualServerStartResult {
|
||||
SUCCESS,
|
||||
BIND_FAILED,
|
||||
SERVER_IS_DEFAULT,
|
||||
CUSTOM,
|
||||
|
||||
SERVER_IS_NOT_OFFLINE
|
||||
};
|
||||
|
||||
enum struct VirtualServerStopResult {
|
||||
SUCCESS,
|
||||
SERVER_IS_NOT_RUNNING,
|
||||
STATE_LOCK_FAILED
|
||||
};
|
||||
|
||||
class VirtualServerBase {
|
||||
public:
|
||||
//TODO: Constructor
|
||||
//VirtualServerBase();
|
||||
~VirtualServerBase();
|
||||
|
||||
[[nodiscard]] inline std::shared_ptr<VirtualServerBase> server_ref() { return this->server_ref_.lock(); }
|
||||
|
||||
/* basic initialize to the STOPPED state */
|
||||
virtual bool initialize(std::string& /* error */);
|
||||
VirtualServerStartResult start_server_virtual(std::string& /* error */);
|
||||
VirtualServerStartResult start_server(std::string& /* error */);
|
||||
VirtualServerStopResult stop_server();
|
||||
|
||||
/* basic information */
|
||||
[[nodiscard]] inline ServerId server_id() const { return this->server_id_; }
|
||||
[[nodiscard]] inline VirtualServerStatus::value server_status() const { return this->status_; }
|
||||
[[nodiscard]] inline ts::rwshared_lock<ts::rw_mutex> lock_server_status() { return ts::rwshared_lock{this->status_mutex_, ts::rw_lock_shared}; }
|
||||
|
||||
[[nodiscard]] inline sql::SqlManager* sql_manager() { return this->sql_manager_; }
|
||||
[[nodiscard]] inline const std::shared_ptr<stats::ConnectionStatistics>& server_connection_statistics() { return this->server_connection_statistics_; }
|
||||
|
||||
[[nodiscard]] inline permissions::PermissionService& permission_service() { return this->permission_service_; }
|
||||
|
||||
[[nodiscard]] inline channels::ClientChannelService& channel_service() { return this->channel_service_; }
|
||||
[[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<groups::GroupManager>& group_manager() { return 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 */);
|
||||
|
||||
/* 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 */);
|
||||
[[nodiscard]] std::shared_ptr<ConnectedClient> find_client_by_id(ClientId /* id */);
|
||||
[[nodiscard]] std::vector<std::shared_ptr<ConnectedClient>> find_clients_by_uid(ClientId /* id */);
|
||||
[[nodiscard]] std::vector<std::shared_ptr<ConnectedClient>> find_clients_by_channel(const std::shared_ptr<ServerChannel>& /* channel */);
|
||||
protected:
|
||||
std::weak_ptr<VirtualServerBase> server_ref_{};
|
||||
|
||||
sql::SqlManager* sql_manager_{nullptr}; //TODO!
|
||||
|
||||
ServerId server_id_{0};
|
||||
ts::rw_mutex status_mutex_{};
|
||||
VirtualServerStatus::value status_{VirtualServerStatus::STOPPED};
|
||||
|
||||
std::shared_ptr<Properties> server_properties_{nullptr};
|
||||
std::shared_ptr<ServerChannelTree> server_channel_tree_{nullptr};
|
||||
|
||||
/*
|
||||
* Lock whenever we want to read/write from the channel tree or the clients within
|
||||
* Attention: We're not allowed to do permission calculations while having the channel tree in write lock!
|
||||
*/
|
||||
ts::rw_mutex channel_clients_lock_{};
|
||||
std::mutex client_nickname_lock_{};
|
||||
|
||||
/* some services */
|
||||
permissions::PermissionService permission_service_;
|
||||
channels::ClientChannelService channel_service_;
|
||||
|
||||
struct {
|
||||
size_t count{0};
|
||||
std::mutex mutex{};
|
||||
std::vector<std::shared_ptr<ConnectedClient>> clients{};
|
||||
} registered_clients;
|
||||
|
||||
std::shared_ptr<groups::GroupManager> group_manager_;
|
||||
|
||||
std::shared_ptr<stats::ConnectionStatistics> server_connection_statistics_;
|
||||
|
||||
/* private methods */
|
||||
virtual VirtualServerStartResult start_server_virtual_(ts::rwshared_lock<ts::rw_mutex>& /* state lock */, std::string& /* error */);
|
||||
virtual VirtualServerStartResult start_server_(ts::rwshared_lock<ts::rw_mutex>& /* state lock */, std::string& /* error */);
|
||||
virtual void stop_server_(ts::rwshared_lock<ts::rw_mutex>& /* state lock */);
|
||||
|
||||
virtual void execute_tick(const std::chrono::system_clock::time_point& /* now */) = 0;
|
||||
};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user