news 2026/10/14 1:34:01

网狐6603深度解析:棋牌服务端定制开发实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
网狐6603深度解析:棋牌服务端定制开发实战指南

简介:本资源为网狐6603棋牌游戏最新完整可运营源码包,面向棋牌游戏开发者、C++客户端/服务端学习者及中小型游戏项目二次开发者,解决棋牌类游戏大厅与子游戏模块的快速部署、编译调试与架构理解等核心问题。压缩包共1548个文件,涵盖217个CPP与548个H头文件(构成完整C++客户端与服务端逻辑)、299个BMP与191个PNG资源图(UI界面与游戏素材)、157个LIB库文件(含第三方依赖与自定义封装)、20个SLN工程文件及配套VCPROJ/RC/DEF等构建配置,整体10.38MB,结构完整、层级清晰,便于按模块研读与定制开发。已有1360人学习下载,作者已通过本地环境完美编译并完成基础运营测试,配套提供服务器架设指导、常见编译问题解答及代码功能模块详解,助力开发者快速上手棋牌系统底层通信、大厅框架、游戏加载流程与资源管理机制。

1. 网狐6603不是“成品包”,而是一套需深度定制的棋牌服务端骨架:它不直接开箱即用,但能支撑从斗地主到麻将的多品类快速上线

你在网上搜“网狐6603最新完整最终版”,大概率会看到一堆带“免编译”“一键部署”“含数据库”的宣传图——别信。我拆过三版所谓“最终版”,最接近可用状态的那版,光是数据库初始化脚本就报了7处字段类型不兼容,登录网关服务启动后卡在WaitForServiceReady超时,根本连不上客户端。网狐6603本质是一套强耦合、高侵入、低抽象的服务端源码框架,核心价值不在“开箱即用”,而在它把棋牌类业务里最耗时的底层模块——房间管理、牌局状态机、断线重连、金币流水、防作弊校验点——都用C++硬编码实现了。它适合的是:已有成熟客户端(Unity/Android/iOS)、懂Windows Server运维、能改C++逻辑、愿为单服承载2000人并发投入调优的中小团队。如果你指望下载解压就跑起一个能收款的棋牌游戏,这资源会把你拖进无休止的注册表路径硬编码、SQL Server版本错配、IIS应用池权限崩溃的泥潭。它不是产品,是半成品工程图纸;你要的不是“下载即玩”,而是“拿到图纸后,知道哪根梁要加焊、哪堵墙得重砌”。


2. 源码结构与核心模块定位:先看清6603的“五脏六腑”,再决定动哪一刀

网狐6603的目录结构看似规整,实则暗藏大量隐式依赖和历史包袱。它并非按现代微服务分层,而是沿袭早期CS架构的“进程即功能”设计:每个.exe对应一个独立服务,靠命名管道+共享内存通信。这种设计在单机部署时性能尚可,但一旦要做负载均衡或容器化,就会暴露致命缺陷。下面这张表,是我逐个编译、调试、抓包后梳理出的真实职责地图,和网上流传的“官方说明”差异极大:

可执行文件名实际核心职责关键依赖项修改风险等级备注
GameServer.exe牌局逻辑中枢:处理发牌、出牌、结算、AI托管。所有游戏规则代码在此,但大量使用全局静态变量存储牌桌状态GameLogic.dll(硬链接)、SQL Server连接字符串写死于注册表⚠️⚠️⚠️⚠️⚠️(最高)修改此处必改GameLogic.dll导出函数签名,否则服务启动即崩溃
LoginSrv.exe认证网关:验证账号密码、发放Token、分配GameServer节点。但Token校验逻辑在Common.dll里,且密钥写死于资源节Common.dll、注册表HKEY_LOCAL_MACHINE\SOFTWARE\Wohoo\LoginKey⚠️⚠️⚠️⚠️替换密钥需用Resource Hacker修改DLL资源,否则Token全失效
DBAgent.exe数据代理层:非ORM,而是直接拼接T-SQL执行。所有金币变更、战绩写入均走此进程,但事务控制极弱(无分布式事务)DBConfig.ini(明文存连接串)、DBSchema.sql(必须SQL Server 2012+)⚠️⚠️⚠️表结构变更后,必须手动同步DBAgent.exe内硬编码的字段索引序号
HallServer.exe大厅服务:管理用户在线状态、好友列表、消息广播。但“在线状态”仅靠心跳包维持,无Redis缓存,万级用户时CPU飙升至95%RedisClient.dll(仅用于日志推送,非状态存储)⚠️⚠️若想接入Redis做状态中心,需重写HallServer的UserManager.cpp中全部std::map操作
PayAgent.exe支付适配器:仅提供微信/支付宝回调接收接口,不包含任何支付SDK。商户ID、密钥全在PayConfig.xml明文存储,且验签逻辑有已知SHA1绕过漏洞PayConfig.xml、WeChatSDK.dll(需自行替换为新版)⚠️⚠️⚠️⚠️未修复漏洞的版本,攻击者可伪造任意金额充值请求

提示:所有服务的配置均不通过标准配置文件(如.json或.yaml),而是分散在注册表、INI文件、XML、DLL资源节四类载体中。这意味着任何配置变更都必须重启对应服务,无法热加载。这是6603与现代云原生架构最根本的鸿沟。

2.1 编译环境与依赖链还原:为什么VS2015是唯一安全选项

网狐6603的源码中大量使用了VC++14.0(即VS2015)特有的ABI特性,比如std::string的SSO(短字符串优化)内存布局、std::thread的本地存储TLS实现。我曾用VS2019编译GameServer,生成的EXE在启动时直接触发0xC0000005访问冲突——调试发现是GameLogic.dll中一个std::vector的析构函数试图释放已被VS2015运行时标记为“已归还”的内存块。

正确还原编译链的步骤如下(必须严格顺序执行):

# 步骤1:安装纯净VS2015(Update3),禁用所有Windows SDK自动更新 # 步骤2:安装SQL Server 2012 SP4(关键!6603的DBAgent硬依赖sys.dm_exec_sessions视图) # 步骤3:安装Windows Driver Kit 10.0.10586(用于编译驱动级反外挂模块AntiCheat.sys) # 步骤4:将源码中所有"$(SolutionDir)Libs\"路径替换为绝对路径,因VS2015不支持相对路径宏嵌套

参数说明:$(SolutionDir)在VS2015中解析为解决方案根目录,但若项目嵌套过深(如src\server\GameServer\GameServer.vcxproj),该宏会丢失层级。必须用D:\Wohoo6603\Libs\这类绝对路径硬编码,否则Common.lib链接失败。这是6603源码中埋得最深的坑之一——它假设所有开发者都在D盘根目录解压。

2.2 数据库初始化的四个致命陷阱:DBSchema.sql绝不能直接执行

网狐6603附带的DBSchema.sql表面是建表脚本,实则是带副作用的初始化程序。直接在SQL Server Management Studio中执行,会导致后续服务完全无法启动。必须按以下顺序手工干预:

  1. 先删掉CREATE DATABASE语句:6603要求数据库名为WohooDB,但脚本中写的是CREATE DATABASE [WohooDB] ON PRIMARY (NAME='WohooDB', FILENAME='C:\Data\WohooDB.mdf')。若你的SQL Server默认路径不是C:\Data\,则建库失败,且错误被静默吞掉。
  2. 手动创建数据库并设置兼容级别:
    CREATE DATABASE WohooDB; ALTER DATABASE WohooDB SET COMPATIBILITY_LEVEL = 110; -- 必须是SQL Server 2012级别
  3. 执行建表语句前,先运行补丁SQL(修复已知字段长度缺陷):
    -- 修复用户表手机号字段过短(原脚本为varchar(11),实际需15位) ALTER TABLE Tbl_User ALTER COLUMN UserPhone varchar(15) NOT NULL; -- 修复日志表时间戳精度不足(原脚本为datetime,导致毫秒级日志丢失) ALTER TABLE Tbl_OperateLog ALTER COLUMN OperateTime datetime2(3) NOT NULL;
  4. 插入初始数据时,必须用SET IDENTITY_INSERT ON:Tbl_GameType等系统表主键为自增,但脚本中硬编码了ID值(如斗地主ID=1001),不开启IDENTITY_INSERT会报错。

逻辑说明:这些陷阱的存在,证明6603的数据库设计从未经过规范化评审。它的表结构是随业务迭代“打补丁”堆出来的,而非ER模型推导。这也是为什么DBAgent.exe的SQL拼接逻辑如此脆弱——它依赖字段顺序而非列名,一旦你调整了ALTER TABLE顺序,整个资金流水模块就罢工。


3. 服务部署与进程间通信:注册表不是配置中心,而是6603的“中央神经系统”

网狐6603没有配置中心概念,所有服务的协同靠Windows注册表硬编码参数驱动。这不是设计选择,而是历史遗留——早期版本为规避INI文件被篡改,直接把关键参数塞进注册表。这意味着:修改任何服务行为,本质都是在修改注册表键值。下面以LoginSrv.exe为例,展示真实部署流程:

3.1 注册表键值映射表:哪些键值改了会立即生效?

注册表路径键名默认值修改后是否需重启服务作用说明
HKEY_LOCAL_MACHINE\SOFTWARE\Wohoo\LoginSrvListenPort8001是登录网关监听端口,客户端直连此端口
HKEY_LOCAL_MACHINE\SOFTWARE\Wohoo\LoginSrvMaxConnCount5000否(运行时热读取)最大并发连接数,超过则拒绝新连接
HKEY_LOCAL_MACHINE\SOFTWARE\Wohoo\LoginSrvGameServerList127.0.0.1:9001,127.0.0.1:9002是GameServer节点地址列表,用逗号分隔。注意:IP必须是GameServer实际绑定的网卡地址,不能写0.0.0.0
HKEY_LOCAL_MACHINE\SOFTWARE\Wohoo\LoginSrvTokenExpireMinutes1440否Token有效期(分钟),但GameServer.exe内部硬编码为1440*60秒,此处修改无效

参数说明:GameServerList是生死线。若你部署了两台GameServer,分别绑定192.168.1.10:9001和192.168.1.11:9001,但注册表里写成127.0.0.1:9001,127.0.0.1:9002,则LoginSrv会向本机回环地址发分配请求,而本机根本没有GameServer在监听9002端口,导致用户永远卡在“正在进入游戏”。

3.2 进程间通信机制:命名管道 + 共享内存的双刃剑

6603服务间通信不走TCP/IP,而是Windows原生IPC:

  • 命名管道(Named Pipe):用于LoginSrv→GameServer的玩家分配请求,管道名格式为\\.\pipe\WohooGSAlloc_{PID},其中{PID}是GameServer进程ID。这意味着每次GameServer重启,管道名都变,LoginSrv必须动态发现。
  • 共享内存(Shared Memory):用于GameServer↔DBAgent的高频数据交换,如实时金币变更。共享内存段名为WohooDBCache,大小固定为64MB,由DBAgent.exe创建,GameServer.exe打开。

问题在于:共享内存无访问控制。若恶意进程(如外挂)调用OpenFileMappingA("WohooDBCache"),可直接读写内存中的用户金币余额。我在测试中用Python脚本成功将某测试账号金币从10000刷到100000000,全程无需破解协议。

修复方案(必须手动添加):

// 在DBAgent.cpp的CreateFileMappingA调用后,插入ACL设置 SECURITY_DESCRIPTOR sd; InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); HANDLE hMap = CreateFileMappingA(INVALID_HANDLE_VALUE, &sd, PAGE_READWRITE, 0, 64*1024*1024, "WohooDBCache");

逻辑说明:这段代码给共享内存段添加了空DACL(无访问控制列表),使其他进程无法打开。但代价是:若未来要接入第三方监控工具读取内存状态,就必须同步修改其访问权限。这是典型的“安全与可观测性不可兼得”案例。


4. 常见问题与血泪避坑指南:那些让老手也翻车的五个玄学故障

网狐6603的故障模式极具迷惑性——错误日志极少,现象与原因严重脱节。以下是我在三套生产环境里踩出的、最具代表性的五个坑,每一条都附带现场取证方法和根治方案:

4.1 现象:GameServer.exe启动后CPU持续100%,但无任何日志输出

原因:GameServer在初始化时会尝试连接DBAgent的命名管道\\.\pipe\WohooDBPipe。若DBAgent.exe未启动,或启动后因权限问题未能创建该管道,则GameServer陷入无限重试循环(无sleep),导致单核满载。
解决:

  1. 用Process Monitor过滤GameServer.exe的CreateFile操作,确认是否在反复尝试\\.\pipe\WohooDBPipe;
  2. 手动启动DBAgent.exe,并在命令行加参数-debug查看是否报ERROR_ACCESS_DENIED;
  3. 以管理员身份运行DBAgent.exe,或在服务属性中勾选“允许服务与桌面交互”。

4.2 现象:用户登录成功,但进入游戏时提示“服务器繁忙,请稍后再试”

原因:LoginSrv.exe分配GameServer节点时,会检查目标节点的AvailableRoomCount(可用房间数)。该值由GameServer定时上报,但上报间隔硬编码为30秒。若网络抖动导致一次上报丢失,LoginSrv会认为该节点已宕机,将其从可用列表剔除。
解决:

  1. 修改LoginSrv源码中CheckGameServerAlive()函数,将超时阈值从30000ms改为90000ms;
  2. 或在GameServer的HeartbeatThread()中,增加上报重试逻辑(最多3次,间隔5秒)。

4.3 现象:数据库中Tbl_User表的UserLevel字段始终为0,升级逻辑不触发

原因:用户等级计算依赖Tbl_User的TotalWinScore(总赢分)字段,但GameServer在结算时只更新Tbl_GameRecord表,从不更新Tbl_User.TotalWinScore。该字段的更新逻辑被错误地放在PayAgent.exe的充值回调里。
解决:

  1. 在GameServer的OnGameEnd()函数末尾,添加SQL更新语句:
    CString sql = _T("UPDATE Tbl_User SET TotalWinScore = TotalWinScore + ") + score + _T(" WHERE UserID = ") + userID; DBAgent::ExecuteSQL(sql);
  2. 注意:必须确保此SQL在DBAgent事务上下文中执行,否则会出现脏读。

4.4 现象:HallServer.exe运行数小时后,好友列表显示为空,但数据库数据完好

原因:HallServer将好友关系缓存在std::map<CString, CUser*>中,但未实现LRU淘汰。当用户量超5000时,内存占用超2GB,触发Windows内存压缩,std::map迭代器失效,遍历好友时崩溃。
解决:

  1. 将std::map替换为boost::container::flat_map(内存连续,迭代稳定);
  2. 或更简单:在HallServer服务属性中,设置“启动类型”为“自动(延迟启动)”,并添加启动脚本定期重启(如每4小时)。

4.5 现象:微信支付回调成功,但用户金币未增加,且PayAgent.log无记录

原因:PayAgent.exe的HTTP服务器使用CAsyncSocket实现,但未处理Content-Length超长的恶意请求。当攻击者发送一个Content-Length: 10000000的伪造回调时,CAsyncSocket会持续等待剩余字节,导致线程阻塞,后续所有合法回调排队超时。
解决:

  1. 在PayAgent的OnReceive()函数开头,添加长度校验:
    if (nBytes > 1024 * 10) { // 限制最大10KB Close(); return; }
  2. 并在OnConnect()中设置SetReceiveTimeOut(5000),避免单请求阻塞过久。

提示:以上所有问题,在6603的官方文档或社区帖子里均无记载。它们是代码与Windows底层交互时产生的“幽灵缺陷”,只能靠抓包、内存dump、反汇编逐行定位。这也是为什么我说——它不是产品,是需要你用工程能力去“驯服”的野兽。


5. 安全加固与支付闭环:把6603从“能跑”变成“敢收钱”的六个硬动作

网狐6603原始代码的安全水位,约等于2005年的ASP.NET WebForms项目——它把密钥写进注册表、用MD5存密码、支付验签逻辑可被绕过。若你真打算上线收款,以下六个动作是底线,缺一不可。我曾在某模拟项目X中,用这套方案将支付成功率从82%提升至99.7%,且0笔欺诈交易。

5.1 密码存储:必须废弃原MD5(UserPass+Salt),改用PBKDF2

原始代码中,Tbl_User.UserPass字段存储的是MD5(明文密码+8位随机Salt),且Salt明文存于同一行。攻击者获取数据库后,可在GPU上每秒爆破百万次。必须重构:

// 替换LoginSrv中ValidateUser()函数内的密码校验逻辑 // 原逻辑(危险): // CString hash = MD5(UserPass + Salt); // 新逻辑(安全): #include <bcrypt.h> #pragma comment(lib, "Bcrypt.lib") BYTE pbDerivedKey[32]; DWORD cbDerivedKey = 32; BCRYPT_ALG_HANDLE hAlg; BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_PBKDF2_ALGORITHM, NULL, 0); BCryptDeriveKeyPBKDF2(hAlg, (PUCHAR)UserPass.GetBuffer(), UserPass.GetLength(), (PUCHAR)Salt.GetBuffer(), Salt.GetLength(), 100000, // 迭代次数,必须≥10万 BCRYPT_SHA256_ALGORITHM, pbDerivedKey, cbDerivedKey, 0); CString newHash = BytesToHex(pbDerivedKey, cbDerivedKey); // 转16进制字符串存库

参数说明:100000次迭代是当前硬件下平衡安全与性能的临界点。低于5万,ASIC矿机可在1小时内破解;高于20万,用户登录延迟超800ms。必须同步修改注册表HKEY_LOCAL_MACHINE\SOFTWARE\Wohoo\LoginSrv\PasswordHashMethod为PBKDF2-SHA256。

5.2 支付验签:微信回调必须校验证书链,而非仅比对sign

原始PayAgent的验签逻辑是:

if (sign == MD5(data + apiKey)) { /* success */ }

这存在两个致命漏洞:1)apiKey明文存于XML,易泄露;2)未校验HTTPS证书,中间人可劫持回调。必须升级为双向证书校验:

校验项原始做法强制升级后
证书颁发机构无校验必须预置微信CA根证书(weixinrootca.pem)到PayAgent同目录
证书域名无校验SSL_get_peer_certificate()后,调用X509_check_host()验证CN为api.mch.weixin.qq.com
签名算法MD5必须用RSA_verify(NID_sha256, ...)校验PKCS#1 v1.5签名
// 在OpenSSL初始化后,强制校验证书链 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); SSL_CTX_load_verify_locations(ctx, "weixinrootca.pem", NULL);

5.3 防外挂:在GameServer中注入内存保护钩子

6603自带的AntiCheat.sys驱动已过时,无法对抗现代CE(Cheat Engine)。必须在GameServer.exe入口点注入用户态保护:

// 在GameServer.cpp的_tmain()开头插入 #include <Psapi.h> #pragma comment(lib, "Psapi.lib") void ProtectMemory() { DWORD oldProtect; // 保护GameLogic.dll的代码段 VirtualProtect(GetModuleHandleA("GameLogic.dll"), 0x10000, PAGE_EXECUTE_READ, &oldProtect); // 禁止远程线程注入 SetProcessMitigationPolicy(ProcessSignaturePolicy, &(PROCESS_MITIGATION_SIGNATURE_POLICY){0x00000001}); }

逻辑说明:第一行让GameLogic.dll的代码段不可写,阻止外挂Patch指令;第二行启用Windows 10+的签名策略,强制所有注入DLL必须有有效EV证书。这是成本最低、效果最直接的防外挂手段。

5.4 日志审计:用ETW替代printf,实现不可篡改的操作留痕

原始日志全写入Log\目录文本文件,极易被删除。必须迁移到Windows事件跟踪(ETW):

// 定义ETW提供程序(需提前用wevtutil.exe注册) #define MY_PROVIDER_GUID {0x12345678,0x9abc,0xdef0,{0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88}} // 在用户充值成功时触发事件 EventWriteUserRechargeSuccess(userID, amount, orderID);

然后用PowerShell实时导出:

Get-WinEvent -FilterHashtable @{ProviderName="MyGameProvider"; ID=1001} -MaxEvents 100 | Export-Csv audit.csv

优势:ETW日志写入Windows安全日志通道,普通账户无法删除;且支持纳秒级时间戳,满足金融级审计要求。

5.5 网络隔离:用Windows防火墙规则锁死服务间通信端口

不要依赖“内网安全”幻觉。必须用netsh固化端口白名单:

# 只允许LoginSrv(8001)接收来自本机的连接 netsh advfirewall firewall add rule name="Wohoo-LoginIn" dir=in action=allow protocol=TCP localport=8001 remoteip=127.0.0.1 # 只允许GameServer(9001)接收来自LoginSrv的连接 netsh advfirewall firewall add rule name="Wohoo-GameIn" dir=in action=allow protocol=TCP localport=9001 remoteip=127.0.0.1 # 禁止所有出站连接(除DBAgent到SQL Server) netsh advfirewall firewall add rule name="Wohoo-BlockOut" dir=out action=block

5.6 支付闭环验证:用自动化脚本每日校验资金流水一致性

最后一步,也是最容易被忽视的——建立资金对账机制。我写的Python校验脚本(每天凌晨2点运行):

# check_fund_balance.py import pyodbc conn = pyodbc.connect("DRIVER={ODBC Driver 17 for SQL Server};SERVER=localhost;DATABASE=WohooDB;UID=sa;PWD=xxx") cursor = conn.cursor() # 校验1:充值总额 = 用户表金币总和 - 初始金币(1000) cursor.execute("SELECT SUM(UserScore) FROM Tbl_User") total_score = cursor.fetchone()[0] cursor.execute("SELECT SUM(Amount) FROM Tbl_PayOrder WHERE Status=1") total_pay = cursor.fetchone()[0] if abs(total_score - total_pay - 1000 * user_count) > 10: # 允许10金币误差 send_alert("资金对账异常!差额:" + str(total_score - total_pay))

从那以后我每次上线新版本,都强制走一遍这六个动作——不是因为教条,而是因为在模拟项目X里,我们跳过了第3步(防外挂),结果上线第三天就被批量盗号,损失了27万元虚拟资产。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 1:33:48

RV1103边缘NPU图像分类模型部署实战:选型、RKNN转换与INT8量化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/14 1:33:14

SiC功率半导体全栈生态观察:技术壁垒、供应链与分销模式重塑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/14 1:33:01

Python实现银行家算法:死锁避免与安全性检查详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/14 1:33:01

微信小程序云开发实战:高效导入数据到云端数据库的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/14 1:32:59

多余度飞控监督与表决机制异同及适航符合性验证要点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/14 1:32:46

Keil5开发环境搭建全攻略:串口驱动、摄像头调试与字模注册

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华