Current section
Files
Jump to
Current section
Files
src/ow_session_util.erl
%%=========================================================================
%% Overworld Session Utilities
%%
%% This module holds various RPCs for sessions and utilty functions for
%% handling sessions and session-related messages
%%
%%=========================================================================
-module(ow_session_util).
% RPC functions
-export([session_ping/2, session_request/2]).
% Utility functions
-export([disconnect/1, notify_clients/3]).
%%===========================================================================
%% RPC API
%%===========================================================================
-rpc_encoder(#{app => overworld, lib => overworld_pb, interface => ow_msg}).
-rpc_client([session_beacon, session_new, session_pong]).
-rpc_server([session_request, session_ping]).
%%----------------------------------------------------------------------------
%% @doc Calculate the latency based on the RTT to the client
%% @end
%%----------------------------------------------------------------------------
-spec session_ping(map(), pid()) -> {atom(), map()}.
session_ping(Msg, SessionPID) ->
BeaconID = maps:get(id, Msg),
Last = ow_beacon:get_by_id(BeaconID),
Now = erlang:monotonic_time(),
% NOTE: This change to RTT compared to ow_session (v1)
Latency = erlang:convert_time_unit(
round(Now - Last), native, millisecond
),
{ok, Latency} = ow_session:latency(Latency, SessionPID),
{session_pong, #{latency => Latency}}.
%%----------------------------------------------------------------------------
%% @doc Request a new session, or rejoin an existing one
%% @end
%%----------------------------------------------------------------------------
-spec session_request(map(), pid()) -> ok.
session_request(Msg, SessionPID) ->
logger:debug("Got session request: ~p", [Msg]),
Token = maps:get(token, Msg, undefined),
case Token of
undefined ->
logger:debug("No token defined, starting a new session"),
% No session existing, start a new one
NewToken = ow_token_serv:new(SessionPID),
ID = ow_session:id(SessionPID),
Reply = #{id => ID, reconnect_token => NewToken},
% Send the reply back through the proxy
notify_clients(session_new, Reply, [SessionPID]);
_ ->
{PrevSessionPID, NewToken} = ow_token_serv:exchange(Token),
% Inform the proxy process to update its session ID to refer to the
% previous, existing one. Internal clients will probably(?) never
% need to do this
ProxyPID = ow_session:proxy(SessionPID),
ProxyPID ! {reconnect_session, PrevSessionPID},
% Inform the zone that the client has reconnected
ZonePid = ow_session:zone(PrevSessionPID),
ow_zone:reconnect(ZonePid, PrevSessionPID),
% Update the session server with the new token
{ok, NewToken} = ow_session:token(NewToken, PrevSessionPID),
% Stop the temporary session
ok = ow_session_sup:delete(SessionPID)
end,
% Register the process of the caller
gproc:reg({p, l, client_session}),
ok.
%%===========================================================================
%% Utility API
%%===========================================================================
%%----------------------------------------------------------------------------
%% @doc Set the session to disconnected state and run the appropriate callback
%% handler
%% @end
%%----------------------------------------------------------------------------
-spec disconnect(pid()) -> ok.
disconnect(SessionPID) ->
% We've caught an error or otherwise asked to stop, clean up the session
case ow_session:disconnect_callback(SessionPID) of
{Module, Fun, Args} ->
logger:notice("Calling: ~p:~p(~p)", [Module, Fun, Args]),
erlang:apply(Module, Fun, Args);
undefined ->
ok
end,
% Reset the session proxy
{ok, _} = ow_session:disconnect(SessionPID),
ok.
%%----------------------------------------------------------------------------
%% @doc Send a message to a list of clients
%% @end
%%----------------------------------------------------------------------------
-spec notify_clients(atom(), map(), [pid()]) -> ok.
notify_clients(_MsgType, _Msg, []) ->
ok;
notify_clients(MsgType, Msg, [SessionPID | Rest]) ->
logger:debug("Notifying client ~p: ~p", [SessionPID, {MsgType, Msg}]),
try
ProxyPID = ow_session:proxy(SessionPID),
case ProxyPID of
undefined ->
logger:debug("no proxy defined, doing nothing"),
ok;
_ ->
% Send a message to the client, let the connection handler figure
% out how to serialize it further
logger:debug("Sending client message to ~p", [ProxyPID]),
ProxyPID ! {self(), client_msg, {MsgType, Msg}}
end
catch
exit:{noproc, _} ->
logger:debug("Couldn't communicate with session ~p: noproc", [SessionPID]),
ok
end,
notify_clients(MsgType, Msg, Rest).