711 lines
26 KiB
C++
711 lines
26 KiB
C++
#include <iostream>
|
|
#include <bitset>
|
|
#include <algorithm>
|
|
#include "ConnectedClient.h"
|
|
#include "voice/VoiceClient.h"
|
|
#include "../server/file/FileServer.h"
|
|
#include "../server/VoiceServer.h"
|
|
#include "../InstanceHandler.h"
|
|
#include "../server/QueryServer.h"
|
|
#include "music/MusicClient.h"
|
|
#include <log/LogUtils.h>
|
|
#include <misc/sassert.h>
|
|
#include <misc/timer.h>
|
|
|
|
using namespace std::chrono;
|
|
using namespace std;
|
|
using namespace ts;
|
|
using namespace ts::server;
|
|
using namespace ts::token;
|
|
|
|
extern ts::server::InstanceHandler* serverInstance;
|
|
|
|
#define INVOKER(command, invoker) \
|
|
do { \
|
|
if(invoker) { \
|
|
command["invokerid"] = invoker->getClientId(); \
|
|
command["invokername"] = invoker->getDisplayName(); \
|
|
command["invokeruid"] = invoker->getUid(); \
|
|
} else { \
|
|
command["invokerid"] = 0; \
|
|
command["invokername"] = "undefined"; \
|
|
command["invokeruid"] = "undefined"; \
|
|
} \
|
|
} while(0)
|
|
|
|
|
|
bool ConnectedClient::notifyServerGroupList() {
|
|
Command cmd(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyservergrouplist" : "");
|
|
int index = 0;
|
|
|
|
for (const auto& group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableServerGroups(true)) {
|
|
if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) {
|
|
cmd[index]["cgid"] = group->groupId();
|
|
} else {
|
|
cmd[index]["sgid"] = group->groupId();
|
|
}
|
|
for (const auto &prop : group->properties().list_properties(property::FLAG_GROUP_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0))
|
|
cmd[index][prop.type().name] = prop.value();
|
|
|
|
|
|
auto modify_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_modify_power);
|
|
auto add_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_member_add_power);
|
|
auto remove_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_member_remove_power);
|
|
cmd[index]["n_modifyp"] = modify_power.has_value ? modify_power.value : 0;
|
|
cmd[index]["n_member_addp"] = add_power.has_value ? add_power.value : 0;
|
|
cmd[index]["n_member_removep"] = remove_power.has_value ? remove_power.value : 0;
|
|
index++;
|
|
}
|
|
|
|
this->sendCommand(cmd);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyGroupPermList(const std::shared_ptr<Group>& group, bool as_sid) {
|
|
Command cmd(this->getExternalType() == CLIENT_TEAMSPEAK ? group->target() == GROUPTARGET_SERVER ? "notifyservergrouppermlist" : "notifychannelgrouppermlist" : "");
|
|
if (group->target() == GROUPTARGET_SERVER)
|
|
cmd["sgid"] = group->groupId();
|
|
else
|
|
cmd["cgid"] = group->groupId();
|
|
|
|
int index = 0;
|
|
|
|
auto permissions = group->permissions()->permissions();
|
|
for (const auto &permission_data : permissions) {
|
|
auto& permission = get<1>(permission_data);
|
|
if(!permission.flags.value_set)
|
|
continue;
|
|
|
|
auto type = permission::resolvePermissionData(get<0>(permission_data));
|
|
if(as_sid) {
|
|
cmd[index]["permsid"] = type->name;
|
|
} else {
|
|
cmd[index]["permid"] = (uint16_t) type->type;
|
|
}
|
|
cmd[index]["permvalue"] = permission.values.value;
|
|
cmd[index]["permnegated"] = permission.flags.negate;
|
|
cmd[index]["permskip"] = permission.flags.skip;
|
|
index++;
|
|
}
|
|
for (const auto &permission_data : permissions) {
|
|
auto& permission = get<1>(permission_data);
|
|
if(!permission.flags.grant_set)
|
|
continue;
|
|
|
|
auto type = permission::resolvePermissionData(get<0>(permission_data));
|
|
if(as_sid) {
|
|
cmd[index]["permsid"] = type->name;
|
|
} else {
|
|
cmd[index]["permid"] = (uint16_t) type->type;
|
|
}
|
|
cmd[index]["permvalue"] = permission.values.value;
|
|
cmd[index]["permnegated"] = permission.flags.negate;
|
|
cmd[index]["permskip"] = permission.flags.skip;
|
|
index++;
|
|
}
|
|
|
|
if (index == 0)
|
|
return false;
|
|
|
|
this->sendCommand(cmd);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyClientPermList(ClientDbId cldbid, const std::shared_ptr<permission::v2::PermissionManager>& mgr, bool perm_sids) {
|
|
Command res(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientpermlist" : "");
|
|
|
|
auto permissions = mgr->permissions();
|
|
if(permissions.empty())
|
|
return false;
|
|
|
|
int index = 0;
|
|
res[index]["cldbid"] = cldbid;
|
|
|
|
for (const auto &permission_data : permissions) {
|
|
auto& permission = std::get<1>(permission_data);
|
|
if(permission.flags.value_set) {
|
|
if (perm_sids)
|
|
res[index]["permsid"] = permission::resolvePermissionData(get<0>(permission_data))->name;
|
|
else
|
|
res[index]["permid"] = get<0>(permission_data);
|
|
res[index]["permvalue"] = permission.values.value;
|
|
|
|
res[index]["permnegated"] = permission.flags.negate;
|
|
res[index]["permskip"] = permission.flags.skip;
|
|
index++;
|
|
}
|
|
|
|
|
|
if(permission.flags.grant_set) {
|
|
if (perm_sids)
|
|
res[index]["permsid"] = permission::resolvePermissionData(get<0>(permission_data))->grant_name;
|
|
else
|
|
res[index]["permid"] = (get<0>(permission_data) | PERM_ID_GRANT);
|
|
res[index]["permvalue"] = permission.values.grant;
|
|
res[index]["permnegated"] = 0;
|
|
res[index]["permskip"] = 0;
|
|
index++;
|
|
}
|
|
}
|
|
|
|
this->sendCommand(res);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyChannelGroupList() {
|
|
Command cmd(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifychannelgrouplist" : "");
|
|
int index = 0;
|
|
for (const auto &group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableChannelGroups(false)) {
|
|
if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) {
|
|
cmd[index]["cgid"] = group->groupId();
|
|
} else {
|
|
cmd[index]["sgid"] = group->groupId();
|
|
}
|
|
for (auto &prop : group->properties().list_properties(property::FLAG_GROUP_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0))
|
|
cmd[index][prop.type().name] = prop.value();
|
|
|
|
|
|
auto modify_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_modify_power);
|
|
auto add_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_member_add_power);
|
|
auto remove_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_member_remove_power);
|
|
cmd[index]["n_modifyp"] = modify_power.has_value ? modify_power.value : 0;
|
|
cmd[index]["n_member_addp"] = add_power.has_value ? add_power.value : 0;
|
|
cmd[index]["n_member_removep"] = remove_power.has_value ? remove_power.value : 0;
|
|
index++;
|
|
}
|
|
if(index == 0) return false;
|
|
this->sendCommand(cmd);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyTextMessage(ChatMessageMode mode, const shared_ptr<ConnectedClient> &invoker, uint64_t targetId, const string &textMessage) {
|
|
//notifytextmessage targetmode=1 msg=asdasd target=2 invokerid=1 invokername=WolverinDEV invokeruid=xxjnc14LmvTk+Lyrm8OOeo4tOqw=
|
|
Command cmd("notifytextmessage");
|
|
INVOKER(cmd, invoker);
|
|
cmd["targetmode"] = mode;
|
|
cmd["target"] = targetId;
|
|
cmd["msg"] = textMessage;
|
|
this->sendCommand(cmd);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyServerGroupClientAdd(const shared_ptr<ConnectedClient> &invoker, const shared_ptr<ConnectedClient> &client, const shared_ptr<Group> &group) {
|
|
Command cmd("notifyservergroupclientadded");
|
|
INVOKER(cmd, invoker);
|
|
|
|
cmd["sgid"] = group->groupId();
|
|
cmd["clid"] = client->getClientId();
|
|
cmd["name"] = client->getDisplayName();
|
|
cmd["cluid"] = client->getUid();
|
|
|
|
this->sendCommand(cmd);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyServerGroupClientRemove(std::shared_ptr<ConnectedClient> invoker, std::shared_ptr<ConnectedClient> client, std::shared_ptr<Group> group) {
|
|
Command cmd("notifyservergroupclientdeleted");
|
|
INVOKER(cmd, invoker);
|
|
|
|
cmd["sgid"] = group->groupId();
|
|
cmd["clid"] = client->getClientId();
|
|
cmd["name"] = client->getDisplayName();
|
|
cmd["cluid"] = client->getUid();
|
|
|
|
this->sendCommand(cmd);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyClientChannelGroupChanged(
|
|
const std::shared_ptr<ConnectedClient> &invoker,
|
|
const std::shared_ptr<ConnectedClient> &client,
|
|
const std::shared_ptr<BasicChannel>& channel,
|
|
const std::shared_ptr<BasicChannel>& inherited,
|
|
const std::shared_ptr<Group>& group,
|
|
bool lock_channel_tree) {
|
|
assert(!lock_channel_tree); /* not supported */
|
|
|
|
if(!this->isClientVisible(client, false) && client != this) return false;
|
|
if(client->getChannel() != channel) return false;
|
|
|
|
assert(client);
|
|
assert(channel);
|
|
assert(inherited);
|
|
assert(group);
|
|
Command cmd("notifyclientchannelgroupchanged");
|
|
INVOKER(cmd, invoker);
|
|
|
|
cmd["cgid"] = group->groupId();
|
|
cmd["clid"] = client->getClientId();
|
|
cmd["cid"] = channel->channelId();
|
|
cmd["cgi"] = inherited ? inherited->channelId() : channel->channelId(); //The inherited channel
|
|
|
|
this->sendCommand(cmd);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyConnectionInfo(std::shared_ptr<ConnectedClient> target, std::shared_ptr<ConnectionInfoData> info) {
|
|
Command notify("notifyconnectioninfo");
|
|
notify["clid"] = target->getClientId();
|
|
|
|
if(target->getClientId() != this->getClientId()){
|
|
for(const auto& elm : info->properties)
|
|
notify[elm.first] = elm.second;
|
|
notify["connection_connected_time"] = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now() - target->connectTimestamp).count();
|
|
notify["connection_filetransfer_bandwidth_sent"] = 0;
|
|
notify["connection_filetransfer_bandwidth_received"] = 0;
|
|
}
|
|
|
|
if(target->getClientId() == this->getClientId() || this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_remoteaddress_view, 1, target->currentChannel, true)) {
|
|
notify["connection_client_ip"] = target->getLoggingPeerIp();
|
|
notify["connection_client_port"] = target->getPeerPort();
|
|
}
|
|
|
|
notify["connection_client2server_packetloss_speech"] = 0.f;
|
|
notify["connection_client2server_packetloss_keepalive"] = 0.f;
|
|
notify["connection_client2server_packetloss_control"] = 0.f;
|
|
notify["connection_client2server_packetloss_total"] = 0.f;
|
|
|
|
notify["connection_idle_time"] = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now() - target->idleTimestamp).count();
|
|
this->sendCommand(notify);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyClientMoved(const shared_ptr<ConnectedClient> &client,
|
|
const std::shared_ptr<BasicChannel> &target_channel,
|
|
ViewReasonId reason,
|
|
std::string msg,
|
|
std::shared_ptr<ConnectedClient> invoker,
|
|
bool lock_channel_tree) {
|
|
assert(!lock_channel_tree);
|
|
assert(client->getClientId() > 0);
|
|
assert(client->currentChannel);
|
|
assert(target_channel);
|
|
assert(this->isClientVisible(client, false) || &*client == this);
|
|
|
|
Command mv("notifyclientmoved");
|
|
|
|
mv["clid"] = client->getClientId();
|
|
mv["cfid"] = client->currentChannel->channelId();
|
|
mv["ctid"] = target_channel->channelId();
|
|
mv["reasonid"] = reason;
|
|
if (invoker)
|
|
INVOKER(mv, invoker);
|
|
if (!msg.empty()) mv["reasonmsg"] = msg;
|
|
else mv["reasonmsg"] = "";
|
|
|
|
this->sendCommand(mv);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyClientUpdated(const std::shared_ptr<ConnectedClient> &client, const deque<shared_ptr<property::PropertyDescription>> &props, bool lock) {
|
|
shared_lock channel_lock(this->channel_lock, defer_lock);
|
|
if(lock)
|
|
channel_lock.lock();
|
|
|
|
if(!this->isClientVisible(client, false) && client != this)
|
|
return false;
|
|
|
|
auto client_id = client->getClientId();
|
|
if(client_id == 0) {
|
|
logError(this->getServerId(), "{} Attempted to send a clientupdate for client id 0. Updated client: {}", CLIENT_STR_LOG_PREFIX, CLIENT_STR_LOG_PREFIX_(client));
|
|
return false;
|
|
}
|
|
Command response(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyclientupdated" : "");
|
|
response["clid"] = client_id;
|
|
for (const auto &prop : props) {
|
|
if(lastOnlineTimestamp.time_since_epoch().count() > 0 && (prop->property_index == property::CLIENT_TOTAL_ONLINE_TIME || prop->property_index == property::CLIENT_MONTH_ONLINE_TIME))
|
|
response[prop->name] = client->properties()[prop].as<int64_t>() + duration_cast<seconds>(system_clock::now() - client->lastOnlineTimestamp).count();
|
|
else
|
|
response[prop->name] = client->properties()[prop].value();
|
|
}
|
|
|
|
this->sendCommand(response);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyPluginCmd(std::string name, std::string msg, std::shared_ptr<ConnectedClient> sender) {
|
|
Command notify("notifyplugincmd");
|
|
notify["name"] = name;
|
|
notify["data"] = msg;
|
|
INVOKER(notify, sender);
|
|
this->sendCommand(notify);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyClientChatComposing(const shared_ptr<ConnectedClient> &client) {
|
|
Command notify("notifyclientchatcomposing");
|
|
notify["clid"] = client->getClientId();
|
|
notify["cluid"] = client->getUid();
|
|
this->sendCommand(notify);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyClientChatClosed(const shared_ptr<ConnectedClient> &client) {
|
|
Command notify("notifyclientchatclosed");
|
|
notify["clid"] = client->getClientId();
|
|
notify["cluid"] = client->getUid();
|
|
this->sendCommand(notify);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyChannelMoved(const std::shared_ptr<BasicChannel> &channel, ChannelId order, const std::shared_ptr<ConnectedClient> &invoker) {
|
|
if(!channel || !this->channels->channel_visible(channel)) return false;
|
|
|
|
Command notify("notifychannelmoved");
|
|
INVOKER(notify, invoker);
|
|
notify["reasonid"] = ViewReasonId::VREASON_MOVED;
|
|
notify["cid"] = channel->channelId();
|
|
notify["cpid"] = channel->parent() ? channel->parent()->channelId() : 0;
|
|
notify["order"] = order;
|
|
this->sendCommand(notify);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyChannelCreate(const std::shared_ptr<BasicChannel> &channel, ChannelId orderId, const std::shared_ptr<ConnectedClient> &invoker) {
|
|
Command notify("notifychannelcreated");
|
|
for (auto &prop : channel->properties().list_properties(property::FLAG_CHANNEL_VARIABLE | property::FLAG_CHANNEL_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) {
|
|
if(prop.type() == property::CHANNEL_ORDER)
|
|
notify[prop.type().name] = orderId;
|
|
else
|
|
notify[prop.type().name] = prop.value();
|
|
}
|
|
INVOKER(notify, invoker);
|
|
this->sendCommand(notify);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyChannelHide(const std::deque<ChannelId> &channel_ids, bool lock_channel_tree) {
|
|
if(channel_ids.empty()) return true;
|
|
|
|
if(this->getType() == ClientType::CLIENT_TEAMSPEAK) { //Voice hasnt that event
|
|
shared_lock server_channel_lock(this->server->channel_tree_lock, defer_lock);
|
|
unique_lock client_channel_lock(this->channel_lock, defer_lock);
|
|
if(lock_channel_tree) {
|
|
server_channel_lock.lock();
|
|
client_channel_lock.lock();
|
|
} else {
|
|
assert(mutex_locked(this->channel_lock));
|
|
}
|
|
|
|
deque<shared_ptr<ConnectedClient>> clients_to_remove;
|
|
{
|
|
for(const auto& w_client : this->visibleClients) {
|
|
auto client = w_client.lock();
|
|
if(!client) continue;
|
|
if(client->currentChannel) {
|
|
auto id = client->currentChannel->channelId();
|
|
for(const auto cid : channel_ids) {
|
|
if(cid == id) {
|
|
clients_to_remove.push_back(client);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//TODO: May send a unsubscribe and remove the clients like that?
|
|
this->notifyClientLeftView(clients_to_remove, "Channel gone out of view", false, ViewReasonServerLeft);
|
|
return this->notifyChannelDeleted(channel_ids, this->server->serverRoot);
|
|
} else {
|
|
Command notify("notifychannelhide");
|
|
int index = 0;
|
|
for(const auto& channel : channel_ids)
|
|
notify[index++]["cid"] = channel;
|
|
this->sendCommand(notify);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyChannelShow(const std::shared_ptr<ts::BasicChannel> &channel, ts::ChannelId orderId) {
|
|
if(!channel)
|
|
return false;
|
|
|
|
auto result = false;
|
|
if(this->getType() == ClientType::CLIENT_TEAMSPEAK) { //Voice hasn't that event
|
|
result = this->notifyChannelCreate(channel, orderId, this->server->serverRoot);
|
|
} else {
|
|
Command notify("notifychannelshow");
|
|
for (auto &prop : channel->properties().list_properties(property::FLAG_CHANNEL_VARIABLE | property::FLAG_CHANNEL_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) {
|
|
if(prop.type() == property::CHANNEL_ORDER)
|
|
notify[prop.type().name] = orderId;
|
|
else
|
|
notify[prop.type().name] = prop.value();
|
|
}
|
|
this->sendCommand(notify);
|
|
}
|
|
if(result && this->subscribeToAll)
|
|
this->subscribeChannel({channel}, false, true);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyChannelDescriptionChanged(std::shared_ptr<BasicChannel> channel) {
|
|
if(!this->channels->channel_visible(channel)) return false;
|
|
Command notifyDesChanges("notifychanneldescriptionchanged");
|
|
notifyDesChanges["cid"] = channel->channelId();
|
|
this->sendCommand(notifyDesChanges);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyChannelPasswordChanged(std::shared_ptr<BasicChannel> channel) {
|
|
if(!this->channels->channel_visible(channel)) return false;
|
|
Command notifyDesChanges("notifychannelpasswordchanged");
|
|
notifyDesChanges["cid"] = channel->channelId();
|
|
this->sendCommand(notifyDesChanges);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyClientEnterView(const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<ConnectedClient> &invoker, const std::string& reason, const std::shared_ptr<BasicChannel> &to, ViewReasonId reasonId, const std::shared_ptr<BasicChannel> &from, bool lock_channel_tree) {
|
|
sassert(client && client->getClientId() > 0);
|
|
sassert(to);
|
|
sassert(!lock_channel_tree); /* we don't support locking */
|
|
sassert(!this->isClientVisible(client, false) || &*client == this);
|
|
|
|
switch (reasonId) {
|
|
case ViewReasonId::VREASON_MOVED:
|
|
case ViewReasonId::VREASON_BAN:
|
|
case ViewReasonId::VREASON_CHANNEL_KICK:
|
|
case ViewReasonId::VREASON_SERVER_KICK:
|
|
if(!invoker) {
|
|
logCritical(this->getServerId(), "{} ConnectedClient::notifyClientEnterView() => missing invoker for reason id {}", CLIENT_STR_LOG_PREFIX, reasonId);
|
|
if(this->server)
|
|
;//invoker = this->server->serverRoot.get();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
Command cmd("notifycliententerview");
|
|
|
|
cmd["cfid"] = from ? from->channelId() : 0;
|
|
cmd["ctid"] = to ? to->channelId() : 0;
|
|
cmd["reasonid"] = reasonId;
|
|
INVOKER(cmd, invoker);
|
|
switch (reasonId) {
|
|
case ViewReasonId::VREASON_MOVED:
|
|
case ViewReasonId::VREASON_BAN:
|
|
case ViewReasonId::VREASON_CHANNEL_KICK:
|
|
case ViewReasonId::VREASON_SERVER_KICK:
|
|
cmd["reasonmsg"] = reason;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
for (const auto &elm : client->properties()->list_properties(property::FLAG_CLIENT_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) {
|
|
cmd[elm.type().name] = elm.value();
|
|
}
|
|
|
|
visibleClients.emplace_back(client);
|
|
this->sendCommand(cmd);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyClientEnterView(const std::deque<std::shared_ptr<ConnectedClient>> &clients, const ts::ViewReasonSystemT &_vrs) {
|
|
if(clients.empty())
|
|
return true;
|
|
assert(mutex_locked(this->channel_lock));
|
|
|
|
Command cmd("notifycliententerview");
|
|
|
|
cmd["cfid"] = 0;
|
|
cmd["reasonid"] = ViewReasonId::VREASON_SYSTEM;
|
|
ChannelId current_channel = 0;
|
|
|
|
size_t index = 0;
|
|
auto it = clients.begin();
|
|
while(it != clients.end()) {
|
|
auto client = *(it++);
|
|
|
|
if(this->isClientVisible(client, false))
|
|
continue;
|
|
|
|
auto channel = client->getChannel();
|
|
sassert(!channel || channel->channelId() != 0);
|
|
if(!channel) /* hmm suspecious */
|
|
continue;
|
|
|
|
if(current_channel != channel->channelId()) {
|
|
if(current_channel == 0)
|
|
cmd[index]["ctid"] = (current_channel = channel->channelId());
|
|
else {
|
|
it--; /* we still have to send him */
|
|
break;
|
|
}
|
|
}
|
|
|
|
this->visibleClients.push_back(client);
|
|
for (const auto &elm : client->properties()->list_properties(property::FLAG_CLIENT_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) {
|
|
cmd[index][elm.type().name] = elm.value();
|
|
}
|
|
|
|
index++;
|
|
if(index > 16) /* max 16 clients per packet */
|
|
break;
|
|
}
|
|
|
|
if(index > 0)
|
|
this->sendCommand(cmd);
|
|
|
|
if(it != clients.end())
|
|
return this->notifyClientEnterView({it, clients.end()}, _vrs);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyChannelEdited(std::shared_ptr<BasicChannel> channel, std::vector<std::string> keys, ConnectedClient *invoker) {
|
|
auto v_channel = this->channels->find_channel(channel->channelId());
|
|
if(!v_channel) return false; //Not visible? Important do not remove!
|
|
|
|
Command notify("notifychanneledited");
|
|
|
|
auto counter = 0;
|
|
for (const auto& key : keys) {
|
|
auto info = property::impl::info<property::ChannelProperties>(key);
|
|
if(*info == property::CHANNEL_UNDEFINED) {
|
|
logError(this->getServerId(), "Tried to edit a non existing channel property: " + key);
|
|
continue;
|
|
}
|
|
counter++;
|
|
if(*info == property::CHANNEL_ORDER) {
|
|
notify[key] = v_channel->previous_channel;
|
|
} else {
|
|
notify[key] = channel->properties()[info].as<std::string>();
|
|
}
|
|
}
|
|
if(counter == 0) return true;
|
|
|
|
notify["cid"] = channel->channelId();
|
|
INVOKER(notify, invoker);
|
|
notify["reasonid"] = ViewReasonId::VREASON_EDITED;
|
|
|
|
this->sendCommand(notify);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyChannelDeleted(const deque<ChannelId>& channel_ids, const std::shared_ptr<ConnectedClient>& invoker) {
|
|
if(channel_ids.empty())
|
|
return true;
|
|
|
|
Command notify("notifychanneldeleted");
|
|
|
|
int index = 0;
|
|
for (const auto& channel_id : channel_ids)
|
|
notify[index++]["cid"] = channel_id;
|
|
|
|
INVOKER(notify, invoker);
|
|
notify["reasonid"] = ViewReasonId::VREASON_EDITED;
|
|
|
|
this->sendCommand(notify);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyServerUpdated(std::shared_ptr<ConnectedClient> invoker) {
|
|
Command response("notifyserverupdated");
|
|
|
|
for (const auto& elm : this->server->properties().list_properties(property::FLAG_SERVER_VARIABLE, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) {
|
|
if(elm.type() == property::VIRTUALSERVER_MIN_WINPHONE_VERSION)
|
|
continue;
|
|
|
|
//if(elm->type() == property::VIRTUALSERVER_RESERVED_SLOTS)
|
|
response[elm.type().name] = elm.value();
|
|
}
|
|
|
|
if(getType() == CLIENT_QUERY)
|
|
INVOKER(response, invoker);
|
|
this->sendCommand(response);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyClientPoke(std::shared_ptr<ConnectedClient> invoker, std::string msg) {
|
|
Command cmd("notifyclientpoke");
|
|
INVOKER(cmd, invoker);
|
|
cmd["msg"] = msg;
|
|
this->sendCommand(cmd);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyChannelSubscribed(const deque<shared_ptr<BasicChannel>> &channels) {
|
|
Command notify("notifychannelsubscribed");
|
|
int index = 0;
|
|
for (const auto& ch : channels) {
|
|
notify[index]["es"] = this->server->getClientsByChannel(ch).empty() ? ch->emptySince() : 0;
|
|
notify[index++]["cid"] = ch->channelId();
|
|
}
|
|
this->sendCommand(notify);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyChannelUnsubscribed(const deque<shared_ptr<BasicChannel>> &channels) {
|
|
Command notify("notifychannelunsubscribed");
|
|
int index = 0;
|
|
for (const auto& ch : channels) {
|
|
notify[index]["es"] = this->server->getClientsByChannel(ch).empty() ? ch->emptySince() : 0;
|
|
notify[index++]["cid"] = ch->channelId();
|
|
}
|
|
this->sendCommand(notify);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyMusicQueueAdd(const shared_ptr<MusicClient>& bot, const shared_ptr<ts::music::SongInfo>& entry, int index, const std::shared_ptr<ConnectedClient>& invoker) {
|
|
Command notify("notifymusicqueueadd");
|
|
notify["bot_id"] = bot->getClientDatabaseId();
|
|
notify["song_id"] = entry->getSongId();
|
|
notify["url"] = entry->getUrl();
|
|
notify["index"] = index;
|
|
INVOKER(notify, invoker);
|
|
this->sendCommand(notify);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyMusicQueueRemove(const std::shared_ptr<MusicClient> &bot, const std::deque<std::shared_ptr<music::SongInfo>> &entry, const std::shared_ptr<ConnectedClient>& invoker) {
|
|
Command notify("notifymusicqueueremove");
|
|
notify["bot_id"] = bot->getClientDatabaseId();
|
|
int index = 0;
|
|
for(const auto& song : entry)
|
|
notify[index++]["song_id"] = song->getSongId();
|
|
INVOKER(notify, invoker);
|
|
this->sendCommand(notify);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyMusicQueueOrderChange(const std::shared_ptr<MusicClient> &bot, const std::shared_ptr<ts::music::SongInfo> &entry, int order, const std::shared_ptr<ConnectedClient>& invoker) {
|
|
Command notify("notifymusicqueueorderchange");
|
|
notify["bot_id"] = bot->getClientDatabaseId();
|
|
notify["song_id"] = entry->getSongId();
|
|
notify["index"] = order;
|
|
INVOKER(notify, invoker);
|
|
this->sendCommand(notify);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectedClient::notifyMusicPlayerStatusUpdate(const std::shared_ptr<ts::server::MusicClient> &bot) {
|
|
Command notify("notifymusicstatusupdate");
|
|
notify["bot_id"] = bot->getClientDatabaseId();
|
|
|
|
auto player = bot->current_player();
|
|
if(player) {
|
|
notify["player_buffered_index"] = player->bufferedUntil().count();
|
|
notify["player_replay_index"] = player->currentIndex().count();
|
|
} else {
|
|
notify["player_buffered_index"] = 0;
|
|
notify["player_replay_index"] = 0;
|
|
}
|
|
this->sendCommand(notify);
|
|
return true;
|
|
}
|
|
|
|
extern void apply_song(Command& command, const std::shared_ptr<ts::music::SongInfo>& element, int index = 0);
|
|
bool ConnectedClient::notifyMusicPlayerSongChange(const std::shared_ptr<MusicClient> &bot, const shared_ptr<music::SongInfo> &newEntry) {
|
|
Command notify("notifymusicplayersongchange");
|
|
notify["bot_id"] = bot->getClientDatabaseId();
|
|
|
|
if(newEntry) {
|
|
apply_song(notify, newEntry);
|
|
} else {
|
|
notify["song_id"] = 0;
|
|
}
|
|
this->sendCommand(notify);
|
|
return true;
|
|
} |