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

目录

匹配队列

匹配管理模块


匹配队列

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

这里用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;       // 获取用户的详细信息
};
相关推荐
暖核1 小时前
kubeadm 搭建 K8s v1.30 集群:从系统模板到 Calico 网络全流程
网络·容器·kubernetes
updayday8541 小时前
印染产线电脑进程异常占用资源却要逐台排查 - Ping64 远程进程管理
大数据·网络·数据库·安全·智能路由器
zxanz12 小时前
一张 SSL 证书,如何降低业务流量劫持与数据泄露风险
网络·网络协议·ssl
Novproxy_Z2 小时前
爬虫任务被拦截:区分 IP 封禁、设备封禁、账号封禁
爬虫·网络协议·tcp/ip
David猪大卫2 小时前
【C++修炼】STL容器—模板进阶:非类型模板参数、模板特化、模板分离编译
c语言·开发语言·c++·经验分享·笔记·学习·面试
茉莉玫瑰花茶2 小时前
GO [ 数据格式 ]
服务器·网络·算法·golang
明航咨询—张老师2 小时前
工业控制系统安全工程师(CISP-ICSSE)全解析:报考条件、知识体系与职业价值
网络
纪念 2292 小时前
C++ vector(一)
开发语言·c++
垆边人似月.2 小时前
最少的元素个数使和为 target
数据结构·c++·算法