A:消息路由
消息路由之前提到过,专门处理消息转发的,比如群发消息什么的,服务器内部通讯的消息要比所感受的要多,比如某个人登录进服务器,他的好友应该可以收到该人登录进来的消息,当他登出去的时候,也应该知道,当他进入某个地图或者游戏室的时候,则在某个区域或者某个游戏室中的应该知道。而在棋牌类游戏中,当他出了一张牌,则只有这个游戏桌中参与的玩家可以看的到。这些,都是通过消息路由来转发的。这样,可以简化服务器的结构,并且维护简单。由于代码比较多,因此只说核心的部分:
%% @doc gen_server的回调函数。
handle_call({test}, _From, State) ->
{reply, ok, State};
handle_call({send_to_player, Id, Msg}, _From, State) ->
Pid = ets:lookup(State#state.id2pid, Id),
case Pid of
[] ->
ok;
Other ->
Pid ! {router,Msg}
end,
{noreply, State};
handle_call({send_to_players, Fun, Msg}, _From, State) ->
case db:search(Fun) of
{atomic, Players} ->
send_toPlayers(Players,{router,Msg}, State);
Other ->
io:format("not find Player:~w~n",[Other])
end,
{reply, ok, State};
handle_call({login, Id, Pid}, _From, State) when is_pid(Pid) ->
ets:insert(State#state.pid2id, {Pid, Id}),
ets:insert(State#state.id2pid, {Id, Pid}),
link(Pid),
{reply, ok, State};
handle_call({logout, Pid}, _From, State) when is_pid(Pid) ->
unlink(Pid),
PidRows = ets:lookup(State#state.pid2id, Pid),
case PidRows of
[] ->
ok;
_ ->
IdRows = [{I, P} || {P, I}<-PidRows],
ets:delete(State#state.pid2id, Pid),
%% [room:room_logout(Obj)||{Obj,_}<-IdRows],
[ets:delete_object(State#state.id2pid, Obj)||Obj<-IdRows]
end,
io:format("Pid ~w logged out!~w ~w\n",[Pid,?MODULE, ?LINE]),
{reply, ok, State};
handle_call({send, Id, Msg}, From, State) ->
F = fun()->
Users = subsmanager:get_subscribers(Id),
io:format("Subscribers of ~w = ~w\n", [Id, Users]),
Pids0 = lists:map(fun(U) ->
[P || {_I, P}<- ets:lookup(State#state.id2pid, U)]
end,
[Id|Users]
),
Pids = lists:flatten(Pids0),
io:format("Pids:~w\n", [Pids]),
M = {router_msg, Msg},
[Pid!M||Pid<-Pids],
gen_server:reply(From, {ok, length(Pids)})
end,
spawn(F),
{noreply, State}.
第一部分是一个消息测试。第二部分的消息处理是发给指定id的玩家指定的消息,该消息在玩家的中间代理人(即玩家在服务器端虚拟的和服务器进行交流的部分)那里表现为从路由发来的消息,当然,如果该玩家不在线的话,则什么也不做了(暂时处理是这样,不支持离线消息)。第三部分是发给所有指定条件的所有的玩家,因为我希望将玩家的信息尽可能的记录到数据库中,因此就从数据库中去查找了。第四部分则是给默认的消息一个归宿,让它不至于去影响别的地方(比如引起消息路由重启)。
发给指定条件玩家的消息没有什么太好说的,就是通过下面的函数实现:
%% @doc 发送指定消息给服务器中的指定的玩家.
send_toPlayers([], _Msg, _State) ->
ok;
send_toPlayers([[Player]|Rest], Msg, State) ->
Pids = [P||{_I, P}<-ets:lookup(State#state.id2pid, Player)],
[Pid!Msg||Pid<-Pids],
send_toPlayers(Rest, Msg, State).
消息路由基本上就是这些。接下来是B篇了。