123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- #ifndef __MATCHER_HPP__
- #define __MATCHER_HPP__
- #include "db.hpp"
- #include "room.hpp"
- #include "util.hpp"
- #include "online.hpp"
- #include <list>
- #include <thread>
- #include <mutex>
- #include <condition_variable>
- /*用户对战匹配管理模块 -- 将用户按分数分为青铜、黄金、王者三档,并分别为它们设计一个匹配队列,队列元素>=2则匹配成功,否则阻塞*/
- /*匹配队列类*/
- template <class T>
- class match_queue {
- public:
- match_queue() {}
- ~match_queue() {}
- /*目标元素入队列,并唤醒线程*/
- void push(const T& data) {
- std::unique_lock<std::mutex> lock(_mutex);
- _list.push_back(data);
- LOG(DEBUG, "%d用户加入匹配队列", data);
- // 匹配队列每新增一个元素,就唤醒对应的匹配线程,判断是否满足匹配要求(队列人数>=2)
- _cond.notify_all();
- }
- /*队头元素出队列并返回队头元素*/
- bool pop(T& data) {
- std::unique_lock<std::mutex> lock(_mutex);
- if(_list.empty()) return false;
- data = _list.front();
- _list.pop_front();
- LOG(DEBUG, "%d用户从匹配队列中移除", data);
- return true;
- }
- /*移除队列中的目标元素*/
- void remove(const T& data) {
- std::unique_lock<std::mutex> lock(_mutex);
- _list.remove(data);
- LOG(DEBUG, "%d用户从匹配队列中移除", data);
- }
-
- /*阻塞线程*/
- void wait() {
- std::unique_lock<std::mutex> lock(_mutex);
- _cond.wait(lock);
- }
- /*获取队列元素个数*/
- int size() {
- std::unique_lock<std::mutex> lock(_mutex);
- return _list.size();
- }
- /*判断队列是否为空*/
- bool empty() {
- std::unique_lock<std::mutex> lock(_mutex);
- return _list.empty();
- }
- private:
- std::list<T> _list; // 使用双向链表而不是queue充当匹配队列,便于用户取消匹配时将该用户从匹配队列中移除
- std::mutex _mutex; // 实现线程安全
- std::condition_variable _cond; // 条件变量,当向队列中push元素时唤醒,用于阻塞消费者
- };
- /*匹配管理类*/
- class matcher {
- private:
- void handler_match(match_queue<uint64_t> &mq) {
- while(true) {
- // 检查匹配条件是否满足(人数>=2),不满足则继续阻塞
- while(mq.size() < 2) mq.wait();
- // 条件满足,从队列中取出两个玩家
- uint64_t uid1, uid2;
- if(mq.pop(uid1) == false) continue;
- if(mq.pop(uid2) == false) {
- // 如果第二个玩家出队列失败,则需要将第一个玩家重新添加到队列中
- this->add(uid1);
- continue;
- }
- // 检查两个玩家是否都处于大厅在线状态,若一方掉线,则需要将另一方重新添加到队列
- wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
- wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
- if(conn1.get() == nullptr) {
- this->add(uid2);
- continue;
- }
- if(conn2.get() == nullptr) {
- this->add(uid1);
- continue;
- }
- // 为两个玩家创建房间,失败则重新添加到队列
- room_ptr rp = _rm->create_room(uid1, uid2);
- if(rp.get() == nullptr) {
- this->add(uid1);
- this->add(uid2);
- continue;
- }
- // 给玩家返回匹配成功的响应
- Json::Value resp;
- resp["optype"] = "match_success";
- resp["result"] = true;
- std::string body;
- json_util::serialize(resp, body);
- conn1->send(body);
- conn2->send(body);
- }
- }
- /*三个匹配队列的线程入口*/
- void th_low_entry() { handler_match(_q_low); }
- void th_mid_entry() { handler_match(_q_mid); }
- void th_high_entry() { handler_match(_q_high); }
- public:
- matcher(user_table *ut, online_manager *om, room_manager *rm)
- : _ut(ut), _om(om), _rm(rm),
- _th_low(std::thread(&matcher::th_low_entry, this)),
- _th_mid(std::thread(&matcher::th_mid_entry, this)),
- _th_high(std::thread(&matcher::th_high_entry, this)) {
- LOG(DEBUG, "游戏对战匹配管理模块初始化完毕");
- }
- ~matcher() {
- LOG(DEBUG, "游戏对战匹配管理模块已被销毁");
- }
- /*添加用户到匹配队列*/
- bool add(uint64_t uid) {
- // 根据用户id获取用户数据库信息
- Json::Value user;
- if(_ut->select_by_id(uid, user) == false) {
- LOG(DEBUG, "查找玩家%d信息失败", uid);
- return false;
- }
- // 根据用户分数将用户添加到对应的匹配队列中去
- int score = user["score"].asInt();
- if(score < 2000) _q_low.push(uid);
- else if(score >= 2000 && score < 3000) _q_mid.push(uid);
- else _q_high.push(uid);
- return true;
- }
- /*将用户从匹配队列中移除*/
- bool remove(uint64_t uid) {
- // 根据用户id获取用户数据库信息
- Json::Value user;
- if(_ut->select_by_id(uid, user) == false) {
- LOG(DEBUG, "查找用户%d信息失败", uid);
- return false;
- }
- // 根据用户分数将用户从对应的匹配队列中移除
- int score = user["score"].asInt();
- if(score < 2000) _q_low.remove(uid);
- else if(score >= 2000 && score < 3000) _q_mid.remove(uid);
- else _q_high.remove(uid);
- return true;
- }
- private:
- // 三个匹配队列(青铜/黄金/王者 -> low/mid/high)
- match_queue<uint64_t> _q_low;
- match_queue<uint64_t> _q_mid;
- match_queue<uint64_t> _q_high;
- // 三个管理匹配队列的线程
- std::thread _th_low;
- std::thread _th_mid;
- std::thread _th_high;
- room_manager *_rm; // 游戏房间管理句柄
- online_manager *_om; // 在线用户管理句柄
- user_table *_ut; // 用户数据管理句柄
- };
- #endif
|