15六/100
一起做网游吧【7】:服务器端注册和登录处理
A:消息路由
消息路由之前提到过,专门处理消息转发的,比如群发消息什么的,服务器内部通讯的消息要比所感受的要多,比如某个人登录进服务器,他的好友应该可以收到该人登录进来的消息,当他登出去的时候,也应该知道,当他进入某个地图或者游戏室的时候,则在某个区域或者某个游戏室中的应该知道。而在棋牌类游戏中,当他出了一张牌,则只有这个游戏桌中参与的玩家可以看的到。这些,都是通过消息路由来转发的。这样,可以简化服务器的结构,并且维护简单。由于代码比较多,因此只说核心的部分:
?Download server_src_router.erl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | %% @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的玩家指定的消息,该消息在玩家的中间代理人(即玩家在服务器端虚拟的和服务器进行交流的部分)那里表现为从路由发来的消息,当然,如果该玩家不在线的话,则什么也不做了(暂时处理是这样,不支持离线消息)。第三部分是发给所有指定条件的所有的玩家,因为我希望将玩家的信息尽可能的记录到数据库中,因此就从数据库中去查找了。第四部分则是给默认的消息一个归宿,让它不至于去影响别的地方(比如引起消息路由重启)。
发给指定条件玩家的消息没有什么太好说的,就是通过下面的函数实现:
?Download server_src_router.erl
1 2 3 4 5 6 7 | %% @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篇了。
原创文章,转载请注明: 转载自RIX 编程应该是快乐的
本文链接地址: 一起做网游吧【7】:服务器端注册和登录处理
文章的脚注信息由WordPress的wp-posturl插件自动生成