您现在的位置:首页
--> 云风的 BLOG
在 skynet 中,有时候为一个服务实现一个前置的代理服务是很有必要的。
比如,你希望对这个服务发起的请求是支持超时的,就不必在功能实现的服务中实现,那样会增加无谓的复杂性。你可以在功能实现的服务前加一个代理服务,当超时发生时,通知请求方。关于这个实现,我在 blog 中给过一个示例。
同理,当你需要做一些负载均衡的处理的时候,也可以做一个代理服务,让请求分摊到多个可以完成类似功能的服务中去,实现比较简单,本文就不展开了。
今天想谈一下怎么利用代理服务更好的为一些热点服务提供过载保护。
作为设计理想,玩家不会成为一个整体,他们之间有竞争。更高的名次有更高的奖励,所以不会轻易降分。
但事实上,由于玩家可以充分的沟通相互信任,不会陷入囚徒困境。竞争多花的钱而获得的高名次奖励,远远比不上交替名次而获得的系统额外发放。另外,在原来的低名次段,原本没有名次奖励的位置,也可以通过操纵排名而获益。
Lua 作为一门嵌入式语言,提供了完备的 C API 供 Lua 代码和宿主程序交互,当然,宿主语言最好是 C 或 C++ 。如果是其它语言,比如最近两年流行的在 mono 环境嵌入 Lua 另当别论。
正确将 Lua 嵌入是不太容易做对的事情,很多刚接触 Lua 的人都容易犯错误。好在做这种语言桥接工作都是项目开始阶段的设计者做的,不必人人学会,所以只要有熟悉 Lua 的人来搞,犯错误的危害不会太大。而且即使做的有问题,日后修改也比较容易。这篇 blog 主要就是谈谈,最容易做错的位置,和一些正确(但看起来麻烦)的实现方法。
这几天 AlphaGo 4:1 战胜了李世石,围棋积分网站 给了 AlphaGo 一个世界排名。在 3:1 的时候是世界第四,4:1 后上升到了世界第二。
我很好奇这个网站是如何给棋手打分的,便顺着链接指引翻了下 wikipedia 读了几篇 paper 。
下面是我的一些理解,由于我的数学基本功不太扎实,难免有错误,所以有兴趣的同学最好自己读论文 。
本文分三个部分:一,什么时候有可能采用 UDP 通讯而不是用 TCP 更好;二,一个可靠的 UDP 通讯模块的 API 接口该如何设计;三,一个简单的实现。
过年回武汉家中,只有一台 2000 块的一体机可以用,自然是跑不动 3d 游戏的。我挑了一款 Invisible Inc 玩,居然很流畅。
这是款 XCOM like 的策略游戏,场景是 isometric 的。但是用 Q E 两个键可以旋转场景,虽然静止下来只有四个方向,但是旋转过程却以动画方式呈现。这让我一度认为它的场景是基于 3d 模型制作的。但令人惊讶的是,在低配置的一体机上却非常流畅。相比较,我最近在玩的 XCOM 2 却在 GTX 550 显卡上也有卡顿。
我仔细观察后,发现其实 Invisible Inc 的场景里的物件都只是 2D 图片。甚至只有正面和背面两张图,通过左右镜像得到了四个视角。它的旋转速度很快,并加了一定的模糊效果,欺骗了人眼。其实只有地板和墙壁是真正旋转的,其它物件只有坐标在做旋转,而图片本身在旋转过程中并没有变化。
也就是说,它的引擎其实是基于 2D 制作的,模拟出了 3d 才有的视觉效果。
skynet 本质上只是一个消息分发器,以服务为单位,给每个服务一个独立的 id ,可以从任意服务向另一个服务发送消息。
在此基础上,我们在服务中接入 Lua 虚拟机,并将消息收发的 api 封装成 lua 模块。目前用 lua 编写的服务在最底层只有一个入口,就是接收并处理一条 skynet 框架转发过来的消息。我们可以通过 skynet.core.callback (这是一个内部 API ,用 C 编写,通常由 skynet.start 调用)把一个 lua 函数设置到所属的服务模块中。每个服务必须设置,且只能设置一个回调函数。这个回调函数在每次收到一条消息时,接收 5 个参数:消息类型、消息指针、消息长度、消息 session 、消息来源。
• 资源包的设计
一般游戏会把所需要资源数据打包成一个大文件,游戏运行时可以像访问普通文件一样,访问包内文件。如何打包如何更新资源包,是设计重点。
现在有很多资源包直接使用通用打包(压缩)格式,比如 zip 。也有自行设计的,多半是为了一些特殊需求。比如资源间有引用关系等。如果资源数量过多,通常还会对原始资源文件名做一次 hash 索引,加快包内文件检索效率。像暴血的 mpq 格式,还有 unity3d 的 asset bundle 格式都是这样的。
一旦资源打包,原始文件名信息就不再需要了。应用程序可以在运行时通过文件名的 hash 值索引到包内文件。(所以第三方的 mpq 解包工具需要提供一份额外的文件名列表)
既然是 hash ,那么就应该在包格式设计中考虑 hash 冲突的解决方案。
苹果从来都不赞成第三方 app 自己搞一套生态的原因之一:你没有系统级的权限,本身就在安全性上打了折扣。
但是,你的 apple (icloud) id 及其密码到底面临多大威胁?我认为危险是完全可控的。
首先:在 ios 系统中,icloud 密码是很高优先级的东西,ios 设计人员再傻也知道要重点保护它。所以,如果通过正常的渠道,在 ios 中输入密码,即使是通过被感染的 app ,也是不可能通过程序获取到 icloud 密码的。唯一的手段是模拟一个输入密码的对话框引诱用户输入密码来钓鱼。
skynet 中使用了一个 hash 表结构来保存服务和 32bit 数字地址的映射关系。
一个 skynet 的服务,其实是一个 C 对象。在有沙盒的系统中,尤其是并行构架,我们很少直接用 C 对象指针来标识一个 C 对象,而是采用数字 id 。用数字做 handle 可以让系统更健壮,更容易校验一个对象是否还有效,还可以减少悬空指针,多次释放对象等潜在问题。比如,操作系统为了把用户程序隔离在用户态,像文件对象就是用数字 id 的形式交给用户程序去用的。
skynet 提供了一个 lua 库 netpack ,用来把 tcp 流中的数据解析成 长度 + 内容的包。虽然使用 skynet 的同学可以不使用它,而自己另外实现一套解析协议来解析外部 TCP 数据流(比如 skynet 中的 redis driver 解析 redis server 的数据流就是用的换行符分割包),但依然有很多同学询问,能不能自定义包头长度。
去赌场参观过的同学应该都见过那种押大小的骰子游戏。庄家投掷三枚骰子,把骰盅盖住,玩家可以押大或小。开盅后,如果发现三个数字之和大于等于 11 就算大,小于等于 10 就算小。如果你猜对了,庄家就 1 赔 1 算给你筹码;否则输掉筹码。另外,还可以以不同赔率压数字,或压三个相同。
为了保障庄家利益,三个相同的数字算不大不小。从概率上讲,这让长时间内庄家必胜。概率分析见这里。
如果把这个游戏搬到网络上如何呢?
我们的游戏中需要对渲染字体做勾边处理,有种简单的方法是将字体多画几遍,向各个方向偏移一两个像素用黑色各画一遍,然后再用需要的颜色画一遍覆盖上去。这个方法的缺点是一个字就要画多次,影响渲染效率。
• 回调还是消息队列
socket 底层有一个 poll 的 api ,通过 epoll 或 kqueue 或 select 取得一系列的事件。用 lua 怎么封装它呢?一个比较直接的想法是注入一个 callback function ,对于每个事件回调一个 lua 函数。但这容易引起许多复杂的问题。因为回调函数很不可控,内部可能抛出异常,也可能引起函数重入,或是做了一些你不喜欢去做的事情。
Objective-C 和 C++ 同样从兼容 C 语言开始,以给 C 语言增加面向对象为初衷,他们的出现的时间都很类似(1983 年左右)。但面向对象编程的源头却不同:C++ 受 Simula 和 Ada 的影响比较多,而 Objective-C 的相关思想源至 Smalltalk ,最终的结果是他们在对象模型上有不小的差异。
在《游戏人工智能编程案例精粹 》 和 《 Windows 游戏编程大师技巧 》 中都分别有一章谈及模糊逻辑。记得前几年我的同事 Soloist 同学曾经研究过一小段时间,给我做过简单介绍,我便仔细把这两章书读了一遍。感觉都是点到为止,所以又翻了一下 Wikipedia 的 Fuzzy Logic 的介绍。午饭时跟做 AI 的同事交流了一下,觉得可以做一点笔记记录理解的部分。
• 内存异常排查
一个层次分明的系统,在物理上就应该是相互隔离的,这种隔离,仅仅存在于人阅读的源代码层是绝对不够的。这就好比 OS 管理下的应用进程,它绝对不依赖应用进程的程序的工作正常,不依赖应用进程准确的申请和释放资源。而是当应用进程结束后,干净的回收它申请过的所有东西。
• 房租分配问题
今天读到策划同学的周报中提到的一个关于合租房子的分摊房租问题。
引用周报中的一节如下:
上周在搬家,和喵、刘阳一起租房子住,遇到一个问题,就是分摊房租。中式的解决方法一般都是商量一下,但具体怎么商量,没有手段,总之就是大家估摸一下,觉得大略上说的过去就OK了。很少有拉下面子认真谈价格的,即使心里其实觉得并不认可。
在这方面,美国人还真能想一些办法,这是一个旅美的留学生在博客上写的,他和老美同学的商议方式:两个人A,B合租一个二居的房子,比如每个月是1500美元,因为主卧和次卧有大有小,价格肯定是不均的,那么两个人分别写两个价格,也就是对主卧和次卧的心理价格。可以很极端,比如1400:100,但总额必须是1500,因为这是A,B必须接受的大条件,然后公开,除掉开价完全相当的情况,两间卧室必然各有一个出价最高的人,价高者入住,而月租则是A,B对这个卧室开价的均值。例如A出价是900:600,B出价是1000:500,那么A住次卧,价格为550,B住主卧,价格为950。两个人都得到了自己认可的房子,而价格还低于自己的预期。
这一方案还有一个优势,就是双方都无法通过恶意的叫价来损害对方,获得利益。相信很多同学会提出一个更直接的解决方案:一个人提价格方案,另一个人选择。但是这一方案也有点问题,提价格的人相对是吃亏的,对吧?
遗憾的是,这种做法,似乎无法推广到三个人的情况。
这几天无意中发现一款开源的 3d engine ,名为 pixel light 。文档 虽然不多,但写的很漂亮。从源码仓库 clone 了一份,读了几天,感觉设计上有许多可圈可点的地方,颇为有趣。今天简略写一篇 blog 和大家分享。
昨天我们发现每日构建的服务器突然在一个晚上内存暴增了 8 G ,显然是发生了内存泄露。之前,我们在 skynet 里留下了许多调试协议,使我们很快的确定了发生泄露的服务:在一张地图的 lua State 中。可以确定是地图的 lua 实现中,有些 lua 对象在不断的生成。生成速度不快,但确实没有人解开引用,导致内存持续增长。
近3天十大热文
- [2527] 代理的加密部分
- [1327] 创业笔记 | 从0到1开公司是什么体验
- [646] vimgtd-在vim(gvim)中实现GT
- [570] 查找第K小的元素
- [70] Oracle MTS模式下 进程地址与会话信
- [65] 【社会化设计】自我(self)部分――欢迎区
- [65] Go Reflect 性能
- [61] 如何拿下简短的域名
- [61] 图书馆的世界纪录
- [61] android 开发入门
赞助商广告