Example #1
0
        /// <summary>
        /// Logs in the given <see cref="Client"/> and binds its <see cref="Account"/> to the <see cref="Client"/> with a <see cref="Session"/>.
        /// <remarks>Fails if the <see cref="Client"/> is already logged in.</remarks>
        /// </summary>
        /// <param name="client">The <see cref="Client"/> that should be logged in.</param>
        /// <param name="userName">The username of the <see cref="Account"/> that should be used in the login.</param>
        /// <param name="password">The password of the <see cref="Account"/> that should be used in the login.</param>
        /// <returns>A <see cref="LoginResult"/> indicating the success of the operation and the newly created <see cref="Session"/>.</returns>
        public Task <LoginResult> LoginClientAsync(Client.Client client, string userName, string password)
        {
            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }

            if (string.IsNullOrWhiteSpace(userName))
            {
                throw new ArgumentException(@"Value cannot be null or whitespace.", nameof(userName));
            }

            if (string.IsNullOrWhiteSpace(password))
            {
                throw new ArgumentException(@"Value cannot be null or whitespace.", nameof(password));
            }

            //Run the database access in another thread.
            var task = new Task <LoginResult>(() =>
            {
                //Check that neither the client is already logged in nor the account with the given username is already in use by another client.
                lock (_Lock)
                {
                    if (_SessionByClient.ContainsKey(client))
                    {
                        throw new InvalidOperationException(@"The given client is already logged in.");
                    }

                    if (_SessionByClient.Values.Any(session =>
                                                    session.Account.UserName.Equals(userName, StringComparison.OrdinalIgnoreCase)))
                    {
                        return(new LoginFailedResult(LoginFailedReason.AccountAlreadyLoggedIn, string.Empty));
                    }


                    using (var context = _ContextFactory.CreateContext())
                    {
                        //Try to get an Account with the given username
                        AccountEntity accountEntity = context.Accounts.FirstOrDefault(ac =>
                                                                                      ac.UserName.Equals(userName, StringComparison.OrdinalIgnoreCase));

                        //An account with the given name does not exist or the password is wrong or is banned.
                        if (accountEntity == null || !_PasswordService.VerifyPassword(password, accountEntity.PasswordHash))
                        {
                            return(new LoginFailedResult(LoginFailedReason.InvalidLoginData, string.Empty));
                        }

                        if (accountEntity.IsBanned)
                        {
                            return(new LoginFailedResult(LoginFailedReason.Banned, accountEntity.BannedReasonText));
                        }

                        //Save last login info to the database.
                        accountEntity.LastLogin     = DateTime.Now;
                        accountEntity.LastIpAddress = client.SystemAddress;
                        context.SaveChanges();

                        // Login successful, create an Account object and bind it to the client with a session.
                        Account account = new Account(accountEntity.UserName, accountEntity.AccountId, accountEntity.PasswordHash, _ComponentSelector);
                        Session session = new Session(client, account);
                        _SessionByClient.Add(client, session);
                        _ClientIdLoggedIn[client.Id] = true;

                        //Invoke the login event. Enqueue it on the dispatcher, so it will be executed after we leave the current lock.
                        _Dispatcher.EnqueueAction(() => ClientLoggedIn?.Invoke(this, new LoginEventArgs(session)));

                        return(new LoginSuccessfulResult(session));
                    }
                }
            });

            //Run the task.
            task.Start();
            return(task);
        }
Example #2
0
        public Task <CharacterCreationResult> CreateHumanPlayerCharacterAsync(CharCreationInfo creationInfo)
        {
            if (creationInfo == null)
            {
                throw new ArgumentNullException(nameof(creationInfo));
            }

            return(_CharacterService.CheckCharacterExistsAsync(creationInfo.Name).ContinueWith <CharacterCreationResult>(task =>
            {
                //Validate the creation info because it comes directly from the client(we do not trust the client).
                if (!creationInfo.Validate(out string invalidProperty, out object value))
                {
                    _Log.Warn($"Character creation failed due to invalid {nameof(CharCreationInfo)}, invalid attribute is '{invalidProperty}' with value '{value}'");
                    return new CharacterCreationFailed(CharacterCreationFailure.InvalidCreationInfo);
                }

                //Check whether the name can be used for a character.
                if (!_NameValidator.IsValid(creationInfo.Name))
                {
                    return (CharacterCreationResult) new CharacterCreationFailed(CharacterCreationFailure.NameIsInvalid);
                }

                //The character does already exist.
                if (task.Result)
                {
                    return (CharacterCreationResult) new CharacterCreationFailed(CharacterCreationFailure
                                                                                 .AlreadyExists);
                }

                //Create the new character
                try
                {
                    using (CharacterManagementContext db = _ContextFactory.Create())
                    {
                        SpawnPoint spawn = _SpawnPointProvider.GetSpawnPoint();

                        //Add the character
                        var characterEntity = new CharacterEntity
                        {
                            CharacterName = creationInfo.Name,
                            PositionX = spawn.Point.X,
                            PositionY = spawn.Point.Y,
                            PositionZ = spawn.Point.Z,
                            Rotation = spawn.Rotation.Yaw,
                            WorldName = spawn.World.Path,
                            TemplateName = _CharacterTemplateSelector.GetTemplate(creationInfo)
                        };
                        db.Characters.Add(characterEntity);

                        //Add the visuals of the character.
                        var customVisuals = new CharacterCustomVisualsEntity
                        {
                            OwnerCharacter = characterEntity,
                            BodyMesh = creationInfo.BodyMesh,
                            BodyTex = creationInfo.BodyTex,
                            HeadMesh = creationInfo.HeadMesh,
                            HeadTex = creationInfo.HeadTex,
                            Voice = creationInfo.Voice,
                            Fatness = creationInfo.Fatness,
                            BodyWidth = creationInfo.BodyWidth
                        };
                        db.CustomVisuals.Add(customVisuals);

                        //Save the changes.
                        db.SaveChanges();


                        Character character = _CharacterBuilder.HumanCharacterFromEntities(characterEntity, customVisuals);

                        //Invoke the character creation event(later so we can inform the client first)
                        _Dispatcher.EnqueueAction(() =>
                        {
                            _CharacterService.OnCharacterCreated(new CharacterCreatedArgs(character));
                        });

                        //Return information about successful character creation.
                        return new CharacterCreationSuccess(character);
                    }
                }
                catch (Exception e)
                {
                    throw new DatabaseRequestException("Something went wrong while adding the new character to the database.", e);
                }
            }));
        }