This commit is contained in:
ChuXun
2025-10-26 16:37:22 +08:00
parent fba64f7100
commit e287aadd3c
5 changed files with 408 additions and 0 deletions

166
关键模块与接口.md Normal file
View File

@@ -0,0 +1,166 @@
# **1\. 数据库 Schema (SQLite)**
\-- 用户表 (Users)
CREATE TABLE IF NOT EXISTS Users (
UserID INTEGER PRIMARY KEY AUTOINCREMENT,
Username TEXT NOT NULL UNIQUE,
PasswordHash TEXT NOT NULL,
CreatedAt DATETIME DEFAULT CURRENT\_TIMESTAMP
);
\-- 角色表 (Characters)
CREATE TABLE IF NOT EXISTS Characters (
CharacterID INTEGER PRIMARY KEY AUTOINCREMENT,
UserID INTEGER NOT NULL,
ClassName TEXT NOT NULL, \-- e.g., "Warrior", "Mage"
Level INTEGER NOT NULL DEFAULT 1,
HP INTEGER NOT NULL DEFAULT 100,
MP INTEGER NOT NULL DEFAULT 50,
Attack INTEGER NOT NULL DEFAULT 10,
Speed INTEGER NOT NULL DEFAULT 5,
FOREIGN KEY(UserID) REFERENCES Users(UserID)
);
# **2\. 网络通信协议 (Protocol.h)**
使用 | 分隔的文本协议。所有消息以换行符 \\n 结束。
## **2.1. 客户端 \-\> 服务器 (C2S)**
* **注册**: C2S\_REG|username|password\\n
* **登录**: C2S\_LOGIN|username|password\\n
* **大厅聊天**: C2S\_CHAT|message\_content\\n
* **获取列表**: C2S\_LIST\_PLAYERS\\n
* **邀请对战**: C2S\_INVITE|target\_username\\n
* **回应邀请**: C2S\_INVITE\_RSP|inviter\_username|accept\_or\_reject\\n (e.g., "accept" / "reject")
* **战斗行动**: C2S\_BATTLE\_ACTION|skill\_name|target\_name\\n
## **2.2. 服务器 \-\> 客户端 (S2C)**
* **通用响应**: S2C\_RESPONSE|OK|message\\n 或 S2C\_RESPONSE|ERROR|error\_message\\n
* **登录成功**: S2C\_LOGIN\_OK|username|ClassName|Level|HP|MP\\n
* **大厅消息**: S2C\_MSG|sender\_username|message\_content\\n
* **玩家列表**: S2C\_LIST\_PLAYERS|user1,user2,user3\\n
* **收到邀请**: S2C\_INVITE|inviter\_username\\n
* **邀请结果**: S2C\_INVITE\_RESULT|target\_username|result\\n (e.g., "accepted" / "rejected")
* **战斗开始**: S2C\_BATTLE\_START|opponent\_name|opponent\_class\\n
* **战斗回合**: S2C\_BATTLE\_TURN|YOUR\_TURN\\n 或 S2C\_BATTLE\_TURN|OPPONENT\_TURN\\n
* **战斗战报**: S2C\_BATTLE\_LOG|log\_message\\n
* **战斗结束**: S2C\_BATTLE\_END|WIN\\n 或 S2C\_BATTLE\_END|LOSE\\n
# **3\. 关键类接口定义 (C++ Header 风格)**
## **3.1. Core: HistoryLog.h (模板 \+ 链表)**
\#pragma once
\#include \<vector\>
// 必须手写实现链表节点
template \<typename T\>
struct Node {
T data;
Node\<T\>\* next;
Node(T d) : data(d), next(nullptr) {}
};
// 必须使用手写的 Node\<T\> 作为底层结构
template \<typename T\>
class HistoryLog {
private:
Node\<T\>\* head;
int size;
public:
HistoryLog();
\~HistoryLog(); // 必须正确释放链表所有节点的内存
void add(T logEntry); // 在链表头部插入
std::vector\<T\> getAllEntries(); // 遍历链表,返回一个 vector
int getSize();
};
## **3.2. Core: ICharacter.h & ISkill.h (多态)**
\#pragma once
\#include \<string\>
\#include \<vector\>
class ICharacter; // 前向声明
// 技能基类
class ISkill {
public:
virtual \~ISkill() {}
virtual std::string getName() \= 0;
virtual void execute(ICharacter& caster, ICharacter& target) \= 0; // 纯虚函数
};
// 角色基类
class ICharacter {
protected:
std::string m\_name;
int m\_hp;
int m\_maxHp;
int m\_speed;
std::vector\<ISkill\*\> m\_skills; // STL
public:
virtual \~ICharacter(); // 必须是虚析构函数,并清理 m\_skills
virtual std::string getName() { return m\_name; }
virtual int getHp() { return m\_hp; }
virtual bool isAlive() { return m\_hp \> 0; }
virtual int getSpeed() { return m\_speed; }
virtual ISkill\* getSkill(const std::string& name);
virtual void takeDamage(int damage) \= 0; // 纯虚函数
virtual void heal(int amount) \= 0;
};
// 派生类 (示例)
class Warrior : public ICharacter {
public:
Warrior(std::string name);
virtual void takeDamage(int damage) override; // 重写,实现格挡逻辑
virtual void heal(int amount) override;
};
class Mage : public ICharacter {
public:
Mage(std::string name);
virtual void takeDamage(int damage) override; // 重写,实现法力护盾逻辑
virtual void heal(int amount) override;
};
## **3.3. Server: ClientHandler.h (网络 \+ STL \+ 多线程)**
\#pragma once
\#include \<thread\>
\#include \<string\>
\#include "SocketWrapper.h" // 假设已封装
class GameServer; // 前向声明
class ClientHandler {
public:
// 构造函数,传入 socket 描述符和服务器实例指针
ClientHandler(int socketFd, GameServer\* server);
\~ClientHandler();
void startHandlerThread(); // 启动 run() 循环
void stopHandler(); // 停止循环并关闭 socket
void sendMessage(const std::string& message); // 发送消息给这个客户端
private:
void run(); // 线程主循环 (recv, parse, handle)
void parseCommand(const std::string& command);
int m\_socketFd;
GameServer\* m\_server; // 用于回调服务器 (e.g., 广播)
std::thread m\_thread; // STL
bool m\_isRunning;
// 玩家状态
std::string m\_username;
enum class State { Unauthenticated, Lobby, InBattle };
State m\_state;
};

111
系统架构设计.md Normal file
View File

@@ -0,0 +1,111 @@
# **1\. 系统架构图 (C/S 架构)**
\+-------------------------------------------------------------------------+
| \[ 客户端 A (Console) \] |
| \+--------------------+ \+--------------------+ |
| | GameClient | | 主线程 (Input) | |
| | (负责网络通信) | | (发送聊天/指令) | |
| \+--------------------+ \+--------------------+ |
| | SocketWrapper | | 接收线程 (Recv) | |
| | (封装Socket API) | | (打印聊天/战报) | |
| \+--------------------+ \+--------------------+ |
\+-------------^----------------------------------+ |
| (TCP/IP) |
| |
\+-------------v----------------------------------+ |
| \[ 客户端 B (Console) \] |
| (结构同 A) |
\+-------------^----------------------------------+ |
| (TCP/IP) |
| |
\+-------------v-----------------------------------------------------------+
| \[ 服务器 (GameServer Console) \] |
| |
| \+---------------------+ \+-----------------------------------------+ |
| | GameServer (主线程) | | \[ 线程池 / 并发管理 \] | |
| | (Socket, Bind, Listen)| | | |
| | (Loop: Accept) | | \+-----------------+ \+-----------------+ | |
| | | | | ClientHandler A | | ClientHandler B | | |
| | (管理所有 Client) | | | (Thread A) | | (Thread B) | | |
| | (std::map\<fd, Ptr\>)| | \+-----------------+ \+-----------------+ | |
| \+----------^----------+ \+---------^---------------+---------^-----+ |
| | | (Lobby/Battle) | |
| | | | |
| \+----------v----------+ \+---------v---------------+ | |
| | GameLobby (单例) | | BattleRoomManager | | |
| | (std::map\<name, Ptr\>)| | (管理所有战斗房间) | | |
| | (负责聊天/邀请) | | (std::map\<id, Ptr\>) | | |
| \+---------------------+ \+-----------------------+ | |
| | |
| \+-------------------------------v-------+ |
| | \[ 数据库接口 (Database) \] | |
| | (封装 SQLite API) | |
| | (使用 Mutex 保证线程安全) | |
| \+-------------------^-------------------+ |
| | |
\+---------------------------------------------------+---------------------+
|
\+---v---+
| DB |
| (users.db)|
\+-------+
# **2\. 关键组件职责**
1. **GameClient (客户端)**
* 负责连接服务器。
* **必须**使用多线程:一个线程(主线程)负责捕获用户输入 (cin) 并 send另一个线程子线程负责阻塞 recv 并打印服务器消息 (cout)。
* 负责提供控制台的菜单和界面。
2. **GameServer (服务器)**
* 负责监听端口 (listen) 和接受新连接 (accept)。
* 每接受一个新连接,**必须**创建一个 ClientHandler 实例,并在一个**新线程 (std::thread)** 中运行它。
* **必须**使用 std::map (STL) 来存储所有 ClientHandler 的指针或引用,以便管理。
* **必须**使用 std::mutex (STL) 来保护这个 map防止多线程冲突。
3. **ClientHandler (服务器)**
* 在独立的线程中运行,代表一个已连接的客户端。
* 负责该客户端的 recv 循环,解析客户端发来的命令 (使用 Protocol.h)。
* 根据命令,调用 GameLobby (聊天/邀请) 或 BattleRoomManager (战斗) 的功能。
* 负责处理该客户端的异常掉线,并通知 GameServer 将自己从 map 中移除。
4. **GameLobby (服务器)**
* 管理所有处于“大厅”状态的玩家。
* 实现 broadcastMessage() (广播聊天),需要加锁 (std::mutex) 遍历 GameServer 的客户端 map 并发送。
* 实现 handleInvite() (处理邀请逻辑)。
5. **BattleRoom (服务器)**
* 负责处理一场 1v1 战斗。
* 持有**多态**的 ICharacter\* 指针。
* 持有**模板+链表**实现的 HistoryLog\<string\> 实例,用于记录战报。
* 实现 runTurn() 战斗循环逻辑,调用 ISkill 的 execute 方法。
6. **Database (服务器)**
* 封装 SQLite3 的 C API。
* 提供上层易用的接口bool registerUser(user, pass)等。
* **必须**保证是线程安全的(例如,对所有数据库写操作使用一个全局 std::mutex
# **3\. 关键数据流**
## **3.1. 数据流玩家A 发送大厅聊天**
1. **\[Client A\]**:主线程 cin 捕获输入 "Hello"。
2. **\[Client A\]**GameClient 将其打包为 "CHAT|Hello",通过 SocketWrapper::send() 发出。
3. **\[Server\]**ClientHandler A (线程A) 的 recv() 循环收到数据。
4. **\[Server\]**ClientHandler A 解析出是 CHAT 命令。
5. **\[Server\]**ClientHandler A 调用 GameLobby::broadcastMessage("A: Hello")。
6. **\[Server\]**GameLobby **加锁**访问 GameServer 的客户端 map。
7. **\[Server\]**GameLobby 遍历 map对所有**非战斗中**的 ClientHandler (如 B, C, D) 调用 send("MSG|A: Hello")。
8. **\[Client B,C,D\]**:接收线程 (子线程) 的 recv() 收到消息cout 打印 "A: Hello" 到控制台。
## **3.2. 数据流玩家A 攻击 玩家B (战斗中)**
1. **\[Server\]**BattleRoom (线程C) 判定轮到 A 行动。
2. **\[Server\]**BattleRoom 向 ClientHandler A (线程A) 发送 "TURN|YOUR\_TURN"。
3. **\[Client A\]**:接收线程收到 "TURN|YOUR\_TURN"cout 打印 "轮到你行动"。
4. **\[Client A\]**:主线程 cin 捕获输入 "attack B"。
5. **\[Client A\]**GameClient 打包为 "BATTLE|USE\_SKILL|Fireball|B"send() 发出。
6. **\[Server\]**ClientHandler A (线程A) 收到数据,解析后调用 BattleRoom::handleAction("A", "Fireball", "B")。
7. \[Server\]BattleRoom (线程C) 加锁(防止多线程同时修改战斗状态),执行战斗逻辑:
a. ICharacter\* charA \= ...;
b. ISkill\* skill \= charA-\>getSkill("Fireball");
c. skill-\>execute(charA, charB); (多态调用)
d. string log \= "A 对 B 使用了\[火球\]...";
e. m\_historyLog.add(log); (模板+链表调用)
8. **\[Server\]**BattleRoom (线程C) 将战报 log 广播给 ClientHandler A 和 ClientHandler B。
9. **\[Client A, B\]**:接收线程收到 logcout 打印战报。

58
详细需求规格.md Normal file
View File

@@ -0,0 +1,58 @@
# **1\. 涉众 (Actors)**
* **玩家 (Player)**:唯一的用户类型。
# **2\. 功能需求 (Use Cases)**
## **2.1. UC-01: 用户账户管理**
* **As a** 玩家, **I want to** 在客户端启动时,可以选择“注册”或“登录”。
* **Acceptance Criteria:**
1. **注册**:需要提供唯一的“用户名”和“密码”。服务器检查用户名是否已存在于数据库。
2. **登录**:需要提供“用户名”和“密码”。服务器在数据库中验证。
3. **数据持久化**:注册信息必须使用 SQLite 数据库存储。
4. **安全**:密码应在数据库中加盐哈希存储(*开发注如果时间紧张MVP 阶段可降级为明文存储*)。
## **2.2. UC-02: 游戏大厅 (Lobby)**
* **As a** 玩家, **I want to** 登录成功后进入游戏大厅,以便和其他玩家互动。
* **Acceptance Criteria:**
1. **公共聊天**:玩家可以发送消息,该消息应被广播给所有**当前在大厅中**的其他玩家。
2. **接收聊天**:玩家可以实时看到大厅中的所有公共聊天消息。
3. **玩家列表**:玩家可以输入特定命令(如 /list从服务器获取当前所有在线玩家的列表。
## **2.3. UC-03: 战斗邀请与匹配**
* **As a** 玩家, **I want to** 邀请大厅里的其他玩家进行 1v1 对战。
* **Acceptance Criteria:**
1. **发起邀请**玩家A可以向玩家B必须在线且不在战斗中发送对战邀请。
2. **接收邀请**玩家B会收到“玩家A向你发起决斗是否接受(y/n)”的提示。
3. **响应**玩家B响应后结果会通知给玩家A。
4. **状态变更**:一旦双方同意,服务器应将两名玩家的状态标记为“战斗中”,他们将不再接收大厅聊天。
## **2.4. UC-04: 回合制战斗 (1v1)**
* **As a** 玩家, **I want to** 在战斗房间中,和对手轮流行动,直到一方 HP 降为 0。
* **Acceptance Criteria:**
1. **战斗逻辑在服务器**所有战斗计算伤害、闪避、Buff**必须**在服务器上进行。
2. **角色职业**:至少实现 2 个职业(如 Warrior, Mage它们具有不同的属性和技能。
3. **技能系统**:每个职业至少有 2 个技能(如 Fireball, Heal
4. **回合制**:服务器根据角色“速度”属性决定行动顺序。轮到某玩家时,服务器通知其客户端“请行动”。
5. **行动**:玩家客户端发送行动指令(如 "USE:Fireball:Target\_B")。
6. **战报**:服务器执行行动后,将结果(如 "A对B使用了\[火球\]造成50伤害")广播给**战斗中的两名**玩家。
7. **胜负**:一方 HP \<= 0 时战斗结束。服务器宣布胜利者并将双方T出战斗房间状态改回“在线”。
# **3\. 非功能需求**
1. **技术栈 (强制)**
* **多态**:必须用于实现 ICharacter (角色基类) 和 ISkill (技能基类)。
* **STL**:必须深度使用 std::map (管理客户端), std::vector (管理战斗序列), std::thread (并发), std::mutex (线程同步)。
* **模板 \+ 链表**:必须**手写一个泛型链表类** HistoryLog\<T\> (模板),用于存储战斗日志(链表)。
* **网络编程**:必须使用**原生 Socket API** (Winsock/POSIX Sockets) 并自行封装。
* **数据库**:必须使用 **SQLite3** 库进行数据持久化。
2. **性能**
* 服务器必须能稳定处理至少 10 个并发的客户端连接。
* 网络通信延迟在局域网内应无明显卡顿。
3. **健壮性**
* 服务器必须处理客户端的异常断开(如 Ctrl+C并正确清理其资源从 map 中移除,释放战斗房间等)。
* 客户端必须处理连接服务器失败、或中途与服务器断开连接的异常。

29
项目愿景与范围.md Normal file
View File

@@ -0,0 +1,29 @@
# **1\. 项目名称**
C++ 在线回合制对战游戏 (Project: OnlineRpg)
# **2\. 项目背景与目标**
本项目旨在为《C++程序设计》课程开发一个结课作业。目标是创建一个C/S架构的、多玩家在线的、控制台界面的回合制对战游戏。
项目**首要目标**是展示对 C++ 核心特性和库的深度理解和综合运用,特别是**多态、模板、STL、链表、网络编程**和**数据库**技术。
项目的**次要目标**是实现一个功能完整、可玩性高的小型游戏原型。
# **3\. 核心功能范围 (MVP)**
为确保在 8 周的开发周期内完成,我们的最小可行产品 (MVP) 范围如下:
1. **用户系统 (C/S \+ DB)**:玩家可以通过客户端注册和登录服务器,数据必须持久化。
2. **游戏大厅 (C/S \+ Chat)**:玩家登录后进入大厅,可以看到大厅的公共聊天信息,并可以发送聊天。
3. **玩家列表与邀请 (C/S)**:玩家在大厅可以查看当前所有在线玩家列表,并向指定玩家发起“对战邀请”。
4. **1v1 战斗 (C/S \+ Polymorphism)**:玩家接受邀请后,双方进入一个独立的战斗房间。战斗在服务器上进行,客户端只负责收发指令和战报。
5. **战斗回放 (Template \+ Linked List)**:战斗结束后,服务器能生成一份该场战斗的完整日志(战报回放)。
# **4\. 关键约束**
1. **开发语言**:必须使用 C++。
2. **运行环境**:必须是**控制台 (Terminal)** 应用,不涉及图形化界面 (GUI)。
3. **技术强制要求**:必须在项目中明确使用(并能在报告中阐述)所有 6 项关键技术。
4. **平台**应能跨平台编译Windows Winsock / Linux POSIX Sockets
5. **依赖**:除 SQLite3 的 C 语言库外,**禁止**使用任何大型第三方网络库或游戏引擎(如 Boost.Asio, libevent, Unreal, Unity 等)。

44
验收标准.md Normal file
View File

@@ -0,0 +1,44 @@
# **项目验收标准 (Checklist)**
以下清单用于验证项目是否满足《C++程序设计》结课作业的核心要求。
## **1\. 功能验收**
* \[ \] **UC-01 (账户)**:可以成功注册新用户,数据库 Users 表中出现新纪录。
* \[ \] **UC-01 (账户)**:可以使用已注册的账户成功登录。
* \[ \] **UC-02 (大厅)**客户端A 在大厅发送消息客户端B、C (均在大厅) 能收到。
* \[ \] **UC-02 (大厅)**:客户端可以正确获取在线玩家列表。
* \[ \] **UC-03 (邀请)**客户端A 邀请 BB 收到提示并能接受或拒绝。
* \[ \] **UC-04 (战斗)**A 和 B 进入战斗后,不再收到大厅聊天。
* \[ \] **UC-04 (战斗)**战斗逻辑在服务器端正确执行e.g., A 攻击 BB 的 HP 减少)。
* \[ \] **UC-04 (战斗)**:客户端能正确收发战报,并显示回合制提示。
* \[ \] **UC-04 (战斗)**:一方 HP 归零后,服务器能正确宣布胜负,并将双方踢回大厅。
* \[ \] **健壮性**客户端A 强行退出 (Ctrl+C),服务器能检测到,并在玩家列表中移除 A。
## **2\. 关键技术验收 (强制)**
* \[ \] **多态**
* \[ \] 提供了 ICharacter 和 ISkill 抽象基类。
* \[ \] 提供了至少 2 种职业和 2 种技能的派生类。
* \[ \] BattleRoom 仅通过 ICharacter\* 和 ISkill\* (基类指针) 来执行战斗逻辑。
* \[ \] **STL**
* \[ \] GameServer 使用 std::map 管理 ClientHandler。
* \[ \] GameServer (或 ClientHandler) 使用 std::thread 为每个客户端创建了线程。
* \[ \] GameLobby 和 BattleRoom 在访问共享数据(如 map, 战斗状态)时,使用了 std::mutex。
* \[ \] BattleRoom 使用 std::vector \+ \<algorithm\> (std::sort) 来管理行动顺序。
* \[ \] **模板 \+ 链表**
* \[ \] 提供了 HistoryLog.h 文件。
* \[ \] 该文件**手写**实现了泛型的 Node\<T\> 结构体。
* \[ \] 该文件**手写**实现了泛型类 HistoryLog\<T\>。
* \[ \] HistoryLog\<T\> **必须**使用 Node\<T\>\* (链表) 作为其底层数据结构,**而不是** std::list 或 std::vector。
* \[ \] BattleRoom 成功实例化并使用了 HistoryLog\<string\>。
* \[ \] **网络编程**
* \[ \] 提供了 SocketWrapper (或等效) 类来封装原生 Socket API。
* \[ \] 服务器实现了 bind, listen, accept 的 C/S 模型。
* \[ \] 客户端实现了 connect 模型。
* \[ \] 客户端使用了**多线程**来分别处理 send 和 recv。
* \[ \] **数据库**
* \[ \] 项目集成了 sqlite3.c / sqlite3.h。
* \[ \] 提供了 Database 封装类。
* \[ \] Users 和 Characters 表已按设计创建。
* \[ \] 注册和登录功能已通过数据库 SELECT/INSERT 验证。