【在線五子棋對戰】十、對戰玩家匹配管理模塊

文章目錄

  • 前言
  • Ⅰ. 匹配隊列實現
  • Ⅱ. 匹配隊列管理類實現
  • 完整代碼

在這里插入圖片描述

前言

五子棋對戰的玩家匹配是根據自己的天梯分數進行匹配的,而服務器中將玩家天梯分數分為三個檔次:

  • 青銅:天梯分數小于 2000
  • 白銀:天梯分數介于 2000~3000 分之間
  • 黃金:天梯分數大于 3000

? 而實現玩家匹配的思想非常簡單,為不同的檔次設計各自的匹配隊列,當一個隊列中的玩家數量大于等于 2 的時候,則意味著同一檔次中,有兩個及以上的人要進行實戰匹配,則出隊隊列中的前兩個用戶,相當于隊首兩個個玩家匹配成功,這時候為其創建房間,并將兩個用戶信息加入房間中。

? 和之前幾個模塊設計理念一樣,我們要有一個局部模塊和全局模塊,對于對戰玩家匹配模塊來說,其實就是將用戶放到匹配隊列中,等匹配到足夠人數的時候,就將匹配人數放到游戲房間里面,所以這里分為兩個類:

  • 匹配隊列類:就是一個阻塞隊列,但其實不一定是用隊列實現,具體看下面的講解
  • 匹配管理類:管理類就是管理多個匹配隊列,并且管理用戶要進入哪個匹配隊列等操作

Ⅰ. 匹配隊列實現

? 匹配隊列雖然看起來用隊列來實現挺不錯的,有先進先出的思想,但是有一個問題,就是玩家可能在匹配的時候,有想取消匹配的操作,那么我們就得提供退出匹配的接口,也就是將用戶從匹配隊列中刪除,但是如果用隊列來實現的話,并不是很好辦,所以我們采用 雙向鏈表來實現匹配隊列

? 除此之外,因為當隊列沒有兩名成員的時候,是不能進行加入房間操作的,所以我們用 條件變量 + 互斥鎖 來實現阻塞隊列的功能!所以我們大概要實現的接口如下所示:

  • 數據入隊
  • 數據出隊
  • 移除指定的數據:注意這和數據出隊不太一樣,數據出隊表示要進入游戲房間了,而 移除指定數據表示取消匹配
  • 獲取隊列元素個數
  • 阻塞
  • 判斷隊列為空

? 因為這些接口實現比較簡單,這里直接給出實現,將它們放到 頭文件 matcher.hpp 中:

template <class T>
class match_queue
{
private:std::list<T> _block_queue;     // 阻塞隊列 -- 用雙向鏈表實現std::mutex _mtx;               // 互斥鎖 -- 實現線程安全std::condition_variable _cond; // 條件變量 -- 主要用于阻塞消費者,當隊列元素個數小于2的時候阻塞
public:// 獲取隊列元素個數int size(){std::unique_lock<std::mutex> lock(_mtx);return _block_queue.size();}// 判斷隊列是否為空bool isEmpty(){std::unique_lock<std::mutex> lock(_mtx);return _block_queue.empty();}// 阻塞線程void wait(){std::unique_lock<std::mutex> lock(_mtx);_cond.wait(lock);}// 數據入隊,并喚醒線程void push(T& data){std::unique_lock<std::mutex> lock(_mtx);_block_queue.push_back(data);_cond.notify_all();}// 數據出隊 -- 相當于匹配成功要進入房間,data是輸出型參數bool pop(T& data){std::unique_lock<std::mutex> lock(_mtx);if(_block_queue.empty())return false;data = _block_queue.front();_block_queue.pop_front();return true;}// 移除指定的數據 -- 相當于取消匹配void remove(T& data){std::unique_lock<std::mutex> lock(_mtx);_block_queue.remove(data);}
};

Ⅱ. 匹配隊列管理類實現

? 因為我們將段位分為了三個段位,為了便于管理,我們 用三個匹配隊列來管理三個段位,并且每個匹配隊列中還要有各自的線程入口函數,因為如果都放在一個線程中跑的話,此時阻塞力度有點大!下面是管理類的一些成員變量設計:

  • 三個匹配隊列對象
  • 三個線程:每個線程分別對應每個匹配隊列對象
  • 房間管理類句柄:因為我們要將對應的用戶放到對應的房間,那么就得有創建房間等操作
  • 數據庫用戶信息表句柄:因為我們需要獲取用戶的天梯分數來判斷要將用戶放到哪個匹配隊列中,所以要有該句柄
  • 在線用戶管理句柄:我們需要在用戶匹配成功之后,判斷一下用戶還是否在線,如果不在線了那么就得做一下特殊處理

而管理類的接口無非就是下面三個:

  • 添加用戶到匹配隊列接口
  • 從匹配隊列中移除用戶接口
  • 線程入口函數
    • 因為涉及到線程,那么就得有線程入口函數,并且要有三個線程入口函數,由于它們的實現其實是類似的,代碼中可以用一個接口來封裝一下這三個線程入口函數,減少代碼量,具體參考代碼
    • 而這三個線程的主要工作無非就是判斷各自的匹配隊列是否人數大于 2,是的話就要出隊兩個用戶,為他們創建房間,并且向它們發送對戰匹配成功的信息,也就是響應,當然匹配失敗也要響應!

? 下面先來看一下匹配對戰的 json 數據格式:

? 開始對戰匹配:

{"optype": "match_start"
}
/* 后臺正確處理后回復 */
{"optype": "match_start", //表?成功加?匹配隊列"result": true
}
/* 后臺處理出錯回復 */
{"optype": "match_start""result": false,"reason": "具體原因...."
}
/* 匹配成功了給客戶端的回復 */
{"optype": "match_success", //表?成匹配成功"result": true
}

? 停止匹配:

{"optype": "match_stop"
}
/* 后臺正確處理后回復 */
{"optype": "match_stop""result": true
}
/* 后臺處理出錯回復 */
{"optype": "match_stop""result": false,"reason": "具體原因...."
}

? 所以大體的實現框架如下所示:

class match_manager
{
private:match_queue<uint64_t> _bronze; // 青銅段位隊列match_queue<uint64_t> _silver; // 白銀段位隊列match_queue<uint64_t> _gold;   // 黃金段位隊列std::thread _bronze_thread; // 青銅段位線程std::thread _silver_thread; // 白銀段位線程std::thread _gold_thread;   // 黃金段位線程online_manager* _onlineptr; // 在線用戶管理句柄user_table* _utableptr;     // 數據庫用戶表信息管理句柄room_manager* _roomptr;     // 房間管理句柄
public:match_manager(online_manager* onlineptr, user_table* utableptr, room_manager* roomptr): _onlineptr(onlineptr), _utableptr(utableptr), _roomptr(roomptr),_bronze_thread(std::thread(&match_manager::_bronze_entry, this)),_silver_thread(std::thread(&match_manager::_silver_entry, this)),_gold_thread(std::thread(&match_manager::_gold_entry, this)){ DLOG("匹配隊列管理類初始化完畢...."); }// 添加用戶到匹配隊列bool addUser(uint64_t uid){}// 將用戶從匹配隊列中刪除,也就是取消匹配bool delUser(uint64_t uid){}private:// 三個段位各自的線程入口函數void _bronze_entry() { return thread_handle(_bronze); }void _silver_entry() { return thread_handle(_silver); }void _gold_entry() { return thread_handle(_gold); }// 總的處理線程入口函數細節的函數// 在這個函數中實現將用戶到匹配隊列、房間的分配、響應等操作void thread_handle(match_queue<uint64_t>& queue){}
};

? 💥其中要注意在構造函數中,對于 c++11 方式的線程初始化的時候,指定入口函數前要先指明在哪個類中,并且要取地址,然后將其參數也附上,對于 成員函數來說,默認要傳一個 this 指針,不要忘記!

? 也可以看到,因為三個入口函數其實操作都是一致的,為了避免寫大量重復的代碼,我們提煉出一個 thread_handle() 函數出來,我們只需要接收一個對應的匹配隊列的參數來進行操作即可!

? 下面我們先來實現添加和刪除用戶的操作,相對比較簡單:

// 根據玩家的天梯分數,來判定玩家檔次,添加到不同的匹配隊列
bool addUser(uint64_t uid)
{// 1. 根據用戶ID,獲取玩家信息Json::Value root;bool ret = _utableptr->select_by_id(uid, root);if(ret == false){DLOG("獲取玩家:%d 信息失敗!!", uid);return false;}uint64_t score = root["score"].asUInt64();// 2. 添加到指定的隊列中if(score < 2000)_bronze.push(uid);else if(score >= 2000 && score < 3000)_silver.push(uid);else_gold.push(uid);return true;
}// 將用戶從匹配隊列中刪除,也就是取消匹配
bool delUser(uint64_t uid)
{// 1. 根據用戶ID,獲取玩家信息Json::Value root;bool ret = _utableptr->select_by_id(uid, root);if(ret == false){DLOG("獲取玩家:%d 信息失敗!!", uid);return false;}uint64_t score = root["score"].asUInt64();// 2. 將用戶從匹配隊列中刪除if(score < 2000)_bronze.remove(uid);else if(score >= 2000 && score < 3000)_silver.remove(uid);else_gold.remove(uid);return true;
}

? 可以看到兩個函數的操作基本是一致的,其實可以封裝一個子接口出來,但是這里就不封裝了,它們的區別主要就是添加和刪除,其它沒有什么問題。

? 接下來就是最重要的線程入口函數的實現:

// 總的處理線程入口函數細節的函數
// 在這個函數中實現將用戶到匹配隊列、房間的分配、響應等操作
void thread_handle(match_queue<uint64_t>& queue)
{// 放到死循環中while(1){// 1. 判斷隊列人數是否大于2,如果小于2則阻塞等待if(queue.size() < 2)queue.wait();// 2. 走到這代表人數夠了,出隊兩個玩家//    這里有細節,如果第一個人出隊的時候失敗了,那么只需要continue重新開始出隊//    但是如果是第二個人出隊時候失敗了,就要先將已經出隊的第一個人的信息重新入隊再continueuint64_t uid1;bool ret = queue.pop(uid1);if(ret == false)continue;uint64_t uid2;ret = queue.pop(uid2);if(ret == false){queue.push(uid1); // 要先將出隊的那個人重新放到隊列中再continuecontinue;}// 3. 校驗兩個玩家是否在線,如果有人掉線,也就是通信句柄是無效的//    則要把另一個人重新添加入隊列,因為當前玩家掉線,而另一個人則需要重新匹配wsserver_t::connection_ptr conn1 =  _onlineptr->get_conn_from_hall(uid1);if(conn1.get() == nullptr){this->addUser(uid2);continue;}wsserver_t::connection_ptr conn2 =  _onlineptr->get_conn_from_hall(uid2);if(conn1.get() == nullptr){this->addUser(uid1);continue;}// 4. 為兩個玩家創建房間,并將玩家加入房間中 -- 創建失敗的話要重新將用戶放到匹配隊列room_ptr rp = _roomptr->addRoom(uid1, uid2);if(rp.get() == nullptr){this->addUser(uid1);this->addUser(uid2);continue;}// 5. 對兩個玩家進行json數據響應Json::Value response;response["optype"] = "match_success";response["result"] = true;std::string body;json_util::serialize(response, body);conn1->send(body);conn2->send(body);}
}

完整代碼

#ifndef __MY_MATCH_H__
#define __MY_MATCH_H__
#include "util.hpp"
#include "online.hpp"
#include "room.hpp"
#include "db.hpp"
#include <mutex>
#include <thread>
#include <condition_variable>
#include <list>template <class T>
class match_queue
{
private:std::list<T> _block_queue;     // 阻塞隊列 -- 用雙向鏈表實現std::mutex _mtx;               // 互斥鎖 -- 實現線程安全std::condition_variable _cond; // 條件變量 -- 主要用于阻塞消費者,當隊列元素個數小于2的時候阻塞
public:// 獲取隊列元素個數int size(){std::unique_lock<std::mutex> lock(_mtx);return _block_queue.size();}// 判斷隊列是否為空bool isEmpty(){std::unique_lock<std::mutex> lock(_mtx);return _block_queue.empty();}// 阻塞線程void wait(){std::unique_lock<std::mutex> lock(_mtx);_cond.wait(lock);}// 數據入隊,并喚醒線程void push(T& data){std::unique_lock<std::mutex> lock(_mtx);_block_queue.push_back(data);_cond.notify_all();}// 數據出隊 -- 相當于匹配成功要進入房間,data是輸出型參數bool pop(T& data){std::unique_lock<std::mutex> lock(_mtx);if(_block_queue.empty())return false;data = _block_queue.front();_block_queue.pop_front();return true;}// 移除指定的數據 -- 相當于取消匹配void remove(T& data){std::unique_lock<std::mutex> lock(_mtx);_block_queue.remove(data);}
};class match_manager
{
private:match_queue<uint64_t> _bronze; // 青銅段位隊列match_queue<uint64_t> _silver; // 白銀段位隊列match_queue<uint64_t> _gold;   // 黃金段位隊列std::thread _bronze_thread; // 青銅段位線程std::thread _silver_thread; // 白銀段位線程std::thread _gold_thread;   // 黃金段位線程online_manager* _onlineptr; // 在線用戶管理句柄user_table* _utableptr;     // 數據庫用戶表信息管理句柄room_manager* _roomptr;     // 房間管理句柄
public:match_manager(online_manager* onlineptr, user_table* utableptr, room_manager* roomptr): _onlineptr(onlineptr), _utableptr(utableptr), _roomptr(roomptr),_bronze_thread(std::thread(&match_manager::_bronze_entry, this)),_silver_thread(std::thread(&match_manager::_silver_entry, this)),_gold_thread(std::thread(&match_manager::_gold_entry, this)){ DLOG("匹配隊列管理類初始化完畢...."); }// 根據玩家的天梯分數,來判定玩家檔次,添加到不同的匹配隊列bool addUser(uint64_t uid){// 1. 根據用戶ID,獲取玩家信息Json::Value root;bool ret = _utableptr->select_by_id(uid, root);if(ret == false){DLOG("獲取玩家:%d 信息失敗!!", uid);return false;}uint64_t score = root["score"].asUInt64();// 2. 添加到指定的隊列中if(score < 2000)_bronze.push(uid);else if(score >= 2000 && score < 3000)_silver.push(uid);else_gold.push(uid);return true;}// 將用戶從匹配隊列中刪除,也就是取消匹配bool delUser(uint64_t uid){// 1. 根據用戶ID,獲取玩家信息Json::Value root;bool ret = _utableptr->select_by_id(uid, root);if(ret == false){DLOG("獲取玩家:%d 信息失敗!!", uid);return false;}uint64_t score = root["score"].asUInt64();// 2. 將用戶從匹配隊列中刪除if(score < 2000)_bronze.remove(uid);else if(score >= 2000 && score < 3000)_silver.remove(uid);else_gold.remove(uid);return true;}private:// 三個段位各自的線程入口函數void _bronze_entry() { return thread_handle(_bronze); }void _silver_entry() { return thread_handle(_silver); }void _gold_entry() { return thread_handle(_gold); }// 總的處理線程入口函數細節的函數// 在這個函數中實現將用戶到匹配隊列、房間的分配、響應等操作void thread_handle(match_queue<uint64_t>& queue){// 放到死循環中while(1){// 1. 判斷隊列人數是否大于2,如果小于2則阻塞等待if(queue.size() < 2)queue.wait();// 2. 走到這代表人數夠了,出隊兩個玩家//    這里有細節,如果第一個人出隊的時候失敗了,那么只需要continue重新開始出隊//    但是如果是第二個人出隊時候失敗了,就要先將已經出隊的第一個人的信息重新入隊再continueuint64_t uid1;bool ret = queue.pop(uid1);if(ret == false)continue;uint64_t uid2;ret = queue.pop(uid2);if(ret == false){queue.push(uid1); // 要先將出隊的那個人重新放到隊列中再continuecontinue;}// 3. 校驗兩個玩家是否在線,如果有人掉線,也就是通信句柄是無效的//    則要把另一個人重新添加入隊列,因為當前玩家掉線,而另一個人則需要重新匹配wsserver_t::connection_ptr conn1 =  _onlineptr->get_conn_from_hall(uid1);if(conn1.get() == nullptr){this->addUser(uid2);continue;}wsserver_t::connection_ptr conn2 =  _onlineptr->get_conn_from_hall(uid2);if(conn1.get() == nullptr){this->addUser(uid1);continue;}// 4. 為兩個玩家創建房間,并將玩家加入房間中 -- 創建失敗的話要重新將用戶放到匹配隊列room_ptr rp = _roomptr->addRoom(uid1, uid2);if(rp.get() == nullptr){this->addUser(uid1);this->addUser(uid2);continue;}// 5. 對兩個玩家進行json數據響應Json::Value response;response["optype"] = "match_success";response["result"] = true;std::string body;json_util::serialize(response, body);conn1->send(body);conn2->send(body);}}
};#endif

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/916111.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/916111.shtml
英文地址,請注明出處:http://en.pswp.cn/news/916111.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

k8s之ingress定義https訪問方式

接上文&#xff1a;https://blog.csdn.net/soso678/article/details/149607069?spm1001.2014.3001.5502定義后端應用與service [rootmaster ingress]# cat my-nginx.yml apiVersion: apps/v1 kind: Deployment metadata:name: my-nginx spec:selector:matchLabels:run: my-n…

《C++ vector 完全指南:vector的模擬實現》

《C vector 完全指南&#xff1a;vector的模擬實現》 文章目錄《C vector 完全指南&#xff1a;vector的模擬實現》一、定義vector的成員變量二、用vector實現動態二維數組三、vector的接口實現1.vector的默認成員函數&#xff08;1&#xff09;構造函數實現&#xff08;2&…

騰訊云代碼助手使用指南

騰訊云代碼助手使用指南什么是騰訊云代碼助手功能區展示功能介紹功能演示一、創建新項目1.先用Chat 把口語化的需求轉換成AI更容易接受的結構化提示詞2.再用Craft 模式進行代碼生成3.成果展示二、老項目探索1.使用Codebase 幫理解項目代碼三、代碼補全1.只需輸入標準的函數名&a…

【vue3+vue-pdf-embed】實現PDF+圖片預覽

【vue3vue-pdf-embed】實現PDF圖片預覽項目背景項目代碼分析代碼項目背景 技術棧&#xff1a;vue3Tselementplus 需要實現PDF和圖片預覽 圖片預覽很好解決了&#xff0c;可以用elementplus 自帶的組件el-image 可實現 PDF預覽可以用搜了一圈&#xff0c;有兩個方案&#xff0c…

Leetcode力扣解題記錄--第21題(合并鏈表)

題目鏈接&#xff1a;21. 合并兩個有序鏈表 - 力扣&#xff08;LeetCode&#xff09; 題目描述 將兩個升序鏈表合并為一個新的 升序 鏈表并返回。新鏈表是通過拼接給定的兩個鏈表的所有節點組成的。 示例 1&#xff1a; 輸入&#xff1a;l1 [1,2,4], l2 [1,3,4] 輸出&…

基于單片機的樓宇門禁系統的設計與實現

2、系統總體設計 2.1硬件的總體設計 為了使門禁系統智能化&#xff0c;需要一個主控芯片對整個門禁系統進行管理控制。接著還需要對應的模塊完成包括數字密碼驗證和IC卡識別驗證的功能。當出現非法闖入、驗證失敗等情況時還需要對操作人員進行警告。最后需要一個人機交互界面方…

全天候自動化數字型智能驅鳥裝置,電網防鳥高科技

鳥類在輸電線路鐵塔、電線桿上筑巢、棲息和排泄是個大問題&#xff0c;很容易引發線路故障導致停電。為了保障電網安全穩定運行&#xff0c;會用到各種智能驅鳥裝置來驅趕鳥類&#xff0c;避免涉鳥故障發生。例如全天候自動化數字型智能驅鳥裝置&#xff0c;其厲害的地方在于它…

技術、生態與商業:從PC到移動的平臺之爭

在科技發展的漫長歷史中&#xff0c;我們常常驚嘆于那些改變世界的偉大技術。然而&#xff0c;深入探究會發現&#xff0c;單純的技術領先并不能保證最終的勝利。從 PC 操作系統到移動終端&#xff0c;乃至服務器軟件&#xff0c;那些最終笑傲江湖的巨頭們都遵循著一個共同的法…

android JXL 導出Excel(.xls/xlsx)

前面使用過 POI 導出xlsx但是它體量比較大&#xff0c;功能較豐富&#xff0c;在一些對包size比較敏感并且導出需求相對簡單的項目中就不太適合。 poi鏈接&#xff1a;Android 導入導出excel xls、xlsx_android excel導入導出-CSDN博客 jxl 包體積小&#xff0c;使用簡單、AP…

mysql 的主從機制是怎么實現的?

MySQL 作為當前最流行的開源關系型數據庫之一&#xff0c;為了滿足數據的高可用、負載均衡和容災備份等需求&#xff0c;廣泛應用主從復制&#xff08;Replication&#xff09;機制。其核心思想是&#xff1a;在一臺主庫&#xff08;Master&#xff09;上發生的所有數據變更都會…

【PHP 函數從入門到精通】

&#x1f9e0; PHP 函數從入門到精通 PHP 函數是編程中最基礎、也是最強大的工具之一。它不僅可以簡化代碼、提高復用性&#xff0c;還能通過各種高級用法&#xff0c;讓你寫出更靈活、更現代的代碼。 下面我們從函數的基礎講起&#xff0c;逐步深入&#xff0c;帶你掌握函數的…

CGA老年綜合評估漢密爾頓抑郁量表與認知評估聯用

一、CGA老年綜合評估漢密爾頓抑郁量表與認知評估聯用的基礎CGA老年綜合評估 &#xff08;一&#xff09;二者評估內容的互補性 CGA老年綜合評估漢密爾頓抑郁量表主要聚焦于老年人的抑郁情緒及相關癥狀&#xff0c;而認知評估則著重考察老年人的記憶力、注意力、思維能力等認知…

教培機構如何開發自己的證件照拍照采集小程序

職業教培機構對學員的證件照采集是進行學生培訓管理、考試報名、證書發放的前置工作&#xff0c;傳統拍照和收集證件照的方式往往面臨效率低、質量參差不齊等問題。開發一款專屬的證件照拍照采集小程序&#xff0c;不僅能提升機構形象&#xff0c;還能大幅優化工作流程。借助“…

GC8872刷式直流電機驅動器詳解:3.6A驅動能力與PWM控制

概述GC8872是一款具有故障報告功能的刷式直流電機驅動芯片&#xff0c;專為打印機、電器、工業設備等機電一體化應用設計。這款芯片采用ESOP8封裝&#xff0c;集成了H橋驅動電路和多種保護功能&#xff0c;支持高達3.6A的峰值電流輸出。關鍵特性寬電壓工作范圍&#xff1a;6.5V…

從0開始學習R語言--Day54--雙重固定模型

對于具有空間差異的數據&#xff0c;如果不知道數據的特征關系或意義&#xff0c;直接用杜賓模型來處理是一個比較通用的思路&#xff0c;只是后續還需要很多檢驗去證明結果的可解釋性和統計性。但如果我們已經知道特征的意義&#xff0c;比如企業經濟發展的數據中有著員工的科…

三生篩法在計算數論中的極限是什么?

AI輔助創作&#xff1a;三生篩法在計算數論中的極限主要體現在?規模邊界?、?算法適應性?及?理論兼容性?三個維度&#xff0c;其核心瓶頸與突破路徑如下&#xff1a;一、規模邊界&#xff1a;計算效率的斷崖式衰減??低維高效區的上限?在 10^15 以內數域&#xff0c;三生…

iOS WebView 加載失敗與緩存刷新問題排查實戰指南

在移動 App 中嵌入網頁后&#xff0c;不少團隊都會遇到一個詭異的問題&#xff1a;用戶看到的是“舊內容”&#xff0c;或“資源加載失敗”&#xff0c;但在瀏覽器調試中一切正常。特別是在 iOS WebView 中&#xff0c;這類緩存和加載問題常常隱匿、難以復現。 這篇文章將通過一…

GoLand 項目從 0 到 1:第二天 —— 數據庫自動化

第二天核心任務&#xff1a;自動化與多數據庫支持第二天的開發聚焦于數據庫自動化流程構建與MongoDB 業務鏈路擴展&#xff0c;通過工具化手段解決數據庫操作的重復性問題&#xff0c;同時完善多數據庫支持能力。經過一天的開發&#xff0c;項目已實現數據庫初始化、遷移、種子…

qt框架,使用webEngine如何調試前端

解決 Qt 5.14.2 中啟用開發者工具的問題問題在于 Qt 5.14.2 中 QWebEngineSettings::DeveloperExtrasEnabled 屬性已被棄用或更改。正確啟用開發者工具的完整方法&#xff08;Qt 5.14.2&#xff09;1. 修改 main.cpp#include <QWebEngineView> #include <QWebEngineSe…

【Atlassian生態】Jira Cloud單站點現可支持10萬用戶:架構升級與龍智云遷移服務

作為Atlassian全球白金合作伙伴&#xff0c;龍智團隊非常激動地宣布&#xff1a;Jira迎來歷史性突破——Jira Cloud單個站點最高可支持10萬用戶&#xff01;覆蓋Enterprise、Premium和Standard版本。現在&#xff0c;更多的團隊可以將Jira作為核心協作中樞&#xff0c;以加速目…