玩家排行榜

这一章我们新增一个游戏最常见的功能:玩家胜利次数排行榜。

看上去好像很复杂,先要记录每局的数据,再去排序一下,然后再进行前十名的截取,但其实使用Redis的某种数据结构的话可以非常容易实现这个功能,那就是Sorted Set有序集合。

Redis Sorted Set:doc.redisfans.com/sorted_set/…

Sorted Set是什么东西呢?顾名思义,其实他就是一个Set结构,也就是里面的元素都是唯一的。那么他是怎么进行排序呢?这是因为在保存一个元素时,还可以为这个元素设定一个设定一个score,当批量获取这个Set里数据的时候,就可以获取到根据score排序后的结果。

下面我们举几个例子:

  1. 操作指令:zadd key score member
  2. 127.0.0.1:6379> zadd page_rank 10 google.com
  3. (integer) 1
  4. 127.0.0.1:6379> zadd page_rank 20 baidu.com
  5. (integer) 1
  6. 127.0.0.1:6379> zadd page_rank 15 bing.com
  7. (integer) 1
  8. 127.0.0.1:6379> ZRANGE page_rank 0 -1 WITHSCORES
  9. 1) "google.com"
  10. 2) "10"
  11. 3) "bing.com"
  12. 4) "15"
  13. 5) "baidu.com"
  14. 6) "20"

可以看到,通过zadd添加元素后,使用zrange指令就可以获取到经过排序后的结果。

有童鞋就要问了,我们的胜利次数是逐渐增加的呀,zadd并不能满足需求,zrange返回的结果也是顺序排列,排行榜应该要按倒序来排。

是的,所以Sorted Set还有zincrbyzrevrange等指令,应该是能满足大部分需求的。

做题时间

  1. 参考Redis文档,使用Sorted Set完成玩家排行榜功能。

DataCenter类:

  1. <?php
  2. ...
  3. class DataCenter
  4. {
  5. ...
  6. public static function addPlayerWinTimes($playerId)
  7. {
  8. $key = self::PREFIX_KEY . ':player_rank';
  9. self::redis()->zIncrBy($key, 1, $playerId);
  10. }
  11. public static function getPlayersRank()
  12. {
  13. $key = self::PREFIX_KEY . ':player_rank';
  14. return self::redis()->zRevRange($key, 0, 9, true);
  15. }
  16. ...
  17. }

Logic类:

  1. <?php
  2. ...
  3. class Logic
  4. {
  5. ...
  6. private function checkGameOver($roomId)
  7. {
  8. ...
  9. if ($gameManager->isGameOver()) {
  10. ...
  11. DataCenter::addPlayerWinTimes($winner);
  12. ...
  13. }
  14. }
  15. ...
  16. }

代码写出来了,我们玩几局游戏,再到Redis中查看一下是否正常。

  1. 127.0.0.1:6379> zrevrange game:player_rank 0 9 withscores
  2. 1) "player_789"
  3. 2) "2"
  4. 3) "player_951"
  5. 4) "1"
  6. 5) "player_655"
  7. 6) "1"
  8. 7) "player_351"
  9. 8) "1"

可以看到,排行榜数据正确,下一步就是要把排行榜显示到页面上,我们采用一个最简单的方式:新增一个接口返回数据。

做题时间

  1. 新增一个接口,返回排行榜前十名数据。
  2. 前端调用接口,并渲染在页面上。

Server类:

  1. <?php
  2. ...
  3. class Server
  4. {
  5. ...
  6. public function onRequest($request, $response)
  7. {
  8. ...
  9. if ($action == 'get_online_player') {
  10. ...
  11. } elseif ($action == 'get_player_rank') {
  12. $data = [
  13. 'players_rank' => DataCenter::getPlayersRank()
  14. ];
  15. $response->end(json_encode($data));
  16. }
  17. }
  18. }
  19. ...
  20. ...
  21. <div id="app">
  22. ...
  23. <div v-else>
  24. <div v-if="onlinePlayer">
  25. 当前在线玩家:{{onlinePlayer}}
  26. </div>
  27. <div v-if="playersRank">
  28. <br>
  29. 游戏排行榜:
  30. <br>
  31. <template v-for="times, player in playersRank">
  32. 玩家:{{player}} 胜利次数:{{times}}
  33. <br>
  34. </template>
  35. </div>
  36. </div>
  37. ...
  38. </div>
  39. <script>
  40. var app = new Vue({
  41. ...
  42. data: {
  43. ...
  44. playersRank:null
  45. },
  46. ...
  47. methods: {
  48. getServerInfo() {
  49. ...
  50. $.ajax({
  51. url: 'http://192.168.3.41:8812',
  52. type: 'get',
  53. dataType: 'json',
  54. data: {
  55. 'a': 'get_player_rank'
  56. },
  57. success: function (result) {
  58. that.playersRank = result.players_rank
  59. },
  60. error: function () {
  61. }
  62. })
  63. },
  64. ...
  65. }
  66. })
  67. </script>
  68. ...

代码写完后,重启服务器并访问游戏首页。

14 玩家排行榜 - 图1

本章对应Github Commit扩展四:玩家排行榜