public async Task <IActionResult> GetCharacterSessionDataByAccount([FromRoute(Name = "id")] int accountId) { if (!await CharacterSessionRepository.AccountHasActiveSession(accountId) .ConfigureAwait(false)) { return(Ok(new CharacterSessionDataResponse(CharacterSessionDataResponseCode.NoSessionAvailable))); } //TODO: There is a dangerous race condition where the zoneserver can release a session inbetween the last databse call and the characte rsessin data call //This is unlikely to be exploitably but it is dangerous ProjectVersionStage.AssertBeta(); try { ClaimedSessionsModel claimedSessionsModel = await CharacterSessionRepository.RetrieveClaimedSessionByAccountId(accountId) .ConfigureAwait(false); return(Ok(new CharacterSessionDataResponse(claimedSessionsModel.Session.ZoneId, claimedSessionsModel.CharacterId))); } catch (Exception e) { if (Logger.IsEnabled(LogLevel.Error)) { Logger.LogError($"Failed to query for character session data for active character session on AccountId: {accountId} Exception: {e.GetType().Name} - {e.Message}"); } return(Ok(new CharacterSessionDataResponse(CharacterSessionDataResponseCode.GeneralServerError))); } }
[NoResponseCache] //we don't want to cache this, if they are removed from a guild then we want this reflected immediately or they may be taking in a guild chat they aren't apart of due to a race condition public async Task <IActionResult> GetCharacterMembershipGuildStatus([FromRoute(Name = "id")] int characterId, [NotNull][FromServices] IGuildCharacterMembershipRepository guildCharacterMembershipRepository) { if (guildCharacterMembershipRepository == null) { throw new ArgumentNullException(nameof(guildCharacterMembershipRepository)); } //If guild membership repo doesn't have the character id as an entry then it means there is no guild associated with them. if (!(await guildCharacterMembershipRepository.ContainsAsync(characterId).ConfigureAwait(false))) { return(Json(new CharacterGuildMembershipStatusResponse(CharacterGuildMembershipStatusResponseCode.NoGuild))); } //TODO: There is technically a race condition here. They could have just been kicked from a guild but the cached model may say they are in a guild //this could result in incredibly rare cases of a kicked member joining at the perfect moment who can talk in guild chat but no longer be in the guild ProjectVersionStage.AssertBeta(); //Otherwise, they are in a guild try { return(Json(new CharacterGuildMembershipStatusResponse((await guildCharacterMembershipRepository.RetrieveAsync(characterId).ConfigureAwait(false)).GuildId))); } catch (Exception e) { if (Logger.IsEnabled(LogLevel.Error)) { Logger.LogError($"Encountered error in expected guild membership status request. CharacterId: {characterId} Reason: {e.Message}\n\nStack: {e.StackTrace}"); } return(Json(new CharacterGuildMembershipStatusResponse(CharacterGuildMembershipStatusResponseCode.GeneralServerError))); } }
private async Task <string> GetSocialServiceAuthorizationToken([JetBrains.Annotations.NotNull] IAuthenticationService authService) { if (authService == null) { throw new ArgumentNullException(nameof(authService)); } //TODO: Don't hardcode the authentication details ProjectVersionStage.AssertBeta(); //TODO: Handle errors return((await authService.TryAuthenticate(new AuthenticationRequestModel("SocialService", "Test69!"))).AccessToken); }
/// <inheritdoc /> public bool Destroy(NetworkEntityGuid obj) { //This removes the world entity from it's special collection AND removes it from the relevant map GameObject rootEntityGameObject = GuidToGameObjectMappable[obj]; GameObjectToEntityMap.ObjectToEntityMap.Remove(rootEntityGameObject); GameObject.Destroy(rootEntityGameObject); foreach (var removable in RemovableCollections) { ProjectVersionStage.AssertBeta(); //TODO: Should we check this? removable.RemoveEntityEntry(obj); } return(true); }
/// <inheritdoc /> public string GetUserId(HubConnectionContext connection) { //TODO: This could fail if they don't put the header in ProjectVersionStage.AssertBeta(); //We trust the client to send us a header that contains the character id //You may be freaking out, but we aren't taking the client at face value here. //This is and MUST be verified in the Hub's OnConnected method to prevent //malicious uses from spoofing. int characterId = connection.GetHttpContext().Request.GetTypedHeaders().Get <int>(SocialNetworkConstants.CharacterIdRequestHeaderName); if (characterId <= 0) { if (Logger.IsEnabled(LogLevel.Warning)) { Logger.LogWarning($"Encountered client: {ClaimsReader.GetUserName(connection.User)}:{ClaimsReader.GetUserId(connection.User)} with invalid characterId {characterId}"); } connection.Abort(); return(String.Empty); } return(characterId.ToString()); }
static GlobalEntityResourceLockingPolicy() { //TODO: There is kinda data race here, we need to a global for this collection too to prevent it being modidified before the lock occurs inbetween lookup ProjectVersionStage.AssertBeta(); }
/// <inheritdoc /> protected override void HandleEvent(SessionStatusChangeEventArgs args) { //The reason this system is so simple is basically the other threads just need a way to queue up //cleanup on the main thread. The reasoning being that GameObject destruction needs to occur //as well as collection modification needs to happen, and the main thread is where the majority if collection //iteration should be taking place. //it's possible that we're attempting to clean up an entity for a connection //that doesn't have one. This can happen if they disconnect during claim or before claim. if (!ConnectionToEntityMap.ContainsKey(args.Details.ConnectionId)) { if (Logger.IsInfoEnabled) { Logger.Info($"ConnectionId: {args.Details.ConnectionId} had entity exit cleanup but contained no entity. This is not an error."); } //We may be in this method, handling cleanup for an entity that has a claim going on //in the claim handler, but is still awaiting a response for character data from the gameserver. MEANING we could end up with hanging entites. //This is not mitgated here, but inside the player entery factory //which SHOULD make a check for the connection still being valid AFTER creation //Not before because we still want to create, and then deconstruct. Reasoning being that gameserver session //will still be claimed unless we go through th cleanup process. return; } NetworkEntityGuid entityGuid = ConnectionToEntityMap[args.Details.ConnectionId]; //First we need to save the entity data since it DOES own an entity. UnityExtended.UnityMainThreadContext.PostAsync(async() => { //Nothing should really be taking a write lock, except session entry I guess. //This could be bad //We no longer do a read lock because Player Entry is actually trying to //aquire a write lock and this could block if for a LONG time. KILLING the server basically //So, we just assume it's safe to save entity data. Even if it's changing mid save like this. //using(await LockingPolicy.ReaderLockAsync(null, CancellationToken.None)) { //We know that an entity exists, so we must save it. Before we even queue it up for removal. await EntitySaver.SaveAsync(entityGuid) .ConfigureAwait(true); } //At this point we MUST write lock, since we are actually modifying entity collections and entries using (await LockingPolicy.WriterLockAsync(null, CancellationToken.None)) //we can use async await since we're in a async context too!! Which is good. { SessionDestructor.Destroy(new PlayerSessionDeconstructionContext(args.Details.ConnectionId)); } //TODO: We have a big problem if this fails, we need to handle it properly. Otherwise the player cannot log in again.\ ProjectVersionStage.AssertBeta(); //We need to async send the release request, a very important part of session cleanup. //if this failes we have BIG problems. BIG BIG BIG. await ZoneClientGameService.ReleaseActiveSession(entityGuid.EntityId) .ConfigureAwait(false); if (Logger.IsInfoEnabled) { Logger.Info($"Cleaned up Entity Player Session for ConnectionId: {args.Details.ConnectionId} Guid: {entityGuid} {entityGuid.EntityType}:{entityGuid.EntityId}"); } }); }