本系统是基于QT框架开发的网络斗地主游戏核心模块,采用C/S架构实现多玩家在线游戏大厅功能。主要技术栈包括:
cpp
Copy
class GameHub : public QObject {
Q_OBJECT
public:
QVector<LoungeData> getLounges();
int enterLounge(int id, int loungeID);
int quickStart(int id);
// ...其他方法
signals:
void playerJoin(int id, const PublicData &publicData);
void playerReady(int id, int playerID, bool isReady);
// ...其他信号
};
功能特性:
cpp
Copy
class Lounge {
public:
bool enterLounge(const PublicData &publicData);
void readyGame(int id, bool isReady);
bool isReadyGame();
// ...其他方法
private:
QVector<PlayerData> m_players;
int m_loungeID;
};
关键功能:
https://via.placeholder.com/600x400?text=PlayerData+Class+Diagram
cpp
Copy
class PlayerData {
public:
PlayerData(const PublicData &publicData);
bool operator==(const PlayerData &playerData) const;
// ...属性访问方法
private:
int m_id;
QString m_nickname;
QPixmap m_pfp;
bool m_isReady;
};
数据特征:
cpp
Copy
int GameHub::quickStart(int id) {
// 优先查找2人房间
for(int num = 2; num >= 1; num--){
for(auto it = m_lounges.begin(); it != m_lounges.end(); it++){
if(it.value().playerNum() == num){
return enterLounge(id, it.key());
}
}
}
// 递归创建新房间
return newLounge(id);
}
算法特点:
sequence
Copy
实现要点:
cpp
Copy
void GameHub_Server::rcv_disConnected(int id) {
m_gameHub->leaveLounge(id); // 自动清理玩家数据
}
容错策略:
https://via.placeholder.com/600x300?text=GameHub+UI
组件构成:
xml
Copy
<widget class="QWidget" name="Widget_PlayerList">
<layout class="QVBoxLayout">
<item><widget class="Widget_PlayerInfo"/>item>
layout>
widget>
交互特性:
json
Copy
{
"title": "gameHub",
"name": "enterLounge",
"body": [{
"loungeID": 1024,
"isReady": true
}]
}
协议规范:
cpp
Copy
class GameHub_Socket : public
#include "gamehub_socket.h"
#include "src/common/pixtostr.h"
#include <QJsonArray>
GameHub_Socket::GameHub_Socket(Socket *socket)
{
m_socket = socket;
this->setParent(m_socket->parent());
connect(m_socket, &Socket::rcv_gameHubJSON, this, &GameHub_Socket::getGameHubJSON);
}
void GameHub_Socket::send_getLounges() const
{
// Message body
QJsonArray bodyArr;
QJsonObject bodyObj;
bodyArr.append(bodyObj);
// Message title
QJsonObject object;
object["title"] = "gameHub";
object["name"] = "getLounges";
object["body"] = bodyArr;
// Send message
m_socket->sendJSON(object);
}
void GameHub_Socket::send_getPublicData() const
{
// Message body
QJsonArray bodyArr;
QJsonObject bodyObj;
bodyArr.append(bodyObj);
// Message title
QJsonObject object;
object["title"] = "gameHub";
object["name"] = "getPublicData";
object["body"] = bodyArr;
// Send message
m_socket->sendJSON(object);
}
void GameHub_Socket::send_enterLounge(int loungeID) const
{
// Message body
QJsonArray bodyArr;
QJsonObject loungeObj;
loungeObj["loungeID"] = loungeID;
bodyArr.append(loungeObj);
// Message title
QJsonObject object;
object["title"] = "gameHub";
object["name"] = "enterLounge";
object["body"] = bodyArr;
// Send message
m_socket->sendJSON(object);
}
void GameHub_Socket::send_quickStart() const
{
// Message body
QJsonArray bodyArr;
QJsonObject bodyObj;
bodyArr.append(bodyObj);
// Message title
QJsonObject object;
object["title"] = "gameHub";
object["name"] = "quickStart";
object["body"] = bodyArr;
// Send message
m_socket->sendJSON(object);
}
void GameHub_Socket::send_newLounge() const
{
// Message body
QJsonArray bodyArr;
QJsonObject bodyObj;
bodyArr.append(bodyObj);
// Message title
QJsonObject object;
object["title"] = "gameHub";
object["name"] = "newLounge";
object["body"] = bodyArr;
// Send message
m_socket->sendJSON(object);
}
void GameHub_Socket::send_getPlayers() const
{
// Message body
QJsonArray bodyArr;
QJsonObject bodyObj;
bodyArr.append(bodyObj);
// Message title
QJsonObject object;
object["title"] = "gameHub";
object["name"] = "getPlayers";
object["body"] = bodyArr;
// Send message
m_socket->sendJSON(object);
}
void GameHub_Socket::send_readyGame(int loungeID, bool isReady) const
{
// Message body
QJsonArray bodyArr;
QJsonObject loungeObj;
loungeObj["loungeID"] = loungeID;
loungeObj["isReady"] = isReady;
bodyArr.append(loungeObj);
// Message title
QJsonObject object;
object["title"] = "gameHub";
object["name"] = "readyGame";
object["body"] = bodyArr;
// Send message
m_socket->sendJSON(object);
}
void GameHub_Socket::send_leaveLounge(int loungeID) const
{
// Message body
QJsonArray bodyArr;
QJsonObject loungeObj;
loungeObj["loungeID"] = loungeID;
bodyArr.append(loungeObj);
// Message title
QJsonObject object;
object["title"] = "gameHub";
object["name"] = "leaveLounge";
object["body"] = bodyArr;
// Send message
m_socket->sendJSON(object);
}
void GameHub_Socket::getGameHubJSON(const QJsonObject &object)
{
// Main
QString name = object["name"].toString();
if(name == "getLounges"){
this->proc_getLounges(object);
}else if(name == "getPublicData"){
this->proc_getPublicData(object);
}else if(name == "enterLoungeOK"){
this->proc_enterLoungeOK(object);
}else if(name == "enterLoungeFailed"){
this->proc_enterLoungeFailed(object);
}else if(name == "getPlayers"){
this->proc_getPlayers(object);
}else if(name == "playerJoin"){
this->proc_playerJoin(object);
}else if(name == "playerLeave"){
this->proc_playerLeave(object);
}else if(name == "playerReady"){
this->proc_playerReady(object);
}else if(name == "gameStart"){
this->proc_gameStart(object);
}
}
void GameHub_Socket::proc_getLounges(const QJsonObject &object)
{
// Get bodyArr
QJsonArray bodyArr = object["body"].toArray();
// Get data
QVector<LoungeData> lounges;
for(int i=0; i<bodyArr.size(); i++){
QJsonObject loungeObj = bodyArr.at(i).toObject();
LoungeData loungeData;
loungeData.setLoungeID(loungeObj["loungeID"].toInt());
loungeData.setPlayerNum(loungeObj["playerNum"].toInt());
lounges.push_back(loungeData);
}
// Send signal with data
emit rcv_getLounges(lounges);
}
void GameHub_Socket::proc_getPublicData(const QJsonObject &object)
{
// Get bodyArr
QJsonArray bodyArr = object["body"].toArray();
// Get data
QJsonObject userObj = bodyArr.first().toObject();
PublicData publicData;
publicData.setId(userObj["id"].toInt());
publicData.setNickname(userObj["nickname"].toString());
publicData.setPfp(PixToStr::strToPix(userObj["pfp"].toString()));
// Send signal with data
emit rcv_getPublicData(publicData);
}
void GameHub_Socket::proc_enterLoungeOK(const QJsonObject &object)
{
// Get bodyArr
QJsonArray bodyArr = object["body"].toArray();
// Get data
QJsonObject bodyObj = bodyArr.first().toObject();
int loungeID = bodyObj["loungeID"].toInt();
// Send signal with data
emit rcv_enterLoungeOK(loungeID);
}
void GameHub_Socket::proc_enterLoungeFailed(const QJsonObject &object)
{
// Get bodyArr
QJsonArray bodyArr = object["body"].toArray();
// Get data
QJsonObject bodyObj = bodyArr.first().toObject();
int status = bodyObj["status"].toInt();
// Send signal with data
emit rcv_enterLoungeFailed(status);
}
void GameHub_Socket::proc_getPlayers(const QJsonObject &object)
{
// Get bodyArr
QJsonArray bodyArr = object["body"].toArray();
// Get data
QVector<PlayerData> players;
for(int i=0; i<bodyArr.size(); i++){
QJsonObject loungeObj = bodyArr.at(i).toObject();
PlayerData playerData;
playerData.setId(loungeObj["id"].toInt());
playerData.setNickname(loungeObj["nickname"].toString());
playerData.setPfp(PixToStr::strToPix(loungeObj["pfp"].toString()));
playerData.setIsReady(loungeObj["isReady"].toBool());
players.push_back(playerData);
}
// Send signal with data
emit rcv_getPlayers(players);
}
void GameHub_Socket::proc_playerJoin(const QJsonObject &object)
{
// Get bodyArr
QJsonArray bodyArr = object["body"].toArray();
// Get data
QJsonObject playerObj = bodyArr.first().toObject();
PublicData publicData;
publicData.setId(playerObj["id"].toInt());
publicData.setNickname(playerObj["nickname"].toString());
publicData.setPfp(PixToStr::strToPix(playerObj["pfp"].toString()));
// Send signal with data
emit rcv_playerJoin(publicData);
}
void GameHub_Socket::proc_playerLeave(const QJsonObject &object)
{
// Get bodyArr
QJsonArray bodyArr = object["body"].toArray();
// Get data
QJsonObject playerObj = bodyArr.first().toObject();
int id = playerObj["id"].toInt();
// Send signal with data
emit rcv_playerLeave(id);
}
void GameHub_Socket::proc_playerReady(const QJsonObject &object)
{
// Get bodyArr
QJsonArray bodyArr = object["body"].toArray();
// Get data
QJsonObject playerObj = bodyArr.first().toObject();
int id = playerObj["id"].toInt();
bool isReady = playerObj["isReady"].toBool();
// Send signal with data
emit rcv_playerReady(id,isReady);
}
void GameHub_Socket::proc_gameStart(const QJsonObject &object)
{
// Get bodyArr
QJsonArray bodyArr = object["body"].toArray();
// Get data
QJsonObject bodyObj = bodyArr.first().toObject();
int gameID = bodyObj["gameID"].toInt();
// Send signal with data
emit rcv_gameStart(gameID);
}
// 包含 GameHub 类的头文件,用于引入该类的声明
#include "gamehub.h"
// 包含公共文件头文件,用于获取公共数据
#include "src/common/public_file.h"
// GameHub 类的构造函数,接收一个 QObject 指针作为父对象
GameHub::GameHub(QObject *parent)
: QObject{parent}
{
// 构造函数体为空,可能用于初始化一些成员变量,这里没有具体操作
}
// 获取所有房间的信息
// 返回值:包含所有房间信息的向量,每个元素是一个 LoungeData 对象
QVector<LoungeData> GameHub::getLounges()
{
// 用于存储所有房间信息的向量
QVector<LoungeData> lounges;
// 遍历 m_lounges 中的每个房间
for(auto it = m_lounges.begin(); it != m_lounges.end(); it++){
// 创建一个新的 LoungeData 对象
LoungeData loungeData;
// 设置房间的 ID
loungeData.setLoungeID(it.value().loungeID());
// 设置房间的玩家数量
loungeData.setPlayerNum(it.value().playerNum());
// 将该房间信息添加到向量中
lounges.push_back(loungeData);
}
// 返回包含所有房间信息的向量
return lounges;
}
// 根据玩家 ID 获取公共数据
// 参数 id:玩家的 ID
// 返回值:该玩家的公共数据
PublicData GameHub::getPublicData(int id)
{
// 调用 Public_File 类的静态方法获取玩家的公共数据
return Public_File::publicData(id);
}
// 玩家尝试加入指定房间
// 参数 id:玩家的 ID
// 参数 loungeID:要加入的房间 ID
// 返回值:加入结果的状态码,1 表示成功,2 表示房间满员,3 表示房间不存在
int GameHub::enterLounge(int id, int loungeID)
{
// 检查房间是否存在,如果不存在则返回 3
if(m_lounges.contains(loungeID) == false)
return 3;
// 获取指定房间的引用
Lounge &lounge = m_lounges[loungeID];
// 检查房间是否已满,如果已满则返回 2
if(lounge.playerNum() >= 3)
return 2;
// 获取玩家的公共数据
PublicData publicData = this->getPublicData(id);
// 玩家加入房间
lounge.enterLounge(publicData);
// 获取房间内所有玩家的 ID
QVector<int> IDs = lounge.playerIDs();
// 向房间内所有玩家发送有新玩家加入的信息
for(int &playerID: IDs)
emit playerJoin(playerID,publicData);
// 加入成功,返回 1
return 1;
}
// 玩家快速开始游戏,自动匹配房间
// 参数 id:玩家的 ID
// 返回值:匹配到的房间 ID 或新创建房间的 ID,-1 表示出错
int GameHub::quickStart(int id)
{
/* 查找人数为 2 的房间,若不存在,则查找人数为 1 的房间
若仍不存在,则创建房间 */
int loungeID;
bool exists = false;
// 先查找人数为 2 的房间,再查找人数为 1 的房间
for(int num = 2; num >= 1 and exists == false; num--){
for(auto it = m_lounges.begin(); it != m_lounges.end(); it++){
if(it.value().playerNum() == num){
// 找到合适的房间,记录房间 ID
loungeID = it.value().loungeID();
exists = true;
break;
}
}
}
// 若房间存在,尝试加入房间。若不存在,则创建房间
if(exists == true){
int status = this->enterLounge(id,loungeID);
if(status == 1)
// 加入成功,返回房间 ID
return loungeID;
else
// 加入失败,递归调用 quickStart 继续尝试
return this->quickStart(id);
}else
// 没有合适的房间,创建新房间
return this->newLounge(id);
// 出错,返回 -1
return -1;
}
// 创建一个新的房间
// 参数 id:创建房间的玩家 ID
// 返回值:新创建房间的 ID,-1 表示出错
int GameHub::newLounge(int id)
{
// 获取一个不存在的房间号
for(int loungeID=1; loungeID<INT_MAX; loungeID++){
if(m_lounges.contains(loungeID))
// 房间号已存在,继续查找
continue;
// 创建一个新的房间
Lounge lounge(loungeID);
// 让创建房间的玩家加入该房间
lounge.enterLounge(this->getPublicData(id));
// 将新房间添加到 m_lounges 中
m_lounges.insert(loungeID,lounge);
// 返回新房间的 ID
return loungeID;
}
// 出错,返回 -1
return -1;
}
// 获取指定房间的玩家列表
// 参数 id:玩家的 ID
// 参数 loungeID:房间的 ID,默认为 -1
// 返回值:该房间的玩家列表
QVector<PlayerData> GameHub::getPlayers(int id, int loungeID)
{
// 检查指定房间是否存在且玩家在该房间内
if(loungeID != -1 and m_lounges[loungeID].containsID(id) == true)
// 存在则返回该房间的玩家列表
return m_lounges[loungeID].players();
// 遍历所有房间,查找包含该玩家的房间
for(auto it = m_lounges.begin(); it != m_lounges.end(); it++)
if(it.value().containsID(id) == true)
// 找到后返回该房间的玩家列表
return it.value().players();
// 未找到,返回空的玩家列表
return QVector<PlayerData>();
}
// 玩家准备游戏
// 参数 id:玩家的 ID
// 参数 isReady:玩家是否准备好
// 参数 loungeID:房间的 ID,默认为 -1
void GameHub::readyGame(int id, bool isReady, int loungeID)
{
// 获取房间号
int newLoungeID = loungeID;
if(loungeID == -1 or m_lounges.contains(loungeID) == false){
// 若房间号无效或房间不存在,遍历所有房间查找该玩家所在的房间
for(auto it = m_lounges.begin(); it != m_lounges.end(); it++)
if(it.value().containsID(id) == true){
// 找到后记录房间号
newLoungeID = it.key();
}
if(newLoungeID == -1)
// 未找到该玩家所在的房间,直接返回
return;
}
// 获取该房间的引用
Lounge &lounge = m_lounges[newLoungeID];
// 设置该玩家的准备状态
lounge.readyGame(id,isReady);
// 获取房间状态,判断是否所有玩家都已准备好
bool start = lounge.isReadyGame();
if(start == true)
// 若准备就绪,则发出 signal_gameStart 信号,携带所有玩家的 ID
emit signal_gameStart(lounge.playerIDs());
else{
// 若有玩家未准备好,则向房间内所有玩家发送有玩家准备状态改变的信息
QVector<int> playerIDs = lounge.playerIDs();
for(int &playerId: playerIDs)
emit playerReady(playerId,id,isReady);
}
}
// 玩家离开房间
// 参数 id:玩家的 ID
// 参数 loungeID:房间的 ID,默认为 -1
void GameHub::leaveLounge(int id, int loungeID)
{
// 获取房间号
int newLoungeID = loungeID;
if(loungeID == -1 or m_lounges.contains(loungeID) == false){
// 若房间号无效或房间不存在,遍历所有房间查找该玩家所在的房间
for(auto it = m_lounges.begin(); it != m_lounges.end(); it++)
if(it.value().containsID(id) == true){
// 找到后记录房间号
newLoungeID = it.key();
}
if(newLoungeID == -1)
// 未找到该玩家所在的房间,直接返回
return;
}
// 获取该房间的引用
Lounge &lounge = m_lounges[newLoungeID];
// 玩家离开房间
lounge.leaveLounge(id);
// 若房间人数不为零,向房间内所有玩家发送有玩家离开的信息
if(lounge.playerNum() != 0){
QVector<int> playerIDs = lounge.playerIDs();
for(int &playerID: playerIDs)
emit playerLeave(playerID,id);
// 若房间为空,删除该房间
}else
m_lounges.remove(newLoungeID);
}