{"action":"created","comment":{"url":"https://api.github.com/repos/leancloud/docs/pulls/comments/187908344","pull_request_review_id":119780014,"id":187908344,"diff_hunk":"@@ -0,0 +1,720 @@\n+# Play 开发指南 &middot; Unity\n+\n+## 前言\n+Play 是一个基于 C# 编写的 Unity 的组件，它具备如下几项主要的功能（包括但不限于）：\n+\n+- 使用 UserID 建立与服务端的通信\n+- 创建对战房间(PlayRoom)\n+- 获取房间列表\n+- 加入房间\n+- 随机加入房间\n+- 根据匹配条件加入房间\n+- 寻找好友所在的房间\n+- 设置房间属性\n+- 获取房间玩家（Player）列表\n+- 修改玩家的属性\n+- 发送和接收 RPC 消息\n+- 离开房间\n+\n+Play 解决了如下几种游戏场景的服务端需求（包括但不限于）：\n+\n+- MOBA 多人在线实时对战\n+- 卡牌棋牌桌游\n+- 战棋\n+\n+> Play 为有强联网需求的网络游戏提供了一整套的客户端 SDK 解决方案，因此开发团队不再需要自建服务端，从而节省大部分开发和运维成本。\n+\n+## 内测申请\n+\n+Play 尚未正式发布，内测正在进行中。<button class=\"btn btn-sm btn-default\" href=\"https://blog.leancloud.cn/6177/\">申请内测</a>\n+\n+\n+## 必备的基础知识\n+\n+Play 是基于 C# 开发运行在 Unity 的 Mono .NET 下的 SDK，因此开发者需要掌握如下基础知识：\n+\n+- C# 的编程基础知识，特别是 Attribute、Event 等概念。\n+- WebSocket 是什么？如何使用 WebSocket 实现服务端和客户端的互相通信？\n+- 帧同步和状态同步的两种网游类型分别是如何实现的？\n+\n+以上问题答案请开发者自行学习掌握。\n+\n+## SDK 导入和初始化\n+\n+请阅读 [安装和初始化](play-quick-start.html#安装和初始化)，获取动态链接库。\n+\n+### 打开客户端日志\n+\n+如下代码将打开客户端的日志，可以帮助调试和进行问题反馈：\n+\n+```cs\n+// 日志都会使用 Unity.Debug 打印在控制台\n+Play.ToggleLog();\n+```\n+\n+## 使用 UserID 建立与服务端的通信\n+\n+Play 目前只需要客户端设置一个 UserID 就可以直接连接云端，这个 UserID 拥有如下限制：\n+\n+- 不支持中文和特殊字符（包括 `，！@#￥%……&*（）` 等），但可以是下划线 `_`。\n+- 长度不能超过 64 字符\n+- 一个应用内全局唯一\n+\n+下面我们使用 Mario 作为 UserID 连接云端服务器，成功之后服务端会回调 `OnAuthenticated`：\n+\n+```cs\n+using LeanCloud;\n+\n+namespace PlayDocSample\n+{\n+    /// <summary>\n+    /// 注册回调的类型，必须继承自 PlayMonoBehaviour\n+    /// </summary>\n+    public class SampleConnect : PlayMonoBehaviour\n+    {\n+\n+        /// <summary>\n+        /// 并且一定要定无参数的构造函数，而且一定要调用父类的构造函数\n+        /// </summary>\n+        public SampleConnect() : base()\n+        {\n+\n+        }\n+\n+        void start()\n+        {\n+            // 打开调试日志\n+            Play.ToggleLog(true);\n+            // 设置 UserID\n+            Play.UserID = \"Mario\";\n+            // 声明游戏版本，不同的游戏版本的玩家是不会匹配到同一个房间\n+            Play.Connect(\"0.0.1\");\n+        }\n+\n+\n+        [PlayEvent]\n+        public override void OnAuthenticated()\n+        {\n+            Play.Log(\"OnAuthenticated\");\n+        }\n+    }\n+}\n+```\n+\n+### 重要的步骤\n+\n+在上面的例子中可以看见，代码声明了一个 `ConnectSample` 类，它继承自 `PlayMonoBehaviour`，并要包含一个无参的构造函数，且构造函数需要调用父类的构造函数 `base()`，最后要为所有的事件回调加上 `[PlayEvent]` 属性标记，这些都是**必须的**。后面的示例代码都会如此，开发者的代码也需要遵守这一约定，少了一项，事件回调就不会生效。\n+\n+\n+## 房间匹配\n+\n+### 创建房间 PlayRoom\n+\n+#### 指定房间名\n+\n+```cs\n+using LeanCloud;\n+\n+namespace TestUnit.NetFx46.Docs\n+{\n+\n+    public class SampleCreateRoom : PlayMonoBehaviour\n+    {\n+        /// <summary>\n+        /// 一定要有无参数的构造函数，而且一定要调用父类的构造函数\n+        /// </summary>\n+        public SampleCreateRoom() : base()\n+        {\n+\n+        }\n+\n+        void start()\n+        {\n+            // 打开调试日志\n+            Play.ToggleLog(true);\n+            // 设置 UserID\n+            Play.UserID = \"Mario\";\n+            // 声明游戏版本，不同游戏版本的玩家会被分配在不同的隔离区域中\n+            Play.Connect(\"0.0.1\");\n+        }\n+\n+\n+        [PlayEvent]\n+        public override void OnAuthenticated()\n+        {\n+            Play.Log(\"OnAuthenticated\");\n+            // 设置房间全局唯一的名称为 「test-game-001」 \n+            Play.CreateRoom(\"test-game-001\");\n+        }\n+\n+        /// <summary>\n+        /// 如果成功创建，则会回调到 OnCreatedRoom\n+        /// </summary>\n+        [PlayEvent]\n+        public override void OnCreatedRoom()\n+        {\n+            // 打印房间名称\n+            Play.Log(\"OnCreatedRoom\", Play.Room.Name);\n+        }\n+\n+\n+        /// <summary>\n+        /// 如果创建失败，则会回调 OnCreateRoomFailed\n+        /// </summary>\n+        /// <param name=\"errorCode\"></param>\n+        /// <param name=\"reason\"></param>\n+        [PlayEvent]\n+        public override void OnCreateRoomFailed(int errorCode, string reason)\n+        {\n+            Play.LogError(errorCode, reason);\n+        }\n+    }\n+}\n+\n+```\n+\n+注：因为创建房间的逻辑和顺序都一致，因此本小节之后介绍创建房间时，为了突出重点及更好的阅读体验，我们将不再给出每一种回调的实例代码，请参照上述代码即可。\n+\n+**适用场景**：根据用户输入名称创建房间。\n+\n+```cs\n+var roomName = inputLable.text;\n+Play.CreateRoom(roomName);\n+```\n+\n+#### 随机房间名\n+\n+如果不传入任何参数创建房间，**客户端会随机生成一个名称**。这个不用开发者担心，算法是经过测试，名字重复的概率约等于 GUID 重复的概率，所以几乎不可能。\n+\n+```cs\n+Play.CreateRoom();\n+```\n+\n+#### 指定玩家 ID\n+\n+这个功能的解决的需求是：只允许一些特定的 ID 玩家可以加入到房间或者当前玩家开好房间，等待一些特定的朋友来加入。示例代码如下：\n+\n+```cs\n+// 指定 bill 和 steve\n+Play.CreateRoom(expectedUsers: new string[] { \"bill\", \"steve\" });\n+\n+// 或者顺便设置一下 Room Name\n+Play.CreateRoom(\"RichMen\", new string[] { \"bill\", \"steve\" });\n+```\n+\n+注意，这种方式创建成功之后，被指定的玩家（例如上述代码中的 steve）也还需要调用一次 `JoinRoom(\"RichMen\")` 主动加入到这个房间。`Play.CreateRoom(new string[] { \"bill\", \"steve\" });` 只是表示 bill 和 steve 已经把房间位置给「占位」了，但是等到他们自己上线之后，还需要主动加入才能真正进入房间。\n+\n+**适用场景**：好友组局一起玩游戏（代码参照基础用法里面的示例）。\n+\n+#### 限制玩家人数\n+\n+目前 Play 所有的房间最多允许 **10** 人同时在一个房间进行游戏对战，有一些游戏甚至需要设置房间最多人数要更少，比如斗地主只允许 3 个人加入，一旦人满了，其他的人是不可以加入的，因此在创建房间的时候可以设置房间的 MaxPlayerCount：\n+\n+```cs\n+var roomConfig = PlayRoom.PlayRoomConfig.Default;\n+\n+roomConfig.MaxPlayerCount = 4;\n+\n+Play.CreateRoom(roomConfig);\n+\n+// 也可以顺便设置一下 room name \n+Play.CreateRoom(roomConfig,\"max4-room\");\n+```\n+\n+**适用场景**：游戏本身有严格的人数要求，比如三国杀区分 1v1 两人局、五人局、3v3 六人局和标准八人局。下面示例将会创建一个 3v3 六人局的三国杀房间：\n+\n+```cs\n+var roomConfig = PlayRoom.PlayRoomConfig.Default;\n+\n+roomConfig.MaxPlayerCount = 6;\n+// 房间名随机即可，因为 room name 没有必要一定要显示出来\n+Play.CreateRoom(roomConfig);\n+```\n+\n+### 设置房间的自定义属性\n+\n+房间的自定义属性指的是一些 key-value 的键值对，在创建成功之后，云端也会保留一份拷贝（注意这里不是持久化存储，当该房间失效之后所有的自定义属性都会被销毁）。\n+\n+```cs\n+var roomConfig = PlayRoom.PlayRoomConfig.Default;\n+\n+roomConfig.CustomRoomProperties = new Hashtable()\n+{\n+    { \"level\", 1000 },\n+    { \"rankPoints\", 3011 }\n+};\n+roomConfig.LobbyMatchKeys = new string[] { \"rankPoints\" };\n+\n+Play.CreateRoom(roomConfig);\n+```\n+\n+此处需要配合 [根据条件随机加入](#根据条件随机加入) 来理解 `LobbyMatchKeys` 的作用。\n+\n+\n+### 是否开放房间允许别人加入\n+\n+创建一个房间后，不希望其他人家加入，比如有一些定时副本，玩家创建好房间之后，等到副本开始之后，再允许其他玩家加入：\n+\n+```cs\n+var roomConfig = PlayRoom.PlayRoomConfig.Default;\n+\n+roomConfig.IsOpen = false;\n+\n+// 如果不设置 IsOpen，云端创建的房间默认都允许其他人加入的\n+Play.CreateRoom(roomConfig);\n+```\n+\n+### 房间是否可见\n+\n+创建一个房间的时候选择不可见，这个房间就不会被其他人匹配到（其他人调用 `JoinRandomRoom` 的时候不会参与匹配）：\n+\n+```cs\n+var roomConfig = PlayRoom.PlayRoomConfig.Default;\n+\n+roomConfig.IsVisible = false;\n+\n+// 如果不设置 IsVisible，云端创建的房间默认都会参与 JoinRandomRoom 的匹配中\n+Play.CreateRoom(roomConfig);\n+```\n+\n+### 房间的相关限制和规则\n+\n+#### 房间的失效\n+\n+下列情况会导致房间在云端被清除，也就是认为这个房间失效了：\n+\n+1. 所有成员都离开了\n+2. 所有成员都掉线，并且都没有重连回来，超过 30 分钟，云端就会认为失效\n+\n+\n+### 加入房间\n+\n+#### 根据 Name 加入\n+\n+```cs\n+public class SampleJoinRoom : PlayMonoBehaviour\n+{\n+    /// <summary>\n+    /// 并且一定要定无参数的构造函数，而且一定要调用父类的构造函数\n+    /// </summary>\n+    public SampleJoinRoom() : base()\n+    {\n+\n+    }\n+\n+    void start()\n+    {\n+        // 打开调试日志\n+        Play.ToggleLog(true);\n+        // 设置 UserID\n+        Play.UserID = \"Mario\";\n+        // 声明游戏版本，不同游戏版本的玩家会被分配在不同的隔离区域中\n+        Play.Connect(\"0.0.1\");\n+    }\n+\n+\n+    [PlayEvent]\n+    public override void OnAuthenticated()\n+    {\n+        Play.Log(\"OnAuthenticated\");\n+\n+        // 加入 RichMen 房间\n+        Play.JoinRoom(\"RichMen\");\n+\n+    }\n+\n+    [PlayEvent]\n+    public override void OnJoinedRoom()\n+    {\n+        Play.Log(\"OnJoinedRoom\");\n+\n+        var name = Play.Room.Name;\n+    }\n+\n+    [PlayEvent]\n+    public override void OnJoinRoomFailed(int errorCode, string reason)\n+    {\n+\n+    }\n+}\n+```\n+\n+加入成功则会回调 `OnJoinedRoom`，加入失败则会回调 `OnJoinRoomFailed`。\n+\n+**适用场景**：与好友一起游戏。用户自己输入了一个房间名，并且创建成功，然后通过聊天工具告诉了好友，然后好友通过房间名直接加入进来。\n+\n+```cs\n+var roomName = \"get_from_qq\";\n+Play.JoinRoom(roomName);\n+```\n+\n+#### 随机加入\n+\n+随机加入任何一个开放的有空位的房间：\n+\n+```cs\n+Play.JoinRandomRoom();\n+```\n+\n+除非是所有可用的房间都满员了，才会出现随机加入失败的情况，一般情况下都会成功。\n+\n+如果随机加入失败，会触发 `OnJoinedRoomFailed` 回调，可以选择在这个回调里面继续 [创建房间](#创建房间)。\n+\n+注意，不可见的房间是不会参与随机房间的匹配的。\n+\n+**适用场景**：快速加入已有房间（不创建新的房间）\n+\n+#### 根据条件随机加入\n+\n+配合前面的[设置房间的自定义属性](#设置房间的自定义属性)的代码：\n+\n+```cs\n+var randomLobbyMatchKeys = new Hashtable\n+{\n+    { \"rankPoints\", 3000 }\n+};\n+\n+// 根据 key-value 匹配加入\n+Play.JoinRandomRoom(randomLobbyMatchKeys);\n+```\n+\n+加入成功则会回调 `OnJoinedRoom`，加入失败则会回调 `OnJoinRoomFailed`。\n+\n+注意，不可见的房间是不会参与随机房间的匹配的。\n+\n+### 加入或者创建\n+\n+解决的需求是：优先检测传入的 room name 对应的房间是否存在，如果存在就直接加入，如果不存在，就直接创建：\n+\n+```cs\n+var roomName = \"i-want-this-name\";\n+Play.JoinOrCreate(roomName);\n+```\n+\n+或者指定一些 key-value 的自定义属性，例如，我要加入一个指定名字的房间，如果不存在，请帮我创建这个房间并且把我指定的自定义属性也赋值给房间：\n+\n+```cs\n+var createWithAttributes = new Hashtable\n+{\n+    { \"rankPoints\", 1000 }\n+};\n+\n+var roomConfig = PlayRoom.PlayRoomConfig.Default;\n+\n+roomConfig.CustomRoomProperties = createWithAttributes;\n+\n+roomConfig.LobbyMatchKeys = new string[] { \"rankPoints\" };\n+\n+Play.JoinOrCreate(roomConfig);\n+```\n+\n+如果加入成功则会回调 `OnJoinedRoom`，如果是创建成功，则会先调用 `OnCreatedRoom` 然后再调用 `OnJoinedRoom`。\n+\n+**适用场景**：两人（或者多人）约定了一个房间名，但是这些人进入游戏的时间不一样，因此所有人都调用 `JoinOrCreate` 并且传入一样的名字，则 SDK 会确保这些人一定会加入到一个相同的房间内。\n+\n+## 开始游戏\n+\n+<!-- ## 获取房间列表\n+\n+### 一次性拉取 20 个房间\n+\n+20 是 SDK 默认设置的数量，可以通过修改 limit 参数来修改这一数值。\n+\n+```cs\n+[PlayEvent]\n+public override void OnAuthenticated()\n+{\n+    Play.FetchRoomList();\n+    // 或者一次性查询 30 个 room\n+    // Play.FetchRoomList(30);\n+}\n+\n+[PlayEvent]\n+public override void OnFetchedRoomList(IEnumerable<PlayRoom> rooms)\n+{\n+    Play.Log(rooms.Count());\n+}\n+```\n+\n+如果还想继续查询更多，只要继续调用 `Play.FetchRoomList()` 即可，如果想从头重新开始查询（从当前时间最新的房间开始），只要传入一个 reset 参数即可：\n+\n+```cs\n+Play.FetchRoomList(reset:true);\n+``` \n+\n+即使掉线了，只要重连回来，SDK 都会记录查询的索引标记，可以继续调用 `Play.FetchRoomList()` 来获取更多的房间。 \n+\n+\n+注意：房间的变化频率较快，所以本地 SDK 并没有做缓存，调用加入之后，需要监听加入的回调来判断是否真正加入成功，详细请看 [加入房间](#加入房间)。\n+-->\n+\n+## MasterClient\n+\n+结合前面的章节，我们需要明确一点，在无服务端进行网游对战的时候，通常都会有一个主机的概念（参照魔兽争霸局域网游戏里面的创建房间的 Host），因此 Play 提供了一个 MasterClient 的概念，指的就是房间 Host 玩家，它可能由以下几种玩家承担：\n+\n+1. 房间最初的创建者，在他加入之后，自动成为第一任 MasterClient。\n+2. A 创建了房间，随后 B 和 C 加入，然后因为 A 的网络异常，TA 掉线了，这个时候服务端会从现有的 B 和 C 里面挑选一个作为新任的 MasterClient，并且会下发 `OnMasterClientSwitched` 的事件回调通知\n+3. 在 A 回来之后，A 也不会重新成为 MasterClient。\n+\n+如下代码演示如何监听 MasterClient 产生变化的事件回调：\n+\n+```cs\n+[PlayEvent]\n+public override void OnMasterClientSwitched(Player masterPlayer) \n+{\n+    Play.Log(\"new master client is\",masterPlayer.UserID);\n+}\n+```\n+\n+## 房间的自定义属性\n+\n+房间的自定义属性是预留给开发者自定义业务逻辑的，当你设置了一个属性之后，SDK 会上传到云端，云端会将这些属性下发给在同一个房间的其他玩家。\n+\n+### 初始化房间属性\n+\n+[设置房间的自定义属性](#设置房间的自定义属性) 小节里面的代码就是初始化房间属性的代码，这里要详细描述一下自定义属性支持的类型：\n+\n+- bool\n+- string\n+- 数字类型，像 byte、int、float、double 等基础类型都支持。\n+- 字典类型 Dictionary\n+- List 类型，如梦 List<int>、List<string> 等。","path":"views/play-unity.md","position":481,"original_position":481,"commit_id":"e23f7540572d1a47a17fd40e1ee8c0371bd9597f","original_commit_id":"e23f7540572d1a47a17fd40e1ee8c0371bd9597f","user":{"login":"jwfing","id":985038,"avatar_url":"https://avatars2.githubusercontent.com/u/985038?v=4","gravatar_id":"","url":"https://api.github.com/users/jwfing","html_url":"https://github.com/jwfing","followers_url":"https://api.github.com/users/jwfing/followers","following_url":"https://api.github.com/users/jwfing/following{/other_user}","gists_url":"https://api.github.com/users/jwfing/gists{/gist_id}","starred_url":"https://api.github.com/users/jwfing/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jwfing/subscriptions","organizations_url":"https://api.github.com/users/jwfing/orgs","repos_url":"https://api.github.com/users/jwfing/repos","events_url":"https://api.github.com/users/jwfing/events{/privacy}","received_events_url":"https://api.github.com/users/jwfing/received_events","type":"User","site_admin":false},"body":"如梦。","created_at":"2018-05-14T10:46:17Z","updated_at":"2018-05-14T10:46:18Z","html_url":"https://github.com/leancloud/docs/pull/2556#discussion_r187908344","pull_request_url":"https://api.github.com/repos/leancloud/docs/pulls/2556","author_association":"OWNER","_links":{"self":{"href":"https://api.github.com/repos/leancloud/docs/pulls/comments/187908344"},"html":{"href":"https://github.com/leancloud/docs/pull/2556#discussion_r187908344"},"pull_request":{"href":"https://api.github.com/repos/leancloud/docs/pulls/2556"}}},"pull_request":{"url":"https://api.github.com/repos/leancloud/docs/pulls/2556","id":186862400,"html_url":"https://github.com/leancloud/docs/pull/2556","diff_url":"https://github.com/leancloud/docs/pull/2556.diff","patch_url":"https://github.com/leancloud/docs/pull/2556.patch","issue_url":"https://api.github.com/repos/leancloud/docs/issues/2556","number":2556,"state":"open","locked":false,"title":"Play Game docs","user":{"login":"nicecui","id":10130959,"avatar_url":"https://avatars1.githubusercontent.com/u/10130959?v=4","gravatar_id":"","url":"https://api.github.com/users/nicecui","html_url":"https://github.com/nicecui","followers_url":"https://api.github.com/users/nicecui/followers","following_url":"https://api.github.com/users/nicecui/following{/other_user}","gists_url":"https://api.github.com/users/nicecui/gists{/gist_id}","starred_url":"https://api.github.com/users/nicecui/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nicecui/subscriptions","organizations_url":"https://api.github.com/users/nicecui/orgs","repos_url":"https://api.github.com/users/nicecui/repos","events_url":"https://api.github.com/users/nicecui/events{/privacy}","received_events_url":"https://api.github.com/users/nicecui/received_events","type":"User","site_admin":false},"body":"","created_at":"2018-05-09T09:15:57Z","updated_at":"2018-05-14T10:46:18Z","closed_at":null,"merged_at":null,"merge_commit_sha":"7de83c0885dd3bfc3616ca79e62751a98b446395","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/leancloud/docs/pulls/2556/commits","review_comments_url":"https://api.github.com/repos/leancloud/docs/pulls/2556/comments","review_comment_url":"https://api.github.com/repos/leancloud/docs/pulls/comments{/number}","comments_url":"https://api.github.com/repos/leancloud/docs/issues/2556/comments","statuses_url":"https://api.github.com/repos/leancloud/docs/statuses/e23f7540572d1a47a17fd40e1ee8c0371bd9597f","head":{"label":"leancloud:gameDocs","ref":"gameDocs","sha":"e23f7540572d1a47a17fd40e1ee8c0371bd9597f","user":{"login":"leancloud","id":6660518,"avatar_url":"https://avatars2.githubusercontent.com/u/6660518?v=4","gravatar_id":"","url":"https://api.github.com/users/leancloud","html_url":"https://github.com/leancloud","followers_url":"https://api.github.com/users/leancloud/followers","following_url":"https://api.github.com/users/leancloud/following{/other_user}","gists_url":"https://api.github.com/users/leancloud/gists{/gist_id}","starred_url":"https://api.github.com/users/leancloud/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/leancloud/subscriptions","organizations_url":"https://api.github.com/users/leancloud/orgs","repos_url":"https://api.github.com/users/leancloud/repos","events_url":"https://api.github.com/users/leancloud/events{/privacy}","received_events_url":"https://api.github.com/users/leancloud/received_events","type":"Organization","site_admin":false},"repo":{"id":25067518,"name":"docs","full_name":"leancloud/docs","owner":{"login":"leancloud","id":6660518,"avatar_url":"https://avatars2.githubusercontent.com/u/6660518?v=4","gravatar_id":"","url":"https://api.github.com/users/leancloud","html_url":"https://github.com/leancloud","followers_url":"https://api.github.com/users/leancloud/followers","following_url":"https://api.github.com/users/leancloud/following{/other_user}","gists_url":"https://api.github.com/users/leancloud/gists{/gist_id}","starred_url":"https://api.github.com/users/leancloud/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/leancloud/subscriptions","organizations_url":"https://api.github.com/users/leancloud/orgs","repos_url":"https://api.github.com/users/leancloud/repos","events_url":"https://api.github.com/users/leancloud/events{/privacy}","received_events_url":"https://api.github.com/users/leancloud/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/leancloud/docs","description":"LeanCloud Documentation","fork":false,"url":"https://api.github.com/repos/leancloud/docs","forks_url":"https://api.github.com/repos/leancloud/docs/forks","keys_url":"https://api.github.com/repos/leancloud/docs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/leancloud/docs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/leancloud/docs/teams","hooks_url":"https://api.github.com/repos/leancloud/docs/hooks","issue_events_url":"https://api.github.com/repos/leancloud/docs/issues/events{/number}","events_url":"https://api.github.com/repos/leancloud/docs/events","assignees_url":"https://api.github.com/repos/leancloud/docs/assignees{/user}","branches_url":"https://api.github.com/repos/leancloud/docs/branches{/branch}","tags_url":"https://api.github.com/repos/leancloud/docs/tags","blobs_url":"https://api.github.com/repos/leancloud/docs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/leancloud/docs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/leancloud/docs/git/refs{/sha}","trees_url":"https://api.github.com/repos/leancloud/docs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/leancloud/docs/statuses/{sha}","languages_url":"https://api.github.com/repos/leancloud/docs/languages","stargazers_url":"https://api.github.com/repos/leancloud/docs/stargazers","contributors_url":"https://api.github.com/repos/leancloud/docs/contributors","subscribers_url":"https://api.github.com/repos/leancloud/docs/subscribers","subscription_url":"https://api.github.com/repos/leancloud/docs/subscription","commits_url":"https://api.github.com/repos/leancloud/docs/commits{/sha}","git_commits_url":"https://api.github.com/repos/leancloud/docs/git/commits{/sha}","comments_url":"https://api.github.com/repos/leancloud/docs/comments{/number}","issue_comment_url":"https://api.github.com/repos/leancloud/docs/issues/comments{/number}","contents_url":"https://api.github.com/repos/leancloud/docs/contents/{+path}","compare_url":"https://api.github.com/repos/leancloud/docs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/leancloud/docs/merges","archive_url":"https://api.github.com/repos/leancloud/docs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/leancloud/docs/downloads","issues_url":"https://api.github.com/repos/leancloud/docs/issues{/number}","pulls_url":"https://api.github.com/repos/leancloud/docs/pulls{/number}","milestones_url":"https://api.github.com/repos/leancloud/docs/milestones{/number}","notifications_url":"https://api.github.com/repos/leancloud/docs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/leancloud/docs/labels{/name}","releases_url":"https://api.github.com/repos/leancloud/docs/releases{/id}","deployments_url":"https://api.github.com/repos/leancloud/docs/deployments","created_at":"2014-10-11T07:14:37Z","updated_at":"2018-05-14T09:41:53Z","pushed_at":"2018-05-14T09:39:41Z","git_url":"git://github.com/leancloud/docs.git","ssh_url":"git@github.com:leancloud/docs.git","clone_url":"https://github.com/leancloud/docs.git","svn_url":"https://github.com/leancloud/docs","homepage":"https://leancloud.cn/docs/","size":67144,"stargazers_count":487,"watchers_count":487,"language":"CSS","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":246,"mirror_url":null,"archived":false,"open_issues_count":82,"license":{"key":"lgpl-3.0","name":"GNU Lesser General Public License v3.0","spdx_id":"LGPL-3.0","url":"https://api.github.com/licenses/lgpl-3.0"},"forks":246,"open_issues":82,"watchers":487,"default_branch":"master"}},"base":{"label":"leancloud:master","ref":"master","sha":"539a677ca79dc85ca8b26fb37e0ada3ca90376ac","user":{"login":"leancloud","id":6660518,"avatar_url":"https://avatars2.githubusercontent.com/u/6660518?v=4","gravatar_id":"","url":"https://api.github.com/users/leancloud","html_url":"https://github.com/leancloud","followers_url":"https://api.github.com/users/leancloud/followers","following_url":"https://api.github.com/users/leancloud/following{/other_user}","gists_url":"https://api.github.com/users/leancloud/gists{/gist_id}","starred_url":"https://api.github.com/users/leancloud/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/leancloud/subscriptions","organizations_url":"https://api.github.com/users/leancloud/orgs","repos_url":"https://api.github.com/users/leancloud/repos","events_url":"https://api.github.com/users/leancloud/events{/privacy}","received_events_url":"https://api.github.com/users/leancloud/received_events","type":"Organization","site_admin":false},"repo":{"id":25067518,"name":"docs","full_name":"leancloud/docs","owner":{"login":"leancloud","id":6660518,"avatar_url":"https://avatars2.githubusercontent.com/u/6660518?v=4","gravatar_id":"","url":"https://api.github.com/users/leancloud","html_url":"https://github.com/leancloud","followers_url":"https://api.github.com/users/leancloud/followers","following_url":"https://api.github.com/users/leancloud/following{/other_user}","gists_url":"https://api.github.com/users/leancloud/gists{/gist_id}","starred_url":"https://api.github.com/users/leancloud/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/leancloud/subscriptions","organizations_url":"https://api.github.com/users/leancloud/orgs","repos_url":"https://api.github.com/users/leancloud/repos","events_url":"https://api.github.com/users/leancloud/events{/privacy}","received_events_url":"https://api.github.com/users/leancloud/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/leancloud/docs","description":"LeanCloud Documentation","fork":false,"url":"https://api.github.com/repos/leancloud/docs","forks_url":"https://api.github.com/repos/leancloud/docs/forks","keys_url":"https://api.github.com/repos/leancloud/docs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/leancloud/docs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/leancloud/docs/teams","hooks_url":"https://api.github.com/repos/leancloud/docs/hooks","issue_events_url":"https://api.github.com/repos/leancloud/docs/issues/events{/number}","events_url":"https://api.github.com/repos/leancloud/docs/events","assignees_url":"https://api.github.com/repos/leancloud/docs/assignees{/user}","branches_url":"https://api.github.com/repos/leancloud/docs/branches{/branch}","tags_url":"https://api.github.com/repos/leancloud/docs/tags","blobs_url":"https://api.github.com/repos/leancloud/docs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/leancloud/docs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/leancloud/docs/git/refs{/sha}","trees_url":"https://api.github.com/repos/leancloud/docs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/leancloud/docs/statuses/{sha}","languages_url":"https://api.github.com/repos/leancloud/docs/languages","stargazers_url":"https://api.github.com/repos/leancloud/docs/stargazers","contributors_url":"https://api.github.com/repos/leancloud/docs/contributors","subscribers_url":"https://api.github.com/repos/leancloud/docs/subscribers","subscription_url":"https://api.github.com/repos/leancloud/docs/subscription","commits_url":"https://api.github.com/repos/leancloud/docs/commits{/sha}","git_commits_url":"https://api.github.com/repos/leancloud/docs/git/commits{/sha}","comments_url":"https://api.github.com/repos/leancloud/docs/comments{/number}","issue_comment_url":"https://api.github.com/repos/leancloud/docs/issues/comments{/number}","contents_url":"https://api.github.com/repos/leancloud/docs/contents/{+path}","compare_url":"https://api.github.com/repos/leancloud/docs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/leancloud/docs/merges","archive_url":"https://api.github.com/repos/leancloud/docs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/leancloud/docs/downloads","issues_url":"https://api.github.com/repos/leancloud/docs/issues{/number}","pulls_url":"https://api.github.com/repos/leancloud/docs/pulls{/number}","milestones_url":"https://api.github.com/repos/leancloud/docs/milestones{/number}","notifications_url":"https://api.github.com/repos/leancloud/docs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/leancloud/docs/labels{/name}","releases_url":"https://api.github.com/repos/leancloud/docs/releases{/id}","deployments_url":"https://api.github.com/repos/leancloud/docs/deployments","created_at":"2014-10-11T07:14:37Z","updated_at":"2018-05-14T09:41:53Z","pushed_at":"2018-05-14T09:39:41Z","git_url":"git://github.com/leancloud/docs.git","ssh_url":"git@github.com:leancloud/docs.git","clone_url":"https://github.com/leancloud/docs.git","svn_url":"https://github.com/leancloud/docs","homepage":"https://leancloud.cn/docs/","size":67144,"stargazers_count":487,"watchers_count":487,"language":"CSS","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":246,"mirror_url":null,"archived":false,"open_issues_count":82,"license":{"key":"lgpl-3.0","name":"GNU Lesser General Public License v3.0","spdx_id":"LGPL-3.0","url":"https://api.github.com/licenses/lgpl-3.0"},"forks":246,"open_issues":82,"watchers":487,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/leancloud/docs/pulls/2556"},"html":{"href":"https://github.com/leancloud/docs/pull/2556"},"issue":{"href":"https://api.github.com/repos/leancloud/docs/issues/2556"},"comments":{"href":"https://api.github.com/repos/leancloud/docs/issues/2556/comments"},"review_comments":{"href":"https://api.github.com/repos/leancloud/docs/pulls/2556/comments"},"review_comment":{"href":"https://api.github.com/repos/leancloud/docs/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/leancloud/docs/pulls/2556/commits"},"statuses":{"href":"https://api.github.com/repos/leancloud/docs/statuses/e23f7540572d1a47a17fd40e1ee8c0371bd9597f"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"jwfing"}}