一起做网游吧【7】:服务器端注册和登录处理

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篇了。

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据