/// <summary> /// Get the player info from game's databse /// </summary> static private async Task <NameOutfitFactionRecord> FetchPlayerInfo(JsonString id) { JsonObject json; int retry = 0; // try to resolve player info from database at most 3 times before returning unresolved record do { string uri = $@"http://census.daybreakgames.com/s:{PS2APIConstants.ServiceId}/get/ps2/character_name/?character_id={id.InnerString}&c:join=character^inject_at:character^show:faction_id(outfit_member_extended^inject_at:outfit^show:alias%27name)"; json = await PS2APIUtils.RestAPIRequestClient(uri); } while (json == null && ++retry < 3); NameOutfitFactionRecord record = new NameOutfitFactionRecord(); if ((record.Name = (json?["character_name_list"]?[0]?["name"]?["first"] as JsonString)?.InnerString) == null) { record.Name = $"<Character:{id.InnerString}>"; } if ((record.Faction = (json?["character_name_list"]?[0]?["character"]?["faction_id"] as JsonString)?.InnerString) == null) { record.Faction = $"<CharacterFaction:{id.InnerString}>"; } // no outfit is possible value record.Outfit = (json?["character_name_list"]?[0]?["character"]?["outfit"]?["alias"] as JsonString)?.InnerString; record.Id = id; return(record); }
/// <summary> /// Gets player name from cache if present or otherwise directly from Rest APIa /// </summary> public static async Task <NameOutfitFactionRecord> GetPlayer(JsonString id) { CacheStruct cacheItem; if (id == null || id.InnerString == "0") { return(default(NameOutfitFactionRecord)); } try { // check whether player is cached and still valid (fast check) rwl.EnterReadLock(); try { if (ResolvedPlayers.TryGetValue(id, out cacheItem) && DateTime.Now - cacheItem.cachedTime < cacheTimeoutMinutes) { return(cacheItem.cachedValue); } } finally { rwl.ExitReadLock(); } // check if this player's query is already in progress and either get that query's semaphore or create new one SemaphoreSlim queryLock; lock (QueriesInProgress) { if (!QueriesInProgress.TryGetValue(id, out queryLock)) { QueriesInProgress.Add(id, queryLock = new SemaphoreSlim(1, 1)); } } // wait for query lock await queryLock.WaitAsync(); try { // while we waited for query semaphore, someone else finished query and result is now available if (ResolvedPlayers.TryGetValue(id, out cacheItem) && DateTime.Now - cacheItem.cachedTime < cacheTimeoutMinutes) { return(cacheItem.cachedValue); } // fetch player info from Rest API NameOutfitFactionRecord record = await FetchPlayerInfo(id); // if all mandatory record items are valid, save the record to cache if (!record.Name.StartsWith("<") && !record.Faction.StartsWith("<")) { cacheItem.cachedTime = DateTime.Now; cacheItem.cachedValue = record; rwl.EnterWriteLock(); try { ResolvedPlayers[id] = cacheItem; } finally { rwl.ExitWriteLock(); } } return(record); } finally { queryLock.Release(); } } catch (Exception e) { Console.WriteLine($"Player cache ID:{id}\n{e.ToString()}"); return(default(NameOutfitFactionRecord)); } }