生活还要继续,代码还是要不断的写。这次的代码是为未来写的,因为我现在在windows下,而基础的结构代码大部分都写了好,现在在整理,同时进行这个教程。整理好的代码都在linux下,在windows下却又没办法访问。虚拟机还在编译我定制的系统。这些都在之前提到过,因此,先将代码整理出来,回头切换系统的时候再添加进去,然后接着写新的。
这个系统的启动结构,没记错的话,应该是从mochiweb学来的,《Erlang程序设计》也提到过。首先定义一个app:
{application, netgame,
[{description, "netgame"},
{vsn, "0.0.1"},
{modules, [
netgame,
netgame_app,
netgame_sup,
netgame_deps
]},
{registered, []},
{mod, {netgame_app, []}},
{env, []},
{applications, [kernel, stdlib, crypto]}]}.
上面每个描述的含义在《Erlang程序设计》中均有提到。这里不说了。
一个erlang的application的启动,很标准的:
%% @author --==RIX==--
%% @copyright GPL2.0 --==RIX==--.
%% @doc TEMPLATE.
-module(netgame).
-author('--==RIX==-- ').
-export([start/0, stop/0]).
ensure_started(App) ->
case application:start(App) of
ok ->
ok;
{error, {already_started, App}} ->
ok
end.
%% @spec start() -> ok
%% @doc Start the netgame server.
start() ->
netgame_deps:ensure(),
ensure_started(crypto),
application:start(netgame).
%% @spec stop() -> ok
%% @doc Stop the netgame server.
stop() ->
Res = application:stop(netgame),
application:stop(crypto),
Res.
PS:我故意隐去了我的邮箱,只是为了防止垃圾邮件。
%% @author --==RIX==--
%% @copyright GPL2.0 --==RIX==--.
%% @doc Ensure that the relatively-installed dependencies are on the code
%% loading path, and locate resources relative
%% to this application's path.
-module(netgame_deps).
-author('--==RIX==-- ').
-export([ensure/0, ensure/1]).
-export([get_base_dir/0, get_base_dir/1]).
-export([local_path/1, local_path/2]).
-export([deps_on_path/0, new_siblings/1]).
%% @spec deps_on_path() -> [ProjNameAndVers]
%% @doc List of project dependencies on the path.
deps_on_path() ->
F = fun (X, Acc) ->
ProjDir = filename:dirname(X),
case {filename:basename(X),
filename:basename(filename:dirname(ProjDir))} of
{"ebin", "deps"} ->
[filename:basename(ProjDir) | Acc];
_ ->
Acc
end
end,
ordsets:from_list(lists:foldl(F, [], code:get_path())).
%% @spec new_siblings(Module) -> [Dir]
%% @doc Find new siblings paths relative to Module that aren't already on the
%% code path.
new_siblings(Module) ->
Existing = deps_on_path(),
SiblingEbin = filelib:wildcard(local_path(["deps", "*", "ebin"], Module)),
Siblings = [filename:dirname(X) || X <- SiblingEbin,
ordsets:is_element(
filename:basename(filename:dirname(X)),
Existing) =:= false],
lists:filter(fun filelib:is_dir/1,
lists:append([[filename:join([X, "ebin"]),
filename:join([X, "include"])] ||
X <- Siblings])).
%% @spec ensure(Module) -> ok
%% @doc Ensure that all ebin and include paths for dependencies
%% of the application for Module are on the code path.
ensure(Module) ->
code:add_paths(new_siblings(Module)),
code:clash(),
ok.
%% @spec ensure() -> ok
%% @doc Ensure that the ebin and include paths for dependencies of
%% this application are on the code path. Equivalent to
%% ensure(?Module).
ensure() ->
ensure(?MODULE).
%% @spec get_base_dir(Module) -> string()
%% @doc Return the application directory for Module. It assumes Module is in
%% a standard OTP layout application in the ebin or src directory.
get_base_dir(Module) ->
{file, Here} = code:is_loaded(Module),
filename:dirname(filename:dirname(Here)).
%% @spec get_base_dir() -> string()
%% @doc Return the application directory for this application. Equivalent to
%% get_base_dir(?MODULE).
get_base_dir() ->
get_base_dir(?MODULE).
%% @spec local_path([string()], Module) -> string()
%% @doc Return an application-relative directory from Module's application.
local_path(Components, Module) ->
filename:join([get_base_dir(Module) | Components]).
%% @spec local_path(Components) -> string()
%% @doc Return an application-relative directory for this application.
%% Equivalent to local_path(Components, ?MODULE).
local_path(Components) ->
local_path(Components, ?MODULE).
接下来是netgame_app:
%%%-------------------------------------------------------------------
%%% File : netgame_app.erl
%%% Author : --==RIX==--
%%% Description :
%%%
%%% Created : 1 Jan 2010 by --==RIX==--
%%%-------------------------------------------------------------------
-module(netgame_app).
-behaviour(application).
%% Application callbacks
-export([start/2, stop/1]).
%%====================================================================
%% Application callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start(Type, StartArgs) -> {ok, Pid} |
%% {ok, Pid, State} |
%% {error, Reason}
%% Description: This function is called whenever an application
%% is started using application:start/1,2, and should start the processes
%% of the application. If the application is structured according to the
%% OTP design principles as a supervision tree, this means starting the
%% top supervisor of the tree.
%%--------------------------------------------------------------------
start(_Type, StartArgs) ->
netgame_deps:ensure(),
netgame_sup:start_link().
%%--------------------------------------------------------------------
%% Function: stop(State) -> void()
%% Description: This function is called whenever an application
%% has stopped. It is intended to be the opposite of Module:start/2 and
%% should do any necessary cleaning up. The return value is ignored.
%%--------------------------------------------------------------------
stop(_State) ->
ok.
%%====================================================================
%% Internal functions
%%====================================================================
最后一个文件,负责最终启动的supervisor:
%%%-------------------------------------------------------------------
%%% File : netgame_sup.erl
%%% Author : --==RIX==--
%%% Description :
%%%
%%% Created : 1 Jan 2010 by --==RIX==--
%%%-------------------------------------------------------------------
-module(netgame_sup).
-behaviour(supervisor).
%% API
-export([start_link/0, upgrade/0]).
%% Supervisor callbacks
-export([init/1]).
-define(SERVER, ?MODULE).
%%====================================================================
%% API functions
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the supervisor
%%--------------------------------------------------------------------
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
upgrade() ->
{ok, {_, Specs}} = init([]),
Old = sets:from_list(
[Name || {Name, _, _, _} <- supervisor:which_children(?MODULE)]),
New = sets:from_list([Name || {Name, _, _, _, _, _} <- Specs]),
Kill = sets:subtract(Old, New),
sets:fold(fun (Id, ok) ->
supervisor:terminate_child(?MODULE, Id),
supervisor:delete_child(?MODULE, Id),
ok
end, ok, Kill),
[supervisor:start_child(?MODULE, Spec) || Spec <- Specs],
ok.
%%====================================================================
%% Supervisor callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Func: init(Args) -> {ok, {SupFlags, [ChildSpec]}} |
%% ignore |
%% {error, Reason}
%% Description: Whenever a supervisor is started using
%% supervisor:start_link/[2,3], this function is called by the new process
%% to find out about restart strategy, maximum restart frequency and child
%% specifications.
%%--------------------------------------------------------------------
init([]) ->
AChild = [{router,{router,start_link,[]},
permanent,2000,worker,[router]},
{server, {server, start,[]},
permanent, 2000, worker, [server]}
],
{ok,{{one_for_one,3,10}, AChild}}.
%%====================================================================
%% Internal functions
%%====================================================================
上述代码没有太多好说的,如果使用emacs的话,95%的代码都是自动生成的,对netgame_sup.erl文件中的init函数,总共启动了两个节点:router和server。
server的功能就是处理游戏中的一切逻辑,router是负责处理玩家连接的,以及消息的散发,比如向游戏中的全部玩家或者指定的玩家,当玩家登陆进入游戏中的时候,router会生成关于玩家ID和进程的对应表,当玩家退出的时候,就会销毁相对应的记录。在这儿启动是因为这部分代码在很大程度上都是不更改的,而且,对于gen_server的方式,如果要想直接调用route:XXX的话,最好放在supervisor中,当然,这不是绝对的,只是建议,我也不想每次都要手动的输入:
1>router:start().
因此,这里就让它直接启动了。
上述文件均存放于server/src目录中。
PS:slitaz在定制的时候,发现一个问题,在定制的包中(base软件包-busybox+bash+erlang+libunixODBC),如果包含了erlang,则在virtualbox中启动的时候,直接挂掉,虽然会能看到部分的函数栈,但我从没有研究过linux内核的调试,对这方面不清楚,但是,使用
tazlito emu-iso
命令,调用slitaz中的qemu模拟器,却没有问题,而在vmware的虚拟机中,也没有问题,因此,出现了比较搞笑的一幕:
在virtualbox中定制,在vmware中运行。我电脑中的qemu模拟器windows版或许是因为版本太低的问题,直接会挂掉。