matcher.hpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #ifndef __MATCHER_HPP__
  2. #define __MATCHER_HPP__
  3. #include "db.hpp"
  4. #include "room.hpp"
  5. #include "util.hpp"
  6. #include "online.hpp"
  7. #include <list>
  8. #include <thread>
  9. #include <mutex>
  10. #include <condition_variable>
  11. /*用户对战匹配管理模块 -- 将用户按分数分为青铜、黄金、王者三档,并分别为它们设计一个匹配队列,队列元素>=2则匹配成功,否则阻塞*/
  12. /*匹配队列类*/
  13. template <class T>
  14. class match_queue {
  15. public:
  16. match_queue() {}
  17. ~match_queue() {}
  18. /*目标元素入队列,并唤醒线程*/
  19. void push(const T& data) {
  20. std::unique_lock<std::mutex> lock(_mutex);
  21. _list.push_back(data);
  22. LOG(DEBUG, "%d用户加入匹配队列", data);
  23. // 匹配队列每新增一个元素,就唤醒对应的匹配线程,判断是否满足匹配要求(队列人数>=2)
  24. _cond.notify_all();
  25. }
  26. /*队头元素出队列并返回队头元素*/
  27. bool pop(T& data) {
  28. std::unique_lock<std::mutex> lock(_mutex);
  29. if(_list.empty()) return false;
  30. data = _list.front();
  31. _list.pop_front();
  32. LOG(DEBUG, "%d用户从匹配队列中移除", data);
  33. return true;
  34. }
  35. /*移除队列中的目标元素*/
  36. void remove(const T& data) {
  37. std::unique_lock<std::mutex> lock(_mutex);
  38. _list.remove(data);
  39. LOG(DEBUG, "%d用户从匹配队列中移除", data);
  40. }
  41. /*阻塞线程*/
  42. void wait() {
  43. std::unique_lock<std::mutex> lock(_mutex);
  44. _cond.wait(lock);
  45. }
  46. /*获取队列元素个数*/
  47. int size() {
  48. std::unique_lock<std::mutex> lock(_mutex);
  49. return _list.size();
  50. }
  51. /*判断队列是否为空*/
  52. bool empty() {
  53. std::unique_lock<std::mutex> lock(_mutex);
  54. return _list.empty();
  55. }
  56. private:
  57. std::list<T> _list; // 使用双向链表而不是queue充当匹配队列,便于用户取消匹配时将该用户从匹配队列中移除
  58. std::mutex _mutex; // 实现线程安全
  59. std::condition_variable _cond; // 条件变量,当向队列中push元素时唤醒,用于阻塞消费者
  60. };
  61. /*匹配管理类*/
  62. class matcher {
  63. private:
  64. void handler_match(match_queue<uint64_t> &mq) {
  65. while(true) {
  66. // 检查匹配条件是否满足(人数>=2),不满足则继续阻塞
  67. while(mq.size() < 2) mq.wait();
  68. // 条件满足,从队列中取出两个玩家
  69. uint64_t uid1, uid2;
  70. if(mq.pop(uid1) == false) continue;
  71. if(mq.pop(uid2) == false) {
  72. // 如果第二个玩家出队列失败,则需要将第一个玩家重新添加到队列中
  73. this->add(uid1);
  74. continue;
  75. }
  76. // 检查两个玩家是否都处于大厅在线状态,若一方掉线,则需要将另一方重新添加到队列
  77. wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
  78. wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
  79. if(conn1.get() == nullptr) {
  80. this->add(uid2);
  81. continue;
  82. }
  83. if(conn2.get() == nullptr) {
  84. this->add(uid1);
  85. continue;
  86. }
  87. // 为两个玩家创建房间,失败则重新添加到队列
  88. room_ptr rp = _rm->create_room(uid1, uid2);
  89. if(rp.get() == nullptr) {
  90. this->add(uid1);
  91. this->add(uid2);
  92. continue;
  93. }
  94. // 给玩家返回匹配成功的响应
  95. Json::Value resp;
  96. resp["optype"] = "match_success";
  97. resp["result"] = true;
  98. std::string body;
  99. json_util::serialize(resp, body);
  100. conn1->send(body);
  101. conn2->send(body);
  102. }
  103. }
  104. /*三个匹配队列的线程入口*/
  105. void th_low_entry() { handler_match(_q_low); }
  106. void th_mid_entry() { handler_match(_q_mid); }
  107. void th_high_entry() { handler_match(_q_high); }
  108. public:
  109. matcher(user_table *ut, online_manager *om, room_manager *rm)
  110. : _ut(ut), _om(om), _rm(rm),
  111. _th_low(std::thread(&matcher::th_low_entry, this)),
  112. _th_mid(std::thread(&matcher::th_mid_entry, this)),
  113. _th_high(std::thread(&matcher::th_high_entry, this)) {
  114. LOG(DEBUG, "游戏对战匹配管理模块初始化完毕");
  115. }
  116. ~matcher() {
  117. LOG(DEBUG, "游戏对战匹配管理模块已被销毁");
  118. }
  119. /*添加用户到匹配队列*/
  120. bool add(uint64_t uid) {
  121. // 根据用户id获取用户数据库信息
  122. Json::Value user;
  123. if(_ut->select_by_id(uid, user) == false) {
  124. LOG(DEBUG, "查找玩家%d信息失败", uid);
  125. return false;
  126. }
  127. // 根据用户分数将用户添加到对应的匹配队列中去
  128. int score = user["score"].asInt();
  129. if(score < 2000) _q_low.push(uid);
  130. else if(score >= 2000 && score < 3000) _q_mid.push(uid);
  131. else _q_high.push(uid);
  132. return true;
  133. }
  134. /*将用户从匹配队列中移除*/
  135. bool remove(uint64_t uid) {
  136. // 根据用户id获取用户数据库信息
  137. Json::Value user;
  138. if(_ut->select_by_id(uid, user) == false) {
  139. LOG(DEBUG, "查找用户%d信息失败", uid);
  140. return false;
  141. }
  142. // 根据用户分数将用户从对应的匹配队列中移除
  143. int score = user["score"].asInt();
  144. if(score < 2000) _q_low.remove(uid);
  145. else if(score >= 2000 && score < 3000) _q_mid.remove(uid);
  146. else _q_high.remove(uid);
  147. return true;
  148. }
  149. private:
  150. // 三个匹配队列(青铜/黄金/王者 -> low/mid/high)
  151. match_queue<uint64_t> _q_low;
  152. match_queue<uint64_t> _q_mid;
  153. match_queue<uint64_t> _q_high;
  154. // 三个管理匹配队列的线程
  155. std::thread _th_low;
  156. std::thread _th_mid;
  157. std::thread _th_high;
  158. room_manager *_rm; // 游戏房间管理句柄
  159. online_manager *_om; // 在线用户管理句柄
  160. user_table *_ut; // 用户数据管理句柄
  161. };
  162. #endif