黄小华的个人网站
熬过无人问津的日子才有诗和远方!
erlang阶段学习总结

%%%------------------------------------------------------------------- %%% @author hxh %%% @copyright 2021, SY %%% Created : 2021 3月 27 9:59 %%%------------------------------------------------------------------- -module(guild_mgr). -author("hxh"). -behaviour(gen_fsm). -include_lib("guild.hrl").

-define(INFO(Msg), io:format(lists:concat(["##[INFO][~w:~w]", Msg, "~n"]), [?MODULE, ?LINE])). -define(INFO(F, A), io:format(lists:concat(["##[INFO][~w:~w]", F, "~n"]), [?MODULE, ?LINE | A])). -define(ERR(Msg), io:format(lists:concat(["##[ERROR][~w:~w]", Msg, "~n"]), [?MODULE, ?LINE])). -define(ERR(F, A), io:format(lists:concat(["##[ERROR][~w:~w]", F, "~n"]), [?MODULE, ?LINE | A])). -define(IF(__Cond, __A, __B), case (__Cond) of true -> (__A); _ -> (__B) end).

%% 带catch的gen_fsm:sync_send_all_state_event/2 %% 返回{error, timeout} | {error, noproc} | {error, term()} | term() | {exit, normal} -define(FSM_SYNC_EVENT(_Call_Pid, _Call_Request), ?FSM_SYNC_EVENT(_Call_Pid, _Call_Request, 10000)). -define(FSM_SYNC_EVENT(_Call_Pid, _Call_Request, _Time_Out), case catch gen_fsm:sync_send_all_state_event(_Call_Pid, _Call_Request, _Time_Out) of {'EXIT', {timeout, _}} -> {error, timeout}; {'EXIT', {noproc, _}} -> {error, noproc}; {'EXIT', {normal, _}} -> {error, exit}; {'EXIT', _Call_Err} -> {error, _Call_Err}; _Call_Return -> _Call_Return end ).

%% 外部接口 -export([start_link/0, create/3, join/3, lookup_acc/1, lookup_guild/1, exit/1, disband/1, rank/2]).

%% 内部处理 -export([init/1, handle_event/3, handle_sync_event/4, handle_info/3, judge_time/0, continue/3, terminate/3, code_change/4]).

%% 状态函数 -export([event/2, normal/2, idle/2]).

%% ---------------------------------------------------- %% 外部接口 %% ----------------------------------------------------

%% @doc 启动 -spec start_link() -> {ok, pid()} | ignore | {error, term()}. start_link() -> gen_fsm:start_link({local, ?MODULE}, ?MODULE, [], []). -spec create(Account, Name, GuildName) -> Result when Account :: atom(), Name :: bitstring(), GuildName :: bitstring(), Result :: {true, GuildId} | {false, Reason}, GuildId :: non_neg_integer(), Reason :: bitstring(). create(Account, Name, GuildName) -> gen_fsm:sync_send_all_state_event(?MODULE, {sync_event, create, {Account, Name, GuildName}}).

-spec join(Account, Name, GuildId) -> Result when Account :: atom(), Name :: bitstring(), GuildId :: non_neg_integer(), Result :: true | {false, Reason}, Reason :: bitstring(). join(Account, Name, GuildId) -> gen_fsm:sync_send_all_state_event(?MODULE, {sync_event, join, {Account, Name, GuildId}}).

-spec lookup_acc(Account) -> Result when Account :: atom(), Result :: {true, #guild_role{}} | false. lookup_acc(Account) -> gen_fsm:sync_send_all_state_event(?MODULE, {sync_event, lookup_acc, }).

-spec lookup_guild(GuildId) -> Result when GuildId :: non_neg_integer(), Result :: {true, #guild{}} | false. lookup_guild(GuildId) -> gen_fsm:sync_send_all_state_event(?MODULE, {sync_event, lookup_guild, }). exit(Account) -> gen_fsm:sync_send_all_state_event(?MODULE, {sync_event, exit, }). disband(Account) -> gen_fsm:sync_send_all_state_event(?MODULE, {sync_event, disband, }). rank(SortType, Num) -> gen_fsm:sync_send_all_state_event(?MODULE, {sync_event, rank, {SortType, Num}}).

%% ---------------------------------------------------- %% 内部处理 %% ----------------------------------------------------

init([]) -> ?INFO("[~w] 正在启动...", [?MODULE]), process_flag(trap_exit, true), %声明系统进程 load(), %加载恢复数据 List1 = ets:tab2list(guild_list), List2 = [A || #guild <- List1], #guild = lists:last(lists:keysort(#guild.guild_id, List1)), Count = ?IF(List1 == [], 0, B1), {StateName, Timeout} = judge_time(), State = #state, ?INFO("[~w] 启动完成...", [?MODULE]), {ok, StateName, State, Timeout}.

load() -> dets:open_file(guild_list, [{file, "guild_list"}, {keypos, #guild.guild_id}]), ets:new(guild_list, [named_table, {keypos, #guild.guild_id}]), ets:new(guild_role_list, [named_table, {keypos, #guild_role.account}]), upgrade_ver().

upgrade_ver() -> Upgrade = fun(Data = #guild{}) -> convert_guild_date(guild_list, Data), continue; (_Data) -> io:format("got some data error! ~n"), error end, traverse(guild_list, Upgrade).

traverse(CacheName, Fun) -> case dets:first(CacheName) of '$end_of_table' -> io:format("there is no element left! ~n"), true; _ -> case dets:traverse(CacheName, Fun) of [] -> io:format("there is no element left! ~n"), true; Error -> {false, Error} end end.

convert_guild_date(_role_table_name, Data) -> io:format("show the Data ~p ~n", [Data]), {guild, Guild_id, Guild_name, Create_time, Create_account, Create_name, Member_num, Members} = Data, guild:start_link(Guild_id, Guild_name, Create_account, Create_name), [ets:insert(guild_role_list, #guild_role) || {Account, Name} <- Members], %%忘记将除创造公会的其他用户加入到role中 Guild_name ! {info, Member_num, Members}, ets:insert(guild_list, Data#guild{}).

judge_time() -> {M, S, MS} = os:timestamp(), {_, Time} = calendar:now_to_local_time({M, S, MS}), {Hour, _, _} = Time, if (Hour >= 8) and (Hour < 18) -> Timeout = calendar:time_to_seconds({18, 00, 00}) - calendar:time_to_seconds(Time), {normal, Timeout}; (Hour >= 18) and (Hour < 20) -> Timeout = calendar:time_to_seconds({20, 00, 00}) - calendar:time_to_seconds(Time), {event, Timeout}; (Hour < 8) -> Timeout = calendar:time_to_seconds({8, 00, 00}) - calendar:time_to_seconds(Time), {idle, Timeout}; (Hour >= 20) -> Timeout = calendar:time_to_seconds({32, 00, 00}) - calendar:time_to_seconds(Time), {idle, Timeout} end.

%% 同步消息处理 handle_sync_event({sync_event, Msg, Args}, _From, StateName, State) -> case catch do_handle_sync_event(State, StateName, Msg, Args) of {ok, Reply} -> continue(StateName, Reply, State); {ok, Reply, NewState} -> continue(StateName, Reply, NewState); {ok, Reply, NewStateName, NewState} -> continue(NewStateName, Reply, NewState); {false, Reason} -> io:format("~p ~n", [Reason]), continue(StateName, State); Else -> io:format("~p ~n", [Else]), continue(StateName, State) end; handle_sync_event(_Request, _From, _StateName, _State) -> ok.

%% 异步消息处理 handle_event({event, Msg, Args}, StateName, State) -> case catch do_handle_event(State, StateName, Msg, Args) of ok -> continue(StateName, State); {ok, NewState} -> continue(StateName, NewState); {ok, NewStateName, NewState} -> continue(NewStateName, NewState); {stop, NewState} -> {stop, normal, NewState}; {false, Reason} -> io:format("~p ~n", [Reason]), continue(StateName, State); Else -> io:format("~p ~n", [Else]), continue(StateName, State) end; handle_event(_Msg, _StateName, _State) -> ok.

%% info消息处理 handle_info({info, Msg, Args}, StateName, State) -> case catch do_handle_info(State, StateName, Msg, Args) of ok -> continue(StateName, State); {ok, NewState} -> continue(StateName, NewState); {ok, NewStateName, NewState} -> continue(NewStateName, NewState); {stop, NewState} -> {stop, normal, NewState}; {false, Reason} -> io:format("11 ~p ~n", [Reason]), continue(StateName, State); _ -> continue(StateName, State) end;

handle_info({'EXIT', Pid, normal}, StateName, State) -> io:format("exit ~p", [Pid]), continue(StateName, State);

handle_info(_Info, StateName, State) -> continue(StateName, State).

code_change(_OldVsn, StateName, State, _Extra) -> continue(StateName, State).

terminate(_Reason, _StateName, _State) -> ?INFO("[~w] 正在关闭...", [?MODULE]), dets:close(guild_list), ?INFO("[~w] 关闭完成", [?MODULE]), ok.

exited(#guild = Guild, Account_id) -> List = lists:keydelete(Account_id, 1, AccountList), ets:update_element(guild_role_list, Account_id, {#guild_role.join_time, unixtime()}), ets:update_element(guild_role_list, Account_id, {#guild_role.guild_id, 0}), ets:update_element(guild_list, Guild_id, {#guild.member_num, Num - 1}), ets:update_element(guild_list, Guild_id, ), dets:insert(guild_list, Guild#guild{member_num = Num - 1, members = List}), Guild_name ! {info, Num - 1, List}, true. disband(Account, GuildId, GuildName, State, List) -> ets:delete(guild_role_list, Account), ets:delete(guild_list, GuildId), dets:delete(guild_list, GuildId), GuildName ! stop, {true, State#state{name_list = lists:delete(GuildName, List)}}.

do_create([], _Args) -> {ok, ok}; do_create([judge_state | L], # = Args) -> case StateName =:= idle of false -> do_create([judge_account | L], Args); true -> {{false, idle_not_create}, State} end; do_create([judge_account | L], # = Args) -> Ms = unixtime(), case ets:lookup(guild_role_list, Account) of [#guild_role] when Ms - Times > 60 -> do_create(L, Args); [#guild_role] when Ms - Times < 60 -> {cooling_not_create, State}; [_] -> {{false, the_player_has_joined_the_guild}, State#state}; [] -> do_create(L, Args) end;

do_create([judge_guild_name | L], # = Args) -> case lists:member(GuildName, List) of true -> {{false, the_guild_name_already_exists}, State#state}; false -> do_create(L, Args) end; do_create([create], # = _Args) -> guild:start_link(Count + 1, GuildName, Account, Name), Guild = #guild{guild_id = Count + 1, guild_name = GuildName, create_time = unixtime(), create_account = Account, create_name = Name, member_num = 1, members = [{Account, Name}]}, ets:insert(guild_list, Guild), ets:insert(guild_role_list, #guild_role{account = Account, name = Name, join_time = unixtime(), guild_id = Count + 1}), dets:insert(guild_list, Guild), {{true, Count + 1}, State#state{count = Count + 1, name_list = [GuildName | List]}}; do_create(_, _) -> {error, <<"args_error">>}.

do_join([], _Args) -> {ok, ok}; do_join([judge_state | L], # = Args) -> case StateName =:= event of false -> do_join(L, Args); true -> {{false, event}, State} end; do_join([judge_account | L], # = Args) -> Ms = unixtime(), case ets:lookup(guild_role_list, Account) of [#guild_role] when Ms - Times > 60 -> do_join(L, Args); [#guild_role] when Ms - Times < 60 -> {{false, cooling_not_join}, State}; [_] -> {{false, the_player_has_joined_the_guild}, State}; [] -> do_join(L, Args) end; do_join([join], # = Args) -> case ets:lookup(guild_list, GuildId) of [#guild = Guild] -> List = [{Account, Name} | AccountList], ets:update_element(guild_list, GuildId, {#guild.member_num, Num + 1}), ets:update_element(guild_list, GuildId, ), dets:insert(guild_list, Guild#guild{member_num = Num + 1, members = List}), ets:insert(guild_role_list, #guild_role), Guild_name ! {info, Num + 1, List}, {true, State}; [] -> {{false, not_the_guild}, State} end; do_join(, _) -> {error, <<"args_error">>}. %% ---------------------------- %% 同步消息 %% ---------------------------- do_handle_sync_event(#state = State, StateName, create, _Args = {Account, Name, GuildName}) -> case do_create([judge_state, judge_account, judge_guild_name, create], #{count => Count, list => List, statename => StateName, account => Account, name => Name, guild_name => GuildName, state => State}) of {Reply, NewState} -> {ok, Reply, NewState} end;

do_handle_sync_event(State, StateName, join, _Args = {Account, Name, GuildId}) -> case do_join([judge_state, judge_account, join], #{state => State, statename => StateName, account => Account, name => Name, guild_id => GuildId}) of {Reply, NewState} -> {ok, Reply, NewState} end;

do_handle_sync_event(State, _StateName, lookup_acc, _Args = ) -> Reply = case ets:lookup(guild_role_list, Account) of [Guild_role] -> {true, Guild_role}; [] -> false end, {ok, Reply, State};

do_handle_sync_event(State, _StateName, lookup_guild, _Args = ) -> Reply = case ets:lookup(guild_list, GuildId) of [Guild] -> {true, Guild}; [] -> false end, {ok, Reply, State};

do_handle_sync_event(State, _StateName, exit, _Args = ) -> [#guild_role] = ets:lookup(guild_role_list, Account), [Guild] = ets:lookup(guild_list, Guild_id), #guild = Guild, Reply = case Create_account =:= Account of true -> {false, the_creator_may_not_withdraw_from_the_guild}; false -> exited(Guild, Account) end, {ok, Reply, State};

do_handle_sync_event(#state = State, _StateName, disband, _Args = ) -> [#guild_role] = ets:lookup(guild_role_list, Account), [#guild] = ets:lookup(guild_list, GuildId), {Reply, NewState} = ?IF(Num =:= 1, disband(Account, GuildId, GuildName, State, List), {{false, disband_false}, State}), {ok, Reply, NewState};

do_handle_sync_event(State, _StateName, rank, _Args = {SortType, Num}) -> GuildList = ets:tab2list(guild_list), Reply = case SortType of 1 -> SortFun = fun(A, B) -> A#guild.member_num > B#guild.member_num end, List1 = lists:sort(SortFun, GuildList), lists:sublist(List1, Num); 2 -> List1 = lists:keysort(#guild.create_time, GuildList), lists:sublist(List1, Num) end, {ok, Reply, State};

do_handle_sync_event(_State, _StateName, _Msg, _Args) -> ok.

%% ---------------------------- %% 异步消息 %% ----------------------------

%% 容错 do_handle_event(_State, _StateName, _Msg, _Args) -> ok.

%% ---------------------------------- %% info %% ---------------------------------- %% 容错 do_handle_info(State, _StateName, stop, _Args) -> {stop, State}; do_handle_info(_State, _StateName, _Msg, _Args) -> ok.

%% ---------------------------------- %% @doc 状态函数 %% ----------------------------------

normal(timeout, State) -> {StateName, Timeout} = judge_time(), NewState = State#state, continue(StateName, NewState); normal(_Any, State) -> continue(normal, State).

event(timeout, State) -> {StateName, Timeout} = judge_time(), NewState = State#state, continue(StateName, NewState); event(_Any, State) -> continue(event, State).

idle(timeout, State) -> {StateName, Timeout} = judge_time(), NewState = State#state, continue(StateName, NewState); idle(_Any, State) -> continue(idle, State).

%% ---------------------------------- %% @doc 状态切换函数 %% ---------------------------------- continue(StateName, #state = State) -> {next_state, StateName, State, infinity}; continue(StateName, #state = State) -> {next_state, StateName, State, time_left(Timeout * 1000, Ts)}.

continue(StateName, Reply, #state = State) -> {reply, Reply, StateName, State, infinity}; continue(StateName, Reply, #state = State) -> {reply, Reply, StateName, State, time_left(Timeout * 1000, Ts)}.

%% ---------------------------------- %% @doc 时间相关函数 %% ----------------------------------

%% @doc 剩余时间 -spec time_left(TimeMax :: integer(), Begin :: non_neg_integer()) -> integer(). time_left(TimeMax, Begin) -> max(0, TimeMax - (unixtime(ms) - Begin)).

%% @doc 取得当前的unix时间戳 -spec unixtime() -> non_neg_integer(). unixtime() -> {M, S, _} = os:timestamp(), M * 1000000 + S.

%% @doc 取得当前的unix时间戳(精确到毫秒) -spec unixtime(X :: ms | zero) -> non_neg_integer(). unixtime(ms) -> {S1, S2, S3} = os:timestamp(), trunc(S1 * 1000000000 + S2 * 1000 + S3 / 1000); unixtime(zero) -> {M, S, MS} = os:timestamp(), {_, Time} = calendar:now_to_local_time({M, S, MS}), M * 1000000 + S - calendar:time_to_seconds(Time).