V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  dogfeet  ›  全部回复第 2 页 / 共 4 页
回复总数  74
1  2  3  4  
2020-07-03 18:37:05 +08:00
回复了 Renco 创建的主题 程序员 普通业务功能的开发,和游戏方向的开发有什么差异吗
@b00tyhunt3r 说远了,从头到位都是在说服务端
2020-07-03 11:24:52 +08:00
回复了 Renco 创建的主题 程序员 普通业务功能的开发,和游戏方向的开发有什么差异吗
@ygtq erlang 项目我也参与过,事实上 C++ 的很多服务端也都是封装一个 Actor 的概念出来,用 java akka 的也是这么个概念,skynet 用的人也多,也是 actor 的概念。

我一直想说的就是游戏与 Web 都有他们各自复杂的地方,但是 Web 在解决他们面临的问题的时候更上层,那么多库框架中间件都是在尝试更系统的解决他们遇到的问题,游戏界这块真的不咋地。实际情况就是我看到的绝大部分代码真的很烂很烂。顶级项目吧,也没看到有什么开源的好的。
2020-07-03 11:13:14 +08:00
回复了 Renco 创建的主题 程序员 普通业务功能的开发,和游戏方向的开发有什么差异吗
@b00tyhunt3r 我想说的就是类似这样的问题,很多游戏服务端看起来好像解决了这个问题,实际上连最终一致性都没法保证
就以这个例子:玩家背包物品至场景地图再至另外玩家背包,大部分游戏实现是玩家背包物品在玩家 Actor 中(为了方便大量的养成玩法直接改变消耗背包物品),场景地图中的这块区域又是一个单独的 Actor 。Actor 与 Actor 的某一次通信,没有保证(也很难保证)绝对送达且只送达一次。
就我看过的所有实现都是,actor 发送个消息,改变自己的数据,做的更进一步的是会等待一个回复。整个过程实际上是一个事务过程,游戏服务端一般数据都是存储在内存中直接操作,绝大部分实现没有保证布操作是事务的。这个实现过程几乎就是一个不出问题就不出问题的实现。打架默认都是走事后补偿路线。
我在游戏行业十多年了,客户端服务端都写过不少垃圾代码,服务端的项目更是如此。

上面的场景在游戏业务中太常见了,但是并没有哪个组织哪个企业尝试去从上层给出一个框架或者库的解决方案。反观 Web 领域,他们的场景也游戏不太一样,但是会在另外的地方有难点。Web 最后是怎么做的?会有开源的解决方案出现,比如依赖数据库的强一致性性能瓶颈后会有人做消息队列,消息保证有且仅有一次消费,消息能持久化保证不丢失,大量的这些中间件,各种 Web 框架给开发者提供源源不断的动力。而且代码质量非常高,往往技术选型上升到范式,友好度,维护性这些上去了。

反观游戏界,以 java 为例,用个 netty 就算比较现代了,akka 的算是很前沿了,这些东西都不是为游戏界输出的,当然也不是游戏界产生的。老实说游戏界没产出啥开源方案。大家都是网络框架选型完了,数据库选型完了,剩下的就又开始在那个小圈子转来转去了,多少年都是在解决那些同样的问题。
2020-07-03 10:21:37 +08:00
回复了 Renco 创建的主题 程序员 普通业务功能的开发,和游戏方向的开发有什么差异吗
看过很多很多所谓的流水很高的 C++ 游戏服务端代码,大部分都是一坨烂泥。游戏界真的很奇怪,你说难点吧,确实很多,一些方面比 Web 领域的要复杂很多,这些难点吧,都还被一定程度的解决了或者说绕过去了。
听起来像很牛是吧,实际一看代码后,你会怀疑人生。
比如说数据一致性,分布式,包括像现在流行的微服务中的一些设施很多游戏服务端都有自己实现类似的概念,但是,和系统成理论的,成体系的,讲范式讲使用体验的现代搞法完全不一样。
很多这样的 C++ 的代码只是在一个小小的项目中不停的被重复拷贝粘贴修改,其中一堆的隐含前置条件,只能在本项目中保证绝大部分情况下不出问题而已。
看看游戏界的开源情况就知道了,可能因为本身相对还是太小众吧。
就技术而言,特别是 java 这块,总体平均水平看的话还是 Web 方向有技术含量些,你看看 github 上那些开源的 java 游戏服务端的项目都是些啥。
2020-05-12 10:14:02 +08:00
回复了 tctc4869 创建的主题 Java 现在有既能处理 http 服务又能处理 tcp 服务的成熟框架么?
akka-stream,前面 gateway 将 tcp,http,websocket 消息统一转换成内部消息。
剩下的就是一致的消息进入到流中统一处理。后期换可以方便的扩展成集群模式。
2020-04-10 11:23:27 +08:00
回复了 fancy2020 创建的主题 程序员 没人觉得 TS 的代码非常丑吗..
以题主链接中的片段为例:

```typescript
function factorial (n: number): number {
if (n == 0) return 1;

return n * factorial (n-1);
}
```

在 js 中,不看完这个函数,你知道调用的时候可以传什么参数进去吗? string 支持不支持?返回值是什么类型?
js 中这个函数最后类型还是会在注释中描述出来。

复杂点的例子就是支持 uniontype 的时候,js 就更吃力,注释里面往往大段还说不清楚,必须跟到代码逻辑中才能确定。

更吃力的是,js 代码你在看的时候想跟进去看,你的编辑器跳转的根本没法保证正确,有时候跳对了地方,有时候又不是。最后你也不敢完全相信编辑器的跳转。只能自己不停的在脑子中过一些没必要的逻辑。

ts 正是因为比较严格的类型系统,编辑器能准确的跳转的函数定义的地方,真的跟起复杂项目的逻辑得心应手。

看代码,不是看字符串多少,是理解逻辑脉络。ts 这方面绝对是甩 js 几条街。

其他什么重构就不说了。
2020-04-10 11:15:29 +08:00
回复了 fancy2020 创建的主题 程序员 没人觉得 TS 的代码非常丑吗..
以题主链接中的片段为例:
2017-11-06 09:44:13 +08:00
回复了 aetmY 创建的主题 程序员 马上毕业了,职业方向是选择 PHP 还是 Java 还是前端?
都是从 0 开始,选 PHP 的优势是什么?
2017-04-25 10:28:38 +08:00
回复了 banli 创建的主题 问与答 编程过程中, 你是习惯提前返回, 还是统一返回。
@geelaw 既然都是自己 malloc 了,那操作这块内存的时候大小一定是自己很自信了。实在看不出 vector<uchar> 额定大小再操作 data 比你上面的写法性能低效在哪里,至少肯定不会是比 malloc 低。
2017-04-21 11:06:04 +08:00
回复了 explon 创建的主题 分享发现 说苹果 IAP 抽成高的知道腾讯游戏分发抽成比例吗
话说,刚同事跟我说,现在退款苹果不拿分成了。
2017-04-21 11:00:25 +08:00
回复了 explon 创建的主题 分享发现 说苹果 IAP 抽成高的知道腾讯游戏分发抽成比例吗
@mcone 是的,追溯肯定是有办法追溯的,但是并不是那么简单。首先退款订单是要 CP 自己主动去查询的,其次,退款是可以在充值后很长一段时间的。这个查询周期非常场,加上小号养大号等行为,前面的收益很有可能扩散出去。大部分情况下,我们还是视退款为正常行为。黑产充钱据我们运营下来的数据看,还是黑卡,外币居多。退款的限制还是比较大的。

上面这些不是重点哇,重点是,无论厂商怎么做,玩家正常退款后,厂商还是要自己贴钱买单分成的那部分啊。。。。
2017-04-20 10:05:18 +08:00
回复了 explon 创建的主题 分享发现 说苹果 IAP 抽成高的知道腾讯游戏分发抽成比例吗
人家不是说了吗,
别的渠道给你导个 1000W 量,流水 5000W ,分成 1500W , cp 得 3500 W 。
腾讯渠道给你导个 1 亿量,流水 5 亿,分成 4.5 亿, cp 得 5000 W 。
虽然帐是这么算没错,但是还是好像哪里不对。

苹果也好不到哪里去,每个用户每年都可以任性退那么几次款,本来是好事。
坑爹的是,用户充值 1 W ,拿了游戏 1 W 的虚拟道具,然后退款,虚拟道具没办法收回不说
你还得倒贴给苹果那 1W 中他该得的 4000 分成。
流氓。
2017-03-14 10:35:48 +08:00
回复了 ipchy 创建的主题 程序员 Windows 10 Enterprise 1067 使用 Xshell 经常性卡死,有无解决办法
求教 xshell 怎么让光标在 vim 中编辑状态是块状,插入模式是竖线状?现在很蛋疼的在使用 minttty
2017-03-08 12:04:17 +08:00
回复了 davinci 创建的主题 职场话题 吐槽一下 今日头条的面试
@davinci 不是 2 个版本哟,是一个。本意是, head == None 只需要第一次判断一次, head.nex = None 只需要最后递归完后原先的头节点执行一次。中间递归的过程中可以省略这 2 步。所以 reverse 调用的 reverseImpl 。

不过可能有问题吧,没装 py 环境没试 :(
2017-03-08 11:41:30 +08:00
回复了 davinci 创建的主题 职场话题 吐槽一下 今日头条的面试
def reverseImpl(head):
if head.next == None:
return head
result = reverseImpl(head.next)
head.next.next = head
return result

def reverse(head):
if head == None:
return head
ret = reverseImpl(head)
head.next = None
return ret

不会 Python ,写成这样是不是对的呀?
@vghdjgh 是的。 Box 也是可以的。
其实这么写挺蛋疼的,必须要用 trait object ,因为闭包的类型是匿名的。用 Map 类似的结构,如果闭包是传入的,那么传入的类型是可以推倒出来的,如果没有传入,就很麻烦了。
其实这个需求很早就有在讨论了,开启 conservative_impl_trait 就可以使用 impl trait 这种方式约束返回类型了。

#![feature(conservative_impl_trait)]

fn sum<F, G>(f: F, g: G) -> Box<(impl Fn(i32) -> i32)>
where F: Fn(i32) -> i32,
G: Fn(i32) -> i32
{
Box::new(move |x: i32| f(x) + g(x))
}

fn main() {
let f1 = |x: i32| x * x;
let f2 = |x: i32| x * 2 + 1;
let a = sum(f1, f2);
println!("{}", a(1i32));
println!("{}", a(2i32));
println!("{}", a(3i32));
}
声明周期标记的有问题, sum 中的 2 个参数都被标记为 ’ static 了,实际传入的壁报声明周期并不是 'static 的。而且返回的 Box 实际上是引用了传入的 2 个参数,实际上返回值这里有个非常绕的问题。正常 move 的 closure 会使用非引用的形式捕获,但是由于你传入的是 2 个 trait object ,所以无论怎样返回的 Box 都有生命周期的依赖。
改写了一下,下面的形式可以通过,总之, Rust 的闭包我是真心感觉蛋疼:

```rust
fn sum<'a, F, G>(f: &'a F, g: &'a G) -> Box<(Fn(i32) -> i32) + 'a>
where F: Fn(i32) -> i32,
G: Fn(i32) -> i32
{
Box::new(move |x: i32| f(x) + g(x))
}

fn main() {
let f1 = |x: i32| x * x;
let f2 = |x: i32| x * 2 + 1;
let a = sum(&f1, &f2);
println!("{}", a(1i32));
println!("{}", a(2i32));
println!("{}", a(3i32));
}
```
2017-02-13 21:01:54 +08:00
回复了 keelii 创建的主题 Vim 可能是 Windows 下最漂亮的 Gvim 配置了
同难受呐。 @zwik 求分享去掉边框的版本 ):
2016-04-11 09:44:36 +08:00
回复了 ifane 创建的主题 程序员 程序员的桌面都什么样子
到底是要看壁纸还是桌面呀?
1  2  3  4  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   868 人在线   最高记录 6543   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 50ms · UTC 19:23 · PVG 03:23 · LAX 12:23 · JFK 15:23
Developed with CodeLauncher
♥ Do have faith in what you're doing.