深入学习 C++20 协程(Coroutines)

深入学习 C++20 协程(Coroutines) 头文件:<coroutine> 命名空间:std 编译器要求:GCC 11+ / Clang 14+ / MSVC 19.28+(均需 -std=c++20 或以上) 注意:GCC 10 / Clang 8~13 可通过 -fcoroutines 和 <experimental/coroutine> 使用实验性支持 一、为什么需要协程? 1.1 异步编程的传统痛点 游戏服务器中充斥着异步操作——数据库查询、网络 I/O、定时器回调。传统方案各有各的痛: 方案 A:回调地狱(Callback Hell) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void HandleLogin(Connection* conn, const LoginPacket& pkt) { // 第1步:查询数据库验证账号 dbManager->QueryAsync("SELECT * FROM accounts WHERE name=?", pkt.name, [conn, pkt](const DBResult& result) { if (!result.ok) { conn->SendError("DB错误"); return; } // 第2步:查询角色列表 dbManager->QueryAsync("SELECT * FROM characters WHERE account_id=?", result.accountId, [conn](const DBResult& charResult) { if (!charResult.ok) { conn->SendError("DB错误"); return; } // 第3步:加载角色数据 dbManager->QueryAsync("SELECT * FROM inventory WHERE char_id=?", charResult.charId, [conn, charResult](const DBResult& invResult) { // 第4步:终于可以发送登录成功了... conn->SendLoginSuccess(charResult, invResult); }); }); }); } 方案 B:状态机(State Machine) ...

April 8, 2026 · 22 min · 4611 words

深入学习 C++17 PMR(Polymorphic Memory Resource)

深入学习 C++17 PMR(Polymorphic Memory Resource) 头文件:<memory_resource> 命名空间:std::pmr 编译器要求:GCC 9+ / Clang 9+ / MSVC 19.13+(均需 -std=c++17 或以上) 一、为什么需要 PMR? 1.1 传统 Allocator 模型的痛点 C++98 引入的 Allocator 是模板参数,这意味着: 1 2 3 4 std::vector<int, MyAlloc<int>> vec1; std::vector<int, std::allocator<int>> vec2; // vec1 和 vec2 是不同类型!无法互相赋值、放进同一个容器 核心问题: 痛点 说明 类型传染 Allocator 是模板参数,换一个 Allocator 就变了类型,所有接口签名都要跟着改 无法运行时切换 编译期绑定,测试时想换成 debug allocator?重新编译 难以组合 想让 vector 内部的 string 也用同一个 arena?极其繁琐 状态传播困难 有状态 allocator(如持有内存池指针)在容器拷贝/移动时语义复杂 1.2 PMR 的解法:运行时多态 PMR 用一个虚基类 std::pmr::memory_resource 取代模板参数,容器统一使用 std::pmr::polymorphic_allocator<T>: ...

April 6, 2026 · 12 min · 2482 words

DeerFlow 2.0 本地部署与排坑实战指南

🦌 DeerFlow 2.0 本地化部署与排坑实战指南 文档维护: Hical 适用环境: Windows + Docker Desktop (企业内网管控环境) 📌 项目简介 DeerFlow 是一个强大的多智能体(Multi-Agent)协作框架,专为长时间运行的复杂自主任务设计(如自动化编码、深度调研、排障分析)。底层基于 LangGraph,支持沙盒(Sandbox)隔离执行。 🛠️ 部署前置准备 由于公司终端管控策略(如 IP-Guard 等防泄密软件)可能会禁用系统 WSL (Windows Subsystem for Linux) 或拦截 C 盘挂载,建议 Docker Desktop 配置如下: 禁用 WSL:若启动 Docker 报错,修改 %APPDATA%\Docker\settings.json,将 "wslEngineEnabled" 改为 false,强制使用 Hyper-V 引擎。 准备代码: 1 2 3 git clone https://github.com/bytedance/deer-flow.git cd deer-flow ⚙️ 核心配置修改 (避坑指南) 为了防止在启动和编译过程中出现各种“水土不服”的报错,在执行启动命令前,请务必完成以下 5 步修改: 配置根目录 .env:复制 .env.example 重命名为 .env,并在末尾追加以下关键变量: 1 2 3 4 5 6 # 大模型 Token ANTHROPIC_API_KEY=XXXXXX # 必须配置 Auth 组件的 Base URL,否则前端 SSR 渲染会报 500 错误!(端口固定为 2026) BETTER_AUTH_BASE_URL=http://localhost:2026 BETTER_AUTH_SECRET=glacier_network_super_secret_key_2026 配置大模型 config.yaml:复制 config.example.yaml 重命名为 config.yaml,配置内网模型: models: name: claude-sonnet-4-6 display_name: Claude Sonnet 4.6 (Claude Code OAuth) use: langchain_anthropic:ChatAnthropic model: claude-sonnet-4-6 api_key: $ANTHROPIC_API_KEY max_tokens: 8192 记得把系统默认的模型指向刚才配置好的这个内网模型 default_model: claude-sonnet-4-6 ...

March 25, 2026 · 8 min · 1579 words

极客专属博客搭建全记录

🚀 极客专属博客搭建全记录 (Hugo + GitHub Actions) 技术栈:Hugo (Extended版) + GitHub Pages + GitHub Actions CI/CD 主题:PaperMod (极简名片风 / 深色模式) 作者:Hical 阶段一:基础设施搭建 (域名与云端仓库) 1. 配置 DNS 域名解析 在域名控制台(如腾讯云 DNSPod)添加以下记录,将域名指向 GitHub 全球 CDN 节点: A 记录:主机记录 @,记录值分别填入(官方建议填2-4个做负载均衡): 185.199.108.153 185.199.109.153 CNAME 记录:主机记录 www,记录值填入你的 GitHub 默认分配域名(如 hical61.github.io)。 2. 创建空白 GitHub 仓库 登录 GitHub,新建一个 Public 仓库,命名为 你的用户名.github.io。 注意避坑:创建时务必保持完全空白,绝对不要勾选生成 README 或 .gitignore,以免与本地推送产生冲突。 阶段二:本地引擎组装 (Hugo 初始化) 1. 安装 Hugo 引擎(windows环境下) 去 GitHub Releases 下载带 extended 和 windows-amd64 的最新版 Hugo 压缩包。 解压出 hugo.exe,放入固定目录(如 D:\hugo\bin)。 将该路径加入 Windows 系统的 环境变量 PATH 中。 在终端执行 hugo version 验证安装成功。 2. 初始化博客目录与防掉线机制 在本地新建一个空文件夹作为博客源码库,打开 Git 终端执行: ...

March 25, 2026 · 3 min · 488 words

圆桌随机算法

前言 多年项目开发,总是会遇到很多随机算法场景,特别是策划要求进行权重累加且不放回抽取模式的场景。现总结一套圆桌随机算法以供参考。 第 1 版 —— 基础实现 第 1 版为最初版本,为了解决基本的项目需求而设计,实现了权重区间查找的核心逻辑。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 -- 圆桌随机类 g_tRoundTable = {} g_tRoundTable.__index = g_tRoundTable -- 构造函数 function g_tRoundTable:new() local instance = { items = {}, -- 保存随机项,形式: { {id=1, weight=10}, {id=2, weight=20} } totalWeight = 0, -- 权重总和 lastError = "" -- 保存最近的错误信息 } setmetatable(instance, self) return instance end -- 添加随机项 function g_tRoundTable:addItem(id, weight) if weight <= 0 then self.lastError = "Invalid weight: " .. tostring(weight) return false end table.insert(self.items, {id = id, weight = weight}) self.totalWeight = self.totalWeight + weight return true end -- 清空所有随机项 function g_tRoundTable:clearItems() self.items = {} self.totalWeight = 0 end -- 获取随机数(工具函数) function g_tRoundTable:getRandom(low, high) return math.random(low, high) -- 返回 [low, high] 间的随机数 end -- 查找随机数属于哪个区间 function g_tRoundTable:getZoneIndex(randomValue) for i, item in ipairs(self.items) do if randomValue < item.weight then return i else randomValue = randomValue - item.weight end end -- 理论上不会执行到这里 self.lastError = "Random value out of range" return 1 end -- 提取随机项 function g_tRoundTable:fetchItems(count, independent) local ntotalItems = #self.items if self.totalWeight <= 0 or ntotalItems == 0 then self.lastError = "No items to fetch from" return {} -- 空数组 end local results = {} for _ = 1, count do if self.totalWeight <= 0 or ntotalItems == 0 then break -- 如果池中没有可用项,跳出 end -- 生成随机数[0, totalWeight) local randomValue = self:getRandom(0, self.totalWeight - 1) local zoneIndex = self:getZoneIndex(randomValue) -- 添加结果 table.insert(results, self.items[zoneIndex].id) -- 如果是非独立模式,需要移除已取出的项 if not independent then self.totalWeight = self.totalWeight - self.items[zoneIndex].weight table.remove(self.items, zoneIndex) end end return results end -- 获取最近的错误 function g_tRoundTable:getLastError() return self.lastError end 第 2 版 —— 性能与功能升级 后来因需求越来越复杂,要求也越来越多,比如需要频繁动态调整、需要重复抽取,且性能也急需提升,故而 2.0 版本诞生了。 ...

March 24, 2026 · 25 min · 5123 words

深入学习 io_uring(三):C++ 封装、协程集成与高性能架构

系列导航:入门篇 | 进阶篇 | 实战篇 前置知识 已阅读入门篇和进阶篇,掌握 io_uring 双环形缓冲区和 liburing API 了解 C++20 协程基础(co_await、coroutine_handle、promise_type) 建议先阅读 深入学习 Boost.Asio(三):实战篇 中的协程部分作为对照 1. RAII 封装:安全管理 io_uring 资源 1.1 为什么需要 C++ 封装 直接使用 liburing 的 C API 有三个痛点: io_uring_queue_init / io_uring_queue_exit 手动配对,容易遗漏 user_data 是 void* 或 uint64_t,类型安全全靠人肉 提交→收割的事件循环代码高度模板化,每个项目重写一遍 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 封装层次: 应用代码(协程/回调) │ ▼ ┌──────────────────────┐ │ IoUringAwaitable │ ← 协程集成层(co_await 一个 I/O 操作) └──────────────────────┘ │ ▼ ┌──────────────────────┐ │ IoUringContext │ ← 事件循环层(submit / wait / dispatch) └──────────────────────┘ │ ▼ ┌──────────────────────┐ │ IoUring (RAII) │ ← 资源管理层(init / exit) └──────────────────────┘ │ ▼ liburing C API 1.2 IoUring:RAII 包装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 // IoUring.hpp — RAII 封装 io_uring 实例 // 编译:g++ -std=c++20 -O2 xxx.cpp -luring -o xxx #pragma once #include <liburing.h> #include <stdexcept> #include <string> #include <cstring> class IoUring { public: // 构造时初始化 io_uring,指定 SQ 大小和可选标志 explicit IoUring(unsigned entries, unsigned flags = 0) { int ret = io_uring_queue_init(entries, &ring_, flags); if (ret < 0) { throw std::runtime_error( "io_uring_queue_init 失败: " + std::string(strerror(-ret))); } } // 禁止拷贝(io_uring 资源不可共享) IoUring(const IoUring&) = delete; IoUring& operator=(const IoUring&) = delete; // 允许移动 IoUring(IoUring&& other) noexcept : ring_(other.ring_) { other.moved_ = true; } // 析构时自动清理 ~IoUring() { if (!moved_) { io_uring_queue_exit(&ring_); } } // 获取 SQE(SQ 满时自动 submit 腾出空间) io_uring_sqe* getSqe() { io_uring_sqe* sqe = io_uring_get_sqe(&ring_); if (!sqe) { // SQ 满了,先提交当前积压的请求 io_uring_submit(&ring_); sqe = io_uring_get_sqe(&ring_); if (!sqe) { throw std::runtime_error("SQ 空间不足,即使 submit 后仍无法获取 SQE"); } } return sqe; } int submit() { return io_uring_submit(&ring_); } // 阻塞等待至少一个 CQE io_uring_cqe* waitCqe() { io_uring_cqe* cqe = nullptr; int ret = io_uring_wait_cqe(&ring_, &cqe); if (ret < 0) { throw std::runtime_error( "io_uring_wait_cqe 失败: " + std::string(strerror(-ret))); } return cqe; } // 非阻塞查看 CQE io_uring_cqe* peekCqe() { io_uring_cqe* cqe = nullptr; int ret = io_uring_peek_cqe(&ring_, &cqe); if (ret == -EAGAIN) return nullptr; // 无就绪 CQE if (ret < 0) { throw std::runtime_error( "io_uring_peek_cqe 失败: " + std::string(strerror(-ret))); } return cqe; } // 标记 CQE 已消费 void seenCqe(io_uring_cqe* cqe) { io_uring_cqe_seen(&ring_, cqe); } // 访问底层 io_uring(高级用法需要) io_uring* raw() { return &ring_; } private: io_uring ring_{}; bool moved_ = false; }; 设计原则:RAII 保证 io_uring_queue_exit 一定被调用,即使异常传播也不会泄漏内核资源。getSqe() 中自动 submit 是防御性编程——避免 SQ 满导致的隐性 bug。 ...

October 3, 2025 · 14 min · 2887 words

深入学习 io_uring(二):高级特性与 TCP 网络编程

系列导航:入门篇 | 进阶篇 | 实战篇 前置知识 已阅读入门篇,理解 SQ/CQ 双环形缓冲区和 liburing 基本 API 熟悉 TCP socket 编程基础(socket、bind、listen、accept) 1. SQPOLL:零系统调用提交 1.1 常规模式的瓶颈 入门篇中每次 io_uring_submit() 底层都会调用 io_uring_enter() 系统调用: 1 2 3 4 5 6 7 8 9 10 常规模式: 用户态 内核态 │ │ │ SQE 写入共享内存 │ │ │ │ io_uring_enter(to_submit=N) │ ├────────系统调用──────────────→│ ← 仍有上下文切换 │ │ 读取 SQ,执行 I/O │ 返回 │ │←─────────────────────────────┤ 对于超高频提交场景(如高频交易、高吞吐数据库),连这一次系统调用都嫌多。 ...

October 2, 2025 · 15 min · 3155 words

Docker 新手入门:从零开始容器化你的应用

Docker 新手入门:从零开始容器化你的应用 如果你的程序在你电脑上能跑,那就把你的电脑也一起发给客户吧。——Docker 之前的世界 写在前面 这篇文章适合谁? 听说过 Docker 但从未用过的开发者 被「在我电脑上明明能跑」折磨过的人 想了解容器化部署但不知道从哪开始的人 读完你将获得什么? 理解 Docker 核心概念(镜像、容器、仓库) 能独立编写 Dockerfile 并构建镜像 能用 Docker Compose 编排多容器应用 能将一个 Web 应用容器化部署 一、Docker 是什么? 1.1 一句话解释 Docker 是一个应用打包、分发、运行的平台。它把你的应用和所有依赖(库、配置、系统工具)打包成一个镜像,然后在任何安装了 Docker 的机器上以容器的形式运行。 1.2 虚拟机 vs 容器 对比项 虚拟机 (VM) Docker 容器 隔离级别 硬件级(Hypervisor) 进程级(内核共享) 启动速度 分钟级 秒级 体积 GB 级 MB 级 性能损耗 10-20% 接近原生 资源占用 高(每个 VM 一个完整 OS) 低(共享宿主内核) 1 2 3 4 5 6 7 8 9 10 11 12 13 ┌─────────────────────────────────┐ ┌─────────────────────────────────┐ │ 虚拟机架构 │ │ Docker 架构 │ ├─────────────────────────────────┤ ├─────────────────────────────────┤ │ App A │ App B │ App C │ │ App A │ App B │ App C │ │ Libs │ Libs │ Libs │ │ Libs │ Libs │ Libs │ │ OS │ OS │ OS │ ├─────────────────────────────────┤ ├─────────────────────────────────┤ │ Docker Engine │ │ Hypervisor │ ├─────────────────────────────────┤ ├─────────────────────────────────┤ │ Host OS │ │ Host OS │ ├─────────────────────────────────┤ ├─────────────────────────────────┤ │ Hardware │ │ Hardware │ └─────────────────────────────────┘ └─────────────────────────────────┘ 1.3 核心三概念 镜像(Image):只读模板,包含运行应用所需的一切。类比:安装光盘 容器(Container):镜像的运行实例。类比:用光盘装好的一台电脑 仓库(Registry):存放镜像的地方。类比:应用商店(Docker Hub) 三者关系: ...

October 1, 2025 · 10 min · 2113 words

深入学习 io_uring(一):从原理到第一个异步程序

系列导航:入门篇 | 进阶篇 | 实战篇 引言:epoll 之后,还能更快吗? 假设你用 epoll 写了一个高并发 TCP 服务器,性能已经不错——C10K 问题解决了。但当你把连接数推到 C1M(百万级) 时: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 epoll 的瓶颈: 用户态 内核态 │ │ │ epoll_wait() │ ├───────系统调用─────────────→│ ← 每次至少一次上下文切换 │ │ │ 返回就绪的 fd 列表 │ │←───────────────────────────┤ │ │ │ recv(fd_1, buf, ...) │ ├───────系统调用─────────────→│ ← 每个 fd 又一次系统调用! │ │ │ send(fd_1, resp, ...) │ ├───────系统调用─────────────→│ ← 再一次! │ │ │ recv(fd_2, buf, ...) │ ├───────系统调用─────────────→│ ← N 个连接 = 2N+ 次系统调用 问题:epoll 只解决了"哪些 fd 就绪"的问题,每次 I/O 操作仍然需要独立的系统调用。百万连接下,系统调用的开销成为主要瓶颈——上下文切换、数据拷贝、内核锁竞争。 ...

October 1, 2025 · 13 min · 2737 words

深入学习 ODB(八):实战——构建完整的游戏存档系统

系列导航:编译器与注解 | 连接与事务 | 对象关系 | 类型安全查询 | 继承与视图 | 迁移与多库 | 性能与实践 | 实战项目(本文) 引言:从零到完整 经过前七篇的学习,我们掌握了 ODB 的所有核心能力。本篇将综合运用它们,构建一个完整可运行的游戏存档系统——这不是简化的教学示例,而是接近生产级别的架构。 我们要实现以下游戏系统: 1 2 3 4 5 6 7 ┌─────────────────────────────────────────────────────────┐ │ 游戏存档系统 │ ├─────────────┬──────────────┬────────────┬───────────────┤ │ 玩家系统 │ 背包系统 │ 公会系统 │ 交易系统 │ │ Player │ Inventory │ Guild │ TradeLog │ │ Profile │ Equipment │ Members │ AuditLog │ └─────────────┴──────────────┴────────────┴───────────────┘ 1. 需求分析与数据建模 1.1 E-R 关系图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ┌──────────────┐ │ PlayerProfile│ │ (1:1) │ └──────┬───────┘ │ ┌──────────┐ ┌──────┴───────┐ ┌──────────────┐ │ Equipment│◀── 1:N ┤ Player ├ M:N ──▶│ Guild │ │ (装备栏) │ │ │ │ │ └──────────┘ └──────┬───────┘ └──────────────┘ │ ┌──────┴───────┐ │InventoryItem │ │ (1:N 背包) │ └──────────────┘ ┌──────────────┐ │ TradeLog │ ← 独立审计表 └──────────────┘ 1.2 实体清单 实体 说明 关键关系 BaseEntity 抽象基类,提供 id / createdAt / updatedAt 所有实体继承 Player 玩家核心数据 一对一 Profile,一对多背包/装备,多对多公会 PlayerProfile 玩家扩展档案 反向引用 Player InventoryItem 背包道具 多对一 Player Equipment 装备栏(穿戴中的装备) 多对一 Player Guild 公会 多对多 Player TradeLog 交易日志 独立表,通过 playerId 关联 2. ODB 对象定义 2.1 抽象基类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 // db/objects/base_entity.hxx #ifndef BASE_ENTITY_HXX #define BASE_ENTITY_HXX #include <string> #include <cstdint> #include <odb/core.hxx> // 所有实体的公共字段 #pragma db object abstract class BaseEntity { public: uint64_t id() const { return id_; } const std::string& createdAt() const { return createdAt_; } const std::string& updatedAt() const { return updatedAt_; } void setCreatedAt(const std::string& t) { createdAt_ = t; } void setUpdatedAt(const std::string& t) { updatedAt_ = t; } protected: friend class odb::access; #pragma db id auto uint64_t id_{0}; #pragma db type("DATETIME") column("created_at") not_null std::string createdAt_; #pragma db type("DATETIME") column("updated_at") not_null std::string updatedAt_; }; #endif 2.2 玩家与档案 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 // db/objects/player_profile.hxx #ifndef PLAYER_PROFILE_HXX #define PLAYER_PROFILE_HXX #include <string> #include <odb/core.hxx> #include <odb/lazy-ptr.hxx> #include "base_entity.hxx" class Player; #pragma db object table("player_profile") class PlayerProfile : public BaseEntity { public: PlayerProfile() = default; PlayerProfile(const std::string& avatar, const std::string& signature) : avatar_(avatar), signature_(signature) {} const std::string& avatar() const { return avatar_; } const std::string& signature() const { return signature_; } int totalKills() const { return totalKills_; } int totalDeaths() const { return totalDeaths_; } int totalPlayTime() const { return totalPlayTime_; } void setAvatar(const std::string& a) { avatar_ = a; } void setSignature(const std::string& s) { signature_ = s; } void addKill() { ++totalKills_; } void addDeath() { ++totalDeaths_; } void addPlayTime(int seconds) { totalPlayTime_ += seconds; } odb::lazy_weak_ptr<Player>& owner() { return owner_; } private: friend class odb::access; #pragma db inverse(profile_) odb::lazy_weak_ptr<Player> owner_; #pragma db type("VARCHAR(256)") std::string avatar_; #pragma db type("VARCHAR(128)") std::string signature_; #pragma db column("total_kills") not_null int totalKills_{0}; #pragma db column("total_deaths") not_null int totalDeaths_{0}; #pragma db column("total_play_time") not_null int totalPlayTime_{0}; // 累计在线秒数 }; #endif 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 // db/objects/player.hxx #ifndef PLAYER_HXX #define PLAYER_HXX #include <string> #include <vector> #include <map> #include <memory> #include <cstdint> #include <odb/core.hxx> #include <odb/lazy-ptr.hxx> #include <odb/section.hxx> #include "base_entity.hxx" class PlayerProfile; class InventoryItem; class Equipment; class Guild; // 玩家状态枚举 enum class PlayerStatus : int { Active = 0, // 正常 Banned = 1, // 封禁 Deleted = 2 // 已删除(软删除) }; #pragma db model version(1, 1) #pragma db object table("player") class Player : public BaseEntity { public: Player() = default; Player(const std::string& name, int level = 1) : name_(name), level_(level) {} // --- 核心字段访问 --- const std::string& name() const { return name_; } int level() const { return level_; } uint64_t exp() const { return exp_; } uint64_t gold() const { return gold_; } PlayerStatus status() const { return status_; } void setName(const std::string& n) { name_ = n; } void setLevel(int lv) { level_ = lv; } void addExp(uint64_t e) { exp_ += e; } void addGold(uint64_t g) { gold_ += g; } void deductGold(uint64_t g) { gold_ -= g; } void setStatus(PlayerStatus s) { status_ = s; } // --- 关系访问 --- odb::lazy_shared_ptr<PlayerProfile>& profile() { return profile_; } std::vector<odb::lazy_shared_ptr<InventoryItem>>& inventory() { return inventory_; } std::vector<odb::lazy_shared_ptr<Equipment>>& equipments() { return equipments_; } std::vector<odb::lazy_shared_ptr<Guild>>& guilds() { return guilds_; } // --- 扩展段访问 --- odb::section& skillSection() { return skillSection_; } const std::map<uint64_t, int>& skills() const { return skills_; } void setSkillLevel(uint64_t skillId, int level) { skills_[skillId] = level; } private: friend class odb::access; // ===== 核心字段(默认加载)===== #pragma db type("VARCHAR(32)") not_null unique std::string name_; #pragma db not_null index int level_{1}; uint64_t exp_{0}; uint64_t gold_{0}; #pragma db column("vip_level") not_null int vipLevel_{0}; #pragma db not_null PlayerStatus status_{PlayerStatus::Active}; // ===== 关系映射 ===== // 一对一:玩家档案 #pragma db not_null odb::lazy_shared_ptr<PlayerProfile> profile_; // 一对多:背包道具 #pragma db inverse(owner_) value_not_null std::vector<odb::lazy_shared_ptr<InventoryItem>> inventory_; // 一对多:装备栏 #pragma db inverse(wearer_) value_not_null std::vector<odb::lazy_shared_ptr<Equipment>> equipments_; // 多对多:加入的公会 #pragma db inverse(members_) value_not_null std::vector<odb::lazy_shared_ptr<Guild>> guilds_; // ===== 扩展段(按需加载)===== #pragma db load(lazy) update(change) section(skillSection_) \ table("player_skills") key_column("skill_id") value_column("level") std::map<uint64_t, int> skills_; #pragma db transient odb::section skillSection_; }; #endif 2.3 背包与装备 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 // db/objects/inventory_item.hxx #ifndef INVENTORY_ITEM_HXX #define INVENTORY_ITEM_HXX #include <cstdint> #include <odb/core.hxx> #include <odb/lazy-ptr.hxx> #include "base_entity.hxx" class Player; // 道具品质 enum class ItemQuality : int { White = 1, Green = 2, Blue = 3, Purple = 4, Orange = 5 }; #pragma db object table("inventory") class InventoryItem : public BaseEntity { public: InventoryItem() = default; InventoryItem(uint64_t templateId, int count, int slot, ItemQuality quality) : templateId_(templateId), count_(count), slot_(slot), quality_(quality) {} uint64_t templateId() const { return templateId_; } int count() const { return count_; } int slot() const { return slot_; } ItemQuality quality() const { return quality_; } void setCount(int c) { count_ = c; } void addCount(int n) { count_ += n; } void setSlot(int s) { slot_ = s; } odb::lazy_shared_ptr<Player>& owner() { return owner_; } private: friend class odb::access; #pragma db not_null odb::lazy_shared_ptr<Player> owner_; #pragma db column("template_id") not_null index uint64_t templateId_{0}; #pragma db not_null int count_{1}; #pragma db not_null int slot_{0}; #pragma db not_null ItemQuality quality_{ItemQuality::White}; }; #endif 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 // db/objects/equipment.hxx #ifndef EQUIPMENT_HXX #define EQUIPMENT_HXX #include <cstdint> #include <odb/core.hxx> #include <odb/lazy-ptr.hxx> #include "base_entity.hxx" class Player; // 装备部位 enum class EquipSlot : int { Head = 0, Chest = 1, Legs = 2, Feet = 3, MainHand = 4, OffHand = 5, Ring = 6, Necklace = 7 }; #pragma db object table("equipment") class Equipment : public BaseEntity { public: Equipment() = default; Equipment(uint64_t templateId, EquipSlot slot, int attack, int defense, int enhanceLevel) : templateId_(templateId), slot_(slot), attack_(attack), defense_(defense), enhanceLevel_(enhanceLevel) {} uint64_t templateId() const { return templateId_; } EquipSlot slot() const { return slot_; } int attack() const { return attack_; } int defense() const { return defense_; } int enhanceLevel() const { return enhanceLevel_; } int maxEnhance() const { return maxEnhance_; } void setEnhanceLevel(int lv) { enhanceLevel_ = lv; } void addAttack(int a) { attack_ += a; } void addDefense(int d) { defense_ += d; } odb::lazy_shared_ptr<Player>& wearer() { return wearer_; } private: friend class odb::access; #pragma db not_null odb::lazy_shared_ptr<Player> wearer_; #pragma db column("template_id") not_null uint64_t templateId_{0}; #pragma db not_null EquipSlot slot_{EquipSlot::MainHand}; int attack_{0}; int defense_{0}; #pragma db column("enhance_level") not_null int enhanceLevel_{0}; #pragma db column("max_enhance") not_null int maxEnhance_{15}; }; #endif 2.4 公会 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 // db/objects/guild.hxx #ifndef GUILD_HXX #define GUILD_HXX #include <string> #include <vector> #include <cstdint> #include <odb/core.hxx> #include <odb/lazy-ptr.hxx> #include "base_entity.hxx" class Player; #pragma db object table("guild") class Guild : public BaseEntity { public: Guild() = default; Guild(const std::string& name, uint64_t leaderId) : name_(name), leaderId_(leaderId) {} const std::string& name() const { return name_; } uint64_t leaderId() const { return leaderId_; } int level() const { return level_; } const std::string& announcement() const { return announcement_; } void setLevel(int lv) { level_ = lv; } void setLeaderId(uint64_t id) { leaderId_ = id; } void setAnnouncement(const std::string& a) { announcement_ = a; } std::vector<odb::lazy_shared_ptr<Player>>& members() { return members_; } private: friend class odb::access; #pragma db type("VARCHAR(32)") not_null unique std::string name_; #pragma db column("leader_id") not_null uint64_t leaderId_{0}; #pragma db not_null int level_{1}; #pragma db column("max_members") not_null int maxMembers_{50}; #pragma db type("VARCHAR(256)") std::string announcement_; #pragma db value_not_null unordered table("guild_members") std::vector<odb::lazy_shared_ptr<Player>> members_; }; #endif 2.5 交易日志 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 // db/objects/trade_log.hxx #ifndef TRADE_LOG_HXX #define TRADE_LOG_HXX #include <string> #include <cstdint> #include <odb/core.hxx> #include "base_entity.hxx" enum class TradeAction : int { Buy = 1, // 商店购买 Sell = 2, // 商店出售 Transfer = 3, // 玩家间转账 Enhance = 4, // 装备强化消耗 Reward = 5 // 系统奖励 }; #pragma db object table("trade_log") class TradeLog : public BaseEntity { public: TradeLog() = default; TradeLog(uint64_t playerId, TradeAction action, uint64_t itemId, int quantity, uint64_t goldChange, const std::string& detail) : playerId_(playerId), action_(action), itemId_(itemId), quantity_(quantity), goldChange_(goldChange), detail_(detail) {} uint64_t playerId() const { return playerId_; } TradeAction action() const { return action_; } uint64_t itemId() const { return itemId_; } int quantity() const { return quantity_; } uint64_t goldChange() const { return goldChange_; } const std::string& detail() const { return detail_; } private: friend class odb::access; #pragma db column("player_id") not_null index uint64_t playerId_{0}; #pragma db not_null TradeAction action_{TradeAction::Buy}; #pragma db column("item_id") uint64_t itemId_{0}; int quantity_{0}; #pragma db column("gold_change") uint64_t goldChange_{0}; #pragma db type("VARCHAR(256)") std::string detail_; }; #endif 2.6 View 定义 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 // db/views/game_views.hxx #ifndef GAME_VIEWS_HXX #define GAME_VIEWS_HXX #include <string> #include <cstdint> #include <odb/core.hxx> #include "db/objects/player.hxx" #include "db/objects/guild.hxx" #include "db/objects/trade_log.hxx" // 排行榜条目 #pragma db view object(Player) struct LeaderboardEntry { #pragma db column(Player::id_) uint64_t id; #pragma db column(Player::name_) std::string name; #pragma db column(Player::level_) int level; #pragma db column(Player::exp_) uint64_t exp; }; // 全服统计 #pragma db view object(Player) struct ServerStats { #pragma db column("count(" + Player::id_ + ")") uint64_t totalPlayers; #pragma db column("avg(" + Player::level_ + ")") double avgLevel; #pragma db column("max(" + Player::level_ + ")") int maxLevel; #pragma db column("sum(" + Player::gold_ + ")") uint64_t totalGold; }; // 公会战力排名 #pragma db view object(Guild) object(Player: Guild::members_) struct GuildPowerRank { #pragma db column(Guild::id_) uint64_t guildId; #pragma db column(Guild::name_) std::string guildName; #pragma db column("count(" + Player::id_ + ")") int memberCount; #pragma db column("sum(" + Player::level_ + ")") int totalPower; }; // 每日交易汇总 #pragma db view query( \ "SELECT DATE(created_at) AS trade_date, " \ " action AS trade_action, " \ " COUNT(*) AS trade_count, " \ " SUM(gold_change) AS total_gold " \ "FROM trade_log " \ "GROUP BY DATE(created_at), action " \ "ORDER BY trade_date DESC, action ASC") struct DailyTradeReport { #pragma db column("trade_date") type("DATE") std::string tradeDate; #pragma db column("trade_action") int tradeAction; #pragma db column("trade_count") uint64_t tradeCount; #pragma db column("total_gold") uint64_t totalGold; }; #endif 3. Repository 层实现 Repository 层封装所有数据库操作,让业务层不直接依赖 ODB 类型。 ...

July 12, 2025 · 24 min · 4920 words