玩家匹配是根据自己的天梯分数进行匹配的,而服务器中将玩家天梯分数分为三个档次:
1. 普通:天梯分数小于2000分 2. 高手:天梯分数介于2000~3000分之间 3. 大神:天梯分数大于3000分
当玩家进行对战匹配时,服务器会根据档次,将玩家送到不同档次的匹配队列当中。共有3个匹配队列,分别是普通队列、高手队列和大神队列,每一条队列由单独的线程去控制。因此,匹配对战模块,需要由两个类,一个类是匹配队列的类,另外一个是管理匹配队列的类。
匹配队列类
当玩家进行匹配对战的请求后,服务器会将玩家添加至相应的匹配队列当中,匹配成功后,会从匹配队列中移除该玩家,而在匹配成功前,玩家可能会中止匹配。因此,匹配队列应该包含的功能有入队、出队、和移除指定玩家,玩家处在的位置可能是队列的中间,因此,匹配队列采用的是双向循环链表。在匹配过程中,如果暂时为达到匹配玩家个数,该线程会进入阻塞等待状态,因此需要实现的功能还有阻塞等待的方法,获取队列元素个数、判断队列是否为空的方法。
代码语言:javascript复制template<class T>
class match_queue
{
private:
/*由于可能要删除中间数据,因此使用双向链表,而不使用队列*/
std::list<T> _list;
//保证线程安全
std::mutex _mutex;
//使用条件变量实现阻塞,在队列中元素个数小于2的时候进行阻塞
std::condition_variable _cond;
public:
/*入队*/
void push(T& data)
{
std::unique_lock<std::mutex> lock(_mutex);
_list.push_back(data);
/*每次有玩家进入匹配队列后,唤醒线程*/
_cond.notify_all();
}
/*出队*/
bool pop(T& data)
{
std::unique_lock<std::mutex> lock(_mutex);
if(_list.empty()==true)
{
return false;
}
data = _list.front();
_list.pop_front();
return true;
}
/*移除指定元素*/
void remove(T& data)
{
std::unique_lock<std::mutex> lock(_mutex);
_list.remove(data);
}
/*获取队列元素个数*/
int size()
{
std::unique_lock<std::mutex> lock(_mutex);
return _list.size();
}
/*判断队列是否为空*/
bool empty()
{
std::unique_lock<std::mutex> lock(_mutex);
return _list.empty();
}
/*阻塞等待*/
void wait()
{
std::unique_lock<std::mutex> lock(_mutex);
_cond.wait(lock);
}
};
匹配队列管理类
在匹配队列管理类中,创建三个线程,每一个线程分别管理着每一条匹配队列:
普通线程管理普通队列,高手线程管理高手队列,大神线程管理大神队列。
而管理的方法是:实现匹配对战:当玩家数量小于2时,线程继续阻塞。大于2时,将两个玩家出队,然后将玩家添加到房间,最后对玩家进行一个匹配成功的响应。
代码语言:javascript复制#ifndef __M__MATCHER_H__
#define __M__MATCHER_H__
#include<list>
#include <mutex>
#include <condition_variable>
#include "room.hpp"
template<class T>
class match_queue
{
private:
/*由于可能要删除中间数据,因此使用双向链表,而不使用队列*/
std::list<T> _list;
//保证线程安全
std::mutex _mutex;
//使用条件变量实现阻塞,在队列中元素个数小于2的时候进行阻塞
std::condition_variable _cond;
public:
/*入队*/
void push(T& data)
{
std::unique_lock<std::mutex> lock(_mutex);
_list.push_back(data);
/*每次有玩家进入匹配队列后,唤醒线程*/
_cond.notify_all();
}
/*出队*/
bool pop(T& data)
{
std::unique_lock<std::mutex> lock(_mutex);
if(_list.empty()==true)
{
return false;
}
data = _list.front();
_list.pop_front();
return true;
}
/*移除指定元素*/
void remove(T& data)
{
std::unique_lock<std::mutex> lock(_mutex);
_list.remove(data);
}
/*获取队列元素个数*/
int size()
{
std::unique_lock<std::mutex> lock(_mutex);
return _list.size();
}
/*判断队列是否为空*/
bool empty()
{
std::unique_lock<std::mutex> lock(_mutex);
return _list.empty();
}
/*阻塞等待*/
void wait()
{
std::unique_lock<std::mutex> lock(_mutex);
_cond.wait(lock);
}
};
class matcher
{
private:
/*普通队列*/
match_queue<uint64_t> _q_normal;
/*高手队列*/
match_queue<uint64_t> _q_hight;
/*大神队列*/
match_queue<uint64_t> _q_super;
/*普通线程*/
std::thread _th_normal;
/*高手线程*/
std::thread _th_hight;
/*大神线程*/
std::thread _th_super;
room_manager *_rm;
user_table *_ut;
online_manager *_om;
private:
void handle_match(match_queue<uint64_t>& mq)
{
while(1)
{
/*判断队列中玩家个数是否大于2*/
while(mq.size()<2)
{
mq.wait();
}
/*大于2,将两个玩家出队*/
uint64_t uid1,uid2;
bool ret = mq.pop(uid1);
if(ret==false)
{
continue;
}
ret = mq.pop(uid2);
if(ret==false)
{
continue;
}
/*两个玩家出队后,获取对应的通信连接,然后判断是否依然连接在线*/
wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
if(conn1.get()==nullptr)
{
this->add(uid1);
continue;
}
wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
if(conn2.get()==nullptr)
{
this->add(uid2);
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_normal_entry(){return handle_match(_q_normal);}
void _th_hight_entry(){return handle_match(_q_hight);}
void _th_super_entry(){return handle_match(_q_super);}
public:
matcher(room_manager *rm,user_table* ut,online_manager* om)
:_rm(rm),_ut(ut),_om(om)
,_th_normal(std::thread(&matcher::_th_normal_entry,this))
,_th_hight(std::thread(&matcher::_th_hight_entry,this))
,_th_super(std::thread(&matcher::_th_super_entry,this))
{
DLOG("游戏匹配模块初始化完毕...");
}
bool add(uint64_t uid)
{
/*根据uid,获取到玩家的信息*/
Json::Value user;
bool ret = _ut->select_by_id(uid,user);
if(ret==false)
{
DLOG("获取玩家:%d 信息失败",uid);
return false;
}
int score = user["score"].asInt();
if(score < 2000)
{
_q_normal.push(uid);
}
else if(score>=2000 && score < 3000)
{
_q_normal.push(uid);
}
else
{
_q_normal.push(uid);
}
return true;
}
bool del(uint64_t uid)
{
Json::Value user;
bool ret = _ut->select_by_id(uid,user);
if(ret==false)
{
DLOG("获取玩家:%d 信息失败",uid);
return false;
}
int score = user["score"].asInt();
if(score<2000)
{
_q_normal.remove(uid);
}
else if(score>=2000 && score<3000)
{
_q_hight.remove(uid);
}
else
{
_q_super.remove(uid);
}
return true;
}
};
#endif