From e287aadd3c05c37090254ab5d12c32320b502f49 Mon Sep 17 00:00:00 2001 From: ChuXun <70203584+ChuXunYu@users.noreply.github.com> Date: Sun, 26 Oct 2025 16:37:22 +0800 Subject: [PATCH] 1 --- 关键模块与接口.md | 166 ++++++++++++++++++++++++++++++++++++++++++++++ 系统架构设计.md | 111 +++++++++++++++++++++++++++++++ 详细需求规格.md | 58 ++++++++++++++++ 项目愿景与范围.md | 29 ++++++++ 验收标准.md | 44 ++++++++++++ 5 files changed, 408 insertions(+) create mode 100644 关键模块与接口.md create mode 100644 系统架构设计.md create mode 100644 详细需求规格.md create mode 100644 项目愿景与范围.md create mode 100644 验收标准.md diff --git a/关键模块与接口.md b/关键模块与接口.md new file mode 100644 index 0000000..1b7a8db --- /dev/null +++ b/关键模块与接口.md @@ -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 \ + +// 必须手写实现链表节点 +template \ +struct Node { + T data; + Node\\* next; + Node(T d) : data(d), next(nullptr) {} +}; + +// 必须使用手写的 Node\ 作为底层结构 +template \ +class HistoryLog { +private: + Node\\* head; + int size; + +public: + HistoryLog(); + \~HistoryLog(); // 必须正确释放链表所有节点的内存 + + void add(T logEntry); // 在链表头部插入 + std::vector\ getAllEntries(); // 遍历链表,返回一个 vector + int getSize(); +}; + +## **3.2. Core: ICharacter.h & ISkill.h (多态)** + +\#pragma once +\#include \ +\#include \ + +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\ 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 \ +\#include \ +\#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; +}; diff --git a/系统架构设计.md b/系统架构设计.md new file mode 100644 index 0000000..03e5bfc --- /dev/null +++ b/系统架构设计.md @@ -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\)| | \+-----------------+ \+-----------------+ | | +| \+----------^----------+ \+---------^---------------+---------^-----+ | +| | | (Lobby/Battle) | | +| | | | | +| \+----------v----------+ \+---------v---------------+ | | +| | GameLobby (单例) | | BattleRoomManager | | | +| | (std::map\)| | (管理所有战斗房间) | | | +| | (负责聊天/邀请) | | (std::map\) | | | +| \+---------------------+ \+-----------------------+ | | +| | | +| \+-------------------------------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\ 实例,用于记录战报。 + * 实现 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\]**:接收线程收到 log,cout 打印战报。 \ No newline at end of file diff --git a/详细需求规格.md b/详细需求规格.md new file mode 100644 index 0000000..0e6cf62 --- /dev/null +++ b/详细需求规格.md @@ -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\ (模板),用于存储战斗日志(链表)。 + * **网络编程**:必须使用**原生 Socket API** (Winsock/POSIX Sockets) 并自行封装。 + * **数据库**:必须使用 **SQLite3** 库进行数据持久化。 +2. **性能** + * 服务器必须能稳定处理至少 10 个并发的客户端连接。 + * 网络通信延迟在局域网内应无明显卡顿。 +3. **健壮性** + * 服务器必须处理客户端的异常断开(如 Ctrl+C),并正确清理其资源(从 map 中移除,释放战斗房间等)。 + * 客户端必须处理连接服务器失败、或中途与服务器断开连接的异常。 \ No newline at end of file diff --git a/项目愿景与范围.md b/项目愿景与范围.md new file mode 100644 index 0000000..34a6502 --- /dev/null +++ b/项目愿景与范围.md @@ -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 等)。 \ No newline at end of file diff --git a/验收标准.md b/验收标准.md new file mode 100644 index 0000000..78eec83 --- /dev/null +++ b/验收标准.md @@ -0,0 +1,44 @@ +# **项目验收标准 (Checklist)** + +以下清单用于验证项目是否满足《C++程序设计》结课作业的核心要求。 + +## **1\. 功能验收** + +* \[ \] **UC-01 (账户)**:可以成功注册新用户,数据库 Users 表中出现新纪录。 +* \[ \] **UC-01 (账户)**:可以使用已注册的账户成功登录。 +* \[ \] **UC-02 (大厅)**:客户端A 在大厅发送消息,客户端B、C (均在大厅) 能收到。 +* \[ \] **UC-02 (大厅)**:客户端可以正确获取在线玩家列表。 +* \[ \] **UC-03 (邀请)**:客户端A 邀请 B,B 收到提示并能接受或拒绝。 +* \[ \] **UC-04 (战斗)**:A 和 B 进入战斗后,不再收到大厅聊天。 +* \[ \] **UC-04 (战斗)**:战斗逻辑在服务器端正确执行(e.g., A 攻击 B,B 的 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 \+ \ (std::sort) 来管理行动顺序。 +* \[ \] **模板 \+ 链表**: + * \[ \] 提供了 HistoryLog.h 文件。 + * \[ \] 该文件**手写**实现了泛型的 Node\ 结构体。 + * \[ \] 该文件**手写**实现了泛型类 HistoryLog\。 + * \[ \] HistoryLog\ **必须**使用 Node\\* (链表) 作为其底层数据结构,**而不是** std::list 或 std::vector。 + * \[ \] BattleRoom 成功实例化并使用了 HistoryLog\。 +* \[ \] **网络编程**: + * \[ \] 提供了 SocketWrapper (或等效) 类来封装原生 Socket API。 + * \[ \] 服务器实现了 bind, listen, accept 的 C/S 模型。 + * \[ \] 客户端实现了 connect 模型。 + * \[ \] 客户端使用了**多线程**来分别处理 send 和 recv。 +* \[ \] **数据库**: + * \[ \] 项目集成了 sqlite3.c / sqlite3.h。 + * \[ \] 提供了 Database 封装类。 + * \[ \] Users 和 Characters 表已按设计创建。 + * \[ \] 注册和登录功能已通过数据库 SELECT/INSERT 验证。 \ No newline at end of file