<?xml version="1.0" encoding="gb2312"?>
<rss version="2.0">
   <channel>
      <title>云风的 BLOG</title>
      <link>http://blog.codingnow.com/</link>
      <description>思绪来得快去得也快，偶尔会在这里停留</description>
      <language>en</language>
      <copyright>Copyright 2010</copyright>
      <lastBuildDate>Thu, 02 Sep 2010 19:16:31 +0800</lastBuildDate>
      <generator>http://www.sixapart.com/movabletype/?v=3.2b5</generator>
      <docs>http://blogs.law.harvard.edu/tech/rss</docs> 

            <item>
         <title>backtrace-mingw 更新</title>
         <description><![CDATA[<p><a href="http://code.google.com/p/backtrace-mingw/">backtrace-mingw</a> 今天更新了一下。原来的版本不能正确显示 dll 里的符号信息。现在可以了。只是打了个补丁，所以代码比较乱。</p>

<p>不知道 backtrace-mingw 的同学，可以<a href="http://blog.codingnow.com/2010/07/mingw_stack_backtrace.html">看这里</a> 。</p>
]]></description>
         <link>http://blog.codingnow.com/2010/09/update_backtrace-mingw.html</link>
         <guid>http://blog.codingnow.com/2010/09/update_backtrace-mingw.html</guid>
         <category>技术</category>
         <pubDate>Thu, 02 Sep 2010 19:16:31 +0800</pubDate>
      </item>
            <item>
         <title>从数组里删除一个元素</title>
         <description><![CDATA[<p>去年介绍过我在项目中实现的一个<a href="http://blog.codingnow.com/2009/11/array_c.html">动态数组模块的接口</a>。</p>

<p>实际上，我为它提供的接口要更多一些，比如删除一个元素。</p>

<pre>
  void array_erase(struct array *, seqi iter);
</pre>

<p>原来的语义就是删除 iter 引用的元素。但这里引出一个问题：删除后，iter 是否应该保持有效？</p>

<p>从语义上说，iter 应该在调用完毕后变成一个无效引用。但实际应用中，往往需要在迭代 array 的过程中，删除符合条件的元素。让迭代器失效的做法，用起来很不方便。</p>
]]></description>
         <link>http://blog.codingnow.com/2010/08/array_erase.html</link>
         <guid>http://blog.codingnow.com/2010/08/array_erase.html</guid>
         <category>语言与设计</category>
         <pubDate>Tue, 31 Aug 2010 16:14:32 +0800</pubDate>
      </item>
            <item>
         <title>在游戏引擎中播放视频</title>
         <description><![CDATA[<p>美术同学给我们的游戏做了段片头视频，正要加到产品中去时，才发现我们的引擎居然没有提供视频播放的功能。我想这个东西开源库一大堆，那做起来还不是小菜一碟。可没想到还是折腾了一整天才搞定。</p>

<p>第一件事是考察 License 。结果用的人做多的 <a href="http://www.ffmpeg.org">ffmpeg</a> 是 GPL 的，不适合我这种商业应用。虽然有一部分功能可以以 LGPL 的方式使用，但是<a href="http://www.ffmpeg.org/legal.html">遵守起来也是麻烦一大堆</a>。想了一下还是做罢。我可不想学暴风影音和 QQ Player 那样上<a href="http://www.ffmpeg.org/shame.html">耻辱榜</a>。</p>

<p>最终考察结果，居然没太多的选择。还是 google 的 vp8 最好。License 最为宽松，所以就去下载了一份 <a href="http://www.webmproject.org/code/">libvpx</a> 的最新版试用。</p>
]]></description>
         <link>http://blog.codingnow.com/2010/08/libvpx.html</link>
         <guid>http://blog.codingnow.com/2010/08/libvpx.html</guid>
         <category>游戏开发</category>
         <pubDate>Tue, 31 Aug 2010 01:19:08 +0800</pubDate>
      </item>
            <item>
         <title>记一个 Bug</title>
         <description><![CDATA[<p>今天周末，<a href="http://bg.codingnow.com">桌游店</a>里却没客人，昨天打电话预约的朋友没来，所以我就奔到办公室测试上周写的代码。</p>

<p>上周的工作主要是<a href="http://blog.codingnow.com/2010/08/resource_pack.html">设计了一个新的包格式</a>，然后整合入前段时间实现的<a href="http://blog.codingnow.com/2010/04/vfs.html">虚拟文件系统</a>中。</p>

<p>这个工作和前段实现的 zipfs 有相似之处，所以做起来也很快。不过前面没仔细测试。今天比较闲，就设计了几组复杂的测试数据，感觉覆盖了各种边界情况。一测试果然发现了 Bug 。</p>

<p>这个 Bug 有点启发意义，所以在解决掉之后，决定记录一下。</p>
]]></description>
         <link>http://blog.codingnow.com/2010/08/bug.html</link>
         <guid>http://blog.codingnow.com/2010/08/bug.html</guid>
         <category>技术</category>
         <pubDate>Sat, 28 Aug 2010 23:30:50 +0800</pubDate>
      </item>
            <item>
         <title>游戏资源的压缩、打包与补丁更新</title>
         <description><![CDATA[<p>9 年前，我设计了网易游戏的资源包以及补丁包的数据格式。</p>

<p>当初的设计目的是：方便解析，快速定位资源包内的文件，方便更新、每次更新尽可能的节约带宽。这些年来，虽然各个项目修修补补的改进了资源包的格式，但本质上并没有特别大的修改。</p>

<p>一开始我们直接把需要打包的文件连接起来，在文件末尾附上文件索引表。当初为了快速定位文件名，文件名做了 hash 处理，可以用 hash 值直接定位文件。而资源包里并没有储存文件名信息，而是保存在一个额外的 index 文件中。这个 index 文件并不对外发布。所以直接对资源包解包是无法准确还原文件名的。</p>

<p>btw, 暴雪的 mpq 文件也是作类似处理的。除非你猜测出文件名，否则也很难对文件名还原。网上许多 mpq 解包工具都针对特定游戏附了一个额外的文件名列表。</p>

<p>和许多其它游戏 Client （比如暴雪的 MPQ 文件）不同。我们的包格式里文件与文件之间是允许有空洞的。这是考虑到资源包文件都比较大。如果用传统的打包软件运作的方式：从包内删除一个文件，就重新打包或移动内部数据。在玩家更新资源的时候，就会有大量的文件 IO 操作。比如 WOW 或 SC2 在更新的时候，下载更新包的时间往往只占整个更新时间的一小部分，大部分时间花在把补丁打在已有的资源包上。</p>

<p>如果频繁更新客户端，对于用户，这会有很讨厌的等待。</p>

<p>所以当初考虑到这个因素，我们在删除包内文件时，并不移动资源包内的数据，而是把空间留下来。如果新增加的文件较之小，就重复利用这个空间。如果利用不上，就浪费在那里。这有点像内存管理算法，时间久了，资源包内会有一些空洞，但也是可以接受的。</p>

<p>同时，还有另一个方式更新新的资源。那就是将需要更新的文件单独打包，以相同文件名（后缀不同）保存在用户硬盘上。游戏引擎在读取资源的时候，优先在更新的资源包内检索。这个方式在 Id soft 的 Quake/Doom 系列中也有采用。</p>

<p>为了保证用户补丁更新速度。我们的补丁中并不是保存的资源包内的小文件。而是在开发机上以增量方式重新打包。补丁文件其实是整个资源包的 diff 文件。由于前面所述的打包方案，这个 2 进制 diff 文件其实可以做到很小。尤其对某些文件的局部修改，对整个资源包的影响很小。</p>

<p>在公司，有后来的同事质疑过这种方式，觉得其对减少补丁体积的作用不大。反而增量打包增加了许多制作补丁包的时间。主张直接在补丁中放入更新的小文件，然后让最最终用户机上以小文件为单位做 patching 。</p>

<p>的确，2 进制 diff 的作用有限，现在很多项目改用文本数据格式，很小的修改就会影响整个文件的 diff 结果。不过原始的设计也有其历史原因。因为 10 年前硬盘 I/O 速度很慢，而大话西游在设计时又需要实现无缝加载的大地图。所以地图文件的格式是经过特别设计的。这种方式很适合地图文件的修改和更新。另外，对于未压缩的图片文件的更新也有其意义。</p>
]]></description>
         <link>http://blog.codingnow.com/2010/08/resource_pack.html</link>
         <guid>http://blog.codingnow.com/2010/08/resource_pack.html</guid>
         <category>游戏开发</category>
         <pubDate>Thu, 26 Aug 2010 01:40:19 +0800</pubDate>
      </item>
            <item>
         <title>继续完善 protobuf 库</title>
         <description><![CDATA[<p>又仔细推敲了两天，把 lua 版的 protobuf 库完善了一下。主要是做了两个工作：</p>

<ol>
<li><p>protobuf 本身的格式，google 是自描述的。定义为 google.protobuf.descriptor 。我先自己实现的 parser 图方便，用了自己的中间交换格式。为了日后更通用，稍微修改了一下，可以生成于官方相同的结构。解析的性能稍有下降，不过应该兼容性更好。</p></li>
<li><p>一开始实现的 api 虽然性能非常好。（经简单测试，是 python 库的 30~40 倍，和 C++ 库性能相当）但若消息格式复杂，实现起来稍有麻烦。所以我做了点小封装。即为每个消息生成一对函数，可以用来打包和解包完整的消息，映射到 lua 的 table 结构上。lua 生成代码供自己调用的技巧在 lua 社区广泛使用。比如 kepler 项目。这使得 lua 可以用很短的代码行数完成很复杂的工作，不失性能。我这个封装层只有 100 行代码左右，一大半代码是为了解决消息展开时有递归定义的情况，否则更简短。（message 中有一些 field 的类型是自己，这是一种不多见的用法，但 protobuf 似乎并没有拒绝这种用法）</p></li>
</ol>
]]></description>
         <link>http://blog.codingnow.com/2010/08/protobuf_for_lua.html</link>
         <guid>http://blog.codingnow.com/2010/08/protobuf_for_lua.html</guid>
         <category>lua与虚拟机</category>
         <pubDate>Wed, 18 Aug 2010 00:09:51 +0800</pubDate>
      </item>
            <item>
         <title>Proto Buffers in Lua</title>
         <description><![CDATA[<p><a href="http://www.tektalk.org/2010/08/04/%E6%9D%A5%E8%87%AAjeff-dean%E7%9A%84%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/">Google 的 Jeff Dean 同学说，设计分布式系统一定要有 Protocol Description Language</a>。</p>

<p><a href="http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/overview.html">Google Proto Buffers</a> 的意义在于，定义了一个不错的 PDL 。protobuffers 的实现反而不那么重要了。</p>

<p>这几天我一直在倒腾 lua 下的 proto buffers 的支持。一直在思考，怎样的接口才是最适合 lua 使用的。</p>

<p>大多数语言下的 proto buffers 实现，都是将编码的数据块展开成本地语言的数据结构。对于 C/C++ ，这是最高效的形式。但对于动态语言，那就未必了。虽然 google 为 python 做的 proto buffers 的官方实现也是如此，但我依然想考虑一下，是否有更高效的方式来做这件事。</p>
]]></description>
         <link>http://blog.codingnow.com/2010/08/proto_buffers_in_lua.html</link>
         <guid>http://blog.codingnow.com/2010/08/proto_buffers_in_lua.html</guid>
         <category>lua与虚拟机</category>
         <pubDate>Wed, 11 Aug 2010 00:06:18 +0800</pubDate>
      </item>
            <item>
         <title>Windows 下调试问题一则</title>
         <description><![CDATA[<p>今天跑昨天晚上写的一个小程序，莫名其妙的不能运行。开 gdb 调试没有任意异常，直接退出，连 main 函数都没有进。注释掉大量代码后依旧。把 gdb 从 6.3 升级到 6.8 后，再调试，捕获了一个不知名的异常。</p>

<p>又把代码注释干净，程序正常了。仔细考虑了一下，最后注释掉的代码间接 link 了 lua 的库。而 lua 的库引用了 lua 的动态库。程序运行时可能是因为没有找到 dll 而退出。</p>

<p>把 lua 的 dll 加到 path 中就正常了。但是奇怪的是，系统并没有弹出对话框提示。仔细想了一下，原来是我没有从 explore 启动控制台，而是在编辑器里启动的，可能是编辑器软件没有写好，把本应该系统弹出的缺少 dll 的对话框拦截了。</p>

<p>Soloist 同学说，他也碰到过一次 Total Commander 吃掉缺少 dll 无法启动程序的对话框。记录一下这个问题，祭奠我找问题逝去的半小时。</p>
]]></description>
         <link>http://blog.codingnow.com/2010/08/debug_in_windows.html</link>
         <guid>http://blog.codingnow.com/2010/08/debug_in_windows.html</guid>
         <category>技术</category>
         <pubDate>Fri, 06 Aug 2010 12:25:17 +0800</pubDate>
      </item>
            <item>
         <title>mingw 下的 stack backtrace</title>
         <description><![CDATA[<p>我们的项目的 Windows 版本是用 MinGW 开发的。当程序在 Windows 下挂掉后，固然可以用 gdb 调试，看到调用栈。但有些时候还是不够方便。</p>

<p>比如说今天，我们写的模型编辑器发到广州美术同事使用时，就出了问题。3d 程序在不同显卡环境下的确容易出故障，异地调试程序非常困难。这个时候，多么想看看调用栈啊。</p>

<p>The GNU C Library 是提供了 <a href="http://www.delorie.com/gnu/docs/glibc/libc_665.html">Backtraces</a> 的，可惜 MinGW 不支持 :( 。最后打算自己写一个。</p>
]]></description>
         <link>http://blog.codingnow.com/2010/07/mingw_stack_backtrace.html</link>
         <guid>http://blog.codingnow.com/2010/07/mingw_stack_backtrace.html</guid>
         <category>技术</category>
         <pubDate>Wed, 28 Jul 2010 18:01:25 +0800</pubDate>
      </item>
            <item>
         <title>换了个新手机</title>
         <description><![CDATA[<p>我的那个 Palm Treo 650 终于寿终正寝了。去年曾经买了个 HTC Hero 送老爸，玩过几天 Andriod ，还是有点好感，这次打算选一款 Andriod 系统的。</p>

<p>曾经也考虑过 iPhone 或是 Palm Pre ，请教了我公司的手机大神，大神同学说，他买过两款 iPhone ，用一段时间总想换，不推荐长期使用。至于 Palm Pre ，大神同学也买过一个，评价是极其垃圾，完全比不上 Treo 系列。他最近半年买过 Milestone ，Desire ，Nexus One ，X10i ，i9000 等等几款，每个都有不同的缺憾，最终比较下来，给我推荐了三星 i9000 。</p>

<p>我比较信任亲身使用者的感受，尤其是对手机如此挑剔，以至于每一两个月就情不自禁的买新手机的同学的现身说法。没太多犹豫就去 taobao 下了单。</p>

<p>结果…… 等了 8 天还是没寄来，卖家一直说缺货。昨天忍不住退了款，还是很干脆的。再一看，原来是涨价了，从 3450 涨到了 3600 。换了一家重新下单，今天一早就寄到了。</p>
]]></description>
         <link>http://blog.codingnow.com/2010/07/cellphone.html</link>
         <guid>http://blog.codingnow.com/2010/07/cellphone.html</guid>
         <category>我爱折腾</category>
         <pubDate>Tue, 27 Jul 2010 21:41:05 +0800</pubDate>
      </item>
            <item>
         <title>游戏多服务器架构的一点想法</title>
         <description><![CDATA[<p>把网络游戏服务器分拆成多个进程，分开部署。这种设计的好处是模块自然分离，可以单独设计。分担负荷，可以提高整个系统的承载能力。</p>

<p>缺点在于，网络环境并不那么可靠。跨进程通讯有一定的不可预知性。服务器间通讯往往难以架设调试环境，并很容易把事情搅成一团糨糊。而且正确高效的管理多连接，对程序员来说也是一项挑战。</p>

<p>前些年，我也曾写过好几篇与之相关的设计。这几天在思考一个问题：如果我们要做一个底层通用模块，让后续开发更为方便。到底要解决怎样的需求。这个需求应该是单一且基础的，每个应用都需要的。</p>

<p>正如 TCP 协议解决了互联网上稳定可靠的点对点数据流通讯一样。游戏世界实际需要的是一个稳定可靠的在游戏系统内的点对点通讯需要。</p>

<p>我们可以在一条 TCP 连接之上做到这一点。一旦实现，可以给游戏服务的开发带来极大的方便。</p>
]]></description>
         <link>http://blog.codingnow.com/2010/07/game_network.html</link>
         <guid>http://blog.codingnow.com/2010/07/game_network.html</guid>
         <category>游戏开发</category>
         <pubDate>Mon, 19 Jul 2010 23:07:54 +0800</pubDate>
      </item>
            <item>
         <title>C 语言中统一的函数指针</title>
         <description><![CDATA[<p>有时候，我们需要把多个模块粘合在一起。而这些模块的接口参数上有少许的不同。在 C 语言中，参数（或是返回值）不同的函数指针属于不同的类型，如果混用，编译器会警告你类型错误。</p>

<p>在 C 语言中，函数定义是可以不写参数的。比如：</p>

<p>void foo();</p>

<p>这个函数定义表示了一个返回 void 的函数，参数未定。也就是说，它是个弱类型，诸如：</p>

<p>void foo(int);</p>

<p>void foo(void *);</p>

<p>这些类型都可以无害的转换成它。正如在 C 语言中，具体的指针类型如 int * ，char * 都可以转换为 void * 一样。</p>

<p>注1：如果要严格定义一个无参数的函数，应该写成 void foo(void);</p>

<p>注2：如果有部分参数固定，而其后的参数可变，则定义看起来是这样： void foo(int , ...); 这表示第一个参数为 int ，从第 2 个参数开始可变。</p>
]]></description>
         <link>http://blog.codingnow.com/2010/07/function_c.html</link>
         <guid>http://blog.codingnow.com/2010/07/function_c.html</guid>
         <category>语言与设计</category>
         <pubDate>Mon, 12 Jul 2010 20:45:36 +0800</pubDate>
      </item>
            <item>
         <title>区分一个包含汉字的字符串是 UTF-8 还是 GBK</title>
         <description><![CDATA[<p>今天检查 svn 仓库，发现又有同学没按规定提交包含汉字的代码。我们规律，所有源文件中包含的汉字必须使用 UTF-8 编码方式，而不能使用 GBK 。</p>

<p>总这么人工检查也不是个事。所以我想写一个 svn 的钩子，在提交前检查。在仓库的 hooks/pre-commit.teml 加一行检查脚本应该就可以了。</p>

<p>我想用正则表达式匹配一下，可是想了想又觉得 UTF-8 和 GBK 的编码集有点交集，不太好做。btw, google 了一下，的确有人写过<a href="http://www.dnbcw.com/biancheng/php/hzko119744.html">特定编码的正则表达式</a>。</p>

<p>继续 google ，找到一篇跟我需求有点类似的文章<a href="http://www.kuqin.com/language/20071201/2740.html">UTF-8编码检测失败特例</a>。看了正文，觉得不太靠谱，然后继续看回复，觉得这方法可行。</p>

<p>然后定睛一看，原来文章是孟岩写的，回复是我自己三年多前回复在他的 blog 上的。 -_-</p>
]]></description>
         <link>http://blog.codingnow.com/2010/06/detect_utf-8_gbk.html</link>
         <guid>http://blog.codingnow.com/2010/06/detect_utf-8_gbk.html</guid>
         <category>技术</category>
         <pubDate>Tue, 29 Jun 2010 15:11:00 +0800</pubDate>
      </item>
            <item>
         <title>C 语言的前世今生</title>
         <description><![CDATA[<p>本篇是应《程序员》杂志约稿所写。原本要求是写篇谈 C 语言的短文。4000 字之内 。我刚列了个提纲就去了 三千多字。 -_-</p>

<p>现放在这里，接受大家的批评指正。勿转载。</p>
]]></description>
         <link>http://blog.codingnow.com/2010/06/c_programming_language.html</link>
         <guid>http://blog.codingnow.com/2010/06/c_programming_language.html</guid>
         <category>语言与设计</category>
         <pubDate>Mon, 28 Jun 2010 22:46:20 +0800</pubDate>
      </item>
            <item>
         <title>把 vfs 实现好了</title>
         <description><![CDATA[<p>极尽简洁，然过犹不及(As simple as possible, but not simpler.)——爱因斯坦</p>

<p>这段时间的工作是把上次提到的 <a href="http://blog.codingnow.com/2010/04/vfs.html">VFS</a> 系统实现了。而写这篇 Blog 的促因是 twitter 上有同学想让我谈谈对 <a href="http://www.realworldtech.com/forums/index.cfm?action=detail&amp;id=110618&amp;threadid=110549&amp;roomid=2">Linus 最近的一篇老生常谈</a>的看法。哦，看似既然是语言之争。C 好，还是 C++ 好。但这次他平和了许多。Linus 惯有风格依旧，但少了些须<a href="http://blog.codingnow.com/2007/09/c_vs_cplusplus.html">三年前的争论</a> 中的刻薄。</p>

<p>我想说，<a href="http://blog.codingnow.com/2009/01/the_new_c_standard.html">C 的三个特质(见引用文最后一段)</a> 哪一点都不可忽略。Linus 这次强调的大约是第三点，也是 C++ 程序员们不屑一顾的一点。可对于多人协作构建的项目，这一点实在是太重要了。这并不是人人都聪明就能回避的问题。如果程序员们都足够睿智，反而更能意识到沟通之成本。其实即使是你一个人在做整个项目，从前的你和现在的你以及将来的你，同样有沟通（记忆）的成本。人不可能两次踏进同一条河流。</p>
]]></description>
         <link>http://blog.codingnow.com/2010/06/vfs_implemention.html</link>
         <guid>http://blog.codingnow.com/2010/06/vfs_implemention.html</guid>
         <category>优化与技巧</category>
         <pubDate>Sun, 13 Jun 2010 15:16:32 +0800</pubDate>
      </item>
      
   </channel>
</rss>
