merge master
This commit is contained in:
commit
662cdec589
@ -3,6 +3,8 @@
|
||||
#include "framework/cpp/protoutils.h"
|
||||
#include "framework/cpp/netmsghandler.h"
|
||||
|
||||
#include "perfmonitor.h"
|
||||
|
||||
namespace a8
|
||||
{
|
||||
class AsyncTcpClient;
|
||||
@ -30,6 +32,7 @@ class MSConn
|
||||
{
|
||||
static int msgid = f8::Net_GetMessageId(msg);
|
||||
f8::Net_SendMsg(tcp_client_, 0, msgid, msg);
|
||||
++PerfMonitor::Instance()->ms_send_times;
|
||||
#ifdef DEBUG
|
||||
f8::DumpMsgToLog(msg, "<<<<<<<MSC ");
|
||||
#endif
|
||||
@ -37,6 +40,7 @@ class MSConn
|
||||
void SendMsg(int msgid, ::google::protobuf::Message& msg)
|
||||
{
|
||||
f8::Net_SendMsg(tcp_client_, 0, msgid, msg);
|
||||
++PerfMonitor::Instance()->ms_send_times;
|
||||
#ifdef DEBUG
|
||||
f8::DumpMsgToLog(msg, "<<<<<<<IMC ");
|
||||
#endif
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include "framework/cpp/netmsghandler.h"
|
||||
|
||||
#include "perfmonitor.h"
|
||||
|
||||
//gate listener
|
||||
namespace a8
|
||||
{
|
||||
@ -31,6 +33,7 @@ class WSListener : public a8::Singleton<WSListener>
|
||||
{
|
||||
static int msgid = f8::Net_GetMessageId(msg);
|
||||
f8::Net_SendProxyMsg(tcp_listener_, sockhandle, 0, 0, msgid, msg);
|
||||
++PerfMonitor::Instance()->send_ws_count;
|
||||
#ifdef DEBUG
|
||||
f8::DumpMsgToLog(msg, "<<<<<<<WSL ");
|
||||
#endif
|
||||
@ -41,6 +44,7 @@ class WSListener : public a8::Singleton<WSListener>
|
||||
{
|
||||
static int msgid = f8::Net_GetMessageId(msg);
|
||||
f8::Net_SendProxyMsg(tcp_listener_, sockhandle, seqid, 0, msgid, msg);
|
||||
++PerfMonitor::Instance()->send_ws_count;
|
||||
#ifdef DEBUG
|
||||
f8::DumpMsgToLog(msg, "<<<<<<<WSL ");
|
||||
#endif
|
||||
|
@ -30,6 +30,8 @@
|
||||
#include "MSConnMgr.h"
|
||||
#include "IMConnMgr.h"
|
||||
|
||||
#include "cs_msgid.pb.h"
|
||||
|
||||
#include "framework/cpp/msgqueue.h"
|
||||
#include "framework/cpp/tglog.h"
|
||||
#include "framework/cpp/netmsghandler.h"
|
||||
@ -367,6 +369,22 @@ void App::DispatchMsg()
|
||||
|
||||
void App::ProcessWSProxyMsg(f8::MsgHdr& hdr)
|
||||
{
|
||||
if (hdr.msgid == cs::CMMessageId_e::_CMLogin) {
|
||||
cs::CMLoginCommonHead common_head;
|
||||
bool ok = common_head.ParseFromArray(hdr.buf + hdr.offset, hdr.buflen - hdr.offset);
|
||||
if (ok) {
|
||||
if (common_head.has_server_id()) {
|
||||
cs::CMLoginOld msg;
|
||||
msg.ParseFromArray(hdr.buf + hdr.offset, hdr.buflen - hdr.offset);
|
||||
PlayerMgr::Instance()->_CMLoginOld(hdr, msg);
|
||||
} else {
|
||||
cs::CMLogin msg;
|
||||
msg.ParseFromArray(hdr.buf + hdr.offset, hdr.buflen - hdr.offset);
|
||||
PlayerMgr::Instance()->_CMLogin(hdr, msg);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
f8::NetMsgHandler* handler = f8::GetNetMsgHandler(&HandlerMgr::Instance()->wsmsghandler,
|
||||
hdr.msgid);
|
||||
if (handler) {
|
||||
@ -675,3 +693,10 @@ bool App::IsTimeToReset(int time)
|
||||
{
|
||||
return BetweenDays(nowtime - 60 * SYS_RESET_TIME, time - 60 * SYS_RESET_TIME) > 0;
|
||||
}
|
||||
|
||||
void App::PreProcAvatarUrl(int self_channel, int target_channel, std::string& target_avatar_url)
|
||||
{
|
||||
if (self_channel == 6001 && target_channel != 6001) {
|
||||
target_avatar_url = "https://wx.qlogo.cn/mmopen/vi_32/q4oRsMFYBwPEVAeVI7tiasWSqaibr0GPQia432JhibGRYhqqEJofpWDYxJPq6q0hQ0j7icdACdHL78hrjYYHSjZQ3YA/132";
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ class App : public a8::Singleton<App>
|
||||
long long NewSeqId();
|
||||
time_t BetweenDays(time_t time1, time_t time2);
|
||||
bool IsTimeToReset(int time);
|
||||
void PreProcAvatarUrl(int self_channel, int target_channel, std::string& target_avatar_url);
|
||||
|
||||
private:
|
||||
void QuickExecute();
|
||||
|
@ -62,7 +62,18 @@ struct RecommandFriendTask
|
||||
cs::SMRecommandFriend msg;
|
||||
for (auto& pair : recommand_friends) {
|
||||
if (msg.friend_list_size() < 4) {
|
||||
*msg.add_friend_list() = pair.second;
|
||||
auto p = msg.add_friend_list() ;
|
||||
*p = pair.second;
|
||||
{
|
||||
int channel = f8::ExtractChannelIdFromAccountId
|
||||
(context.account_id);
|
||||
int target_channel = f8::ExtractChannelIdFromAccountId
|
||||
(p->base_data().account_id());
|
||||
App::Instance()->PreProcAvatarUrl
|
||||
(channel,
|
||||
target_channel,
|
||||
*p->mutable_base_data()->mutable_avatar_url());
|
||||
}
|
||||
exclude_account_ids.insert(pair.first);
|
||||
batch_account_ids.insert(pair.first);
|
||||
}
|
||||
@ -76,7 +87,18 @@ struct RecommandFriendTask
|
||||
}
|
||||
if (exclude_account_ids.find(user_info->base_data().account_id()) ==
|
||||
exclude_account_ids.end()) {
|
||||
*msg.add_friend_list() = *user_info;
|
||||
auto p = msg.add_friend_list();
|
||||
*p = *user_info;
|
||||
{
|
||||
int channel = f8::ExtractChannelIdFromAccountId
|
||||
(context.account_id);
|
||||
int target_channel = f8::ExtractChannelIdFromAccountId
|
||||
(p->base_data().account_id());
|
||||
App::Instance()->PreProcAvatarUrl
|
||||
(channel,
|
||||
target_channel,
|
||||
*p->mutable_base_data()->mutable_avatar_url());
|
||||
}
|
||||
exclude_account_ids.insert(user_info->base_data().account_id());
|
||||
batch_account_ids.insert(user_info->base_data().account_id());
|
||||
}
|
||||
|
@ -207,6 +207,11 @@ void DBHelper::ShuaOfflineUsers(Player* hum)
|
||||
fmtstr += " UNION SELECT '%s'";
|
||||
sql_params.push_back(a8::XValue(account_id));
|
||||
}
|
||||
if (f8::IsOnlineEnv()) {
|
||||
sql_params.push_back(10000 + (rand() % 5000));
|
||||
} else {
|
||||
sql_params.push_back(10000 + (rand() % 50));
|
||||
}
|
||||
auto on_ok =
|
||||
[] (a8::XParams& param, const f8::DataSet* data_set)
|
||||
{
|
||||
@ -242,7 +247,7 @@ void DBHelper::ShuaOfflineUsers(Player* hum)
|
||||
" A.user_value2, A.user_value3, A.last_logintime "
|
||||
"FROM `user` A "
|
||||
" LEFT JOIN (" + fmtstr + ") AS B ON B.account_id = A.account_id "
|
||||
"WHERE A.idx > (SELECT 9999 + FLOOR(RAND() * (MAX(idx) - 10000)) FROM `user`) AND "
|
||||
"WHERE A.idx > %d AND "
|
||||
" B.account_id IS NULL LIMIT 1, 10;"
|
||||
).c_str(),
|
||||
sql_params,
|
||||
|
@ -64,7 +64,9 @@ void HandlerMgr::RegisterNetMsgHandlers()
|
||||
RegisterNetMsgHandler(&wsmsghandler, &WSListener::_SS_Ping);
|
||||
|
||||
RegisterNetMsgHandler(&wsmsghandler, &PlayerMgr::_SS_WSP_SocketDisconnect);
|
||||
#if 0
|
||||
RegisterNetMsgHandler(&wsmsghandler, &PlayerMgr::_CMLogin);
|
||||
#endif
|
||||
|
||||
RegisterNetMsgHandler(&wsmsghandler, &Player::_CMPing);
|
||||
RegisterNetMsgHandler(&wsmsghandler, &Player::_CMUpdateUserInfo);
|
||||
|
@ -8,12 +8,16 @@
|
||||
#include "playermgr.h"
|
||||
#include "dbhelper.h"
|
||||
|
||||
#include "framework/cpp/dbpool.h"
|
||||
|
||||
static void SavePerfLog()
|
||||
{
|
||||
a8::UdpLog::Instance()->Info
|
||||
("max_run_delay_time:%d max_timer_idle:%d "
|
||||
"in_data_size:%d out_data_size:%d msgnode_size:%d read_count:%d max_login_time:%d "
|
||||
"login_ok_time:%d login_error_time:%d online_num:%d watch_num:%d cache_users_num:%d ",
|
||||
"login_ok_time:%d login_error_time:%d online_num:%d watch_num:%d cache_users_num:%d "
|
||||
"send_ws_count:%d db.total_query_num:%d db.exec_query_num:%d db.run_loop_num:%d "
|
||||
"ms_send_times:%d ms_recv_times:%d ",
|
||||
{
|
||||
PerfMonitor::Instance()->max_run_delay_time,
|
||||
PerfMonitor::Instance()->max_timer_idle,
|
||||
@ -26,7 +30,13 @@ static void SavePerfLog()
|
||||
PerfMonitor::Instance()->login_error_times,
|
||||
PlayerMgr::Instance()->OnlineNum(),
|
||||
PlayerMgr::Instance()->WatchPlayerNum(),
|
||||
DBHelper::Instance()->cache_users_hash.size()
|
||||
DBHelper::Instance()->cache_users_hash.size(),
|
||||
PerfMonitor::Instance()->send_ws_count,
|
||||
(long long)f8::DBPool::Instance()->total_query_num,
|
||||
(long long)f8::DBPool::Instance()->exec_query_num,
|
||||
(long long)f8::DBPool::Instance()->run_loop_num,
|
||||
PerfMonitor::Instance()->ms_send_times,
|
||||
PerfMonitor::Instance()->ms_recv_times
|
||||
});
|
||||
a8::UdpLog::Instance()->Info
|
||||
("run_times:%d timer_times:%d event_times:%d free_times:%d "
|
||||
|
@ -16,6 +16,9 @@ class PerfMonitor : public a8::Singleton<PerfMonitor>
|
||||
long long out_data_size = 0;
|
||||
long long in_data_size = 0;
|
||||
long long read_count = 0;
|
||||
long long send_ws_count = 0;
|
||||
long long ms_send_times = 0;
|
||||
long long ms_recv_times = 0;
|
||||
|
||||
void Init();
|
||||
void UnInit();
|
||||
|
@ -398,6 +398,14 @@ void Player::_CMFriendAddBlack(f8::MsgHdr& hdr, const cs::CMFriendAddBlack& msg)
|
||||
auto user_info = notifymsg.add_user_infos();
|
||||
TypeConvert::Convert(p->base_data, *user_info->mutable_base_data());
|
||||
TypeConvert::Convert(p->temp_custom_data, *user_info->mutable_temp_custom_data());
|
||||
{
|
||||
int target_channel = f8::ExtractChannelIdFromAccountId
|
||||
(user_info->base_data().account_id());
|
||||
App::Instance()->PreProcAvatarUrl
|
||||
(channel,
|
||||
target_channel,
|
||||
*user_info->mutable_base_data()->mutable_avatar_url());
|
||||
}
|
||||
SendMsg(notifymsg);
|
||||
}
|
||||
black_hash_[p->base_data.account_id] = p;
|
||||
@ -419,6 +427,14 @@ void Player::_CMFriendAddBlack(f8::MsgHdr& hdr, const cs::CMFriendAddBlack& msg)
|
||||
auto user_info = notifymsg.add_user_infos();
|
||||
TypeConvert::Convert(p->base_data, *user_info->mutable_base_data());
|
||||
TypeConvert::Convert(p->temp_custom_data, *user_info->mutable_temp_custom_data());
|
||||
{
|
||||
int target_channel = f8::ExtractChannelIdFromAccountId
|
||||
(user_info->base_data().account_id());
|
||||
App::Instance()->PreProcAvatarUrl
|
||||
(channel,
|
||||
target_channel,
|
||||
*user_info->mutable_base_data()->mutable_avatar_url());
|
||||
}
|
||||
SendMsg(notifymsg);
|
||||
}
|
||||
MarkDirty();
|
||||
@ -732,6 +748,14 @@ void Player::FillFriendList(::google::protobuf::RepeatedPtrField< ::cs::MFUserIn
|
||||
auto p = friend_list->Add();
|
||||
TypeConvert::Convert(pair.second->base_data, *(p->mutable_base_data()));
|
||||
TypeConvert::Convert(pair.second->temp_custom_data, *(p->mutable_temp_custom_data()));
|
||||
{
|
||||
int target_channel = f8::ExtractChannelIdFromAccountId
|
||||
(p->base_data().account_id());
|
||||
App::Instance()->PreProcAvatarUrl
|
||||
(channel,
|
||||
target_channel,
|
||||
*p->mutable_base_data()->mutable_avatar_url());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -741,6 +765,14 @@ void Player::FillBlackList(::google::protobuf::RepeatedPtrField< ::cs::MFUserInf
|
||||
auto p = black_list->Add();
|
||||
TypeConvert::Convert(pair.second->base_data, *(p->mutable_base_data()));
|
||||
TypeConvert::Convert(pair.second->temp_custom_data, *(p->mutable_temp_custom_data()));
|
||||
{
|
||||
int target_channel = f8::ExtractChannelIdFromAccountId
|
||||
(p->base_data().account_id());
|
||||
App::Instance()->PreProcAvatarUrl
|
||||
(channel,
|
||||
target_channel,
|
||||
*p->mutable_base_data()->mutable_avatar_url());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -764,6 +796,13 @@ void Player::FillMFUserInfo(cs::MFUserInfo* user_info)
|
||||
{
|
||||
TypeConvert::Convert(myself.base_data, *(user_info->mutable_base_data()));
|
||||
TypeConvert::Convert(myself.temp_custom_data, *(user_info->mutable_temp_custom_data()));
|
||||
{
|
||||
int target_channel = f8::ExtractChannelIdFromAccountId(user_info->base_data().account_id());
|
||||
App::Instance()->PreProcAvatarUrl
|
||||
(channel,
|
||||
target_channel,
|
||||
*user_info->mutable_base_data()->mutable_avatar_url());
|
||||
}
|
||||
}
|
||||
|
||||
std::set<std::string>& Player::GetExcludeAccountIds()
|
||||
@ -863,7 +902,16 @@ void Player::FillApplyList(const cs::MFPaging& paging, cs::SMFriendApplyList& re
|
||||
if (GetBlackListById(pair.second->base_data.account_id)) {
|
||||
continue;
|
||||
}
|
||||
TypeConvert::Convert(*pair.second, *respmsg.add_apply_list());
|
||||
cs::MFFriendApply* apply_pb = respmsg.add_apply_list();
|
||||
TypeConvert::Convert(*pair.second, *apply_pb);
|
||||
{
|
||||
int target_channel = f8::ExtractChannelIdFromAccountId
|
||||
(apply_pb->base_data().account_id());
|
||||
App::Instance()->PreProcAvatarUrl
|
||||
(channel,
|
||||
target_channel,
|
||||
*apply_pb->mutable_base_data()->mutable_avatar_url());
|
||||
}
|
||||
}
|
||||
++i;
|
||||
}
|
||||
@ -1062,6 +1110,14 @@ void Player::NotifyUserInfoUpdate(Friend* friend_data)
|
||||
auto p = msg.add_user_infos();
|
||||
TypeConvert::Convert(friend_data->base_data, *p->mutable_base_data());
|
||||
TypeConvert::Convert(friend_data->temp_custom_data, *p->mutable_temp_custom_data());
|
||||
{
|
||||
int target_channel = f8::ExtractChannelIdFromAccountId
|
||||
(p->base_data().account_id());
|
||||
App::Instance()->PreProcAvatarUrl
|
||||
(channel,
|
||||
target_channel,
|
||||
*p->mutable_base_data()->mutable_avatar_url());
|
||||
}
|
||||
SendMsg(msg);
|
||||
}
|
||||
}
|
||||
@ -1086,6 +1142,14 @@ void Player::SyncOtherFriend()
|
||||
auto p = msg.add_user_infos();
|
||||
TypeConvert::Convert(myself.base_data, *p->mutable_base_data());
|
||||
TypeConvert::Convert(myself.temp_custom_data, *p->mutable_temp_custom_data());
|
||||
{
|
||||
int target_channel = f8::ExtractChannelIdFromAccountId
|
||||
(p->base_data().account_id());
|
||||
App::Instance()->PreProcAvatarUrl
|
||||
(channel,
|
||||
target_channel,
|
||||
*p->mutable_base_data()->mutable_avatar_url());
|
||||
}
|
||||
|
||||
for (auto& pair : friend_hash_) {
|
||||
Player* hum = PlayerMgr::Instance()->GetPlayerByAccountId(pair.second->base_data.account_id);
|
||||
|
@ -87,6 +87,7 @@ void PlayerMgr::_SS_IM_FriendApply(f8::MsgHdr& hdr, const ss::SS_IM_FriendApply&
|
||||
|
||||
void PlayerMgr::_SS_IM_OnUserOnline(f8::MsgHdr& hdr, const ss::SS_IM_OnUserOnline& msg)
|
||||
{
|
||||
int times = 0;
|
||||
for (auto& account_id : msg.account_ids()) {
|
||||
auto itr = watch_players_.find(account_id);
|
||||
if (itr != watch_players_.end()) {
|
||||
@ -94,6 +95,16 @@ void PlayerMgr::_SS_IM_OnUserOnline(f8::MsgHdr& hdr, const ss::SS_IM_OnUserOnlin
|
||||
list_for_each_entry_safe(node, tmp, &itr->second, watch_node) {
|
||||
node->base_data.online = true;
|
||||
node->hum->NotifyUserInfoUpdate(node);
|
||||
++times;
|
||||
if (times > 100) {
|
||||
a8::UdpLog::Instance()->Warning
|
||||
("OnUserOnLine watch account_id:%s times:%d ",
|
||||
{
|
||||
account_id,
|
||||
times
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -101,6 +112,7 @@ void PlayerMgr::_SS_IM_OnUserOnline(f8::MsgHdr& hdr, const ss::SS_IM_OnUserOnlin
|
||||
|
||||
void PlayerMgr::_SS_IM_OnUserOffline(f8::MsgHdr& hdr, const ss::SS_IM_OnUserOffline& msg)
|
||||
{
|
||||
int times = 0;
|
||||
for (auto& account_id : msg.account_ids()) {
|
||||
auto itr = watch_players_.find(account_id);
|
||||
if (itr != watch_players_.end()) {
|
||||
@ -108,6 +120,16 @@ void PlayerMgr::_SS_IM_OnUserOffline(f8::MsgHdr& hdr, const ss::SS_IM_OnUserOffl
|
||||
list_for_each_entry_safe(node, tmp, &itr->second, watch_node) {
|
||||
node->base_data.online = false;
|
||||
node->hum->NotifyUserInfoUpdate(node);
|
||||
++times;
|
||||
if (times > 100) {
|
||||
a8::UdpLog::Instance()->Warning
|
||||
("OnUserOffLine watch account_id:%s times:%d ",
|
||||
{
|
||||
account_id,
|
||||
times
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -191,6 +213,17 @@ void PlayerMgr::_SS_IM_UpdateUserInfo(f8::MsgHdr& hdr, const ss::SS_IM_UpdateUse
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerMgr::_CMLoginOld(f8::MsgHdr& hdr, const cs::CMLoginOld& msg)
|
||||
{
|
||||
cs::CMLogin new_msg;
|
||||
new_msg.set_account_id(msg.account_id());
|
||||
new_msg.set_session_id(msg.session_id());
|
||||
new_msg.set_nickname(msg.nickname());
|
||||
new_msg.set_avatar_url(msg.avatar_url());
|
||||
new_msg.set_sex(msg.sex());
|
||||
_CMLogin(hdr, new_msg);
|
||||
}
|
||||
|
||||
void PlayerMgr::_CMLogin(f8::MsgHdr& hdr, const cs::CMLogin& msg)
|
||||
{
|
||||
Player* hum = GetPlayerByAccountId(msg.account_id());
|
||||
@ -467,11 +500,17 @@ void PlayerMgr::AsyncLoginOnOk(const std::string& account_id,
|
||||
if (hdr) {
|
||||
Player* hum = GetPlayerByAccountId(account_id);
|
||||
if (hum) {
|
||||
abort();
|
||||
pending_socket_hash_.erase(hdr->socket_handle);
|
||||
pending_account_hash_.erase(account_id);
|
||||
f8::MsgHdr::Destroy(hdr);
|
||||
return;
|
||||
}
|
||||
hum = GetPlayerBySocket(hdr->socket_handle);
|
||||
if (hum) {
|
||||
abort();
|
||||
pending_socket_hash_.erase(hdr->socket_handle);
|
||||
pending_account_hash_.erase(account_id);
|
||||
f8::MsgHdr::Destroy(hdr);
|
||||
return;
|
||||
}
|
||||
{
|
||||
hum = new Player();
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace cs
|
||||
{
|
||||
class CMLoginOld;
|
||||
class CMLogin;
|
||||
}
|
||||
|
||||
@ -53,6 +54,7 @@ class PlayerMgr : public a8::Singleton<PlayerMgr>
|
||||
|
||||
void _SS_IM_UpdateUserInfo(f8::MsgHdr& hdr, const ss::SS_IM_UpdateUserInfo& msg);
|
||||
|
||||
void _CMLoginOld(f8::MsgHdr& hdr, const cs::CMLoginOld& msg);
|
||||
void _CMLogin(f8::MsgHdr& hdr, const cs::CMLogin& msg);
|
||||
|
||||
Player* GetPlayerBySocket(int socket);
|
||||
|
@ -78,7 +78,11 @@ message MFFriendApply
|
||||
}
|
||||
|
||||
//登录好友服
|
||||
message CMLogin
|
||||
message CMLoginCommonHead
|
||||
{
|
||||
optional int32 server_id = 1; //保留
|
||||
}
|
||||
message CMLoginOld
|
||||
{
|
||||
optional string account_id = 3; //账号id
|
||||
optional string session_id = 20; //sessionid
|
||||
@ -86,6 +90,17 @@ message CMLogin
|
||||
optional string avatar_url = 5; //头像
|
||||
optional int32 sex = 6; //性别
|
||||
}
|
||||
message CMLogin
|
||||
{
|
||||
optional int32 server_id = 1; //保留(定死传1)
|
||||
optional string reserved2 = 2; //保留
|
||||
optional string account_id = 3; //账号id
|
||||
optional string session_id = 20; //sessionid
|
||||
optional string nickname = 4; //昵称
|
||||
optional int32 proto_version = 5; //协议版本号Constant_e.ProtoVersion
|
||||
optional int32 sex = 6; //性别
|
||||
optional string avatar_url = 7; //头像
|
||||
}
|
||||
//登录回复
|
||||
message SMLogin
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user