{"action":"created","comment":{"url":"https://api.github.com/repos/spatialos/UnityGDK/pulls/comments/203988622","pull_request_review_id":138988766,"id":203988622,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIwMzk4ODYyMg==","diff_hunk":"@@ -0,0 +1,198 @@\n+﻿using System;\n+using System.Collections.Generic;\n+using System.Reflection;\n+using Unity.Entities;\n+using UnityEngine;\n+\n+namespace Improbable.Gdk.Core\n+{\n+    /// <summary>\n+    /// Retrieves Reader and Writer fields from MonoBehaviours and handles injection into them.\n+    /// </summary>\n+    public class SpatialOSBehaviourLibrary {\n+        private readonly Dictionary<Type, Dictionary<uint, IMemberAdapter>> adapterCache\n+            = new Dictionary<Type, Dictionary<uint, IMemberAdapter>>();\n+\n+        private readonly Dictionary<Type, HashSet<uint>> componentReaderIdsForBehaviours = new Dictionary<Type, HashSet<uint>>();\n+        private readonly Dictionary<Type, HashSet<uint>> componentWriterIdsForBehaviours = new Dictionary<Type, HashSet<uint>>();\n+\n+        private readonly HashSet<Type> invalidMonoBehaviourTypes = new HashSet<Type>();\n+\n+        private readonly ILogDispatcher logger;\n+        private const string LoggerName = \"SpatialOSBehaviourLibrary\";\n+\n+        private const string BadRequiredMemberWarning\n+            = \"[Require] attribute found on member that is not Reader or Writer or incorrectly generated, ignoring this member!\";\n+\n+        private const string MultipleReadersWritersRequiredError\n+            = \"MonoBehaviour found requesting more than one Reader or Writer for the same component, \" +\n+            \"this is invalid and will not be enabled!\";\n+\n+        private const string MalformedReaderOrWriter\n+            = \"Reader or Writer found without a Component ID attribute, this is invalid!\";\n+\n+        public SpatialOSBehaviourLibrary(ILogDispatcher logger)\n+        {\n+            this.logger = logger;\n+        }\n+\n+        public void InjectAllReadersWriters(MonoBehaviour spatialOSBehaviour)\n+        {\n+            EnsureLoaded(spatialOSBehaviour.GetType());\n+            if (invalidMonoBehaviourTypes.Contains(spatialOSBehaviour.GetType()))\n+            {\n+                return;\n+            }\n+\n+            foreach (var readerWriterComponentId in componentReaderIdsForBehaviours[spatialOSBehaviour.GetType()])\n+            {\n+                Inject(spatialOSBehaviour, readerWriterComponentId);\n+            }\n+\n+            foreach (var readerWriterComponentId in componentWriterIdsForBehaviours[spatialOSBehaviour.GetType()])\n+            {\n+                Inject(spatialOSBehaviour, readerWriterComponentId);\n+            }\n+        }\n+\n+        public void DeInjectAllReadersWriters(MonoBehaviour spatialOSBehaviour)\n+        {\n+            EnsureLoaded(spatialOSBehaviour.GetType());\n+            if (invalidMonoBehaviourTypes.Contains(spatialOSBehaviour.GetType()))\n+            {\n+                return;\n+            }\n+\n+            foreach (var readerWriterComponentId in componentReaderIdsForBehaviours[spatialOSBehaviour.GetType()])\n+            {\n+                DeInject(spatialOSBehaviour, readerWriterComponentId);\n+            }\n+\n+            foreach (var readerWriterComponentId in componentWriterIdsForBehaviours[spatialOSBehaviour.GetType()])\n+            {\n+                DeInject(spatialOSBehaviour, readerWriterComponentId);\n+            }\n+        }\n+\n+        public HashSet<uint> GetRequiredReaderComponentIds(Type behaviourType)\n+        {\n+            EnsureLoaded(behaviourType);\n+            return componentReaderIdsForBehaviours[behaviourType];\n+        }\n+\n+        public HashSet<uint> GetRequiredWriterComponentIds(Type behaviourType)\n+        {\n+            EnsureLoaded(behaviourType);\n+            return componentWriterIdsForBehaviours[behaviourType];\n+        }\n+\n+        private void Inject(MonoBehaviour spatialOSBehaviour, uint componentId)\n+        {\n+            var readerWriter = ReaderWriterFactory.CreateReaderWriter(componentId);\n+            var memberAdapter = adapterCache[spatialOSBehaviour.GetType()][componentId];\n+            memberAdapter.SetValue(spatialOSBehaviour, readerWriter);\n+        }\n+\n+        private void DeInject(MonoBehaviour spatialOSBehaviour, uint componentId)\n+        {\n+            var memberAdapter = adapterCache[spatialOSBehaviour.GetType()][componentId];\n+            memberAdapter.SetValue(spatialOSBehaviour, null);\n+        }\n+\n+        private void EnsureLoaded(Type behaviourType)\n+        {\n+            if (adapterCache.ContainsKey(behaviourType))\n+            {\n+                return;\n+            }\n+\n+            var adapters = GetMembersWithMatchingAttributes(behaviourType);\n+            var componentIdsToAdapters = new Dictionary<uint, IMemberAdapter>();\n+            var readerIds = new HashSet<uint>();\n+            var writerIds = new HashSet<uint>();\n+            adapterCache[behaviourType] = componentIdsToAdapters;\n+            componentReaderIdsForBehaviours[behaviourType] = readerIds;\n+            componentWriterIdsForBehaviours[behaviourType] = writerIds;\n+            foreach (var adapter in adapters)\n+            {\n+                // Figure out if reader or writer\n+                // Get component ID\n+                // Store in data structures\n+                Type requiredType = adapter.TypeOfMember;\n+                var isReader = Attribute.IsDefined(requiredType, typeof(ReaderInterfaceAttribute), false);\n+                var isWriter = Attribute.IsDefined(requiredType, typeof(WriterInterfaceAttribute), false);\n+                if (!isReader && !isWriter)\n+                {\n+                    logger.HandleLog(LogType.Warning, new LogEvent(BadRequiredMemberWarning)\n+                        .WithField(LoggingUtils.LoggerName, LoggerName)\n+                        .WithField(\"MonoBehaviour\", behaviourType.Name)\n+                        .WithField(\"RequiredType\", requiredType.Name));\n+                    continue;\n+                }\n+\n+                var componentIdAttribute =\n+                    (ComponentIdAttribute) Attribute.GetCustomAttribute(requiredType, typeof(ComponentIdAttribute), false);\n+                if (componentIdAttribute == null)\n+                {\n+                    logger.HandleLog(LogType.Error, new LogEvent(MalformedReaderOrWriter)\n+                        .WithField(LoggingUtils.LoggerName, LoggerName)\n+                        .WithField(\"MonoBehaviour\", behaviourType.Name)\n+                        .WithField(\"RequiredType\", requiredType.Name));\n+                    continue;\n+                }\n+\n+                var componentId = componentIdAttribute.Id;\n+                if (componentIdsToAdapters.ContainsKey(componentId))\n+                {\n+                    logger.HandleLog(LogType.Error, new LogEvent(MultipleReadersWritersRequiredError)\n+                        .WithField(LoggingUtils.LoggerName, LoggerName)\n+                        .WithField(\"MonoBehaviour\", behaviourType.Name)\n+                        .WithField(\"ComponentID\", componentId)\n+                        .WithField(\"RequiredType\", requiredType.Name));\n+                    invalidMonoBehaviourTypes.Add(behaviourType);\n+                    break;\n+                }\n+\n+                componentIdsToAdapters[componentId] = adapter;\n+                if (isReader)\n+                {\n+                    readerIds.Add(componentId);\n+                }\n+                else\n+                {\n+                    writerIds.Add(componentId);\n+                }\n+            }\n+        }\n+\n+        private const BindingFlags FieldFlags = BindingFlags.Instance | BindingFlags.NonPublic |\n+            BindingFlags.Public | BindingFlags.DeclaredOnly;\n+\n+        private const BindingFlags PropertyFlags =\n+            FieldFlags | BindingFlags.SetProperty | BindingFlags.GetProperty;\n+\n+        private List<IMemberAdapter> GetMembersWithMatchingAttributes(Type targetType)\n+        {\n+            List<IMemberAdapter> adapters = new List<IMemberAdapter>();\n+            foreach (var property in targetType.GetProperties(PropertyFlags))","path":"workers/unity/Packages/com.improbable.gdk.core/GameObjectRepresentation/SpatialOSBehaviourLibrary.cs","position":177,"original_position":177,"commit_id":"785b27a1938a17eb4de1e8a6f21fb888a38d8ff9","original_commit_id":"785b27a1938a17eb4de1e8a6f21fb888a38d8ff9","user":{"login":"samiwh","id":14006669,"node_id":"MDQ6VXNlcjE0MDA2NjY5","avatar_url":"https://avatars0.githubusercontent.com/u/14006669?v=4","gravatar_id":"","url":"https://api.github.com/users/samiwh","html_url":"https://github.com/samiwh","followers_url":"https://api.github.com/users/samiwh/followers","following_url":"https://api.github.com/users/samiwh/following{/other_user}","gists_url":"https://api.github.com/users/samiwh/gists{/gist_id}","starred_url":"https://api.github.com/users/samiwh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/samiwh/subscriptions","organizations_url":"https://api.github.com/users/samiwh/orgs","repos_url":"https://api.github.com/users/samiwh/repos","events_url":"https://api.github.com/users/samiwh/events{/privacy}","received_events_url":"https://api.github.com/users/samiwh/received_events","type":"User","site_admin":false},"body":"I think leave it out and add it in later if we need it. It's not something I've ever seen used and seems brittle.","created_at":"2018-07-20T09:33:26Z","updated_at":"2018-07-20T09:33:26Z","html_url":"https://github.com/spatialos/UnityGDK/pull/120#discussion_r203988622","pull_request_url":"https://api.github.com/repos/spatialos/UnityGDK/pulls/120","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/spatialos/UnityGDK/pulls/comments/203988622"},"html":{"href":"https://github.com/spatialos/UnityGDK/pull/120#discussion_r203988622"},"pull_request":{"href":"https://api.github.com/repos/spatialos/UnityGDK/pulls/120"}},"in_reply_to_id":203970557},"pull_request":{"url":"https://api.github.com/repos/spatialos/UnityGDK/pulls/120","id":202519372,"node_id":"MDExOlB1bGxSZXF1ZXN0MjAyNTE5Mzcy","html_url":"https://github.com/spatialos/UnityGDK/pull/120","diff_url":"https://github.com/spatialos/UnityGDK/pull/120.diff","patch_url":"https://github.com/spatialos/UnityGDK/pull/120.patch","issue_url":"https://api.github.com/repos/spatialos/UnityGDK/issues/120","number":120,"state":"open","locked":false,"title":"UTY-590: SpatialOSBehaviourLibrary","user":{"login":"MPeti01","id":25401316,"node_id":"MDQ6VXNlcjI1NDAxMzE2","avatar_url":"https://avatars1.githubusercontent.com/u/25401316?v=4","gravatar_id":"","url":"https://api.github.com/users/MPeti01","html_url":"https://github.com/MPeti01","followers_url":"https://api.github.com/users/MPeti01/followers","following_url":"https://api.github.com/users/MPeti01/following{/other_user}","gists_url":"https://api.github.com/users/MPeti01/gists{/gist_id}","starred_url":"https://api.github.com/users/MPeti01/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MPeti01/subscriptions","organizations_url":"https://api.github.com/users/MPeti01/orgs","repos_url":"https://api.github.com/users/MPeti01/repos","events_url":"https://api.github.com/users/MPeti01/events{/privacy}","received_events_url":"https://api.github.com/users/MPeti01/received_events","type":"User","site_admin":false},"body":"#### Description\r\nAdded the SpatialOSBehaviourLibrary, a class to be used for reflectively collecting Reader and Writer members in a MonoBehaviour tagged with [Require] and injecting them (once we *have* Readers and Writers that is). Partly inspired by the corresponding implementation in the SDK, partly by common sense and sanity where the legacy code was very legacy code.\r\n#### Tests\r\nIncluded a set of tests to cover the newly added functionality, except the code paths that can't easily tested without actually having the creation of readers and writers implemented - marked those with TODO.\r\n#### Documentation\r\nIn-code documentation.\r\n#### Primary reviewers\r\n@JonasImprobable @firtina-improbable ","created_at":"2018-07-19T10:48:52Z","updated_at":"2018-07-20T09:33:26Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2f8df6e809d586b9e0c4d61a39e04fea47234c22","assignee":{"login":"MPeti01","id":25401316,"node_id":"MDQ6VXNlcjI1NDAxMzE2","avatar_url":"https://avatars1.githubusercontent.com/u/25401316?v=4","gravatar_id":"","url":"https://api.github.com/users/MPeti01","html_url":"https://github.com/MPeti01","followers_url":"https://api.github.com/users/MPeti01/followers","following_url":"https://api.github.com/users/MPeti01/following{/other_user}","gists_url":"https://api.github.com/users/MPeti01/gists{/gist_id}","starred_url":"https://api.github.com/users/MPeti01/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MPeti01/subscriptions","organizations_url":"https://api.github.com/users/MPeti01/orgs","repos_url":"https://api.github.com/users/MPeti01/repos","events_url":"https://api.github.com/users/MPeti01/events{/privacy}","received_events_url":"https://api.github.com/users/MPeti01/received_events","type":"User","site_admin":false},"assignees":[{"login":"MPeti01","id":25401316,"node_id":"MDQ6VXNlcjI1NDAxMzE2","avatar_url":"https://avatars1.githubusercontent.com/u/25401316?v=4","gravatar_id":"","url":"https://api.github.com/users/MPeti01","html_url":"https://github.com/MPeti01","followers_url":"https://api.github.com/users/MPeti01/followers","following_url":"https://api.github.com/users/MPeti01/following{/other_user}","gists_url":"https://api.github.com/users/MPeti01/gists{/gist_id}","starred_url":"https://api.github.com/users/MPeti01/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MPeti01/subscriptions","organizations_url":"https://api.github.com/users/MPeti01/orgs","repos_url":"https://api.github.com/users/MPeti01/repos","events_url":"https://api.github.com/users/MPeti01/events{/privacy}","received_events_url":"https://api.github.com/users/MPeti01/received_events","type":"User","site_admin":false}],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/spatialos/UnityGDK/pulls/120/commits","review_comments_url":"https://api.github.com/repos/spatialos/UnityGDK/pulls/120/comments","review_comment_url":"https://api.github.com/repos/spatialos/UnityGDK/pulls/comments{/number}","comments_url":"https://api.github.com/repos/spatialos/UnityGDK/issues/120/comments","statuses_url":"https://api.github.com/repos/spatialos/UnityGDK/statuses/785b27a1938a17eb4de1e8a6f21fb888a38d8ff9","head":{"label":"spatialos:feature/UTY-590-monobehaviour-spatialosbehaviourlibrary","ref":"feature/UTY-590-monobehaviour-spatialosbehaviourlibrary","sha":"785b27a1938a17eb4de1e8a6f21fb888a38d8ff9","user":{"login":"spatialos","id":23554165,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIzNTU0MTY1","avatar_url":"https://avatars2.githubusercontent.com/u/23554165?v=4","gravatar_id":"","url":"https://api.github.com/users/spatialos","html_url":"https://github.com/spatialos","followers_url":"https://api.github.com/users/spatialos/followers","following_url":"https://api.github.com/users/spatialos/following{/other_user}","gists_url":"https://api.github.com/users/spatialos/gists{/gist_id}","starred_url":"https://api.github.com/users/spatialos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spatialos/subscriptions","organizations_url":"https://api.github.com/users/spatialos/orgs","repos_url":"https://api.github.com/users/spatialos/repos","events_url":"https://api.github.com/users/spatialos/events{/privacy}","received_events_url":"https://api.github.com/users/spatialos/received_events","type":"Organization","site_admin":false},"repo":{"id":137235993,"node_id":"MDEwOlJlcG9zaXRvcnkxMzcyMzU5OTM=","name":"UnityGDK","full_name":"spatialos/UnityGDK","owner":{"login":"spatialos","id":23554165,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIzNTU0MTY1","avatar_url":"https://avatars2.githubusercontent.com/u/23554165?v=4","gravatar_id":"","url":"https://api.github.com/users/spatialos","html_url":"https://github.com/spatialos","followers_url":"https://api.github.com/users/spatialos/followers","following_url":"https://api.github.com/users/spatialos/following{/other_user}","gists_url":"https://api.github.com/users/spatialos/gists{/gist_id}","starred_url":"https://api.github.com/users/spatialos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spatialos/subscriptions","organizations_url":"https://api.github.com/users/spatialos/orgs","repos_url":"https://api.github.com/users/spatialos/repos","events_url":"https://api.github.com/users/spatialos/events{/privacy}","received_events_url":"https://api.github.com/users/spatialos/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/spatialos/UnityGDK","description":"Unity GDK","fork":false,"url":"https://api.github.com/repos/spatialos/UnityGDK","forks_url":"https://api.github.com/repos/spatialos/UnityGDK/forks","keys_url":"https://api.github.com/repos/spatialos/UnityGDK/keys{/key_id}","collaborators_url":"https://api.github.com/repos/spatialos/UnityGDK/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/spatialos/UnityGDK/teams","hooks_url":"https://api.github.com/repos/spatialos/UnityGDK/hooks","issue_events_url":"https://api.github.com/repos/spatialos/UnityGDK/issues/events{/number}","events_url":"https://api.github.com/repos/spatialos/UnityGDK/events","assignees_url":"https://api.github.com/repos/spatialos/UnityGDK/assignees{/user}","branches_url":"https://api.github.com/repos/spatialos/UnityGDK/branches{/branch}","tags_url":"https://api.github.com/repos/spatialos/UnityGDK/tags","blobs_url":"https://api.github.com/repos/spatialos/UnityGDK/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/spatialos/UnityGDK/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/spatialos/UnityGDK/git/refs{/sha}","trees_url":"https://api.github.com/repos/spatialos/UnityGDK/git/trees{/sha}","statuses_url":"https://api.github.com/repos/spatialos/UnityGDK/statuses/{sha}","languages_url":"https://api.github.com/repos/spatialos/UnityGDK/languages","stargazers_url":"https://api.github.com/repos/spatialos/UnityGDK/stargazers","contributors_url":"https://api.github.com/repos/spatialos/UnityGDK/contributors","subscribers_url":"https://api.github.com/repos/spatialos/UnityGDK/subscribers","subscription_url":"https://api.github.com/repos/spatialos/UnityGDK/subscription","commits_url":"https://api.github.com/repos/spatialos/UnityGDK/commits{/sha}","git_commits_url":"https://api.github.com/repos/spatialos/UnityGDK/git/commits{/sha}","comments_url":"https://api.github.com/repos/spatialos/UnityGDK/comments{/number}","issue_comment_url":"https://api.github.com/repos/spatialos/UnityGDK/issues/comments{/number}","contents_url":"https://api.github.com/repos/spatialos/UnityGDK/contents/{+path}","compare_url":"https://api.github.com/repos/spatialos/UnityGDK/compare/{base}...{head}","merges_url":"https://api.github.com/repos/spatialos/UnityGDK/merges","archive_url":"https://api.github.com/repos/spatialos/UnityGDK/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/spatialos/UnityGDK/downloads","issues_url":"https://api.github.com/repos/spatialos/UnityGDK/issues{/number}","pulls_url":"https://api.github.com/repos/spatialos/UnityGDK/pulls{/number}","milestones_url":"https://api.github.com/repos/spatialos/UnityGDK/milestones{/number}","notifications_url":"https://api.github.com/repos/spatialos/UnityGDK/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/spatialos/UnityGDK/labels{/name}","releases_url":"https://api.github.com/repos/spatialos/UnityGDK/releases{/id}","deployments_url":"https://api.github.com/repos/spatialos/UnityGDK/deployments","created_at":"2018-06-13T15:37:54Z","updated_at":"2018-07-19T19:39:14Z","pushed_at":"2018-07-20T08:46:16Z","git_url":"git://github.com/spatialos/UnityGDK.git","ssh_url":"git@github.com:spatialos/UnityGDK.git","clone_url":"https://github.com/spatialos/UnityGDK.git","svn_url":"https://github.com/spatialos/UnityGDK","homepage":null,"size":66321,"stargazers_count":61,"watchers_count":61,"language":"C#","has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":14,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":0,"open_issues":14,"watchers":61,"default_branch":"master"}},"base":{"label":"spatialos:feature/monobehaviour-support-shared","ref":"feature/monobehaviour-support-shared","sha":"d7dcad0adcae514101eb22c1ffa8110649886085","user":{"login":"spatialos","id":23554165,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIzNTU0MTY1","avatar_url":"https://avatars2.githubusercontent.com/u/23554165?v=4","gravatar_id":"","url":"https://api.github.com/users/spatialos","html_url":"https://github.com/spatialos","followers_url":"https://api.github.com/users/spatialos/followers","following_url":"https://api.github.com/users/spatialos/following{/other_user}","gists_url":"https://api.github.com/users/spatialos/gists{/gist_id}","starred_url":"https://api.github.com/users/spatialos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spatialos/subscriptions","organizations_url":"https://api.github.com/users/spatialos/orgs","repos_url":"https://api.github.com/users/spatialos/repos","events_url":"https://api.github.com/users/spatialos/events{/privacy}","received_events_url":"https://api.github.com/users/spatialos/received_events","type":"Organization","site_admin":false},"repo":{"id":137235993,"node_id":"MDEwOlJlcG9zaXRvcnkxMzcyMzU5OTM=","name":"UnityGDK","full_name":"spatialos/UnityGDK","owner":{"login":"spatialos","id":23554165,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIzNTU0MTY1","avatar_url":"https://avatars2.githubusercontent.com/u/23554165?v=4","gravatar_id":"","url":"https://api.github.com/users/spatialos","html_url":"https://github.com/spatialos","followers_url":"https://api.github.com/users/spatialos/followers","following_url":"https://api.github.com/users/spatialos/following{/other_user}","gists_url":"https://api.github.com/users/spatialos/gists{/gist_id}","starred_url":"https://api.github.com/users/spatialos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spatialos/subscriptions","organizations_url":"https://api.github.com/users/spatialos/orgs","repos_url":"https://api.github.com/users/spatialos/repos","events_url":"https://api.github.com/users/spatialos/events{/privacy}","received_events_url":"https://api.github.com/users/spatialos/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/spatialos/UnityGDK","description":"Unity GDK","fork":false,"url":"https://api.github.com/repos/spatialos/UnityGDK","forks_url":"https://api.github.com/repos/spatialos/UnityGDK/forks","keys_url":"https://api.github.com/repos/spatialos/UnityGDK/keys{/key_id}","collaborators_url":"https://api.github.com/repos/spatialos/UnityGDK/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/spatialos/UnityGDK/teams","hooks_url":"https://api.github.com/repos/spatialos/UnityGDK/hooks","issue_events_url":"https://api.github.com/repos/spatialos/UnityGDK/issues/events{/number}","events_url":"https://api.github.com/repos/spatialos/UnityGDK/events","assignees_url":"https://api.github.com/repos/spatialos/UnityGDK/assignees{/user}","branches_url":"https://api.github.com/repos/spatialos/UnityGDK/branches{/branch}","tags_url":"https://api.github.com/repos/spatialos/UnityGDK/tags","blobs_url":"https://api.github.com/repos/spatialos/UnityGDK/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/spatialos/UnityGDK/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/spatialos/UnityGDK/git/refs{/sha}","trees_url":"https://api.github.com/repos/spatialos/UnityGDK/git/trees{/sha}","statuses_url":"https://api.github.com/repos/spatialos/UnityGDK/statuses/{sha}","languages_url":"https://api.github.com/repos/spatialos/UnityGDK/languages","stargazers_url":"https://api.github.com/repos/spatialos/UnityGDK/stargazers","contributors_url":"https://api.github.com/repos/spatialos/UnityGDK/contributors","subscribers_url":"https://api.github.com/repos/spatialos/UnityGDK/subscribers","subscription_url":"https://api.github.com/repos/spatialos/UnityGDK/subscription","commits_url":"https://api.github.com/repos/spatialos/UnityGDK/commits{/sha}","git_commits_url":"https://api.github.com/repos/spatialos/UnityGDK/git/commits{/sha}","comments_url":"https://api.github.com/repos/spatialos/UnityGDK/comments{/number}","issue_comment_url":"https://api.github.com/repos/spatialos/UnityGDK/issues/comments{/number}","contents_url":"https://api.github.com/repos/spatialos/UnityGDK/contents/{+path}","compare_url":"https://api.github.com/repos/spatialos/UnityGDK/compare/{base}...{head}","merges_url":"https://api.github.com/repos/spatialos/UnityGDK/merges","archive_url":"https://api.github.com/repos/spatialos/UnityGDK/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/spatialos/UnityGDK/downloads","issues_url":"https://api.github.com/repos/spatialos/UnityGDK/issues{/number}","pulls_url":"https://api.github.com/repos/spatialos/UnityGDK/pulls{/number}","milestones_url":"https://api.github.com/repos/spatialos/UnityGDK/milestones{/number}","notifications_url":"https://api.github.com/repos/spatialos/UnityGDK/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/spatialos/UnityGDK/labels{/name}","releases_url":"https://api.github.com/repos/spatialos/UnityGDK/releases{/id}","deployments_url":"https://api.github.com/repos/spatialos/UnityGDK/deployments","created_at":"2018-06-13T15:37:54Z","updated_at":"2018-07-19T19:39:14Z","pushed_at":"2018-07-20T08:46:16Z","git_url":"git://github.com/spatialos/UnityGDK.git","ssh_url":"git@github.com:spatialos/UnityGDK.git","clone_url":"https://github.com/spatialos/UnityGDK.git","svn_url":"https://github.com/spatialos/UnityGDK","homepage":null,"size":66321,"stargazers_count":61,"watchers_count":61,"language":"C#","has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":14,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":0,"open_issues":14,"watchers":61,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/spatialos/UnityGDK/pulls/120"},"html":{"href":"https://github.com/spatialos/UnityGDK/pull/120"},"issue":{"href":"https://api.github.com/repos/spatialos/UnityGDK/issues/120"},"comments":{"href":"https://api.github.com/repos/spatialos/UnityGDK/issues/120/comments"},"review_comments":{"href":"https://api.github.com/repos/spatialos/UnityGDK/pulls/120/comments"},"review_comment":{"href":"https://api.github.com/repos/spatialos/UnityGDK/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/spatialos/UnityGDK/pulls/120/commits"},"statuses":{"href":"https://api.github.com/repos/spatialos/UnityGDK/statuses/785b27a1938a17eb4de1e8a6f21fb888a38d8ff9"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"samiwh"}}