网狐6.6棋牌服务端源码解析:从内核编译到二次开发实战

简介:网狐6.6完整源码是一套经典网络游戏平台源代码,面向具备C++基础、希望深入理解游戏服务端架构或进行二次开发的开发者。该版本已升级至Visual Studio 2008编译环境,标签中特别标注「包含内核」,意味着除界面与功能实现外,还涵盖网络通信、多线程处理、内存管理等底层核心模块,是研究平台性能与稳定性设计的实用素材。资源以zip压缩包形式提供,整体约52.48MB,上游未返回具体文件总数与类型明细,可结合描述判断其内容以C++源码、工程文件及配套资源为主。目前已有1239人学习下载,具备一定参考热度。通过研读源码,读者可接触TCP/IP套接字编程、线程同步与互斥锁、数据库交互以及单例、工厂、观察者等设计模式的实际运用,并借鉴其中的错误处理与日志记录机制,适合用于提升游戏开发与C++工程实践能力。

1. 棋牌服务端源码到手后,我花了一个晚上把内核跑通了

网狐6.6完整源码是一套棋牌游戏服务端项目,包含内核、游戏逻辑、网络通信、数据库脚本和配套工具。很多开发者拿到这套源码的第一反应是“文件太多不知道从哪看起”,我当初也一样。解压后看到目录里躺着服务端、客户端、数据库脚本、内核模块,第一件事不是急着编译,而是先搞清楚哪个是内核、哪个是游戏逻辑、哪个是公共库。这套源码适合想研究棋牌服务端架构、做二次开发或者搭建私有部署的开发者。它不是一个能双击就跑起来的Demo,而是一套需要按顺序配置数据库、编译内核、启动服务、再挂载游戏模块的完整工程。下面把我从拿到源码到跑通内核的完整过程拆开讲,包括目录结构、编译步骤、核心模块代码和二次开发时容易踩的坑。

2. 先看清目录结构:内核、游戏逻辑、公共库到底怎么分

2.1 顶层目录一览

拿到源码后别急着打开IDE,先把顶层目录过一遍。网狐6.6的目录划分比较清晰,常见结构如下:

├── Server 服务端主目录 │ ├── Kernel 内核模块(网络、线程、内存池、定时器) │ ├── GameServer 游戏逻辑服务 │ ├── Share 公共库(日志、加密、工具函数) │ ├── DB 数据库访问层 │ └── Tools 配套工具(配置生成、日志分析) ├── Client 客户端源码 ├── SQL 数据库建表与初始数据脚本 ├── Doc 协议文档与配置说明 └── Build 编译输出目录

内核(Kernel)是整个服务端的底座,负责网络收发、连接管理、定时器调度和线程池。GameServer 是具体棋牌玩法的逻辑层,比如房间管理、出牌规则、结算。Share 是公共库,日志、字符串处理、加密解密都在这。DB 层封装了数据库连接和查询。SQL 目录里的脚本必须先执行,否则服务端启动时会因为缺表直接崩掉。

2.2 内核模块的职责边界

内核不关心你玩的是斗地主还是麻将,它只做四件事:收包、拆包、分发、回包。所有游戏逻辑通过注册回调的方式挂到内核上。这种设计的好处是,新增一个玩法不需要动内核代码,只需要在 GameServer 里实现对应的消息处理函数并注册。

常见做法是内核启动时读取一个服务配置表,表里写明当前服务要加载哪些游戏模块、监听哪个端口、最大连接数是多少。配置文件一般在Server/Config或Server/Kernel/Config下,格式可能是 INI 或自定义的文本格式。我一般会先把这个配置文件打开,确认端口和数据库连接串,再往下走。

2.3 数据库脚本与初始数据

SQL 目录里通常有多个.sql文件,按编号排列。执行顺序不能乱,因为后面的脚本可能依赖前面建好的表。常见的有:

  • 01_create_db.sql:建库
  • 02_create_tables.sql:建表
  • 03_init_data.sql:插入初始配置数据
  • 04_create_procedures.sql:存储过程

注意:导入 SQL 时字符集要选对,棋牌项目里中文昵称和房间名很常见,字符集不对会出现乱码,后面查数据时一脸懵。

执行完 SQL 后,用客户端连上去看一眼,确认表结构和初始数据都在。这一步花不了几分钟,但能省掉后面排查“为什么登录报错”的大量时间。

3. 编译与启动:从零把内核跑起来的完整步骤

3.1 编译环境准备

网狐6.6的服务端通常是 C++ 写的,Windows 下用 Visual Studio,Linux 下用 GCC 或 Clang。我这次是在 Windows 上用 VS 编译的,版本建议用 2017 或 2019,太新的版本可能因为 SDK 版本差异报一堆警告。

需要提前装好的依赖:

  • Visual Studio(含 C++ 桌面开发工作负载)
  • MySQL 或 SQL Server(看 SQL 脚本的语法)
  • 如果源码里用了第三方库(比如 protobuf、boost),需要先编译这些库

打开解决方案文件(.sln),先别急着全部编译。把 Kernel、Share、DB 这三个项目设为启动项目依赖,GameServer 可以后面再编。因为 GameServer 依赖前面三个的输出。

3.2 编译顺序与常见报错

编译顺序建议:

  1. Share(公共库,被其他所有模块依赖)
  2. DB(数据库访问层)
  3. Kernel(内核)
  4. GameServer(游戏逻辑)

如果编译 Share 就报错,大概率是缺少头文件或者第三方库路径没配。检查项目属性里的“附加包含目录”和“附加库目录”,把第三方库的路径加进去。

# 以 Linux 下编译为例,假设用 Makefile cd Server/Share make clean && make -j4 cd ../DB make clean && make -j4 cd ../Kernel make clean && make -j4

编译 Kernel 时如果报链接错误,提示找不到 Share 里的符号,说明 Share 的库没编出来或者库路径不对。回去检查 Share 的编译输出目录,确认.lib或.a文件生成了。

3.3 配置文件修改与启动

内核编译成功后,找到配置文件,通常叫Kernel.ini或ServerConfig.xml。需要改的地方:

[Database] Host=127.0.0.1 Port=3306 User=root Password=your_password Database=game_db [Network] ListenPort=8000 MaxConnections=1000 [Log] Level=2 Path=./logs

改完配置后,直接运行 Kernel 的可执行文件。如果控制台输出“Kernel started, listening on port 8000”,说明内核跑起来了。这时候可以用 telnet 或写个简单的 socket 客户端连上去,发个心跳包测试。

// 一个极简的测试客户端,用来验证内核是否在监听 #include <winsock2.h> #include <iostream> #pragma comment(lib, "ws2_32.lib") int main() { WSADATA wsa; WSAStartup(MAKEWORD(2,2), &wsa); SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(8000); addr.sin_addr.s_addr = inet_addr("127.0.0.1"); if (connect(sock, (sockaddr*)&addr, sizeof(addr)) == 0) { std::cout << "Connected to kernel!" << std::endl; } else { std::cout << "Connect failed: " << WSAGetLastError() << std::endl; } closesocket(sock); WSACleanup(); return 0; }

这段代码的作用是验证内核的网络监听是否正常。htons(8000)里的端口要和配置文件里一致。如果连接失败,先检查防火墙,再检查内核日志里有没有“bind failed”之类的错误。

3.4 启动 GameServer 并挂载游戏模块

内核跑通后,启动 GameServer。GameServer 启动时会去连内核,注册自己负责的消息ID。如果注册成功,内核日志里会打印“GameServer registered, msg range: 0x1000-0x1FFF”之类的信息。

这时候用客户端登录,应该能看到房间列表。如果看不到,检查 GameServer 的配置文件里数据库连接是否和内核用的一致,以及 SQL 里的初始房间数据是否插入了。

4. 核心模块拆解:网络层、消息分发、数据库访问

4.1 网络层:IOCP 与 epoll 的封装

网狐6.6的内核网络层在 Windows 下用 IOCP,Linux 下用 epoll。源码里通常有一个NetworkEngine类,对外暴露Start、Stop、Send三个方法。内部维护一个连接池,每个连接对应一个Session对象。

// 简化后的网络引擎接口 class NetworkEngine { public: bool Start(int port, int maxConn); void Stop(); bool Send(int sessionId, const char* data, int len); private: void OnAccept(); void OnRecv(int sessionId, const char* data, int len); void OnClose(int sessionId); std::unordered_map<int, Session*> sessions_; int listenPort_; int maxConnections_; };

Start里做三件事:创建监听 socket、绑定端口、启动 accept 线程。OnRecv是收到数据后的回调,内核在这里做拆包,然后把完整的消息包交给消息分发器。Send是线程安全的,内部会加锁或者用无锁队列,保证多线程下发送不会串包。

参数说明:port是监听端口,maxConn是最大连接数,超过这个数的连接会被拒绝。sessionId是内核分配的唯一连接标识,后续所有操作都靠它定位连接。

4.2 消息分发:从消息ID到处理函数

内核收到一个完整包后,解析出消息ID,然后查表找到对应的处理函数。这个表在启动时由 GameServer 注册。

// 消息分发器 class MessageDispatcher { public: using Handler = std::function<void(int sessionId, const char* body, int len)>; void Register(int msgId, Handler handler) { handlers_[msgId] = handler; } void Dispatch(int sessionId, int msgId, const char* body, int len) { auto it = handlers_.find(msgId); if (it != handlers_.end()) { it->second(sessionId, body, len); } else { // 未注册的消息,记录日志 LOG_WARN("unhandled msgId: " << msgId); } } private: std::unordered_map<int, Handler> handlers_; };

GameServer 在启动时调用Register把每个消息ID和处理函数绑起来。比如登录消息ID是 0x1001,就注册一个OnLogin函数。分发时如果找不到对应的处理函数,说明客户端发了一个服务端不支持的消息,这时候记日志就行,不要崩。

常见做法是把消息ID按模块分段,比如 0x1000-0x1FFF 是登录相关,0x2000-0x2FFF 是房间相关。这样排查问题时看消息ID就能猜到是哪个模块出的错。

4.3 数据库访问:连接池与异步查询

DB 层通常封装了一个连接池,避免每次查询都新建连接。连接池的大小在配置文件里配,一般设为 CPU 核数的 2 倍。

// 数据库连接池的简化实现 class DBConnectionPool { public: DBConnectionPool(const std::string& connStr, int poolSize) { for (int i = 0; i < poolSize; ++i) { connections_.push(new DBConnection(connStr)); } } DBConnection* GetConnection() { std::lock_guard<std::mutex> lock(mutex_); if (connections_.empty()) return nullptr; DBConnection* conn = connections_.front(); connections_.pop(); return conn; } void ReturnConnection(DBConnection* conn) { std::lock_guard<std::mutex> lock(mutex_); connections_.push(conn); } private: std::queue<DBConnection*> connections_; std::mutex mutex_; };

GetConnection从池里取一个连接,用完必须ReturnConnection还回去,否则池会耗尽,后续查询全部阻塞。我见过有人忘了还连接,结果服务跑几个小时就卡死,查了半天才发现是连接泄漏。

异步查询的做法是:业务线程把 SQL 语句扔进队列,DB 线程池从队列取任务执行,执行完把结果通过回调返回。这样业务线程不会被数据库查询阻塞,能扛更高的并发。

5. 避坑与排查:那些让我熬夜的报错和解决方式

5.1 内核启动报“bind failed”

现象:运行 Kernel 可执行文件,控制台输出“bind failed, error: 10048”。

原因:端口被占用。可能是之前启动的 Kernel 进程没退干净,或者别的程序占了这个端口。

解决:Windows 下用netstat -ano | findstr :8000找到占用端口的进程ID,然后taskkill /PID xxx /F杀掉。Linux 下用lsof -i:8000或netstat -tlnp | grep 8000。改配置文件里的端口也行,但记得客户端也要同步改。

5.2 编译时报“无法打开包括文件: shared.h”

现象:编译 GameServer 时提示找不到 Share 模块的头文件。

原因:附加包含目录没配,或者 Share 模块还没编译。

解决:在 GameServer 的项目属性里,把 Share 的头文件目录加到“附加包含目录”。如果 Share 还没编译,先编译 Share。顺序不能反。

5.3 客户端登录后一直转圈,服务端日志无报错

现象:客户端输入账号密码后卡在登录界面,服务端控制台没有任何错误输出。

原因:消息发出去了,但 GameServer 没注册对应的登录消息ID,或者注册的消息ID和客户端发的不一致。

解决:在内核日志里打开调试级别,看收到的消息ID是多少。然后去 GameServer 的注册代码里核对,确认这个ID有没有被注册。常见情况是客户端和服务端的协议版本不一致,消息ID对不上。

5.4 数据库查询返回乱码

现象:从数据库读出来的房间名显示为“???”或乱码。

原因:数据库连接字符集和表字符集不一致。比如表是 utf8mb4,连接用的是 latin1。

解决:在数据库连接串里显式指定字符集,比如charset=utf8mb4。MySQL 的配置文件里也要确认character-set-server=utf8mb4。建表时用DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci。

5.5 GameServer 启动后内核日志显示“register timeout”

现象:GameServer 启动时连不上内核,或者连上了但注册超时。

原因:内核的 IP 和端口配置和 GameServer 里写的不一致,或者防火墙拦了。

解决:先确认两个配置文件里的内核地址和端口完全一致。然后在 GameServer 所在机器上 telnet 一下内核的端口,看能不能通。不通就检查防火墙规则。如果内核和 GameServer 不在同一台机器,还要确认内核监听的是 0.0.0.0 而不是 127.0.0.1。

6. 二次开发:加一个新玩法模块的完整思路

6.1 从消息ID规划开始

假设要加一个“牛牛”玩法。第一步不是写代码,而是规划消息ID。先看现有模块占了哪些段,找一个没用的段,比如 0x3000-0x3FFF。然后列出这个玩法需要的消息:

消息ID方向说明
0x3001C→S请求进入牛牛房间
0x3002S→C返回房间信息
0x3003C→S准备
0x3004S→C发牌
0x3005C→S下注
0x3006S→C结算

消息ID规划好之后,写一个协议文档,客户端和服务端都按这个文档来。别小看这一步,我见过太多因为消息ID冲突导致客户端收到错误包然后崩溃的情况。

6.2 实现消息处理类

在 GameServer 里新建一个NiuniuModule类,继承自GameModule基类。在Init方法里注册所有消息处理函数。

class NiuniuModule : public GameModule { public: void Init(MessageDispatcher* dispatcher) override { dispatcher->Register(0x3001, [this](int sid, const char* body, int len) { OnEnterRoom(sid, body, len); }); dispatcher->Register(0x3003, [this](int sid, const char* body, int len) { OnReady(sid, body, len); }); dispatcher->Register(0x3005, [this](int sid, const char* body, int len) { OnBet(sid, body, len); }); } private: void OnEnterRoom(int sessionId, const char* body, int len) { // 解析请求,查数据库,返回房间信息 // 把玩家加入房间对象 } void OnReady(int sessionId, const char* body, int len) { // 标记玩家准备 // 如果所有人都准备了,开始发牌 } void OnBet(int sessionId, const char* body, int len) { // 扣筹码,记录下注 // 所有人下注完,结算 } };

Init里用 lambda 把成员函数包起来注册,这样代码比较紧凑。OnEnterRoom里要做参数校验,比如房间号是否存在、玩家筹码够不够。校验不过就回错误码,别让非法请求进到逻辑层。

6.3 房间状态机与定时器

牛牛玩法需要定时器:下注倒计时、发牌动画延时、结算延时。内核的定时器模块可以注册一次性或周期性任务。

// 注册一个 5 秒后执行的下注超时任务 Kernel::Timer::Add(5000, [this, roomId]() { Room* room = GetRoom(roomId); if (room && room->state == RoomState::BETTING) { room->ForceSettle(); // 超时强制结算 } });

定时器的回调里要先检查房间状态,因为可能在这 5 秒内玩家已经全部下注完,房间状态已经变了。如果不检查,会出现重复结算的 bug。

6.4 验证方法:用日志和断点走一遍完整流程

新模块写完后,别急着上客户端。先用一个简单的测试脚本模拟客户端发消息,走一遍完整流程:

import socket import struct def send_msg(sock, msg_id, body): # 假设包头是 4 字节长度 + 4 字节消息ID packet = struct.pack('<II', len(body) + 8, msg_id) + body sock.send(packet) s = socket.socket() s.connect(('127.0.0.1', 8000)) # 发送进入房间请求 send_msg(s, 0x3001, b'\x00\x01') # 房间号 1 resp = s.recv(1024) print('enter room resp:', resp.hex()) # 发送准备 send_msg(s, 0x3003, b'') resp = s.recv(1024) print('ready resp:', resp.hex())

跑这个脚本,观察服务端日志。每个消息处理函数入口打一条日志,出口打一条。如果某个函数没打日志,说明消息没分发到。如果打了入口没打出口,说明函数中间卡住了,大概率是数据库查询阻塞或者死锁。

我一般会在关键路径上加断点,单步走一遍。虽然慢,但能发现很多并发问题。比如两个玩家同时准备,房间状态从“等待”变成“发牌”的瞬间,如果没加锁,可能会发两次牌。

6.5 一个容易忽略的细节:协议兼容

新模块上线后,老客户端可能还在用旧协议。如果消息ID段和旧模块有重叠,老客户端会收到它不认识的消息,然后解析出错。解决办法是在消息头里加一个版本号,服务端根据版本号决定发哪种格式的包。或者干脆把新玩法的消息ID段和老模块完全隔开,老客户端收到不认识的消息ID直接丢弃,不崩就行。

从那以后我每次加新模块,都强制走一遍“消息ID规划 → 协议文档 → 服务端实现 → 测试脚本验证 → 客户端联调”的流程,不再跳步。希望帮到你。

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

相关新闻

基于Matlab GUI的颗粒自动计数与图像处理实现详解

基于Matlab GUI的颗粒自动计数与图像处理实现详解

1. 项目思路与整体设计做颗粒自动计数这件事&#xff0c;最早是因为帮一个做材料检测的朋友看电镜图像。他那边每天要数几百张图里的小球颗粒&#xff0c;传统做法是人工在图上点标记&#xff0c;一张图少说几百个颗粒&#xff0c;点完眼睛都花了。后来我查了一圈现成工具&…

2026/10/20 7:44:12 阅读更多 →
WinForms DataGridView 树形列表实现:数据拍平、缩进渲染与展开状态持久化

WinForms DataGridView 树形列表实现:数据拍平、缩进渲染与展开状态持久化

简介&#xff1a;这份资源面向C# WinForms开发者&#xff0c;尤其是需要在Windows Forms应用中展示层次化数据的初中级程序员&#xff0c;解决DataGridView原生不支持树形结构的问题。教程以Visual Studio 2012为环境&#xff0c;通过自定义树节点模型、扩展DataGridView控件、…

2026/10/20 7:44:12 阅读更多 →
波分场景下4G反开站RRU断链与误码排查实战

波分场景下4G反开站RRU断链与误码排查实战

凌晨两点&#xff0c;值班手机连续弹出几条告警&#xff0c;一看就是反开站点出事了&#xff1a;A区域的4G反开站RRU断链、B区域两个小区上报光路误码率超阈值。这类告警在现网里并不少见&#xff0c;通常单个站点断链也就当普通光模块故障处理了&#xff0c;但这次几个站点同时…

2026/10/20 7:44:12 阅读更多 →

最新新闻

手写JSON解析器:从词法分析到语法树的编译原理实战

手写JSON解析器:从词法分析到语法树的编译原理实战

1. 从一段JSON文本到内存对象&#xff1a;解析器到底在做什么很多人第一次接触编译原理&#xff0c;都是从"写一个JSON解析器"开始的。这个选择很聪明——JSON的语法足够简单&#xff0c;简单到一页纸就能写完文法&#xff1b;但它又足够完整&#xff0c;完整到能覆盖…

2026/10/21 5:11:04 阅读更多 →
Java俄罗斯方块源码解析:游戏主循环、碰撞检测与消行实现

Java俄罗斯方块源码解析:游戏主循环、碰撞检测与消行实现

简介&#xff1a;这是一份面向Java初学者与游戏编程爱好者的俄罗斯方块完整源码&#xff0c;以Java语言实现&#xff0c;适合用来理解经典游戏的核心逻辑与面向对象设计。源码围绕游戏主面板、方块形状、流程控制器与程序入口等关键类展开&#xff0c;涵盖游戏循环、方块生成与…

2026/10/21 5:11:04 阅读更多 →
NLP与无人船:从自然语言指令到自主靠泊的完整技术链路

NLP与无人船:从自然语言指令到自主靠泊的完整技术链路

去年我带着组里的几位同学把一个挺有意思的项目从零做到了仿真验证——给一艘无人船加一套自然语言操控链路&#xff0c;让操作员可以直接说“慢速驶向测试场北侧码头&#xff0c;左舷靠泊”或者“绕开右前方漂浮物&#xff0c;停到A区泊位”&#xff0c;船载系统自己去完成意图…

2026/10/21 5:11:04 阅读更多 →
鸿蒙+Flutter跨平台实战:桌面图标焕新方案全解析

鸿蒙+Flutter跨平台实战:桌面图标焕新方案全解析

桌面图标焕新大师这个名字&#xff0c;听起来像某个手机美化App的宣传语&#xff0c;但它本质上是一个很典型的跨平台实战项目&#xff1a;把视觉设计、资源管理、系统桌面机制和Flutter渲染能力串在一起&#xff0c;最终在鸿蒙设备上完成“一键切换桌面图标风格”的完整闭环。…

2026/10/21 5:11:04 阅读更多 →
从ReAct到龙虾架构:AI Agent编排演进与工程实践

从ReAct到龙虾架构:AI Agent编排演进与工程实践

这两年AI Agent架构的文章我看了不下几十篇&#xff0c;名字一个比一个唬人&#xff1a;ReAct、Plan-and-Execute、Reflexion、多Agent系统、图式编排、龙虾架构……但真能把“从A到B是怎么一步步走出来”讲明白的&#xff0c;确实不多。尤其对小程序员来说&#xff0c;最痛苦的…

2026/10/21 5:11:04 阅读更多 →
AI原生知识库重构:从传统检索到语义网络的实践解析

AI原生知识库重构:从传统检索到语义网络的实践解析

做了三年传统知识库&#xff0c;我一直在琢磨一个问题&#xff1a;资料越存越多&#xff0c;知识却越来越难找&#xff0c;团队知道的东西永远比系统记录的多。传统知识库的把式——目录分类、关键词搜索、标签管理——本质上是在用二维平面的方式梳理立体复杂的真实知识&#…

2026/10/21 5:10:03 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事&#xff1a;AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里&#xff0c;按宣传口径&#xff0c;本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”&#xff0c;而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起&#xff1a;用户到底在追求什么“封号重开”这四个字&#xff0c;放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流&#xff0c;背后是大量的时间和精力成本。正常情况下&#xff0c;没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”&#xff0c;而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时&#xff0c;就意识到&#xff1a;单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/21 4:27:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/20 9:50:45 阅读更多 →