summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--conf/yaws.conf.m42
-rw-r--r--src/db/db_node.erl1
-rw-r--r--src/player/plr_handler.erl40
-rw-r--r--src/player/plr_shim.erl23
-rw-r--r--src/player/query/plr_sign_in.erl131
-rw-r--r--src/player/reply/plr_set_session.erl30
-rw-r--r--src/query/qry_handler.erl1
-rw-r--r--src/shared/shr_security.erl9
-rw-r--r--src/shared/struct/shr_player.erl202
10 files changed, 437 insertions, 4 deletions
diff --git a/Makefile b/Makefile
index 91dc448..97a3bf9 100644
--- a/Makefile
+++ b/Makefile
@@ -114,7 +114,7 @@ debug_rebuild:
ifeq ($(wildcard $(DIALYZER_PLT_FILE)),)
debug_run:
- $(DIALYZER_EXEC) --build_plt --apps erts kernel stdlib jiffy mnesia \
+ $(DIALYZER_EXEC) --build_plt --apps erts kernel stdlib crypto jiffy mnesia \
--output_plt $(DIALYZER_PLT_FILE)
$(MAKE) debug_rebuild
$(DIALYZER_EXEC) --add_to_plt --plt $(DIALYZER_PLT_FILE) -r $(BIN_DIR)
diff --git a/conf/yaws.conf.m4 b/conf/yaws.conf.m4
index d108c73..f0bfeef 100644
--- a/conf/yaws.conf.m4
+++ b/conf/yaws.conf.m4
@@ -110,6 +110,6 @@ keepalive_timeout = 30000
listen = 0.0.0.0
docroot = __MAKEFILE_WWW_DIR
auth_log = true
- appmods = btl_character_turn btl_load map_load map_update
+ appmods = btl_character_turn btl_load map_load map_update plr_sign_in plr_sign_up
start_mod = qry_handler
</server>
diff --git a/src/db/db_node.erl b/src/db/db_node.erl
index aaf6ae3..f94e3bd 100644
--- a/src/db/db_node.erl
+++ b/src/db/db_node.erl
@@ -27,6 +27,7 @@ wait_for_stop () ->
start () ->
Mnesia = db_model:new("/tmp/to_db_node.mnesia", []),
db_model:start(Mnesia),
+ db_model:add_db(player_db, Mnesia),
db_model:add_db(battle_db, Mnesia),
db_model:add_db(map_db, Mnesia),
wait_for_stop(),
diff --git a/src/player/plr_handler.erl b/src/player/plr_handler.erl
new file mode 100644
index 0000000..e1cabe3
--- /dev/null
+++ b/src/player/plr_handler.erl
@@ -0,0 +1,40 @@
+-module(plr_handler).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-export([start/1]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-spec ensure_player_exists (binary()) -> 'ok'.
+ensure_player_exists (ID) ->
+ case shr_database:fetch(player_db, ID, admin) of
+ {ok, _} -> ok;
+ not_found ->
+ shr_database:insert
+ (
+ player_db,
+ ID,
+ any,
+ any,
+ plr_shim:generate_random_player(ID)
+ )
+ end,
+
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-spec start (pid()) -> 'ok'.
+start (TimedCachesManagerPid) ->
+ ensure_player_exists(<<"0">>),
+ ensure_player_exists(<<"1">>),
+ shr_timed_caches_manager:new_cache(TimedCachesManagerPid, player_db, none),
+ ok.
diff --git a/src/player/plr_shim.erl b/src/player/plr_shim.erl
new file mode 100644
index 0000000..c5da825
--- /dev/null
+++ b/src/player/plr_shim.erl
@@ -0,0 +1,23 @@
+-module(plr_shim).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-export([generate_random_player/1]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-spec generate_random_player (binary()) -> shr_player:type().
+generate_random_player (ID) ->
+ Result = shr_player:new(ID, ID, <<"kalimero">>),
+
+ Result.
diff --git a/src/player/query/plr_sign_in.erl b/src/player/query/plr_sign_in.erl
new file mode 100644
index 0000000..e872a55
--- /dev/null
+++ b/src/player/query/plr_sign_in.erl
@@ -0,0 +1,131 @@
+-module(plr_sign_in).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-include("../../../include/yaws_api.hrl").
+
+-record
+(
+ input,
+ {
+ username :: binary(),
+ password :: binary()
+ }
+).
+
+-record
+(
+ query_state,
+ {
+ player :: shr_player:type()
+ }
+).
+
+-type input() :: #input{}.
+-type query_state() :: #query_state{}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-export([out/1]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-spec parse_input (binary()) -> input().
+parse_input (Req) ->
+ JSONReqMap = jiffy:decode(Req, [return_maps]),
+ Username = maps:get(<<"usr">>, JSONReqMap),
+ Password = maps:get(<<"pwd">>, JSONReqMap),
+
+ #input
+ {
+ username = Username,
+ password = Password
+ }.
+
+-spec fetch_data (input()) -> query_state().
+fetch_data (Input) ->
+ PlayerID = Input#input.username,
+
+ Player = shr_timed_cache:fetch(player_db, any, PlayerID),
+
+ #query_state
+ {
+ player = Player
+ }.
+
+-spec update_data (query_state(), input()) -> query_state().
+update_data (QueryState, Input) ->
+ InputPassword = Input#input.password,
+ Player = QueryState#query_state.player,
+
+ true = (shr_player:password_is(InputPassword, Player)),
+
+ S0Player = shr_player:new_token(Player),
+ S1Player = shr_player:refresh_active(S0Player),
+
+ QueryState#query_state
+ {
+ player = S1Player
+ }.
+
+-spec commit_update (query_state(), input()) -> 'ok'.
+commit_update (QueryState, Input) ->
+ PlayerID = Input#input.username,
+ UpdatedPlayer = QueryState#query_state.player,
+ NewToken = shr_player:get_token(UpdatedPlayer),
+ NewActiveTime = shr_player:get_last_active(UpdatedPlayer),
+
+ Query =
+ shr_db_query:new
+ (
+ player_db,
+ PlayerID,
+ {user, PlayerID},
+ [
+ shr_db_query:set_field
+ (
+ shr_player:get_token_field(),
+ NewToken
+ ),
+ shr_db_query:set_field
+ (
+ shr_player:get_last_active_field(),
+ NewActiveTime
+ )
+ ]
+ ),
+
+ shr_database:commit(Query),
+ shr_timed_cache:update(player_db, PlayerID, PlayerID, UpdatedPlayer),
+
+ 'ok'.
+
+-spec generate_reply(query_state()) -> binary().
+generate_reply (QueryState) ->
+ Player = QueryState#query_state.player,
+
+ SetSession = plr_set_session:generate(Player),
+ Output = jiffy:encode([SetSession]),
+
+ Output.
+
+-spec handle (binary()) -> binary().
+handle (Req) ->
+ Input = parse_input(Req),
+ QueryState = fetch_data(Input),
+ Update = update_data(QueryState, Input),
+ commit_update(Update, Input),
+ generate_reply(QueryState).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+out(A) ->
+ {
+ content,
+ "application/json; charset=UTF-8",
+ handle(A#arg.clidata)
+ }.
diff --git a/src/player/reply/plr_set_session.erl b/src/player/reply/plr_set_session.erl
new file mode 100644
index 0000000..8b38fd7
--- /dev/null
+++ b/src/player/reply/plr_set_session.erl
@@ -0,0 +1,30 @@
+-module(plr_set_session).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-export([generate/1]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-spec generate (shr_player:type()) -> {list(any())}.
+generate (Player) ->
+ PID = shr_player:get_id(Player),
+ SessionToken = shr_player:get_token(Player),
+
+ {
+ [
+ {<<"msg">>, <<"sse">>},
+ {<<"pid">>, PID},
+ {<<"stk">>, SessionToken}
+ ]
+ }.
diff --git a/src/query/qry_handler.erl b/src/query/qry_handler.erl
index 15f766e..9ba88f0 100644
--- a/src/query/qry_handler.erl
+++ b/src/query/qry_handler.erl
@@ -19,6 +19,7 @@
-spec start (any()) -> 'ok'.
start (_YawsParams) ->
{ok, TimedCachesManagerPid} = shr_timed_caches_manager:start(),
+ ok = plr_handler:start(TimedCachesManagerPid),
ok = btl_handler:start(TimedCachesManagerPid),
ok = map_handler:start(TimedCachesManagerPid),
ok.
diff --git a/src/shared/shr_security.erl b/src/shared/shr_security.erl
index 724b241..740e948 100644
--- a/src/shared/shr_security.erl
+++ b/src/shared/shr_security.erl
@@ -23,8 +23,13 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--spec assert_identity (any(), any()) -> 'unimplemented'.
-assert_identity (_PlayerID, _SessionToken) -> unimplemented.
+-spec assert_identity (any(), any()) -> 'ok'.
+assert_identity (PlayerID, SessionToken) ->
+ Player = shr_timed_cache:fetch(player_db, any, PlayerID),
+
+ true = (shr_player:get_token(Player) == SessionToken),
+
+ ok.
-spec lock_queries (any()) -> 'unimplemented'.
lock_queries (_PlayerID) -> unimplemented.
diff --git a/src/shared/struct/shr_player.erl b/src/shared/struct/shr_player.erl
new file mode 100644
index 0000000..b036cb6
--- /dev/null
+++ b/src/shared/struct/shr_player.erl
@@ -0,0 +1,202 @@
+-module(shr_player).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-type id() :: binary().
+
+-record
+(
+ player,
+ {
+ id :: id(),
+ username :: binary(),
+ % {salt(crypto:strong_rand_bytes(128)), hash(sha384)}
+ password :: {binary(), binary()},
+ token :: binary(), % salt(crypto:strong_rand_bytes(512))
+ email :: binary(),
+ last_active :: integer(),
+ maps :: list(binary()),
+ characters :: list(binary())
+ }
+).
+
+-opaque type() :: #player{}.
+
+-export_type([type/0, id/0]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-export
+(
+ [
+ new/3
+ ]
+).
+
+%%%% Accessors
+-export
+(
+ [
+ get_id/1,
+ get_username/1,
+ get_password/1,
+ get_token/1,
+ get_email/1,
+ get_last_active/1,
+ get_maps/1,
+ get_characters/1,
+
+ set_username/2,
+ set_password/2,
+ new_token/1,
+ set_email/2,
+ refresh_active/1,
+ set_maps/2,
+ set_characters/2
+ ]
+).
+
+-export
+(
+ [
+ get_id_field/0,
+ get_username_field/0,
+ get_password_field/0,
+ get_token_field/0,
+ get_email_field/0,
+ get_last_active_field/0,
+ get_maps_field/0,
+ get_characters_field/0
+ ]
+).
+
+-export
+(
+ [
+ password_is/2
+ ]
+).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-spec secure_value (binary(), binary()) -> binary().
+secure_value (Salt, Val) ->
+ SaltedVal = erlang:iolist_to_binary([Salt, Val]),
+ HashedSaltedVal = crypto:hash(sha384, SaltedVal),
+
+ HashedSaltedVal.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-spec new (binary(), binary(), binary()) -> type().
+new (ID, Username, Password) ->
+ Result =
+ #player
+ {
+ id = ID,
+ username = Username,
+ password = {<<"">>, <<"">>},
+ token = <<"">>,
+ email = <<"">>,
+ last_active = 0,
+ maps = [],
+ characters = []
+ },
+
+ S0Result = set_password(Password, Result),
+ S1Result = new_token(S0Result),
+ S2Result = refresh_active(S1Result),
+
+ S2Result.
+
+%%%% Accessors
+-spec get_id (type()) -> id().
+get_id (Player) -> Player#player.id.
+
+-spec get_username (type()) -> binary().
+get_username (Player) -> Player#player.username.
+
+-spec get_password (type()) -> {binary(), binary()}.
+get_password (Player) -> Player#player.password.
+
+-spec get_token (type()) -> binary().
+get_token (Player) -> Player#player.token.
+
+-spec get_email (type()) -> binary().
+get_email (Player) -> Player#player.email.
+
+-spec get_last_active (type()) -> integer().
+get_last_active (Player) -> Player#player.last_active.
+
+-spec get_maps (type()) -> list(binary()).
+get_maps (Player) -> Player#player.maps.
+
+-spec get_characters (type()) -> list(binary()).
+get_characters (Player) -> Player#player.characters.
+
+-spec set_username (binary(), type()) -> type().
+set_username (Val, Player) -> Player#player{ username = Val }.
+
+-spec set_password (binary(), type()) -> type().
+set_password (Val, Player) ->
+ NewSalt = crypto:strong_rand_bytes(128),
+ HashedSaltedVal = secure_value(NewSalt, Val),
+
+ Player#player
+ {
+ password = {NewSalt, HashedSaltedVal}
+ }.
+
+-spec new_token (type()) -> type().
+new_token (Player) -> Player#player{ token = crypto:strong_rand_bytes(512) }.
+
+-spec set_email (binary(), type()) -> type().
+set_email (Val, Player) -> Player#player{ email = Val }.
+
+-spec refresh_active (type()) -> type().
+refresh_active (Player) ->
+ Player#player
+ {
+ last_active = erlang:system_time(second)
+ }.
+
+-spec set_maps (list(binary()), type()) -> type().
+set_maps (Maps, Player) -> Player#player{ maps = Maps }.
+
+-spec set_characters (list(binary()), type()) -> type().
+set_characters (Characters, Player) -> Player#player{ characters = Characters }.
+
+-spec get_id_field () -> non_neg_integer().
+get_id_field () -> #player.id.
+
+-spec get_username_field () -> non_neg_integer().
+get_username_field () -> #player.username.
+
+-spec get_password_field () -> non_neg_integer().
+get_password_field () -> #player.password.
+
+-spec get_token_field () -> non_neg_integer().
+get_token_field () -> #player.token.
+
+-spec get_email_field () -> non_neg_integer().
+get_email_field () -> #player.email.
+
+-spec get_last_active_field () -> non_neg_integer().
+get_last_active_field () -> #player.last_active.
+
+-spec get_maps_field () -> non_neg_integer().
+get_maps_field () -> #player.maps.
+
+-spec get_characters_field () -> non_neg_integer().
+get_characters_field () -> #player.characters.
+
+-spec password_is (binary(), type()) -> boolean().
+password_is (Val, Player) ->
+ {Salt, HashedSaltedVal} = Player#player.password,
+ HashedSaltedCandidate = secure_value(Salt, Val),
+
+ (HashedSaltedCandidate == HashedSaltedVal).