#ifndef __MATCHER_HPP__ #define __MATCHER_HPP__ #include "db.hpp" #include "room.hpp" #include "util.hpp" #include "online.hpp" #include #include #include #include /*用户对战匹配管理模块 -- 将用户按分数分为青铜、黄金、王者三档,并分别为它们设计一个匹配队列,队列元素>=2则匹配成功,否则阻塞*/ /*匹配队列类*/ template class match_queue { public: match_queue() {} ~match_queue() {} /*目标元素入队列,并唤醒线程*/ void push(const T& data) { std::unique_lock lock(_mutex); _list.push_back(data); LOG(DEBUG, "%d用户加入匹配队列", data); // 匹配队列每新增一个元素,就唤醒对应的匹配线程,判断是否满足匹配要求(队列人数>=2) _cond.notify_all(); } /*队头元素出队列并返回队头元素*/ bool pop(T& data) { std::unique_lock 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 lock(_mutex); _list.remove(data); LOG(DEBUG, "%d用户从匹配队列中移除", data); } /*阻塞线程*/ void wait() { std::unique_lock lock(_mutex); _cond.wait(lock); } /*获取队列元素个数*/ int size() { std::unique_lock lock(_mutex); return _list.size(); } /*判断队列是否为空*/ bool empty() { std::unique_lock lock(_mutex); return _list.empty(); } private: std::list _list; // 使用双向链表而不是queue充当匹配队列,便于用户取消匹配时将该用户从匹配队列中移除 std::mutex _mutex; // 实现线程安全 std::condition_variable _cond; // 条件变量,当向队列中push元素时唤醒,用于阻塞消费者 }; /*匹配管理类*/ class matcher { private: void handler_match(match_queue &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 _q_low; match_queue _q_mid; match_queue _q_high; // 三个管理匹配队列的线程 std::thread _th_low; std::thread _th_mid; std::thread _th_high; room_manager *_rm; // 游戏房间管理句柄 online_manager *_om; // 在线用户管理句柄 user_table *_ut; // 用户数据管理句柄 }; #endif