| 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
 |