业务在哪,核心就在哪

不久前,我在 GitHub 上看到某位开发者的自我介绍:

我的研究重点是高性能并发数据结构和算法、编程语言运行时(Go 和 Rust)、标准库。

这段话像一颗钉子,钉进我的脑海,之后便时常浮现。我羡慕他的目标如此明确。从职业发展的角度看,我认为他在第三阶段。

三个阶段

第一阶段:我能做什么? 刚毕业,保持好奇心,追求广度。

第二阶段:我在什么业务领域最有价值? 工作三到五年,开始深耕某个业务领域。比如游戏——我很庆幸,当年选择工作时,曾刻意考虑行业。

第三阶段:我长期复利的工作技能是什么? 这是更远的思考,关于哪些能力可以随时间不断增值。

自此,我不止一次问自己:在选定游戏行业的前提下,我的下一步是什么? 表面是规划,内里却藏着焦虑:我要掌握什么,才不惧未来失业?游戏服务器有哪些技术值得深挖?

直到最近,我试着换了一个视角。

游戏服务器程序员,有何不同?

“游戏”、“Web”和 “APP” 的服务器程序员,本质上并无区别。游戏只是软件的一个类别。早年腾讯、网易的游戏团队里,很多程序员也来自其他业务部门。

游戏后端工程师这个岗位,主要源于三类需求:

  • 游戏性需求。比如存档,它可以提升用户体验。
  • 多人联机需求。玩家间的互动,能催生更多玩法与趣味。
  • 公平性需求。也就是反作弊。

因此,从技术上刻意区分“游戏”与“非游戏”,并不合适。我在画地为牢,为自己贴上固化的标签。 当然,技术虽无本质区别,却有倾向性。例如游戏业务的特点,决定了大多数服务难以设计成无状态。

我逐渐把问题从“怎样保住岗位”,转向“如何积累长期存在的能力”。一个客观事实是:行业动荡带来的失业,无法完全避免。但如果暂时放下这个忧虑,那么游戏行业何时会释放更多服务器岗位?答案要从行业发展的脉络中找。

游戏服务器岗位的收缩,大约始于 2020 年前后的“品质升级潮”。当时,无论投资人还是从业者,都眼馋国外 3A 。大厂纷纷入局,立项多是高难度的“类 3A”项目。

高难度如何体现?最直接的办法就是“对标 3A”——先把画面做上去,吸引眼球,再融资。很多项目根本不需要服务器深入参与,因为研发进度根本没到需要服务器深度参与的那一步。

但后来大家发现市场回报并未达到预期,原因很简单,重心偏了,“好看”的游戏,未必等于“好玩”。连客户端岗位也开始缩减。

反而近年,SLG 领域的元趣、途游和点点互动等公司,凭借高速增长悄然上位,甚至出现“点点互动单品营收杀穿网易手游总和”这类话题。

风向正在悄然转变

一个趋势正在浮现:高品质休闲游戏。 其实国外移动市场一直以此类为主,只是国内厂商曾一度执着于在手机上复刻 3A。

这个版本,客户端的权重相对降低,大量业务逻辑又回归服务器。游戏市场规模再次扩大,招聘市场也在悄然回暖。

这一次,舞台准备好了,我们准备好上场了吗?

总结

回到最初的问题:游戏服务器程序员,我的第三阶段应该是什么?

写到这儿,我不再焦虑于追逐“特点”,我的答案不再是某个具体的技术名词,而是专注于扎实基础与逻辑思维。

舞台或许随风向变换,但站稳脚下的基石,便能随风起舞。

Read more

以为错过的是一个人,其实错过的是整个人生

嗯…对…我的表白失败了…写这段话的时候,已经晚上2点了,外面刚刚下完一场大雨,外面安静的要死。我翻来覆去睡不着,最近升温了,我开了空调,我还是决定趁着夜深人静把发生的事情记录下来,我怕一觉睡醒,我的情绪就淡化了。虽然我表白失败了…但还是想跟大家讲一下过程,我觉得过程比结果重要,6月7号第一考,语文,是我最拿手的一门课,全卷做下来,还可以,感觉良好。考前我们老师一再强调,考完不要去对答案,忘掉就好,准备下一门。我也难的克制住了那一股想跟她交流的念头,我怕影响到她,所以就没有去找她。

直到下午考完,当天考试结束,我才给她发了消息,问她考的怎么样,她说:“感觉还可以,应该没什么大问题”。我不敢说太多,怕祸从口出,影响到我跟她剩下的几场考试。那几天嘛,精神高度集中,就好像全中国所有人都在为我们让路,这就是祖国的花朵的待遇嘛!直到那最后一张试卷检查完了最后一遍,精神重于松懈了,结束了。把笔放下响铃收卷那一刻,我十八岁的青春,就用一支笔,几张试卷,寥寥草草的画上句号了。那种如释重负的感觉,就好像在说,这三年过得太快了,这就结束了。

当天晚上,班主任说要请我们班所有同学一起吃顿饭,而且,允许喝酒,他请客!随后我面临一个非常严峻的问题。我应该怎么告诉她,我喜欢她呢。那天晚上吃饭的时候,大家都特别开心。如果,抛开毕业典礼不谈,那天应该是我高中生涯的最后一天,也是我整个高中最快乐的一天。酒精是个坏东西,它能让人不自觉的笑,也能让人不自觉的哭,人一旦放纵起来,自己都不知道自己干的是什么事。我最好的朋友,屈哥喝多了拿着酒杯走到我的面前问我:“兄弟,你爱我嘛?你如果爱我!你就再跟我喝一杯酒。”当时在场的人都笑了,她也笑了。我真拿屈哥没办法就倒了一杯酒,起身准备跟他干一杯,屈哥又拦住我说:“兄弟!我们怎么能那么庸俗呢?真男人,就得喝交杯!”那我也拿屈哥也没办法,能咋的,宠着呗,至少大家看到都开心了,她也开心了,她开心我就开心了。后来趁着酒意,我就坐到了她身边,我就问她:“要不…你往我衣服上签个名吧?留个纪念!”她说:“好啊,那你也给我签一个。”我听到之后特别开心,至少我们也穿了三年的情侣服了,对吧。那一天真的是我整个高中最快乐的一天,那天晚上大家畅谈理想,聊未来,聊现在,有人趁着夜色和酒意表白,而那个被表白的姑娘也冲着那个鲁莽的男生点了点头。我并没有选择在那天晚上跟她表达心意,我有我自己的计划。那天晚上回去之后,过了凌晨十二点,突然开始下大雨。而我坐在书桌前,写着一封信。是的,我给她写了一封信,手写信!老子第一次给人写手写信!写了好久,也改了好久,当时我有感觉,她可能会拒绝我,但我还是很认真的写完了。我不是非要跟他在一起,而是我喜欢她这件事情,我想跟她说而已。

第二天,我约她出来吃东西,雨过天晴,新鲜的空气,我还买了一本书,毛姆的《面纱》,把我写的信夹在书里。我递给她的时候,她一脸吃惊,问我为什么要送给她一本书,我说,想送就送咯。她拿到之后,翻了几页就合上了。我们在夕阳下山前,做了告别。其实我有提出要主动送她回家的,但是她拒绝了。那么接下来给大家读一下我的表白信吧,献丑了!

表白信

很抱歉,这封信没有对你的尊称,因为我会亲手把信送到你的手里,我不用舒心的传统格式,正如我想对你说的话,就像这封夹在书里的信,你只要不去翻看,就永远不会知情。我知道你会看的,你总是这样细心。我认识你比你认识我早一点,当初看到你的第一眼就比你吸引,想认识你。

高一跟在你的身后,在倒数第二排看你,高二侥幸又在一起,那天问你的作文题,其实类似的题目我写了三遍有余,在台上你穿着白裙,台下我咬着牙,想让那个男主持人跟你再保持一点距离。谢谢你那天愿意跟我吃泡面,不然的话我可能会尴尬到颜面扫地。你说你喜欢张爱玲,而我喜欢顾城,但那个时候我知识匮乏,只读了作品,我不知道,顾城是一个杀人犯,张爱玲到最后都是个孤寂人。记得曾经跟你一起看过云,引用一首顾城的诗,你,一会儿看我,一会儿看云,我觉得,你看我时很远,你看云时很近,我在看你的时候,你还在看云。我数次觉得我在仰望你,时至今日,我还是觉得我们之前存在着距离。但是我转念一想,人和人之间,本来就该保持距离,我超越过你一次,那次我真的很开心,不是因为成绩,而是因为你对我说,“君今日,令我刮目相看”的这句话语。我应该谢谢你,主观来讲,你就是我学习的动力。我抬头望你,仿若神明低语。你总是能说一些,能让我立马掏出本子记下来的话语,我至今为止都记得“实际上正因为那是爱,才会让我们如此失望。”还有那一句“爱,就是保持距离。”我对这句话有一点自己的看法,我觉得“爱是在靠近之后,主动保持距离。”我觉得人得承认自己的无知吧,我确实在诸多方面都配不上你,我翻山越岭,终极目标就是想跟你并排走,想靠近你。写下这些文字的此刻,外面正下着雨。也不知道为何,好像每年高考完,都要下一场大雨。我倾尽我三年之所想,都写在这封信里,我喜欢你,此乃勇气之举,整整三年,早已做好充足准备,不必感到为难,也不必如我所愿,你看到这里的时候,话基本说尽了。此举只是为了不留遗憾,做青春之告别。从初见至此刻,已想数日,等待许久,多有打扰,期待回信。

这就是我写给她的表白信了,虽然就这么一点文字,但是真的写了又改,改了又写。一直在等她回消息,感觉时间特别慢,我实在忍不住了我就问她,你看了吗?她还是没有回我,有想给她打电话的冲动,但被我克制住了。我妈喊我下楼吃饭,我就匆匆忙忙的跑下楼,匆匆忙忙的扒完饭,再匆匆忙忙的跑上楼,打开手机一看,还是没有消息。当时有点失望,就躺上床闭眼睡觉了,突然噔噔噔,一声微信响了,我匆忙打开微信,没有文字,是一张照片,照片里是一封回信,写给我的回信,是这样写的。

回信

其实你把书塞到我手里的时候,我就看到了中间有缝隙,多半是塞了东西,我到家第一时间就看了,写的不错,有进步,但说实话,你想表达的东西,在我意料之外,但确实也在情理之中。刚开始看到的时候确实有很多话想要说,但写这封回信的时候,已经不知道说什么了。看到的时候想了很多,该如何答复你,你想表达的东西,我都知道,心领神会,只是可惜,就如张爱玲那句话,生命是一袭华美的袍,爬满了虱子,而青春的告别也总是充满了遗憾,你当了我两年的朋友,你应该知道我在想什么,我理应拒绝你,我知道我对你是什么感觉,而你对我是那种可以说很多话的朋友,但朋友始终是朋友,其实有些话不说破,我们还可以相处很久很久。只是可惜,事已至此,无可奈何。我尊重你的表达,但是我能说的也就到这里为止了。身体有恙的话,就多锻炼,按时吃三餐,照顾好自己的身体。望别日君卿相见时,君,已有所成。

唉,被拒绝了,有点可惜,其实表白前就有人跟我说,就算成功了,也会面临异地上学的问题。如果失败了,那就真的连朋友都做不成。谁会甘心做所谓的朋友呢。我想了很久该回她什么,最终回了三个字:“知道了”。她秒回,“嗯”。我把手机关机了,风扇还在转,关灯,拉窗帘,窗外的天空还有一点暗蓝色的余光,我他妈地突然觉得有点难过,但是我哭不出来。于是我拿起了眼药水,往眼睛里,滴了几滴,随后,一滴伪装的“泪水”,从眼角,滑落至耳根,我闭上了眼睛,闭了很久。那些一直没有开口的告别,最终还是让她听见了。可能自始至终属于她身上的那些优秀的光环,都在彰显着,我和你的距离,我一步一步的靠近她,直到我也获得了属于我身上的光环才发现,每个人都有自己的弊端,她也并不是我想象那么的完美无暇,而我们之间的距离,就是我在看她,她在看着云的距离。你们要问我后悔吗,我觉得我不后悔,那些话就是我想说的话,之前我就说了,我不希望我想表达的东西,就这样掩与唇齿,止于岁月。也许就跟她说的一样,生命是一袭华美的袍,爬满了虱子,而青春的告别也总是充满了遗憾。

难过是真的不难过,可惜是真的可惜。我失去的不仅是我喜欢的人,还有一个就是可以畅谈人生理想,说真话的朋友。但说实话,我还是有点难过的,失落居多吧,尽管我早就想过,有可能会被你拒绝的。我始终觉得,喜欢,是一个人的事,我说了我想说的,你也做了你能做的,到此为止,可以了。

说实话,我脑子里,现在又蹦出一个想去找你的念头,但我好像没有勇气了。祝我们,别日相见时,都已有所成。我永远不会忘记在我21岁那年,结束了我与一个幻影的追逐,这个幻影会停留在我的21岁,但是我的生命永远不止21岁。

卿本良人,奈何缘尽。

Read more

我说我为什么抽不到 SSR,原来是这段代码在作祟

在游戏开发的过程中,很多场景都会用到加权随机。例如游戏中的抽奖,我们有 50% 的几率获得金币、40% 的几率获得钻石、9% 的几率获得普通装备,1% 的几率获得极品装备。

什么是加权随机?当我们从某种容器中随机选择一个元素,每个元素被选中的机会并不相等,而是由相对“权重”(或概率)被选中的,也就是说我们想要有“偏心”的得到某种随机结果。举一个例子,假如现在有一个权重数组 w = {1, 2, 4, 8},它们代表如下规则。

  • $\frac{1}{(1+2+4+8)} = \frac{1}{15} \approx 6.6$ % 的机会选中索引 0

  • $\frac{2}{(1+2+4+8)} = \frac{2}{15} \approx 13.3$ % 的机会选中索引 1

  • $\frac{3}{(1+2+4+8)} = \frac{4}{15} \approx 26.6$ % 的机会选中索引 2

  • $\frac{8}{(1+2+4+8)} = \frac{8}{15} \approx 53.3$ % 的机会选中索引 3

解决方案

方案一、笨笨的办法

第一个方法是在我们的候选列表中,包含了基于权重的每个索引的预期数量,然后从该列表中随机选择。

假设现在有权重列表 {1, 2, 4, 8},那我们得到的候选列表将是 {0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3}。

然后通过 rand.Intn() ,获取一个随机数,就完成了,代码如下。

Read more

0 秒改 struct 性能直接提升 15%,产品姐姐都夸我好棒

如果您以前写过 Golang ,那您很可能见过或者写过 Struct 结构体。但是,您可能不知道,通过简单地重新排序结构体中的字段,您可以极大地提高 Go 程序的速度和内存使用率!

难以置信吗?那让我们直接进入正题吧!让我们来看一个例子。如下。

type BadStruct struct {
    age          uint8
    IdCardNumber uint64
    DateOfBirth  uint16
}

type GoodStruct struct {
    age          uint8
    DateOfBirth  uint16
    IdCardNumber uint64
}

在上面的例子中,我们定义了两个具有相同字段的结构体。接下来让我们编写一个简单的程序来输出他们的内存使用情况。点击 此处 您可以获取测试代码。

Bad struct is 24 bytes long
Good struct is 16 bytes long

如您所见,它们占用的内存不同。

到底发生了什么,导致两个字段相同的结构体消耗不同的字节?

答案是数据在操作系统中的内存排列方式。换句话说,数据结构对齐。

CPU 以字长的方式读取数据,而不是通过字节大小。64 位操作系统中一个字长为 8 个字节,而 32 位操作系统中一个字长为 4 个字节。换句话说,CPU 以字长的倍数读取地址。

糟糕的结构体-01
糟糕的结构体-01

在 64 位操作系统中,为了获取变量 IdCardNumber,我们的 CPU 需要两个周期来访问数据,而不是一个周期。

第一个周期将获取到 0 到 7 的内存,其余周期获取其余部分。

把它想象成一个笔记本,每页只能存储一个字大小的数据,此时是 8 个字节。如果 IdCardNumber 分散在两个页面上,则需要翻页两次才能检索完整的数据。

这是低效的。

因此我们需要对齐数据结构,即将数据存储在一个地址等于数据大小的倍数的位置。

Read more