游戏开发并发问题

问:今天去面试,问了一个这样的问题,1000个人打一个boss,打败boss的最后一击可以获得额外奖励,因为有额外奖励,大家都会等boss血量最低的时候打最后一下,如何设计这里,可以保证

  1. 判断不会失误
  2. 延迟低

答:

  1. 我首先想到的是用锁,但是具体的后续人员会有等待锁的情况,但是具体的锁是如何等待的没答上来
  2. 后面又想到用队列,判断谁打的最后一下,给客户端发消息就行

现在的却也是有疑问,这里该如何实现会比较好,希望有大神看看

_jue
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
leo
最佳答案

Redis 的 decrby 应该就足够了。

// 初始化 boss 血量
set boss_hp 10000;

每次攻击时执行

decrby boss_hp $attack

此时 decrby 会返回扣减之后的值

$isFinalAttack = $ret <= 0 && $ret + $attack > 0; // 此处假设 $attack 必然 > 0
1个月前 评论
leo (作者) 1个月前
mirahs 1个月前
_jue (楼主) 1个月前
green_hand 1个月前
讨论数量: 32
leo

Redis 的 decrby 应该就足够了。

// 初始化 boss 血量
set boss_hp 10000;

每次攻击时执行

decrby boss_hp $attack

此时 decrby 会返回扣减之后的值

$isFinalAttack = $ret <= 0 && $ret + $attack > 0; // 此处假设 $attack 必然 > 0
1个月前 评论
leo (作者) 1个月前
mirahs 1个月前
_jue (楼主) 1个月前
green_hand 1个月前

这和电商秒杀差不多呀

1个月前 评论
_jue (楼主) 1个月前

读取 boss 血量减去伤害小于等于 0 用 redis 的 setnx 设置分布式锁, 谁设置上了谁就是最后一击

不知是否会出现意外情况。

1个月前 评论
_jue (楼主) 1个月前
kis龍 (作者) 1个月前
_jue (楼主) 1个月前

用锁啊,最先成功得后就加锁,其他人就直接失效,过段时间后就解锁

1个月前 评论

提供另外一种思路。就是使用队列。然后当一堆人攻击之后,然后使用队列将每个人的攻击血量给记录下来。然后按照记录的顺序进行处理,谁的攻击之后,boss 的血量到了 0 或者 0 以下了,那么那个人就是最后一击

1个月前 评论
daxin001 1个月前
mowangjuanzi (作者) 1个月前

不需要等待锁,没加上锁的直接正常走逻辑就好了

1个月前 评论

boss受到伤害的数据存起来,然后去看boss血量为0的时候,前一个攻击的玩家是谁

1个月前 评论

1.谁先砍最后一刀,代码需要在最开始就判断到,然后立刻加锁。后面的人判断锁住了,则不走额外的奖励。

2.延迟低的话,可以在生成boss的时候,就把奖励都生成好放到奖励表中。到boss死的时候,直接将奖励表的用户ID改一下就可以。

1个月前 评论
leo

Redis 的 decrby 应该就足够了。

// 初始化 boss 血量
set boss_hp 10000;

每次攻击时执行

decrby boss_hp $attack

此时 decrby 会返回扣减之后的值

$isFinalAttack = $ret <= 0 && $ret + $attack > 0; // 此处假设 $attack 必然 > 0
1个月前 评论
leo (作者) 1个月前
mirahs 1个月前
_jue (楼主) 1个月前
green_hand 1个月前

楼主用什么做游戏开发啊,swoole 好像有 channel,,

1个月前 评论
_jue (楼主) 1个月前
mirahs (作者) 1个月前
_jue (楼主) 1个月前
mirahs (作者) 1个月前
mirahs (作者) 1个月前

直接随机一个玩家就是,又不是电商还那么严谨,给谁不是给 :joy:

1个月前 评论
daxin001 1个月前
_jue (楼主) 1个月前

感覺可以用 redis 的 lua 腳本來實現 保證原子性就可以 以laravel為例程序裡面調用就 redis::lua

lua 腳本裡面可以 return 值 返回是否最後一擊就可以了

 $b = <<<LUA
            # 獲取血量
            local a = redis.call('GET', KEYS[1]) # KEYS[1] 血量key

            if a <= 0 then 
                # 已擊殺 無效攻擊
                return 2 
            end

            redis.call('DECRBY', KEYS[1], ARGV[1])

            # ARGV[1] 攻擊力
             if ARGV[1] >= a then
                # 最後一擊
                return 1 
             end

             # 未擊殺
             return 0 
        LUA;

$c = redis()->eval($b, 1, '血量key''攻擊力值')
1个月前 评论

php菜鸟求教。php有锁吗?有人用php做游戏?有状态的服务还是无状态的服务?

1个月前 评论
_jue (楼主) 1个月前
jcleng 1个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!