This commit is contained in:
2025-10-26 20:44:58 +08:00
parent e287aadd3c
commit e73c08abf7
45 changed files with 280774 additions and 0 deletions

130
include/client/GameClient.h Normal file
View File

@@ -0,0 +1,130 @@
#pragma once
#include "SocketWrapper.h"
#include <string>
#include <thread>
#include <atomic>
#include <memory>
/**
* @brief 游戏客户端
*
* 技术点:
* 1. STL - std::thread (多线程)
* 2. 网络编程 - Socket Client
*
* 设计:
* - 主线程: 处理用户输入和发送
* - 接收线程: 处理服务器消息接收和显示
*/
class GameClient {
private:
std::string m_serverAddress;
int m_serverPort;
SocketWrapper m_socket;
std::thread m_recvThread;
std::atomic<bool> m_isRunning;
std::atomic<bool> m_isConnected;
std::atomic<bool> m_isAuthenticated;
std::string m_username;
std::atomic<bool> m_inBattle;
std::atomic<bool> m_waitingForTurn;
public:
/**
* @brief 构造函数
*/
GameClient(const std::string& serverAddress, int serverPort);
/**
* @brief 析构函数
*/
~GameClient();
/**
* @brief 连接到服务器
*/
bool connect();
/**
* @brief 断开连接
*/
void disconnect();
/**
* @brief 运行客户端主循环
*/
void run();
/**
* @brief 发送消息
*/
bool sendMessage(const std::string& message);
private:
/**
* @brief 接收线程主循环
*/
void recvLoop();
/**
* @brief 处理服务器消息
*/
void handleServerMessage(const std::string& message);
/**
* @brief 显示主菜单
*/
void showMainMenu();
/**
* @brief 显示大厅菜单
*/
void showLobbyMenu();
/**
* @brief 显示战斗菜单
*/
void showBattleMenu();
/**
* @brief 处理注册
*/
void handleRegister();
/**
* @brief 处理登录
*/
void handleLogin();
/**
* @brief 处理大厅输入
*/
void handleLobbyInput(const std::string& input);
/**
* @brief 处理战斗输入
*/
void handleBattleInput(const std::string& input);
/**
* @brief 显示提示符
*/
void showPrompt();
/**
* @brief 清屏
*/
void clearScreen();
/**
* @brief 打印分隔线
*/
void printSeparator();
/**
* @brief 打印标题
*/
void printTitle(const std::string& title);
};

171
include/common/Database.h Normal file
View File

@@ -0,0 +1,171 @@
#pragma once
#include <string>
#include <vector>
#include <mutex>
#include <memory>
// 前向声明 SQLite 结构
struct sqlite3;
/**
* @brief 用户数据结构
*/
struct UserData {
int userId;
std::string username;
std::string passwordHash;
std::string createdAt;
};
/**
* @brief 角色数据结构
*/
struct CharacterData {
int characterId;
int userId;
std::string className;
int level;
int hp;
int mp;
int attack;
int speed;
};
/**
* @brief 数据库管理类
*
* 技术点: 数据库 (Database) - SQLite3
* 线程安全: 使用 std::mutex 保护所有数据库操作
*/
class Database {
private:
sqlite3* m_db;
std::string m_dbPath;
mutable std::mutex m_mutex; // 保证线程安全 (STL)
bool m_isOpen;
public:
/**
* @brief 构造函数
* @param dbPath 数据库文件路径
*/
explicit Database(const std::string& dbPath);
/**
* @brief 析构函数
*/
~Database();
// 禁止拷贝
Database(const Database&) = delete;
Database& operator=(const Database&) = delete;
/**
* @brief 打开数据库连接
*/
bool open();
/**
* @brief 关闭数据库连接
*/
void close();
/**
* @brief 初始化数据库表结构
*/
bool initializeTables();
/**
* @brief 检查数据库是否已打开
*/
bool isOpen() const { return m_isOpen; }
// ===== 用户相关操作 =====
/**
* @brief 注册新用户
* @param username 用户名
* @param password 密码 (将被哈希)
* @return bool 是否成功
*/
bool registerUser(const std::string& username, const std::string& password);
/**
* @brief 验证用户登录
* @param username 用户名
* @param password 密码
* @return bool 是否验证成功
*/
bool verifyUser(const std::string& username, const std::string& password);
/**
* @brief 检查用户名是否存在
*/
bool userExists(const std::string& username);
/**
* @brief 获取用户ID
*/
int getUserId(const std::string& username);
/**
* @brief 获取用户数据
*/
bool getUserData(const std::string& username, UserData& userData);
// ===== 角色相关操作 =====
/**
* @brief 创建角色
* @param userId 用户ID
* @param className 职业名称
* @return bool 是否成功
*/
bool createCharacter(int userId, const std::string& className);
/**
* @brief 获取用户的角色数据
*/
bool getCharacterData(int userId, CharacterData& charData);
/**
* @brief 更新角色数据
*/
bool updateCharacter(const CharacterData& charData);
/**
* @brief 检查用户是否有角色
*/
bool hasCharacter(int userId);
// ===== 工具函数 =====
/**
* @brief 执行 SQL 语句
* @param sql SQL 语句
* @return bool 是否成功
*/
bool executeSql(const std::string& sql);
/**
* @brief 获取最后插入的行 ID
*/
int getLastInsertId();
/**
* @brief 获取最后的错误信息
*/
std::string getLastError();
private:
/**
* @brief 密码哈希函数 (简单实现)
* 注意: 生产环境应使用更安全的哈希算法 (如 bcrypt, argon2)
*/
std::string hashPassword(const std::string& password);
/**
* @brief 验证密码
*/
bool verifyPassword(const std::string& password, const std::string& hash);
};

98
include/common/Protocol.h Normal file
View File

@@ -0,0 +1,98 @@
#pragma once
#include <string>
#include <vector>
#include <map>
/**
* @brief 网络通信协议定义
*
* 协议格式: COMMAND|param1|param2|...\n
* 所有消息以换行符 \n 结尾
*/
namespace Protocol {
// ===== 协议命令定义 =====
// === 客户端 -> 服务器 (C2S) ===
const std::string C2S_REGISTER = "C2S_REG"; // 注册: C2S_REG|username|password
const std::string C2S_LOGIN = "C2S_LOGIN"; // 登录: C2S_LOGIN|username|password
const std::string C2S_CHAT = "C2S_CHAT"; // 聊天: C2S_CHAT|message
const std::string C2S_LIST_PLAYERS = "C2S_LIST"; // 玩家列表: C2S_LIST
const std::string C2S_INVITE = "C2S_INVITE"; // 邀请: C2S_INVITE|target_username
const std::string C2S_INVITE_RSP = "C2S_INVITE_RSP"; // 邀请响应: C2S_INVITE_RSP|inviter|accept/reject
const std::string C2S_BATTLE_ACTION = "C2S_ACTION"; // 战斗行动: C2S_ACTION|skill_index|target
const std::string C2S_LOGOUT = "C2S_LOGOUT"; // 登出: C2S_LOGOUT
// === 服务器 -> 客户端 (S2C) ===
const std::string S2C_RESPONSE = "S2C_RESPONSE"; // 通用响应: S2C_RESPONSE|OK/ERROR|message
const std::string S2C_LOGIN_OK = "S2C_LOGIN_OK"; // 登录成功: S2C_LOGIN_OK|username|class|level|hp|mp
const std::string S2C_MSG = "S2C_MSG"; // 大厅消息: S2C_MSG|sender|message
const std::string S2C_LIST_PLAYERS = "S2C_LIST"; // 玩家列表: S2C_LIST|user1,user2,user3
const std::string S2C_INVITE = "S2C_INVITE"; // 收到邀请: S2C_INVITE|inviter_username
const std::string S2C_INVITE_RESULT = "S2C_INV_RES"; // 邀请结果: S2C_INV_RES|target|accepted/rejected
const std::string S2C_BATTLE_START = "S2C_BAT_START"; // 战斗开始: S2C_BAT_START|opponent|class
const std::string S2C_BATTLE_TURN = "S2C_BAT_TURN"; // 回合通知: S2C_BAT_TURN|YOUR_TURN/OPP_TURN
const std::string S2C_BATTLE_LOG = "S2C_BAT_LOG"; // 战斗日志: S2C_BAT_LOG|log_message
const std::string S2C_BATTLE_END = "S2C_BAT_END"; // 战斗结束: S2C_BAT_END|WIN/LOSE
const std::string S2C_SYSTEM = "S2C_SYSTEM"; // 系统消息: S2C_SYSTEM|message
// ===== 协议工具函数 =====
/**
* @brief 构建协议消息
* @param command 命令
* @param params 参数列表
* @return std::string 完整的协议消息 (包含换行符)
*/
std::string buildMessage(const std::string& command,
const std::vector<std::string>& params = {});
/**
* @brief 解析协议消息
* @param message 原始消息
* @param command 输出参数: 命令
* @param params 输出参数: 参数列表
* @return bool 解析是否成功
*/
bool parseMessage(const std::string& message,
std::string& command,
std::vector<std::string>& params);
/**
* @brief 构建响应消息
*/
std::string buildResponse(bool success, const std::string& message);
/**
* @brief 构建登录成功消息
*/
std::string buildLoginOk(const std::string& username,
const std::string& className,
int level, int hp, int mp);
/**
* @brief 构建聊天消息
*/
std::string buildChatMessage(const std::string& sender,
const std::string& message);
/**
* @brief 构建玩家列表消息
*/
std::string buildPlayerList(const std::vector<std::string>& players);
/**
* @brief 构建邀请消息
*/
std::string buildInvite(const std::string& inviter);
/**
* @brief 构建战斗日志消息
*/
std::string buildBattleLog(const std::string& logMessage);
/**
* @brief 构建系统消息
*/
std::string buildSystemMessage(const std::string& message);
}

View File

@@ -0,0 +1,139 @@
#pragma once
#include <string>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
typedef SOCKET SocketFd;
#define INVALID_SOCKET_FD INVALID_SOCKET
#define SOCKET_ERROR_CODE WSAGetLastError()
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
typedef int SocketFd;
#define INVALID_SOCKET_FD -1
#define SOCKET_ERROR_CODE errno
#define closesocket close
#endif
/**
* @brief Socket 封装类
*
* 技术点: 网络编程 (Network Programming) - 原生 Socket API 封装
* 跨平台支持: Windows (Winsock) 和 Linux (POSIX Sockets)
*/
class SocketWrapper {
private:
SocketFd m_socket;
bool m_isValid;
public:
/**
* @brief 构造函数
*/
SocketWrapper();
/**
* @brief 构造函数 (使用现有socket)
*/
explicit SocketWrapper(SocketFd socket);
/**
* @brief 析构函数
*/
~SocketWrapper();
// 禁止拷贝
SocketWrapper(const SocketWrapper&) = delete;
SocketWrapper& operator=(const SocketWrapper&) = delete;
/**
* @brief 初始化 Winsock (仅 Windows)
*/
static bool initializeWinsock();
/**
* @brief 清理 Winsock (仅 Windows)
*/
static void cleanupWinsock();
/**
* @brief 创建 Socket
*/
bool create();
/**
* @brief 绑定地址和端口
*/
bool bind(const std::string& address, int port);
/**
* @brief 监听连接
*/
bool listen(int backlog = 10);
/**
* @brief 接受客户端连接
*/
SocketFd accept(std::string& clientAddress, int& clientPort);
/**
* @brief 连接到服务器
*/
bool connect(const std::string& address, int port);
/**
* @brief 发送数据
*/
int send(const char* data, int length);
/**
* @brief 发送字符串
*/
int send(const std::string& message);
/**
* @brief 接收数据
*/
int recv(char* buffer, int length);
/**
* @brief 接收一行数据 (以 \n 结尾)
*/
std::string recvLine();
/**
* @brief 设置为非阻塞模式
*/
bool setNonBlocking(bool nonBlocking);
/**
* @brief 设置 Socket 选项
*/
bool setReuseAddr(bool reuse);
/**
* @brief 关闭 Socket
*/
void close();
/**
* @brief 检查 Socket 是否有效
*/
bool isValid() const { return m_isValid; }
/**
* @brief 获取原始 Socket 描述符
*/
SocketFd getSocket() const { return m_socket; }
/**
* @brief 获取最后的错误信息
*/
static std::string getLastError();
};

102
include/core/Characters.h Normal file
View File

@@ -0,0 +1,102 @@
#pragma once
#include "ICharacter.h"
/**
* @brief 战士职业
*
* 技术点: 多态 (Polymorphism) - 继承和方法重写
* 特点: 高生命值、高防御、物理攻击
*/
class Warrior : public ICharacter {
private:
int m_rage; // 怒气值 (战士特有资源)
int m_maxRage; // 最大怒气值
public:
/**
* @brief 构造函数
* @param name 角色名称
* @param level 等级
*/
Warrior(const std::string& name, int level = 1);
/**
* @brief 析构函数
*/
virtual ~Warrior() {}
/**
* @brief 受到伤害 (重写) - 战士有格挡机制
*/
virtual void takeDamage(int damage) override;
/**
* @brief 治疗 (重写)
*/
virtual void heal(int amount) override;
/**
* @brief 获取怒气值
*/
int getRage() const { return m_rage; }
/**
* @brief 增加怒气值
*/
void addRage(int amount);
/**
* @brief 消耗怒气值
*/
bool consumeRage(int amount);
};
/**
* @brief 法师职业
*
* 技术点: 多态 (Polymorphism) - 继承和方法重写
* 特点: 高魔法值、魔法攻击、低防御
*/
class Mage : public ICharacter {
private:
int m_shield; // 法力护盾
int m_maxShield; // 最大护盾值
public:
/**
* @brief 构造函数
* @param name 角色名称
* @param level 等级
*/
Mage(const std::string& name, int level = 1);
/**
* @brief 析构函数
*/
virtual ~Mage() {}
/**
* @brief 受到伤害 (重写) - 法师有法力护盾机制
*/
virtual void takeDamage(int damage) override;
/**
* @brief 治疗 (重写)
*/
virtual void heal(int amount) override;
/**
* @brief 获取护盾值
*/
int getShield() const { return m_shield; }
/**
* @brief 添加护盾
*/
void addShield(int amount);
/**
* @brief 重置护盾
*/
void resetShield();
};

193
include/core/HistoryLog.h Normal file
View File

@@ -0,0 +1,193 @@
#pragma once
#include <vector>
#include <stdexcept>
/**
* @brief 链表节点模板类
* @tparam T 节点存储的数据类型
*
* 技术点: 模板 (Template)
*/
template <typename T>
struct Node {
T data;
Node<T>* next;
explicit Node(const T& d) : data(d), next(nullptr) {}
};
/**
* @brief 历史日志记录器 - 使用模板和手写链表实现
* @tparam T 日志条目的数据类型
*
* 技术点:
* 1. 模板 (Template) - 泛型编程
* 2. 链表 (Linked List) - 手写数据结构
*
* 用途: 用于记录战斗日志,满足课程要求的"模板+链表"技术点
*/
template <typename T>
class HistoryLog {
private:
Node<T>* head; // 链表头指针
Node<T>* tail; // 链表尾指针 (用于快速追加)
int size; // 链表大小
public:
/**
* @brief 构造函数
*/
HistoryLog() : head(nullptr), tail(nullptr), size(0) {}
/**
* @brief 析构函数 - 必须正确释放所有节点内存
*/
~HistoryLog() {
clear();
}
/**
* @brief 拷贝构造函数 (深拷贝)
*/
HistoryLog(const HistoryLog& other) : head(nullptr), tail(nullptr), size(0) {
Node<T>* current = other.head;
while (current != nullptr) {
add(current->data);
current = current->next;
}
}
/**
* @brief 拷贝赋值运算符
*/
HistoryLog& operator=(const HistoryLog& other) {
if (this != &other) {
clear();
Node<T>* current = other.head;
while (current != nullptr) {
add(current->data);
current = current->next;
}
}
return *this;
}
/**
* @brief 在链表尾部添加新的日志条目
* @param logEntry 日志条目
*/
void add(const T& logEntry) {
Node<T>* newNode = new Node<T>(logEntry);
if (head == nullptr) {
// 空链表
head = newNode;
tail = newNode;
} else {
// 追加到尾部
tail->next = newNode;
tail = newNode;
}
size++;
}
/**
* @brief 在链表头部插入日志条目
* @param logEntry 日志条目
*/
void addFront(const T& logEntry) {
Node<T>* newNode = new Node<T>(logEntry);
if (head == nullptr) {
head = newNode;
tail = newNode;
} else {
newNode->next = head;
head = newNode;
}
size++;
}
/**
* @brief 获取所有日志条目
* @return std::vector<T> 包含所有日志条目的向量
*/
std::vector<T> getAllEntries() const {
std::vector<T> entries;
entries.reserve(size);
Node<T>* current = head;
while (current != nullptr) {
entries.push_back(current->data);
current = current->next;
}
return entries;
}
/**
* @brief 获取链表大小
* @return int 日志条目数量
*/
int getSize() const {
return size;
}
/**
* @brief 检查链表是否为空
* @return bool true 如果为空
*/
bool isEmpty() const {
return size == 0;
}
/**
* @brief 清空所有日志条目
*/
void clear() {
Node<T>* current = head;
while (current != nullptr) {
Node<T>* temp = current;
current = current->next;
delete temp;
}
head = nullptr;
tail = nullptr;
size = 0;
}
/**
* @brief 获取指定索引的日志条目
* @param index 索引 (0-based)
* @return const T& 日志条目的引用
* @throw std::out_of_range 如果索引超出范围
*/
const T& at(int index) const {
if (index < 0 || index >= size) {
throw std::out_of_range("Index out of range");
}
Node<T>* current = head;
for (int i = 0; i < index; i++) {
current = current->next;
}
return current->data;
}
/**
* @brief 打印所有日志 (仅用于调试)
*/
void print() const {
Node<T>* current = head;
int index = 0;
while (current != nullptr) {
// 这里假设 T 支持 << 运算符
// std::cout << "[" << index++ << "] " << current->data << std::endl;
current = current->next;
index++;
}
}
};

144
include/core/ICharacter.h Normal file
View File

@@ -0,0 +1,144 @@
#pragma once
#include <string>
#include <vector>
#include <memory>
// 前向声明
class ICharacter;
class ISkill;
/**
* @brief 技能接口 (抽象基类)
*
* 技术点: 多态 (Polymorphism) - 纯虚函数
*/
class ISkill {
protected:
std::string m_name; // 技能名称
std::string m_description; // 技能描述
int m_manaCost; // 消耗魔法值
int m_cooldown; // 冷却时间
public:
virtual ~ISkill() {}
/**
* @brief 执行技能
* @param caster 施法者
* @param target 目标
* @return std::string 战斗日志描述
*/
virtual std::string execute(ICharacter& caster, ICharacter& target) = 0;
/**
* @brief 获取技能名称
*/
virtual std::string getName() const { return m_name; }
/**
* @brief 获取技能描述
*/
virtual std::string getDescription() const { return m_description; }
/**
* @brief 获取魔法消耗
*/
virtual int getManaCost() const { return m_manaCost; }
/**
* @brief 获取冷却时间
*/
virtual int getCooldown() const { return m_cooldown; }
};
/**
* @brief 角色接口 (抽象基类)
*
* 技术点: 多态 (Polymorphism) - 纯虚函数、虚析构函数
*/
class ICharacter {
protected:
std::string m_name; // 角色名称
std::string m_className; // 职业名称
int m_level; // 等级
int m_hp; // 当前生命值
int m_maxHp; // 最大生命值
int m_mp; // 当前魔法值
int m_maxMp; // 最大魔法值
int m_attack; // 攻击力
int m_defense; // 防御力
int m_speed; // 速度 (决定行动顺序)
std::vector<std::shared_ptr<ISkill>> m_skills; // 技能列表 (STL)
public:
/**
* @brief 虚析构函数 (必须)
*/
virtual ~ICharacter() {}
// === Getters ===
virtual std::string getName() const { return m_name; }
virtual std::string getClassName() const { return m_className; }
virtual int getLevel() const { return m_level; }
virtual int getHp() const { return m_hp; }
virtual int getMaxHp() const { return m_maxHp; }
virtual int getMp() const { return m_mp; }
virtual int getMaxMp() const { return m_maxMp; }
virtual int getAttack() const { return m_attack; }
virtual int getDefense() const { return m_defense; }
virtual int getSpeed() const { return m_speed; }
/**
* @brief 检查角色是否存活
*/
virtual bool isAlive() const { return m_hp > 0; }
/**
* @brief 获取指定名称的技能
* @param skillName 技能名称
* @return ISkill* 技能指针,如果不存在返回 nullptr
*/
virtual ISkill* getSkill(const std::string& skillName);
/**
* @brief 获取所有技能
*/
virtual const std::vector<std::shared_ptr<ISkill>>& getSkills() const {
return m_skills;
}
/**
* @brief 受到伤害 (纯虚函数 - 必须在派生类中实现)
* @param damage 伤害值
*/
virtual void takeDamage(int damage) = 0;
/**
* @brief 治疗 (纯虚函数 - 必须在派生类中实现)
* @param amount 治疗量
*/
virtual void heal(int amount) = 0;
/**
* @brief 消耗魔法值
* @param amount 消耗量
* @return bool 是否成功消耗
*/
virtual bool consumeMana(int amount);
/**
* @brief 恢复魔法值
* @param amount 恢复量
*/
virtual void restoreMana(int amount);
/**
* @brief 获取角色状态字符串
*/
virtual std::string getStatus() const;
/**
* @brief 添加技能
*/
virtual void addSkill(std::shared_ptr<ISkill> skill);
};

72
include/core/Skills.h Normal file
View File

@@ -0,0 +1,72 @@
#pragma once
#include "ICharacter.h"
#include <random>
/**
* @brief 普通攻击技能
*/
class NormalAttack : public ISkill {
public:
NormalAttack();
virtual std::string execute(ICharacter& caster, ICharacter& target) override;
};
/**
* @brief 战士技能: 重击
* 造成高额伤害,消耗怒气
*/
class HeavyStrike : public ISkill {
public:
HeavyStrike();
virtual std::string execute(ICharacter& caster, ICharacter& target) override;
};
/**
* @brief 战士技能: 防御姿态
* 大幅提升防御,回复少量生命
*/
class DefensiveStance : public ISkill {
public:
DefensiveStance();
virtual std::string execute(ICharacter& caster, ICharacter& target) override;
};
/**
* @brief 法师技能: 火球术
* 造成魔法伤害
*/
class Fireball : public ISkill {
public:
Fireball();
virtual std::string execute(ICharacter& caster, ICharacter& target) override;
};
/**
* @brief 法师技能: 冰霜新星
* 造成伤害并降低目标速度
*/
class FrostNova : public ISkill {
public:
FrostNova();
virtual std::string execute(ICharacter& caster, ICharacter& target) override;
};
/**
* @brief 法师技能: 奥术护盾
* 为自己添加法力护盾
*/
class ArcaneShield : public ISkill {
public:
ArcaneShield();
virtual std::string execute(ICharacter& caster, ICharacter& target) override;
};
/**
* @brief 治疗术
* 恢复生命值
*/
class Heal : public ISkill {
public:
Heal();
virtual std::string execute(ICharacter& caster, ICharacter& target) override;
};

134
include/server/BattleRoom.h Normal file
View File

@@ -0,0 +1,134 @@
#pragma once
#include "ICharacter.h"
#include "HistoryLog.h"
#include <memory>
#include <string>
#include <vector>
#include <mutex>
// 前向声明
class ClientHandler;
/**
* @brief 战斗房间
*
* 技术点:
* 1. 多态 - ICharacter* 和 ISkill*
* 2. 模板+链表 - HistoryLog<string>
* 3. STL - std::vector, std::mutex
*/
class BattleRoom {
private:
int m_roomId;
// 战斗双方
std::shared_ptr<ClientHandler> m_player1;
std::shared_ptr<ClientHandler> m_player2;
// 角色 (多态)
std::shared_ptr<ICharacter> m_char1;
std::shared_ptr<ICharacter> m_char2;
// 战斗日志 (模板+链表)
HistoryLog<std::string> m_historyLog;
// 战斗状态
bool m_isRunning;
int m_currentTurn; // 0 = player1, 1 = player2
int m_turnCount;
std::mutex m_mutex;
public:
/**
* @brief 构造函数
*/
BattleRoom(int roomId,
std::shared_ptr<ClientHandler> player1,
std::shared_ptr<ClientHandler> player2,
std::shared_ptr<ICharacter> char1,
std::shared_ptr<ICharacter> char2);
/**
* @brief 析构函数
*/
~BattleRoom();
/**
* @brief 启动战斗
*/
void start();
/**
* @brief 处理玩家行动
*/
bool handleAction(const std::string& username,
int skillIndex,
const std::string& targetName);
/**
* @brief 检查战斗是否结束
*/
bool isFinished() const;
/**
* @brief 获取胜利者
*/
std::string getWinner() const;
/**
* @brief 获取战斗日志
*/
std::vector<std::string> getBattleLog() const;
/**
* @brief 获取房间ID
*/
int getRoomId() const { return m_roomId; }
/**
* @brief 检查是否轮到某玩家
*/
bool isPlayerTurn(const std::string& username) const;
private:
/**
* @brief 执行一回合
*/
void executeTurn();
/**
* @brief 广播给双方玩家
*/
void broadcastToBoth(const std::string& message);
/**
* @brief 发送给玩家1
*/
void sendToPlayer1(const std::string& message);
/**
* @brief 发送给玩家2
*/
void sendToPlayer2(const std::string& message);
/**
* @brief 添加战斗日志
*/
void addLog(const std::string& log);
/**
* @brief 结束战斗
*/
void endBattle();
/**
* @brief 发送回合通知
*/
void notifyTurn();
/**
* @brief 发送战斗状态
*/
void sendBattleStatus();
};

View File

@@ -0,0 +1,71 @@
#pragma once
#include "BattleRoom.h"
#include <map>
#include <mutex>
#include <memory>
// 前向声明
class GameServer;
/**
* @brief 战斗房间管理器
*
* 技术点: STL - std::map, std::mutex
*/
class BattleRoomManager {
private:
GameServer* m_server;
std::map<int, std::shared_ptr<BattleRoom>> m_rooms; // STL
std::mutex m_mutex; // STL
int m_nextRoomId;
public:
/**
* @brief 构造函数
*/
explicit BattleRoomManager(GameServer* server);
/**
* @brief 析构函数
*/
~BattleRoomManager();
/**
* @brief 创建战斗房间
* @param player1 玩家1用户名
* @param player2 玩家2用户名
* @return int 房间ID失败返回 -1
*/
int createBattle(const std::string& player1, const std::string& player2);
/**
* @brief 获取战斗房间
*/
std::shared_ptr<BattleRoom> getBattleRoom(int roomId);
/**
* @brief 处理战斗行动
*/
bool handleBattleAction(int roomId,
const std::string& username,
int skillIndex,
const std::string& targetName);
/**
* @brief 移除战斗房间
*/
void removeBattleRoom(int roomId);
/**
* @brief 检查玩家是否在战斗中
*/
bool isPlayerInBattle(const std::string& username);
private:
/**
* @brief 创建角色实例
*/
std::shared_ptr<ICharacter> createCharacter(const std::string& className,
const std::string& name,
int level = 1);
};

View File

@@ -0,0 +1,172 @@
#pragma once
#include "SocketWrapper.h"
#include <string>
#include <thread>
#include <atomic>
#include <memory>
#include <vector>
// 前向声明
class GameServer;
/**
* @brief 客户端状态枚举
*/
enum class ClientState {
Unauthenticated, // 未认证
Lobby, // 大厅中
InBattle // 战斗中
};
/**
* @brief 客户端处理器
*
* 技术点:
* 1. STL - std::thread (多线程)
* 2. 网络编程 - Socket 通信
*/
class ClientHandler : public std::enable_shared_from_this<ClientHandler> {
private:
int m_clientId;
SocketFd m_socketFd;
GameServer* m_server;
std::thread m_thread;
std::atomic<bool> m_isRunning;
// 客户端状态
ClientState m_state;
std::string m_username;
int m_userId;
// 战斗相关
std::string m_pendingInviter; // 待处理的邀请者
int m_battleRoomId; // 所在战斗房间ID
public:
/**
* @brief 构造函数
*/
ClientHandler(int clientId, SocketFd socketFd, GameServer* server);
/**
* @brief 析构函数
*/
~ClientHandler();
// 禁止拷贝
ClientHandler(const ClientHandler&) = delete;
ClientHandler& operator=(const ClientHandler&) = delete;
/**
* @brief 启动处理线程
*/
void start();
/**
* @brief 停止处理
*/
void stop();
/**
* @brief 发送消息
*/
bool sendMessage(const std::string& message);
/**
* @brief 获取客户端ID
*/
int getClientId() const { return m_clientId; }
/**
* @brief 获取用户名
*/
std::string getUsername() const { return m_username; }
/**
* @brief 获取用户ID
*/
int getUserId() const { return m_userId; }
/**
* @brief 获取状态
*/
ClientState getState() const { return m_state; }
/**
* @brief 设置状态
*/
void setState(ClientState state) { m_state = state; }
/**
* @brief 设置战斗房间ID
*/
void setBattleRoomId(int roomId) { m_battleRoomId = roomId; }
/**
* @brief 获取战斗房间ID
*/
int getBattleRoomId() const { return m_battleRoomId; }
/**
* @brief 设置待处理邀请
*/
void setPendingInviter(const std::string& inviter) { m_pendingInviter = inviter; }
/**
* @brief 获取待处理邀请
*/
std::string getPendingInviter() const { return m_pendingInviter; }
private:
/**
* @brief 线程主循环
*/
void run();
/**
* @brief 处理客户端命令
*/
void handleCommand(const std::string& command,
const std::vector<std::string>& params);
/**
* @brief 处理注册
*/
void handleRegister(const std::vector<std::string>& params);
/**
* @brief 处理登录
*/
void handleLogin(const std::vector<std::string>& params);
/**
* @brief 处理聊天
*/
void handleChat(const std::vector<std::string>& params);
/**
* @brief 处理玩家列表请求
*/
void handleListPlayers();
/**
* @brief 处理邀请
*/
void handleInvite(const std::vector<std::string>& params);
/**
* @brief 处理邀请响应
*/
void handleInviteResponse(const std::vector<std::string>& params);
/**
* @brief 处理战斗行动
*/
void handleBattleAction(const std::vector<std::string>& params);
/**
* @brief 处理登出
*/
void handleLogout();
};

View File

@@ -0,0 +1,59 @@
#pragma once
#include <string>
#include <memory>
// 前向声明
class GameServer;
/**
* @brief 游戏大厅管理类
*
* 功能:
* 1. 管理大厅聊天
* 2. 处理玩家列表
* 3. 处理对战邀请
*/
class GameLobby {
private:
GameServer* m_server;
public:
/**
* @brief 构造函数
*/
explicit GameLobby(GameServer* server);
/**
* @brief 析构函数
*/
~GameLobby();
/**
* @brief 广播聊天消息
* @param sender 发送者用户名
* @param message 消息内容
* @param excludeClientId 排除的客户端ID (不发送给自己)
*/
void broadcastMessage(const std::string& sender,
const std::string& message,
int excludeClientId = -1);
/**
* @brief 处理对战邀请
* @param inviter 邀请者用户名
* @param target 被邀请者用户名
* @return bool 是否成功发送邀请
*/
bool handleInvite(const std::string& inviter, const std::string& target);
/**
* @brief 处理邀请响应
* @param inviter 邀请者用户名
* @param target 被邀请者用户名
* @param accept 是否接受
* @return bool 是否成功处理
*/
bool handleInviteResponse(const std::string& inviter,
const std::string& target,
bool accept);
};

111
include/server/GameServer.h Normal file
View File

@@ -0,0 +1,111 @@
#pragma once
#include "SocketWrapper.h"
#include "Database.h"
#include <string>
#include <map>
#include <mutex>
#include <memory>
#include <vector>
// 前向声明
class ClientHandler;
class GameLobby;
class BattleRoomManager;
/**
* @brief 游戏服务器主类
*
* 技术点:
* 1. STL - std::map, std::mutex, std::thread
* 2. 网络编程 - Socket Server
*/
class GameServer {
private:
int m_port;
SocketWrapper m_serverSocket;
std::shared_ptr<Database> m_database;
// 客户端管理 (STL)
std::map<int, std::shared_ptr<ClientHandler>> m_clients;
std::mutex m_clientsMutex;
int m_nextClientId;
// 游戏模块
std::shared_ptr<GameLobby> m_lobby;
std::shared_ptr<BattleRoomManager> m_battleManager;
bool m_isRunning;
public:
/**
* @brief 构造函数
* @param port 监听端口
* @param dbPath 数据库文件路径
*/
GameServer(int port, const std::string& dbPath);
/**
* @brief 析构函数
*/
~GameServer();
/**
* @brief 启动服务器
*/
bool start();
/**
* @brief 停止服务器
*/
void stop();
/**
* @brief 服务器主循环 (接受连接)
*/
void run();
/**
* @brief 添加客户端
*/
int addClient(std::shared_ptr<ClientHandler> client);
/**
* @brief 移除客户端
*/
void removeClient(int clientId);
/**
* @brief 根据用户名获取客户端
*/
std::shared_ptr<ClientHandler> getClientByUsername(const std::string& username);
/**
* @brief 获取所有在线玩家列表
*/
std::vector<std::string> getOnlinePlayers();
/**
* @brief 广播消息给所有在大厅的客户端
*/
void broadcastToLobby(const std::string& message, int excludeClientId = -1);
/**
* @brief 获取数据库
*/
std::shared_ptr<Database> getDatabase() { return m_database; }
/**
* @brief 获取大厅
*/
std::shared_ptr<GameLobby> getLobby() { return m_lobby; }
/**
* @brief 获取战斗管理器
*/
std::shared_ptr<BattleRoomManager> getBattleManager() { return m_battleManager; }
/**
* @brief 检查服务器是否运行中
*/
bool isRunning() const { return m_isRunning; }
};