目录
匹配队列
游戏匹配模块,为用户段位分为青铜、白银、黄金三个段位,每个段位都有自己的匹配队列,下面先对匹配队列进行实现
这里用list代替queue,原因是STL中的queue不能删除元素,只能出队,而当用户主动取消匹配时,需要从队列中删除用户
cpp
// 匹配队列(阻塞队列)
template <class T>
class Match_Queue
{
public:
int size() // 获取元素个数
{
std::lock_guard<std::mutex> lock(_mutex);
return _list.size();
}
bool empty() // 判断是否为空
{
std::lock_guard<std::mutex> lock(_mutex);
return _list.empty();
}
void wait() // 阻塞线程
{
std::unique_lock<std::mutex> lock(_mutex);
_cond.wait(lock);
}
void notify_all()
{
_cond.notify_all();
}
void push(const T &uid) // 入队元素
{
std::lock_guard<std::mutex> lock(_mutex);
_list.push_front(uid);
_cond.notify_all(); // 每有一个新用户加入匹配队列,就唤醒一次线程
}
bool pop(T &uid) // 出队元素
{
std::lock_guard<std::mutex> lock(_mutex);
if (empty()) // 队列为空返回false
return false;
uid = _list.front(); // [FIXME] 这样是不是成栈了(拿头部数据不是最后进来的吗)
_list.pop_front();
return true;
}
void remove(const T &uid) // 删除元素
{
std::lock_guard<std::mutex> lock(_mutex);
_list.remove(uid);
}
private:
std::list<T> _list; // 模拟可以删除元素的阻塞队列 (queue只能出队,不能删除!)
std::mutex _mutex; // 保证线程安全
std::condition_variable _cond; // 条件变量 阻塞消费者(线程),当队列元素>2时,唤醒线程
};
匹配管理模块
匹配管理模块用于管理三个匹配队列(对应青铜、白银、黄金)
每个匹配队列都是一个线程,这里用C++的thread线程库
cpp
class Matcher
{
using websocket_server = websocketpp::server<websocketpp::config::asio>;
public:
Matcher(Room_Manager *room, Online_Manager *online, User_Table *user)
: _room(room),
_online(online),
_user(user),
_th_bronze(std::thread(&Matcher::Th_Bronze_Entry, this)),
_th_silver(std::thread(&Matcher::Th_Silver_Entry, this)),
_th_gold(std::thread(&Matcher::Th_Gold_Entry, this))
{
LOG(LogLv::DEBUG, "游戏匹配模块初始化完毕...");
}
~Matcher() // 回收线程
{
_q_bronze.notify_all();
_q_silver.notify_all();
_q_gold.notify_all();
_th_bronze.join();
_th_silver.join();
_th_gold.join();
}
bool Add(int uid) // 根据玩家分数,放到不同匹配队列
{
// 根据uid,获取玩家信息(天梯分数)
Json::Value usr;
if (!_user->SelectById(uid, usr))
{
LOG(LogLv::ERROR, "获取用户[%d]信息失败", uid);
return false;
}
int ladder_points = usr["ladder_points"].asInt(); // 获取该用户天梯分数
if (ladder_points < 2000) // 放入青铜匹配队列
_q_bronze.push(uid);
else if (ladder_points > 2000 && ladder_points < 3000) // 放入白银匹配队列
_q_silver.push(uid);
else if (ladder_points > 3000) // 放入黄金匹配队列
_q_gold.push(uid);
return true;
}
bool Del(int uid)
{
// 根据uid,获取玩家信息(天梯分数)
Json::Value usr;
if (!_user->SelectById(uid, usr))
{
LOG(LogLv::ERROR, "获取用户[%d]信息失败", uid);
return false;
}
int ladder_points = usr["ladder_points"].asInt(); // 获取该用户天梯分数
if (ladder_points < 2000) // 从青铜匹配队列删除
_q_bronze.remove(uid);
else if (ladder_points > 2000 && ladder_points < 3000) // 从白银匹配队列删除
_q_silver.remove(uid);
else if (ladder_points > 3000) // 从黄金匹配队列删除
_q_gold.remove(uid);
return true;
}
private:
void Handle_Entry(Match_Queue<int> &mq)
{
while (true)
{
// 判断队列中人数是否>=2,若<2则阻塞
while (mq.size() < 2)
mq.wait();
// 人数够俩人,出队俩凑房间
int uid1, uid2;
if (!mq.pop(uid1)) // 出队失败,就重新判断
continue;
if (!mq.pop(uid2))
{
// 第二个用户出队失败,先把第一个用户重新放回队列
this->Add(uid1);
continue;
}
// 校验两个玩家是否在线,如果有人掉线,就把另外一个人重新加入队列
websocket_server::connection_ptr conn1 = _online->GetConn_FromHall(uid1);
websocket_server::connection_ptr conn2 = _online->GetConn_FromHall(uid2);
if(conn1.get() == nullptr && conn2.get() != nullptr) // 用户1掉线
{
Add(uid2);
continue;
}
else if(conn1.get() != nullptr && conn2.get() == nullptr) // 用户2掉线
{
Add(uid1);
continue;
}
else if(conn1.get() == nullptr && conn2.get() == nullptr) // 用户1,2掉线
{
continue;
}
// 为两个用户创建房间
room_ptr room = _room->Create_Room(uid1, uid2);
if(room.get() == nullptr) // 若创建房间失败,就重新将他俩放回匹配队列
{
Add(uid1);
Add(uid2);
continue;
}
room->AddUser(uid1, Chess::BLACK_CHESS);
// 对两个玩家进行响应
Json::Value resp = Protocol::MakeMatchSuccess();
std::string body;
Json_Util::Serialization(resp, body);
conn1->send(body);
conn2->send(body);
}
}
void Th_Bronze_Entry() { return Handle_Entry(_q_bronze); } // 青铜匹配队列线程任务
void Th_Silver_Entry() { return Handle_Entry(_q_silver); } // 白银匹配队列线程任务
void Th_Gold_Entry() { return Handle_Entry(_q_gold); } // 黄金匹配队列线程任务
private:
Match_Queue<int> _q_bronze; // 青铜匹配队列
Match_Queue<int> _q_silver; // 白银匹配队列
Match_Queue<int> _q_gold; // 黄金匹配队列
std::thread _th_bronze; // 青铜匹配队列所在线程
std::thread _th_silver; // 白银匹配队列所在线程
std::thread _th_gold; // 黄金匹配队列所在线程
Room_Manager *_room; // 匹配成功后创建房间
Online_Manager *_online; // 获取用户的连接
User_Table *_user; // 获取用户的详细信息
};