Login function and pattern matching

Joel Reymont <>
Fri Aug 26 11:44:21 CEST 2005


I have a whole bunch of conditions that need to be checked when a  
player logs in. I can't use guards to pattern match the record fields  
since only built-in guards are allowed. I came up with this for now,  
any optimizations are welcome.

login(Nick, Pass, Socket)
   when is_list(Nick),
        is_list(Pass),
        is_pid(Socket) -> % socket handler process
     login(fetch(Nick), [Pass]).

login({atomic, []}, _) ->
     %% player not found
     {error, ?ERR_BAD_LOGIN};

Here I'm going running check_player() through a list of guards which  
return {true|false, condition}. If any condition is true then  
evaluation of the rest of the guards stops.

login({atomic, [Player]}, [Pass|Args])
   when is_record(Player, player),
        is_list(Pass),
        is_pid(Socket) ->
     %% replace dead pids with none
     Player1 = Player#player {
         socket = fix_pid(Player#player.socket),
         pid = fix_pid(Player#player.pid)
            },
     %% check player state and login
     Condition = check_player(Player1, [Pass],
                  [fun/2 is_bad_password,
                   fun/2 is_account_disabled,
                   fun/2 is_player_busy,
                   fun/2 is_player_online,
                   fun/2 is_client_down,
                   fun/2 is_offline]),
     {Player, Result} = login(Player1, Condition, Args),
     %% update player record
     Player1 = Player#player {
         last_seen = now()
            },
     F = fun() -> mnesia:write(Player) end,
     case mnesia:transaction(F) of
     {atomic, ok} ->
         Result;
     _ ->
         {error, ?ERR_UNKNOWN}
     end.

Then I just pattern-match on the condition and update the player  
structure as needed.

login(Player, bad_password, _) ->
     N = Player#player.failed_login_attempts + 1,
     {atomic, MaxLoginErrors} =
     db:get(cluster_config, 0, max_login_errors),
     Player1 = if
           N > MaxLoginErrors ->
               %% disable account
               Player#player {
             disabled = true
                };
           true ->
               Player
           end,
     Player2 = Player1#player {
         login_errors = N
            },
     {Player2, {error, ?ERR_BAD_LOGIN}};

login(Player, account_disabled, _) ->
     {Player, {error, ?ERR_ACCOUNT_DISABLED}};

login(Player, player_online, Args) ->
     %% player is idle
     logout(Player#player.oid),
     login(Player, player_offline, Args);

etc.

The reason I return {error, ...} or {ok, pid} instead of using  
exceptions is because I actually need to send the error code back to  
the player through the socket. These are not exceptional conditions  
either, they can be encountered in the normal course of operations.

     Thanks, Joel

On Aug 26, 2005, at 5:29 AM, chandru wrote:
>
> You can do this thus.
>
> -record(login, {name, password, other_field1, other_field2}).
>
> auth_user(#login{name=Name, password=Password} = LoginRec) ->
>     case my_password_db:auth_user(Name, Password) of
>            ok ->
>                 extra_auth(LoginRec);
>            Err ->
>                  Err
>     end.
>
> extra_auth(#login{other_field1 = "val1"}) ->
>      do_stuff;
> extra_auth(#login{other_field2 = "val2"}) ->
>      do_someother_stuff.
>
> Or have I not understood your problem?
>
> cheers
> Chandru
>

--
http://wagerlabs.com/uptick






More information about the erlang-questions mailing list