Example #1
0
        protected override void HandleMessage(EntityActorMessageContext messageContext, DefaultEntityActorStateContainer state, WorldTeleportPlayerEntityActorMessage message)
        {
            //Something just told player we are teleporting to another world

            //TODO: Kind of a potential exploit here. Since the process to transfer sessions
            //is async they could potentially log off and log back in quickly and avoid a trasnfer
            //So if this is a FORCED transfer, like for death or something or a kick, they could potentially avoid it??
            ProjectVersionStage.AssertBeta();

            //TODO: Find a way to make it so actors state is always valid.
            //If this throws then the entity dies. So no reason to check it.
            EntitySaveableConfiguration entity = PersistenceConfigurationMappable.RetrieveEntity(state.EntityGuid);

            entity.isWorldTeleporting = true;

            ZoneToSeverClient.TryWorldTeleportCharacter(new ZoneServerWorldTeleportCharacterRequest(state.EntityGuid, message.WorldTeleportGameObjectId))
            .ContinueWith((task, o) =>
            {
                //Whether this succeeds or not this continuation will occur
                //either way we should just disconnect the player because we
                //either have uncoverable issues or they are going to be transfered after
                //disconnection.


                //ConnectionServiceMappable.RetrieveEntity(args.TeleportingPlayer).Disconnect();
                //Since this is async the entity might actually be gone, so we used the actor messaging system
                //To indicate that it should disconnect
                messageContext.Entity.Tell(new DisconnectNetworkPlayerEntityActorMessage());
            }, TaskContinuationOptions.ExecuteSynchronously);
        }
Example #2
0
        [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).ConfigureAwaitFalse()))
            {
                return(BuildFailedResponseModel(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(BuildSuccessfulResponseModel(new CharacterGuildMembershipStatusResponse((await guildCharacterMembershipRepository.RetrieveAsync(characterId).ConfigureAwaitFalse()).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(BuildFailedResponseModel(CharacterGuildMembershipStatusResponseCode.GeneralServerError));
            }
        }
Example #3
0
        public async Task <IActionResult> GetCharacterActionBar([FromRoute(Name = "id")] int characterId, [FromServices] ICharacterActionBarRepository actionBarRepository,
                                                                [FromServices] ICharacterDefaultActionBarRepository characterDefaultActionBarRepository,
                                                                [FromServices] ITypeConverterProvider <ICharacterActionBarEntry, CharacterActionBarInstanceModel> converter)
        {
            ProjectVersionStage.AssertBeta();
            //TODO: Check that they own the character.

            if (await actionBarRepository.ContainsAsync(characterId))
            {
                CharacterActionBarEntry[] actionBarEntries = await actionBarRepository.RetrieveAllForCharacterAsync(characterId);

                CharacterActionBarInstanceModel[] barInstanceModels = actionBarEntries.Select(converter.Convert)
                                                                      .ToArrayTryAvoidCopy();

                //Just send it as raw JSON.
                return(Json(barInstanceModels));
            }
            else
            {
                //We need the default action bars
                //Right now we only have 1 class so let's use mage.
                CharacterDefaultActionBarEntry[] actionBarEntries = await characterDefaultActionBarRepository.RetrieveAllActionsAsync(EntityPlayerClassType.Mage);

                CharacterActionBarInstanceModel[] barInstanceModels = actionBarEntries.Select(converter.Convert)
                                                                      .ToArrayTryAvoidCopy();

                //TODO: Return default bars.
                return(Json(barInstanceModels));
            }
        }
Example #4
0
		public async Task<IActionResult> GetCharacterSessionDataByAccount([FromRoute(Name = "id")] int accountId)
		{
			if(!await CharacterSessionRepository.AccountHasActiveSession(accountId)
				.ConfigureAwaitFalse())
			{
				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)
					.ConfigureAwaitFalse();

				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));
			}
		}
Example #5
0
        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(GladMMONetworkConstants.SOCIAL_SERVICE_NAME, "Test69!"))).AccessToken);
        }
Example #6
0
        public async Task <IActionResult> GetCharacterData([FromRoute(Name = "id")] int characterId,
                                                           [FromServices][NotNull] ICharacterDataRepository characterDataRepository)
        {
            //TODO: We should only let the user themselves get their own character's data OR zoneservers who have a claimed session.
            ProjectVersionStage.AssertBeta();

            if (!await characterDataRepository.ContainsAsync(characterId))
            {
                return(BuildFailedResponseModel(CharacterDataQueryReponseCode.CharacterNotFound));
            }

            CharacterDataModel characterData = await characterDataRepository.RetrieveAsync(characterId);

            return(BuildSuccessfulResponseModel(new CharacterDataInstance(characterData.ExperiencePoints)));
        }
Example #7
0
        //[AuthorizeJwt(GuardianApplicationRole.ZoneServer)] //TODO: Eventually we'll need to auth these zoneservers.
        public async Task <IActionResult> WorldTeleportCharacter([FromBody][NotNull] ZoneServerWorldTeleportCharacterRequest requestModel,
                                                                 [FromServices][NotNull] ICharacterLocationRepository characterLocationRepository,
                                                                 [FromServices][NotNull] IGameObjectBehaviourDataServiceClient <WorldTeleporterInstanceModel> worldTelporterDataClient,
                                                                 [FromServices][NotNull] IPlayerSpawnPointDataServiceClient playerSpawnDataClient)
        {
            if (requestModel == null)
            {
                throw new ArgumentNullException(nameof(requestModel));
            }
            if (characterLocationRepository == null)
            {
                throw new ArgumentNullException(nameof(characterLocationRepository));
            }
            if (worldTelporterDataClient == null)
            {
                throw new ArgumentNullException(nameof(worldTelporterDataClient));
            }
            if (playerSpawnDataClient == null)
            {
                throw new ArgumentNullException(nameof(playerSpawnDataClient));
            }

            //TODO: Right now there is no verification of WHO/WHAT is actually teleporting the player.
            //We need an authorization system with player-owned zone servers. So that we can determine
            //who is requesting to transfer the session and then verify that a player is even on
            //that zone server.
            ProjectVersionStage.AssertBeta();

            //We don't await so that we can get rolling on this VERY async multi-part process.
            //TODO: Handle failure
            ResponseModel <WorldTeleporterInstanceModel, SceneContentQueryResponseCode> teleporterInstanceResponse = await worldTelporterDataClient.GetBehaviourInstance(requestModel.WorldTeleporterId);

            //TODO: Handle failure
            ResponseModel <PlayerSpawnPointInstanceModel, SceneContentQueryResponseCode> pointInstanceResponse = await playerSpawnDataClient.GetSpawnPointInstance(teleporterInstanceResponse.Result.RemoteSpawnPointId);

            //Remove current location and update the new location.
            await characterLocationRepository.TryDeleteAsync(requestModel.CharacterGuid.EntityId);

            await characterLocationRepository.TryCreateAsync(new CharacterLocationModel(requestModel.CharacterGuid.EntityId,
                                                                                        pointInstanceResponse.Result.InitialPosition.x,
                                                                                        pointInstanceResponse.Result.InitialPosition.y,
                                                                                        pointInstanceResponse.Result.InitialPosition.z,
                                                                                        pointInstanceResponse.Result.WorldId));

            //TODO: Better indicate reason for failure.
            return(Ok());
        }
Example #8
0
        /// <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.GetAccountName(connection.User)}:{ClaimsReader.GetAccountId(connection.User)} with invalid characterId {characterId}");
                }

                connection.Abort();
                return(String.Empty);
            }

            return(characterId.ToString());
        }
Example #9
0
        protected override async Task OnMessageRecieved(IHubConnectionMessageContext <IRemoteSocialHubClient> context, GuildMemberInviteRequestModel payload)
        {
            var nameQueryResponseTask = NameQueryService.RetrievePlayerGuidAsync(payload.MemberToInvite);

            //First we need to check if they're in a guild.
            //We don't really need to handle the response for this, since it should never really happen.
            var guildStatus = await SocialService.GetCharacterMembershipGuildStatus(context.CallerGuid.EntityId);

            if (!guildStatus.isSuccessful)
            {
                if (Logger.IsEnabled(LogLevel.Warning))
                {
                    Logger.LogWarning($"User: {context.CallerGuid} attempted to Invite: {payload.MemberToInvite} to a guild but was not apart of a guild.");
                }

                return;
            }

            //Now we should know what guild the caller is in due to the query.
            //Now we need to do a reverse namequery to get the guid of who they're attempting to invite.
            var nameQueryResponse = await nameQueryResponseTask;

            //If it's not successful, assume the user doesn't exist.
            if (!nameQueryResponse.isSuccessful)
            {
                await SendGuildInviteResponse(context, GuildMemberInviteResponseCode.PlayerNotFound, NetworkEntityGuid.Empty);

                return;
            }

            //Now check if the user is already guilded
            //If they are we should indicate that to the client.
            if (await CheckIfGuilded(nameQueryResponse.Result))
            {
                await SendGuildInviteResponse(context, GuildMemberInviteResponseCode.PlayerAlreadyInGuild, nameQueryResponse.Result);

                return;
            }

            //Ok, the reverse name query was successful. Check if there is a pending invite.
            //TODO: Right now we rely on local state to indicate if there is a pending invite. We need to NOT do that because it won't work when we scale out.
            ProjectVersionStage.AssertBeta();

            //TODO: There is a race condition if multiple invites are sent at the same time, should we care??
            //If they have a pending invite.
            if (PendingInviteData.ContainsKey(nameQueryResponse.Result))
            {
                //If NOT expired then we need to say they're currently pending an invite
                if (!PendingInviteData[nameQueryResponse.Result].isInviteExpired())
                {
                    await SendGuildInviteResponse(context, GuildMemberInviteResponseCode.PlayerAlreadyHasPendingInvite, nameQueryResponse.Result);

                    return;
                }
                else
                {
                    //The invite is EXPIRED so let's added a new one.
                    PendingInviteData.ReplaceObject(nameQueryResponse.Result, GeneratePendingInviteData(context.CallerGuid, guildStatus.Result.GuildId));
                }
            }
            else
            {
                PendingInviteData.AddObject(nameQueryResponse.Result, GeneratePendingInviteData(context.CallerGuid, guildStatus.Result.GuildId));
            }
            //TODO: There is currently no handling to indicate that they are online.

            //Now they have a valid pending invite, so let's address the client
            //that needs to recieve the guild invite.
            IRemoteSocialHubClient playerClient = context.Clients.RetrievePlayerClient(nameQueryResponse.Result);

            //Indicate the player has been invited.
            await SendGuildInviteResponse(context, GuildMemberInviteResponseCode.Success, nameQueryResponse.Result);

            //Now tell the remote/target player they're being invited to a guild.
            await playerClient.ReceiveGuildInviteEventAsync(new GuildMemberInviteEventModel(guildStatus.Result.GuildId, context.CallerGuid));
        }
Example #10
0
 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();
 }
Example #11
0
        protected override void OnGuildStatusChanged(GuildStatusChangedEventModel changeArgs)
        {
            //TODO: This needs to be authorative
            ProjectVersionStage.AssertBeta();
            //If we're now guildless, we need to actually leave the guild chat channel
            //if we're in one.
            if (changeArgs.IsGuildless)
            {
                //TODO: Handle leaving guild channel
                return;
            }

            //We have a guild, let's join the guild channel now.
            UnityAsyncHelper.UnityMainThreadContext.PostAsync(async() =>
            {
                try
                {
                    ResponseModel <VivoxChannelJoinResponse, VivoxLoginResponseCode> channelJoinResponse = await VivoxAutheAuthorizationService.JoinGuildChatAsync();

                    //TODO: Better handle failure.
                    if (!channelJoinResponse.isSuccessful)
                    {
                        if (Logger.IsErrorEnabled)
                        {
                            Logger.Error($"Failed to join Guild world channel. Reason: {channelJoinResponse.ResultCode}");
                        }

                        return;
                    }

                    if (Logger.IsInfoEnabled)
                    {
                        Logger.Info($"Recieved Vivox Channel URI: {channelJoinResponse.Result.ChannelURI}");
                    }

                    //TODO: Awaiting the ChatChannelSession may never complete. We should do a timeout.
                    //TODO: We should share these inputs as shared constants.
                    IChannelSession guildChannel = (await ChatChannelSession).GetChannelSession(new ChannelId(channelJoinResponse.Result.ChannelURI));

                    await guildChannel.ConnectionAsync(false, true, TransmitPolicy.Yes, channelJoinResponse.Result.AuthToken)
                    .ConfigureAwait(true);

                    if (Logger.IsInfoEnabled)
                    {
                        Logger.Info($"Joined Guild Chat");
                    }

                    //Broadcast that we've joined proximity chat.
                    ChannelJoinEventPublisher.PublishEvent(this, new ChatChannelJoinedEventArgs(ChatChannelType.Guild, new DefaultVivoxTextChannelSubscribableAdapter(guildChannel), new DefaultVivoxChatChannelSenderAdapter(guildChannel)));
                }
                catch (Exception e)
                {
                    if (Logger.IsErrorEnabled)
                    {
                        Logger.Error($"Failed to Initialize Guild chat. Reason: {e.Message}\n\nStack: {e.StackTrace}");
                    }

                    throw;
                }
            });
        }
Example #12
0
        public async Task <IActionResult> CreateCharacter([FromRoute] string name,
                                                          [FromServices][NotNull] IPlayfabCharacterClient playfabCharacterClient,
                                                          [FromServices][NotNull] ICharacterAppearanceRepository characterAppearanceRepository,
                                                          [FromServices][NotNull] ICharacterDataRepository characterDataRepository)
        {
            if (playfabCharacterClient == null)
            {
                throw new ArgumentNullException(nameof(playfabCharacterClient));
            }
            if (characterAppearanceRepository == null)
            {
                throw new ArgumentNullException(nameof(characterAppearanceRepository));
            }
            if (characterDataRepository == null)
            {
                throw new ArgumentNullException(nameof(characterDataRepository));
            }
            if (string.IsNullOrWhiteSpace(name))
            {
                throw new ArgumentException("Value cannot be null or whitespace.", nameof(name));
            }

            int accountId = ClaimsReader.GetAccountIdInt(User);

            bool nameIsAvailable = await ValidateNameAvailability(name);

            if (!nameIsAvailable)
            {
                return(BadRequest(new CharacterCreationResponse(CharacterCreationResponseCode.NameUnavailableError)));
            }

            string playfabId = ClaimsReader.GetPlayfabId(User);

            //Now, we actually need to create the character on PlayFab first. It's better to have an orphaned character on PlayFab
            //than to have a character without a PlayFab equivalent.
            PlayFabResultModel <GladMMOPlayFabGrantCharacterToUserResult> playFabResultModel = await playfabCharacterClient.GrantCharacterToUser(new GladMMOPlayFabGrantCharacterToUserRequest(name, "test", playfabId));

            //TODO: Better error handling
            if (playFabResultModel.ResultCode != HttpStatusCode.OK)
            {
                if (Logger.IsEnabled(LogLevel.Error))
                {
                    Logger.LogError($"PlayFab CharacterCreation Erorr: {playFabResultModel.ResultCode}:{playFabResultModel.ResultStatus}");
                }

                return(BadRequest(new CharacterCreationResponse(CharacterCreationResponseCode.GeneralServerError)));
            }

            CharacterEntryModel characterEntryModel = new CharacterEntryModel(accountId, name, playfabId, playFabResultModel.Data.CharacterId);

            //TODO: We need a transition around the creation of the below entries.
            ProjectVersionStage.AssertBeta();
            //TODO: Don't expose the database table model
            //Otherwise we should try to create. There is a race condition here that can cause it to still fail
            //since others could create a character with this name before we finish after checking
            bool result = await CharacterRepository.TryCreateAsync(characterEntryModel);

            //TODO: Also needs to be apart of the transaction
            if (result)
            {
                await characterDataRepository.TryCreateAsync(new CharacterDataModel(characterEntryModel.CharacterId, 0));

                await characterAppearanceRepository.TryCreateAsync(new CharacterAppearanceModel(characterEntryModel.CharacterId, 9));                 //Default is 9 right now.
            }

            return(Json(new CharacterCreationResponse(CharacterCreationResponseCode.Success)));
        }