【在线五子棋对战】游戏匹配模块

目录

匹配队列

匹配管理模块


匹配队列

游戏匹配模块,为用户段位分为青铜、白银、黄金三个段位,每个段位都有自己的匹配队列,下面先对匹配队列进行实现

这里用list代替queue,原因是STL中的queue不能删除元素,只能出队,而当用户主动取消匹配时,需要从队列中删除用户

cpp 复制代码
// 匹配队列(阻塞队列)
template <class T>
class Match_Queue
{
public:
    int size() // 获取元素个数
    {
        std::lock_guard<std::mutex> lock(_mutex);
        return _list.size();
    }

    bool empty() // 判断是否为空
    {
        std::lock_guard<std::mutex> lock(_mutex);
        return _list.empty();
    }

    void wait() // 阻塞线程
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }

    void notify_all()
    {
        _cond.notify_all();
    }

    void push(const T &uid) // 入队元素
    {
        std::lock_guard<std::mutex> lock(_mutex);

        _list.push_front(uid);
        _cond.notify_all(); // 每有一个新用户加入匹配队列,就唤醒一次线程
    }

    bool pop(T &uid) // 出队元素
    {
        std::lock_guard<std::mutex> lock(_mutex);

        if (empty()) // 队列为空返回false
            return false;
        uid = _list.front(); // [FIXME] 这样是不是成栈了(拿头部数据不是最后进来的吗)
        _list.pop_front();
        return true;
    }

    void remove(const T &uid) // 删除元素
    {
        std::lock_guard<std::mutex> lock(_mutex);
        _list.remove(uid);
    }

private:
    std::list<T> _list;            // 模拟可以删除元素的阻塞队列 (queue只能出队,不能删除!)
    std::mutex _mutex;             // 保证线程安全
    std::condition_variable _cond; // 条件变量  阻塞消费者(线程),当队列元素>2时,唤醒线程
};

匹配管理模块

匹配管理模块用于管理三个匹配队列(对应青铜、白银、黄金)

每个匹配队列都是一个线程,这里用C++的thread线程库

cpp 复制代码
class Matcher
{
    using websocket_server = websocketpp::server<websocketpp::config::asio>;

public:
    Matcher(Room_Manager *room, Online_Manager *online, User_Table *user)
        : _room(room),
          _online(online),
          _user(user),
          _th_bronze(std::thread(&Matcher::Th_Bronze_Entry, this)),
          _th_silver(std::thread(&Matcher::Th_Silver_Entry, this)),
          _th_gold(std::thread(&Matcher::Th_Gold_Entry, this))
    {
        LOG(LogLv::DEBUG, "游戏匹配模块初始化完毕...");
    }

    ~Matcher() // 回收线程
    {
        _q_bronze.notify_all();
        _q_silver.notify_all();
        _q_gold.notify_all();

        _th_bronze.join();
        _th_silver.join();
        _th_gold.join();
    }

    bool Add(int uid) // 根据玩家分数,放到不同匹配队列
    {
        // 根据uid,获取玩家信息(天梯分数)
        Json::Value usr;
        if (!_user->SelectById(uid, usr))
        {
            LOG(LogLv::ERROR, "获取用户[%d]信息失败", uid);
            return false;
        }

        int ladder_points = usr["ladder_points"].asInt(); // 获取该用户天梯分数

        if (ladder_points < 2000) // 放入青铜匹配队列
            _q_bronze.push(uid);
        else if (ladder_points > 2000 && ladder_points < 3000) // 放入白银匹配队列
            _q_silver.push(uid);
        else if (ladder_points > 3000) // 放入黄金匹配队列
            _q_gold.push(uid);

        return true;
    }

    bool Del(int uid)
    {
        // 根据uid,获取玩家信息(天梯分数)
        Json::Value usr;
        if (!_user->SelectById(uid, usr))
        {
            LOG(LogLv::ERROR, "获取用户[%d]信息失败", uid);
            return false;
        }

        int ladder_points = usr["ladder_points"].asInt(); // 获取该用户天梯分数

        if (ladder_points < 2000) // 从青铜匹配队列删除
            _q_bronze.remove(uid);
        else if (ladder_points > 2000 && ladder_points < 3000) // 从白银匹配队列删除
            _q_silver.remove(uid);
        else if (ladder_points > 3000) // 从黄金匹配队列删除
            _q_gold.remove(uid);

        return true;
    }

private:
    void Handle_Entry(Match_Queue<int> &mq)
    {
        while (true)
        {
            // 判断队列中人数是否>=2,若<2则阻塞
            while (mq.size() < 2)
                mq.wait();

            // 人数够俩人,出队俩凑房间
            int uid1, uid2;
            if (!mq.pop(uid1)) // 出队失败,就重新判断
                continue;
            if (!mq.pop(uid2))
            {
                // 第二个用户出队失败,先把第一个用户重新放回队列
                this->Add(uid1);
                continue;
            }

            // 校验两个玩家是否在线,如果有人掉线,就把另外一个人重新加入队列
            websocket_server::connection_ptr conn1 = _online->GetConn_FromHall(uid1);
            websocket_server::connection_ptr conn2 = _online->GetConn_FromHall(uid2);
            if(conn1.get() == nullptr && conn2.get() != nullptr) // 用户1掉线
            {
                Add(uid2);
                continue;
            }
            else if(conn1.get() != nullptr && conn2.get() == nullptr) // 用户2掉线
            {
                Add(uid1);
                continue;
            }
            else if(conn1.get() == nullptr && conn2.get() == nullptr) // 用户1,2掉线
            {
                continue;
            }

            // 为两个用户创建房间
            room_ptr room = _room->Create_Room(uid1, uid2);
            if(room.get() == nullptr) // 若创建房间失败,就重新将他俩放回匹配队列
            {
                Add(uid1);
                Add(uid2);
                continue;
            }
            room->AddUser(uid1, Chess::BLACK_CHESS);
            
            // 对两个玩家进行响应
            Json::Value resp = Protocol::MakeMatchSuccess();
            std::string body;
            Json_Util::Serialization(resp, body);
            conn1->send(body);
            conn2->send(body);
        }
    }

    void Th_Bronze_Entry() { return Handle_Entry(_q_bronze); } // 青铜匹配队列线程任务

    void Th_Silver_Entry() { return Handle_Entry(_q_silver); } // 白银匹配队列线程任务

    void Th_Gold_Entry() { return Handle_Entry(_q_gold); } // 黄金匹配队列线程任务

private:
    Match_Queue<int> _q_bronze; // 青铜匹配队列
    Match_Queue<int> _q_silver; // 白银匹配队列
    Match_Queue<int> _q_gold;   // 黄金匹配队列

    std::thread _th_bronze; // 青铜匹配队列所在线程
    std::thread _th_silver; // 白银匹配队列所在线程
    std::thread _th_gold;   // 黄金匹配队列所在线程

    Room_Manager *_room;     // 匹配成功后创建房间
    Online_Manager *_online; // 获取用户的连接
    User_Table *_user;       // 获取用户的详细信息
};
相关推荐
byte轻骑兵11 小时前
【LMP】规范精讲[13]: 参数的户口本、公钥的集装箱与出厂的兜底值
人工智能·网络协议·人机交互·lmp·蓝牙链路管理
apcipot_rain11 小时前
C++转Java日记(02)
java·开发语言·c++
流浪00111 小时前
计算机网络篇17:传输层——TCP 超时重传、三次握手与四次挥手,TIME_WAIT 为什么要等 2MSL
网络协议·tcp/ip·计算机网络·三次握手·四次挥手·超时重传
杜 硕11 小时前
从零开始的C++生活——第七讲——02
开发语言·c++·生活
Rabitebla19 小时前
【Linux 底层原理】进程(十):进程等待详解---从僵尸进程到 wait、waitpid 与 status
linux·c++·笔记·学习·visual studio
西西弗Sisyphus21 小时前
C++ 超时互斥锁(Timed Mutex):std::timed_mutex 与 std::unique_lock
c++
anew___1 天前
《从零手写操作系统 (63):安全与加固——从内核保护到沙箱隔离》
网络·安全
Lzg_na1 天前
VXLAN:背景、机制、报文字段与主机转发流程
网络
今天要早睡_1 天前
C++01_入门基础复习
开发语言·c++
乐维_lwops1 天前
案例解读:大型综合性科创产业园运维监控落地实践
运维·网络·人工智能·安全·it监控·国产运维智能体