Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce GameProfileCache, and extract existing cache method to it #1068

Merged
merged 1 commit into from
Mar 5, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
314 changes: 314 additions & 0 deletions src/main/java/org/spongepowered/api/profile/GameProfileCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
/*
* This file is part of SpongeAPI, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.spongepowered.api.profile;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.spongepowered.api.entity.living.player.User;
import org.spongepowered.api.service.user.UserStorageService;
import org.spongepowered.api.util.GuavaCollectors;

import java.util.Collection;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

import javax.annotation.Nullable;

/**
* Represents a cache of {@link GameProfile}s.
*/
public interface GameProfileCache {

/**
* Add an entry to this cache.
*
* @param profile The profile to cache
* @return {@code true} if the profile was successfully cached,
* otherwise {@code false}
*/
default boolean add(GameProfile profile) {
return this.add(profile, null);
}

/**
* Add an entry to this cache, with an optional expiration date.
*
* @param profile The profile to cache
* @param expiry The expiration date
* @return {@code true} if the profile was successfully cached,
* otherwise {@code false}
*/
default boolean add(GameProfile profile, @Nullable Date expiry) {
return this.add(profile, false, expiry);
}

/**
* Add an entry to this cache, with an optional expiration date.
*
* @param profile The profile to cache
* @param overwrite If we should overwrite the cache entry for
* the provided profile
* @param expiry The expiration date
* @return {@code true} if the profile was successfully cached,
* otherwise {@code false}
*/
boolean add(GameProfile profile, boolean overwrite, @Nullable Date expiry);

/**
* Gets a {@link GameProfile} from this cache by its unique id.
*
* @param uniqueId The unique id of the profile
* @return The profile, if present, or {@link Optional#empty()} if
* the cache did not contain a profile with the provided id
*/
Optional<GameProfile> getById(UUID uniqueId);

/**
* Gets {@link GameProfile}s in bulk by their unique id.
*
* @param uniqueIds The unique ids
* @return A collection of successfully found up profiles
*/
default Map<UUID, Optional<GameProfile>> getByIds(Iterable<UUID> uniqueIds) {
checkNotNull(uniqueIds, "unique ids");

Map<UUID, Optional<GameProfile>> result = Maps.newHashMap();
uniqueIds.forEach(uniqueId -> result.put(uniqueId, this.getById(uniqueId)));

return ImmutableMap.copyOf(result);
}

/**
* Looks a {@link GameProfile} up by its unique id and
* loads it into this cache.
*
* @param uniqueId The unique id of the profile
* @return The profile, if present, or {@link Optional#empty()} if
* we couldn't find a profile with the provided id
*/
Optional<GameProfile> lookupById(UUID uniqueId);

/**
* Looks up {@link GameProfile}s in bulk by their unique id and
* loads them into this cache.
*
* @param uniqueIds The unique ids
* @return A collection of successfully looked up profiles
*/
default Map<UUID, Optional<GameProfile>> lookupByIds(Iterable<UUID> uniqueIds) {
checkNotNull(uniqueIds, "unique ids");

Map<UUID, Optional<GameProfile>> result = Maps.newHashMap();
uniqueIds.forEach(uniqueId -> result.put(uniqueId, this.lookupById(uniqueId)));

return ImmutableMap.copyOf(result);
}

/**
* Gets a {@link GameProfile} from this cache by its id if available,
* or lookups the profile by its unique id.
*
* @param uniqueId The unique id of the profile
* @return The profile, if present, or {@link Optional#empty()} if
* the cache did not contain a profile with the provided id and
* we couldn't lookup a profile with the provided id
*/
default Optional<GameProfile> getOrLookupById(UUID uniqueId) {
Optional<GameProfile> profile = this.getById(uniqueId);
if (profile.isPresent()) {
return profile;
} else {
return this.lookupById(uniqueId);
}
}

/**
* Gets {@link GameProfile}s in bulk from this cache by when available,
* and lookups the profiles by their unique id when not cached.
*
* @param uniqueIds The unique ids of the profiles
* @return A collection of successfully found profiles
*/
default Map<UUID, Optional<GameProfile>> getOrLookupByIds(Iterable<UUID> uniqueIds) {
checkNotNull(uniqueIds, "unique ids");

Collection<UUID> pending = Sets.newHashSet(uniqueIds);
Map<UUID, Optional<GameProfile>> result = Maps.newHashMap();

result.putAll(this.getByIds(pending));
result.forEach((uniqueId, profile) -> pending.remove(uniqueId));
result.putAll(this.lookupByIds(pending));

return ImmutableMap.copyOf(result);
}

/**
* Gets a {@link GameProfile} from this cache by its name.
*
* @param name The name of the profile
* @return The profile, if present, or {@link Optional#empty()} if
* the cache did not contain a profile with the provided name
*/
Optional<GameProfile> getByName(String name);

/**
* Gets {@link GameProfile}s in bulk by their name.
*
* @param names The names
* @return A collection of successfully found up profiles
*/
default Map<String, Optional<GameProfile>> getByNames(Iterable<String> names) {
checkNotNull(names, "names");

Map<String, Optional<GameProfile>> result = Maps.newHashMap();
names.forEach(name -> result.put(name, this.getByName(name)));

return ImmutableMap.copyOf(result);
}

/**
* Looks a {@link GameProfile} up by its name and
* loads it into this cache.
*
* @param name The name of the profile
* @return The profile, if present, or {@link Optional#empty()} if
* we couldn't find a profile with the provided name
*/
Optional<GameProfile> lookupByName(String name);

/**
* Looks up {@link GameProfile}s in bulk by their name and
* loads them into this cache.
*
* @param names The names
* @return A collection of successfully looked up profiles
*/
default Map<String, Optional<GameProfile>> lookupByNames(Iterable<String> names) {
checkNotNull(names, "names");

Map<String, Optional<GameProfile>> result = Maps.newHashMap();
names.forEach(name -> result.put(name, this.lookupByName(name)));

return ImmutableMap.copyOf(result);
}

/**
* Gets a {@link GameProfile} from this cache by its if available,
* or lookups the profile by its name.
*
* @param name The name of the profile
* @return The profile, if present, or {@link Optional#empty()} if
* the cache did not contain a profile with the provided name and
* we couldn't lookup a profile with the provided name
*/
default Optional<GameProfile> getOrLookupByName(String name) {
Optional<GameProfile> profile = this.getByName(name);
if (profile.isPresent()) {
return profile;
} else {
return this.lookupByName(name);
}
}

/**
* Gets {@link GameProfile}s in bulk from this cache by when available,
* and lookups the profiles by their unique id when not cached.
*
* @param names The names of the profiles
* @return A collection of successfully found profiles
*/
default Map<String, Optional<GameProfile>> getOrLookupByNames(Iterable<String> names) {
checkNotNull(names, "names");

Collection<String> pending = Sets.newHashSet(names);
Map<String, Optional<GameProfile>> result = Maps.newHashMap();

result.putAll(this.getByNames(pending));
result.forEach((name, profile) -> pending.remove(name));
result.putAll(this.lookupByNames(pending));

return ImmutableMap.copyOf(result);
}

/**
* Fills a {@link GameProfile} from cached values.
*
* @param profile The profile to fill
* @return The filled profile, if present, or {@link Optional#empty()} if
* we were unable to fill the profile
*/
default Optional<GameProfile> fillProfile(GameProfile profile) {
return this.fillProfile(profile, false);
}

/**
* Fills a {@link GameProfile} from cached values.
*
* @param profile The profile to fill
* @param signed true if we should request that the profile data be signed
* @return The filled profile, if present, or {@link Optional#empty()} if
* we were unable to fill the profile
*/
Optional<GameProfile> fillProfile(GameProfile profile, boolean signed);

/**
* Gets a collection of all cached {@link GameProfile}s.
*
* @return A {@link Collection} of cached {@link GameProfile}s
*/
Collection<GameProfile> getProfiles();

/**
* Returns a collection of matching cached {@link GameProfile}s whose last
* known names start with the given string (case-insensitive).
*
* <p>This collection may also contain profiles of players who never played
* on the server!</p>
*
* <p>Use {@link UserStorageService#match(String)} for a collection that
* only contains {@link GameProfile}s with attached {@link User} data.</p>
*
* <p>This method only searches the local cache, so the data may not be up
* to date.</p>
*
* @param name The name
* @return A {@link Collection} of matching {@link GameProfile}s
*/
default Collection<GameProfile> match(String name) {
final String search = checkNotNull(name, "name").toLowerCase(Locale.ROOT);

return this.getProfiles().stream()
.filter(profile -> profile.getName().isPresent())
.filter(profile -> profile.getName().get().toLowerCase(Locale.ROOT).startsWith(search))
.collect(GuavaCollectors.toImmutableSet());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -199,31 +199,60 @@ default CompletableFuture<GameProfile> fill(GameProfile profile, boolean signed)
*/
CompletableFuture<GameProfile> fill(GameProfile profile, boolean signed, boolean useCache);

/**
* Gets the active {@link GameProfile} cache.
*
* @return The active cache
*/
GameProfileCache getCache();

/**
* Sets the {@link GameProfile} cache.
*
* <p>To restore the original cache, pass the result of {@link #getDefaultCache()}.</p>
*
* @param cache The new cache
*/
void setCache(GameProfileCache cache);

/**
* Gets the default cache.
*
* @return The default cache.
*/
GameProfileCache getDefaultCache();

/**
* Gets a collection of all cached {@link GameProfile}s.
*
* @return A {@link Collection} of {@link GameProfile}s
* @deprecated use {@link GameProfileCache#getProfiles()} instead
*/
Collection<GameProfile> getCachedProfiles();
@Deprecated
default Collection<GameProfile> getCachedProfiles() {
return this.getCache().getProfiles();
}

/**
* Returns a collection of matching cached {@link GameProfile}s whose last
* known user names start with the given string (case-insensitive).
* known names start with the given string (case-insensitive).
*
* <p>This collection may also contain profiles of players who never played
* on the server!</p>
*
* <p>Use
* {@link UserStorageService#match(String)} for
* a collection that only contains {@link GameProfile}s with attached
* {@link User} data.</p>
* <p>Use {@link UserStorageService#match(String)} for a collection that
* only contains {@link GameProfile}s with attached {@link User} data.</p>
*
* <p>This method only searches the local cache, so the data may not be up
* to date.</p>
*
* @param lastKnownName The user name
* @return The result of the request
* @param name The name
* @return A {@link Collection} of matching {@link GameProfile}s
* @deprecated use {@link GameProfileCache#match(String)} instead
*/
Collection<GameProfile> match(String lastKnownName);
@Deprecated
default Collection<GameProfile> match(String name) {
return this.getCache().match(name);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
@org.spongepowered.api.util.annotation.NonnullByDefault package org.spongepowered.api.profile;
@org.spongepowered.api.util.annotation.NonnullByDefault
package org.spongepowered.api.profile;