Skip to content

Commit

Permalink
Merge GameProfileCache additions. Merges #1068.
Browse files Browse the repository at this point in the history
  • Loading branch information
gabizou committed Mar 5, 2016
2 parents 7f000e8 + 8a1f85c commit e15c5f7
Show file tree
Hide file tree
Showing 3 changed files with 354 additions and 10 deletions.
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;

0 comments on commit e15c5f7

Please sign in to comment.