{"action":"created","comment":{"url":"https://api.github.com/repos/SpongePowered/SpongeAPI/pulls/comments/29548629","id":29548629,"diff_hunk":"@@ -0,0 +1,104 @@\n+/*\r\n+ * This file is part of SpongeAPI, licensed under the MIT License (MIT).\r\n+ *\r\n+ * Copyright (c) SpongePowered <https://www.spongepowered.org>\r\n+ * Copyright (c) contributors\r\n+ *\r\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\r\n+ * of this software and associated documentation files (the \"Software\"), to deal\r\n+ * in the Software without restriction, including without limitation the rights\r\n+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n+ * copies of the Software, and to permit persons to whom the Software is\r\n+ * furnished to do so, subject to the following conditions:\r\n+ *\r\n+ * The above copyright notice and this permission notice shall be included in\r\n+ * all copies or substantial portions of the Software.\r\n+ *\r\n+ * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r\n+ * THE SOFTWARE.\r\n+ */\r\n+package org.spongepowered.api.service.user;\r\n+\r\n+import java.util.Collection;\r\n+import java.util.UUID;\r\n+\r\n+import org.spongepowered.api.GameProfile;\r\n+import org.spongepowered.api.entity.player.Player;\r\n+import org.spongepowered.api.entity.player.User;\r\n+import org.spongepowered.api.service.profile.GameProfileResolver;\r\n+\r\n+import com.google.common.base.Optional;\r\n+\r\n+/**\r\n+ * Stores the persistent {@link User} data of a {@link Player}.\r\n+ */\r\n+public interface UserStorage {\r\n+\r\n+    /**\r\n+     * Gets the data of a {@link User} by their unique id.\r\n+     *\r\n+     * @param uniqueId The UUID of the user.\r\n+     * @return {@link User} or Optional.absent() if not found\r\n+     */\r\n+    Optional<User> get(UUID uniqueId);\r\n+\r\n+    /**\r\n+     * Gets the data of a {@link User} by their last known user name\r\n+     * (case-insensitive).\r\n+     *\r\n+     * <p>To get the current name of a player, use the\r\n+     * {@link GameProfileResolver} service.</p>\r\n+     *\r\n+     * @param uniqueId The UUID of the user.\r\n+     * @return {@link User} or Optional.absent() if not found\r\n+     */\r\n+    Optional<User> get(String lastKnownName);\r\n+\r\n+    /**\r\n+     * Gets the data of a {@link User} by their profile.\r\n+     *\r\n+     * @param uniqueId The UUID of the user.\r\n+     * @return {@link User} or Optional.absent() if not found\r\n+     */\r\n+    Optional<User> get(GameProfile profile);\r\n+\r\n+    /**\r\n+     * Gets or creates a persistent {@link User} associated with the given\r\n+     * {@link GameProfile}.\r\n+     *\r\n+     * To obtain a {@link GameProfile}, use the {@link GameProfileResolver}.\r\n+     *\r\n+     * @param profile The profile\r\n+     * @return The user object\r\n+     */\r\n+    User getOrCreate(GameProfile profile);\r\n+\r\n+    /**\r\n+     * Deletes the data associated with a {@link User}.\r\n+     *\r\n+     * @param profile The profile\r\n+     * @return The user object\r\n+     */\r\n+    void delete(GameProfile profile);\r\n+\r\n+    /**\r\n+     * Gets the collection of all stored {@link User}s.\r\n+     *\r\n+     * @return A {@link Collection} of users.\r\n+     */\r\n+    Collection<User> getAll();\r\n+\r\n+    /**\r\n+     * Returns a collection of matching {@code User}s whose last known user\r\n+     * names start with the given string (case-insensitive).\r\n+     *\r\n+     * @param name\r","path":"src/main/java/org/spongepowered/api/service/user/UserStorage.java","position":100,"original_position":100,"commit_id":"7752a334a5ffc3e944d32c391f3dda67c252ccdb","original_commit_id":"7752a334a5ffc3e944d32c391f3dda67c252ccdb","user":{"login":"simon816","id":1497508,"avatar_url":"https://avatars.githubusercontent.com/u/1497508?v=3","gravatar_id":"","url":"https://api.github.com/users/simon816","html_url":"https://github.com/simon816","followers_url":"https://api.github.com/users/simon816/followers","following_url":"https://api.github.com/users/simon816/following{/other_user}","gists_url":"https://api.github.com/users/simon816/gists{/gist_id}","starred_url":"https://api.github.com/users/simon816/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/simon816/subscriptions","organizations_url":"https://api.github.com/users/simon816/orgs","repos_url":"https://api.github.com/users/simon816/repos","events_url":"https://api.github.com/users/simon816/events{/privacy}","received_events_url":"https://api.github.com/users/simon816/received_events","type":"User","site_admin":false},"body":"`@param lastKnownName`, also need description of parameter","created_at":"2015-05-02T16:28:19Z","updated_at":"2015-05-02T16:28:19Z","html_url":"https://github.com/SpongePowered/SpongeAPI/pull/580#discussion_r29548629","pull_request_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/pulls/580","_links":{"self":{"href":"https://api.github.com/repos/SpongePowered/SpongeAPI/pulls/comments/29548629"},"html":{"href":"https://github.com/SpongePowered/SpongeAPI/pull/580#discussion_r29548629"},"pull_request":{"href":"https://api.github.com/repos/SpongePowered/SpongeAPI/pulls/580"}}},"pull_request":{"url":"https://api.github.com/repos/SpongePowered/SpongeAPI/pulls/580","id":33620303,"html_url":"https://github.com/SpongePowered/SpongeAPI/pull/580","diff_url":"https://github.com/SpongePowered/SpongeAPI/pull/580.diff","patch_url":"https://github.com/SpongePowered/SpongeAPI/pull/580.patch","issue_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/issues/580","number":580,"state":"open","locked":false,"title":"Add access to GameProfile cache, a GameProfile resolver and Server.getUser(UUID)","user":{"login":"boformer","id":2014500,"avatar_url":"https://avatars.githubusercontent.com/u/2014500?v=3","gravatar_id":"","url":"https://api.github.com/users/boformer","html_url":"https://github.com/boformer","followers_url":"https://api.github.com/users/boformer/followers","following_url":"https://api.github.com/users/boformer/following{/other_user}","gists_url":"https://api.github.com/users/boformer/gists{/gist_id}","starred_url":"https://api.github.com/users/boformer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/boformer/subscriptions","organizations_url":"https://api.github.com/users/boformer/orgs","repos_url":"https://api.github.com/users/boformer/repos","events_url":"https://api.github.com/users/boformer/events{/privacy}","received_events_url":"https://api.github.com/users/boformer/received_events","type":"User","site_admin":false},"body":"This PR adds a service interface that can be used to lookup player UUIDs by name and player names by UUIDS. The service contacts the Mojang auth servers if no cached profile was found in the local cache.\r\nIt also adds a ``UserStorage`` interface to get, create or delete a ``User`` object.\r\n\r\nThis resolves https://github.com/SpongePowered/SpongeAPI/issues/528\r\n\r\nAccess to cached offline user data is important to:\r\n\r\n* complete or validate names of offline players\r\n* lookup the name of an offline player by an UUID stored by a plugin\r\n* ban offline players\r\n\r\nThings to discuss:\r\n\r\n**Access to the [Name Changes API](http://wiki.vg/Mojang_API#UUID_-.3E_Name_history)?** \r\nMojang provides an API which lists up all names and change dates of a player.","created_at":"2015-04-19T15:14:45Z","updated_at":"2015-05-02T16:28:19Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5068ac460726feb29b3bb2d14c6b211a4a3fe441","assignee":null,"milestone":{"url":"https://api.github.com/repos/SpongePowered/SpongeAPI/milestones/3","html_url":"https://github.com/SpongePowered/SpongeAPI/milestones/Revision%202.1","labels_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/milestones/3/labels","id":1006894,"number":3,"title":"Revision 2.1","description":"This is the first revision of the second stable revision of the Sponge API, 2.1.","creator":{"login":"Zidane","id":744482,"avatar_url":"https://avatars.githubusercontent.com/u/744482?v=3","gravatar_id":"","url":"https://api.github.com/users/Zidane","html_url":"https://github.com/Zidane","followers_url":"https://api.github.com/users/Zidane/followers","following_url":"https://api.github.com/users/Zidane/following{/other_user}","gists_url":"https://api.github.com/users/Zidane/gists{/gist_id}","starred_url":"https://api.github.com/users/Zidane/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Zidane/subscriptions","organizations_url":"https://api.github.com/users/Zidane/orgs","repos_url":"https://api.github.com/users/Zidane/repos","events_url":"https://api.github.com/users/Zidane/events{/privacy}","received_events_url":"https://api.github.com/users/Zidane/received_events","type":"User","site_admin":false},"open_issues":10,"closed_issues":3,"state":"open","created_at":"2015-03-05T02:35:45Z","updated_at":"2015-05-02T09:29:58Z","due_on":null,"closed_at":null},"commits_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/pulls/580/commits","review_comments_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/pulls/580/comments","review_comment_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/pulls/comments{/number}","comments_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/issues/580/comments","statuses_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/statuses/7752a334a5ffc3e944d32c391f3dda67c252ccdb","head":{"label":"boformer:feature/usercache","ref":"feature/usercache","sha":"7752a334a5ffc3e944d32c391f3dda67c252ccdb","user":{"login":"boformer","id":2014500,"avatar_url":"https://avatars.githubusercontent.com/u/2014500?v=3","gravatar_id":"","url":"https://api.github.com/users/boformer","html_url":"https://github.com/boformer","followers_url":"https://api.github.com/users/boformer/followers","following_url":"https://api.github.com/users/boformer/following{/other_user}","gists_url":"https://api.github.com/users/boformer/gists{/gist_id}","starred_url":"https://api.github.com/users/boformer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/boformer/subscriptions","organizations_url":"https://api.github.com/users/boformer/orgs","repos_url":"https://api.github.com/users/boformer/repos","events_url":"https://api.github.com/users/boformer/events{/privacy}","received_events_url":"https://api.github.com/users/boformer/received_events","type":"User","site_admin":false},"repo":{"id":24027915,"name":"SpongeAPI","full_name":"boformer/SpongeAPI","owner":{"login":"boformer","id":2014500,"avatar_url":"https://avatars.githubusercontent.com/u/2014500?v=3","gravatar_id":"","url":"https://api.github.com/users/boformer","html_url":"https://github.com/boformer","followers_url":"https://api.github.com/users/boformer/followers","following_url":"https://api.github.com/users/boformer/following{/other_user}","gists_url":"https://api.github.com/users/boformer/gists{/gist_id}","starred_url":"https://api.github.com/users/boformer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/boformer/subscriptions","organizations_url":"https://api.github.com/users/boformer/orgs","repos_url":"https://api.github.com/users/boformer/repos","events_url":"https://api.github.com/users/boformer/events{/privacy}","received_events_url":"https://api.github.com/users/boformer/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/boformer/SpongeAPI","description":"A Minecraft plugin API (not including an implementation)","fork":true,"url":"https://api.github.com/repos/boformer/SpongeAPI","forks_url":"https://api.github.com/repos/boformer/SpongeAPI/forks","keys_url":"https://api.github.com/repos/boformer/SpongeAPI/keys{/key_id}","collaborators_url":"https://api.github.com/repos/boformer/SpongeAPI/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/boformer/SpongeAPI/teams","hooks_url":"https://api.github.com/repos/boformer/SpongeAPI/hooks","issue_events_url":"https://api.github.com/repos/boformer/SpongeAPI/issues/events{/number}","events_url":"https://api.github.com/repos/boformer/SpongeAPI/events","assignees_url":"https://api.github.com/repos/boformer/SpongeAPI/assignees{/user}","branches_url":"https://api.github.com/repos/boformer/SpongeAPI/branches{/branch}","tags_url":"https://api.github.com/repos/boformer/SpongeAPI/tags","blobs_url":"https://api.github.com/repos/boformer/SpongeAPI/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/boformer/SpongeAPI/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/boformer/SpongeAPI/git/refs{/sha}","trees_url":"https://api.github.com/repos/boformer/SpongeAPI/git/trees{/sha}","statuses_url":"https://api.github.com/repos/boformer/SpongeAPI/statuses/{sha}","languages_url":"https://api.github.com/repos/boformer/SpongeAPI/languages","stargazers_url":"https://api.github.com/repos/boformer/SpongeAPI/stargazers","contributors_url":"https://api.github.com/repos/boformer/SpongeAPI/contributors","subscribers_url":"https://api.github.com/repos/boformer/SpongeAPI/subscribers","subscription_url":"https://api.github.com/repos/boformer/SpongeAPI/subscription","commits_url":"https://api.github.com/repos/boformer/SpongeAPI/commits{/sha}","git_commits_url":"https://api.github.com/repos/boformer/SpongeAPI/git/commits{/sha}","comments_url":"https://api.github.com/repos/boformer/SpongeAPI/comments{/number}","issue_comment_url":"https://api.github.com/repos/boformer/SpongeAPI/issues/comments{/number}","contents_url":"https://api.github.com/repos/boformer/SpongeAPI/contents/{+path}","compare_url":"https://api.github.com/repos/boformer/SpongeAPI/compare/{base}...{head}","merges_url":"https://api.github.com/repos/boformer/SpongeAPI/merges","archive_url":"https://api.github.com/repos/boformer/SpongeAPI/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/boformer/SpongeAPI/downloads","issues_url":"https://api.github.com/repos/boformer/SpongeAPI/issues{/number}","pulls_url":"https://api.github.com/repos/boformer/SpongeAPI/pulls{/number}","milestones_url":"https://api.github.com/repos/boformer/SpongeAPI/milestones{/number}","notifications_url":"https://api.github.com/repos/boformer/SpongeAPI/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/boformer/SpongeAPI/labels{/name}","releases_url":"https://api.github.com/repos/boformer/SpongeAPI/releases{/id}","created_at":"2014-09-14T17:10:37Z","updated_at":"2015-04-19T14:07:17Z","pushed_at":"2015-04-21T13:59:43Z","git_url":"git://github.com/boformer/SpongeAPI.git","ssh_url":"git@github.com:boformer/SpongeAPI.git","clone_url":"https://github.com/boformer/SpongeAPI.git","svn_url":"https://github.com/boformer/SpongeAPI","homepage":"http://www.spongepowered.org/","size":3628,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"SpongePowered:master","ref":"master","sha":"0b4df130116dda1f68f34b63a2e8d121945840aa","user":{"login":"SpongePowered","id":8683473,"avatar_url":"https://avatars.githubusercontent.com/u/8683473?v=3","gravatar_id":"","url":"https://api.github.com/users/SpongePowered","html_url":"https://github.com/SpongePowered","followers_url":"https://api.github.com/users/SpongePowered/followers","following_url":"https://api.github.com/users/SpongePowered/following{/other_user}","gists_url":"https://api.github.com/users/SpongePowered/gists{/gist_id}","starred_url":"https://api.github.com/users/SpongePowered/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SpongePowered/subscriptions","organizations_url":"https://api.github.com/users/SpongePowered/orgs","repos_url":"https://api.github.com/users/SpongePowered/repos","events_url":"https://api.github.com/users/SpongePowered/events{/privacy}","received_events_url":"https://api.github.com/users/SpongePowered/received_events","type":"Organization","site_admin":false},"repo":{"id":23770421,"name":"SpongeAPI","full_name":"SpongePowered/SpongeAPI","owner":{"login":"SpongePowered","id":8683473,"avatar_url":"https://avatars.githubusercontent.com/u/8683473?v=3","gravatar_id":"","url":"https://api.github.com/users/SpongePowered","html_url":"https://github.com/SpongePowered","followers_url":"https://api.github.com/users/SpongePowered/followers","following_url":"https://api.github.com/users/SpongePowered/following{/other_user}","gists_url":"https://api.github.com/users/SpongePowered/gists{/gist_id}","starred_url":"https://api.github.com/users/SpongePowered/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SpongePowered/subscriptions","organizations_url":"https://api.github.com/users/SpongePowered/orgs","repos_url":"https://api.github.com/users/SpongePowered/repos","events_url":"https://api.github.com/users/SpongePowered/events{/privacy}","received_events_url":"https://api.github.com/users/SpongePowered/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/SpongePowered/SpongeAPI","description":"A Minecraft plugin API","fork":false,"url":"https://api.github.com/repos/SpongePowered/SpongeAPI","forks_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/forks","keys_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/keys{/key_id}","collaborators_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/teams","hooks_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/hooks","issue_events_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/issues/events{/number}","events_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/events","assignees_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/assignees{/user}","branches_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/branches{/branch}","tags_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/tags","blobs_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/git/refs{/sha}","trees_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/git/trees{/sha}","statuses_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/statuses/{sha}","languages_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/languages","stargazers_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/stargazers","contributors_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/contributors","subscribers_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/subscribers","subscription_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/subscription","commits_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/commits{/sha}","git_commits_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/git/commits{/sha}","comments_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/comments{/number}","issue_comment_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/issues/comments{/number}","contents_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/contents/{+path}","compare_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/compare/{base}...{head}","merges_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/merges","archive_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/downloads","issues_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/issues{/number}","pulls_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/pulls{/number}","milestones_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/milestones{/number}","notifications_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/labels{/name}","releases_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/releases{/id}","created_at":"2014-09-07T20:39:25Z","updated_at":"2015-05-02T03:25:33Z","pushed_at":"2015-05-02T12:59:13Z","git_url":"git://github.com/SpongePowered/SpongeAPI.git","ssh_url":"git@github.com:SpongePowered/SpongeAPI.git","clone_url":"https://github.com/SpongePowered/SpongeAPI.git","svn_url":"https://github.com/SpongePowered/SpongeAPI","homepage":"http://www.spongepowered.org/","size":27573,"stargazers_count":750,"watchers_count":750,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":337,"mirror_url":null,"open_issues_count":73,"forks":337,"open_issues":73,"watchers":750,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/SpongePowered/SpongeAPI/pulls/580"},"html":{"href":"https://github.com/SpongePowered/SpongeAPI/pull/580"},"issue":{"href":"https://api.github.com/repos/SpongePowered/SpongeAPI/issues/580"},"comments":{"href":"https://api.github.com/repos/SpongePowered/SpongeAPI/issues/580/comments"},"review_comments":{"href":"https://api.github.com/repos/SpongePowered/SpongeAPI/pulls/580/comments"},"review_comment":{"href":"https://api.github.com/repos/SpongePowered/SpongeAPI/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/SpongePowered/SpongeAPI/pulls/580/commits"},"statuses":{"href":"https://api.github.com/repos/SpongePowered/SpongeAPI/statuses/7752a334a5ffc3e944d32c391f3dda67c252ccdb"}}}}