Start work on making the EGS server OTP-compliant. Add egs_app and egs_sup.

This commit is contained in:
Loïc Hoguin 2010-07-18 01:44:19 +02:00
parent 49016cafaa
commit f46ba0e1f9
11 changed files with 218 additions and 52 deletions

View File

@ -17,6 +17,8 @@
% along with EGS. If not, see <http://www.gnu.org/licenses/>.
{'src/egs.erl', [{outdir, "ebin"}]}.
{'src/egs_app.erl', [{outdir, "ebin"}]}.
{'src/egs_sup.erl', [{outdir, "ebin"}]}.
{'src/egs_cron.erl', [{outdir, "ebin"}]}.
{'src/egs_db.erl', [{outdir, "ebin"}]}.
{'src/egs_game.erl', [{outdir, "ebin"}]}.

View File

@ -31,10 +31,10 @@ clean:
rm -f erl_crash.dump
fclean: clean
rm -rf Mnesia.console*
rm -rf Mnesia.egs*
run:
@echo "EGS is free software available under the GNU GPL version 3"
@echo "Copyright (C) 2010 Loic Hoguin"
@echo
erl -ssl protocol_version '{sslv3}' -sname console -pa ebin -eval 'egs:start()'
erl -ssl protocol_version '{sslv3}' -sname egs -pa ebin -s egs

30
ebin/egs.app Normal file
View File

@ -0,0 +1,30 @@
%%-*- mode: erlang -*-
{application, egs, [
{description, "EGS online action-RPG game server"},
{vsn, "0.1"},
{modules, [
egs,
egs_app,
egs_sup,
egs_cron,
egs_db,
egs_game,
egs_login,
egs_patch,
egs_proto,
psu_appearance,
psu_characters,
psu_missions,
psu_parser
]},
{registered, []},
{applications, [
kernel,
stdlib,
crypto,
ssl,
mnesia
]},
{mod, {egs_app, []}},
{env, []}
]}.

View File

@ -1,59 +1,73 @@
% EGS: Erlang Game Server
% Copyright (C) 2010 Loic Hoguin
%
% This file is part of EGS.
%
% EGS is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% EGS is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with EGS. If not, see <http://www.gnu.org/licenses/>.
%% @author Loïc Hoguin <essen@dev-extend.eu>
%% @copyright 2010 Loïc Hoguin.
%% @doc EGS startup code.
%%
%% This file is part of EGS.
%%
%% EGS is free software: you can redistribute it and/or modify
%% it under the terms of the GNU General Public License as published by
%% the Free Software Foundation, either version 3 of the License, or
%% (at your option) any later version.
%%
%% EGS is distributed in the hope that it will be useful,
%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%% GNU General Public License for more details.
%%
%% You should have received a copy of the GNU General Public License
%% along with EGS. If not, see <http://www.gnu.org/licenses/>.
-module(egs).
-compile(export_all).
-include("include/records.hrl").
-define(MODULES, [egs, egs_cron, egs_db, egs_game, egs_login, egs_patch, egs_proto, psu_appearance, psu_characters, psu_missions, psu_parser]).
-define(MODULES, [egs, egs_app, egs_sup, egs_cron, egs_db, egs_game, egs_login, egs_patch, egs_proto, psu_appearance, psu_characters, psu_missions, psu_parser]).
%% @doc Start all the application servers. Return the PIDs of the listening processes.
%% @spec ensure_started(App) -> ok
%% @doc Make sure the given App is started.
ensure_started(App) ->
case application:start(App) of
ok -> ok;
{error, {already_started, App}} -> ok
end.
%% @spec start() -> ok
%% @doc Start the EGS server.
start() ->
application:start(crypto),
application:start(ssl),
ensure_started(crypto),
ensure_started(ssl),
ssl:seed(crypto:rand_bytes(256)),
egs_db:create(),
Cron = egs_cron:start(),
Game = egs_game:start(),
Login = egs_login:start(),
Patch = egs_patch:start(),
[{patch, Patch}, {login, Login}, {game, Game}, {cron, Cron}].
ensure_started(mnesia),
application:start(egs).
%% @spec stop() -> ok
%% @doc Stop the EGS server.
stop() ->
Res = application:stop(egs),
application:stop(mnesia),
application:stop(ssl),
application:stop(crypto),
Res.
%% @doc Reload all the modules.
%% @todo Do it the OTP way.
reload() ->
[code:soft_purge(Module) || Module <- ?MODULES],
[code:load_file(Module) || Module <- ?MODULES].
%% @doc Send a global message.
%% @todo Move that in a psu module.
global(Type, Message) ->
lists:foreach(fun(User) -> egs_proto:send_global(User#users.socket, Type, Message) end, egs_db:users_select_all()).
%% @doc Warp all players to a new map.
%% @todo Move that in a psu module.
warp(QuestID, ZoneID, MapID, EntryID) ->
lists:foreach(fun(User) -> User#users.pid ! {psu_warp, QuestID, ZoneID, MapID, EntryID} end, egs_db:users_select_all()).
%% @doc Warp one player to a new map.
%% @todo Move that in a psu module.
warp(GID, QuestID, ZoneID, MapID, EntryID) ->
User = egs_db:users_select(GID),
User#users.pid ! {psu_warp, QuestID, ZoneID, MapID, EntryID}.

75
src/egs_app.erl Normal file
View File

@ -0,0 +1,75 @@
%% @author Loïc Hoguin <essen@dev-extend.eu>
%% @copyright 2010 Loïc Hoguin.
%% @doc Callbacks for the egs application.
%%
%% This file is part of EGS.
%%
%% EGS is free software: you can redistribute it and/or modify
%% it under the terms of the GNU General Public License as published by
%% the Free Software Foundation, either version 3 of the License, or
%% (at your option) any later version.
%%
%% EGS is distributed in the hope that it will be useful,
%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%% GNU General Public License for more details.
%%
%% You should have received a copy of the GNU General Public License
%% along with EGS. If not, see <http://www.gnu.org/licenses/>.
-module(egs_app).
-behaviour(application).
-export([start/2, stop/1]).
-include("include/records.hrl").
%% @spec start(_Type, _StartArgs) -> ServerRet
%% @doc application start callback for egs.
start(_Type, _StartArgs) ->
case is_fresh_startup() of
true ->
db_init();
{exists, Tables} ->
ok = mnesia:wait_for_tables(Tables, 20000)
end,
egs_sup:start_link().
%% @spec stop(_State) -> ServerRet
%% @doc application stop callback for egs.
stop(_State) ->
ok.
%% @spec is_fresh_startup() -> true | false
%% @doc Returns true if mnesia has not been initialized with
%% the sherl schema.
%% Thanks to Dale Harvey for this function posted to
%% the erlang questions mailing list.
is_fresh_startup() ->
Node = node(),
case mnesia:system_info(tables) of
[schema] -> true;
Tables ->
case mnesia:table_info(schema, cookie) of
{_, Node} -> {exists, Tables};
_ -> true
end
end.
%% @todo doc
%% @todo Rename ids to counters. Remove objects and use a FSM instead.
db_init() ->
Nodes = [node()],
case mnesia:system_info(is_running) of
yes ->
error_logger:info_report("stopping mnesia"),
mnesia:stop();
_ -> pass
end,
mnesia:create_schema(Nodes),
error_logger:info_report("mnesia schema created"),
error_logger:info_report("starting mnesia"),
mnesia:start(),
mnesia:create_table(ids, [{attributes, record_info(fields, ids)}]),
mnesia:create_table(objects, [{attributes, record_info(fields, objects)}]),
mnesia:create_table(users, [{attributes, record_info(fields, users)}]),
error_logger:info_report("mnesia tables created").

View File

@ -26,8 +26,8 @@
start() ->
KeepAlivePid = spawn_link(?MODULE, keepalive, []),
CleanupPid = spawn_link(?MODULE, cleanup, []),
[{keepalive, KeepAlivePid}, {cleanup, CleanupPid}].
spawn_link(?MODULE, cleanup, []),
{ok, KeepAlivePid}.
%% @doc Cleanup the users table of failures to log into the game server.

View File

@ -31,15 +31,6 @@ do(Q) ->
{atomic, Val} = mnesia:transaction(F),
Val.
%% @doc Create the database.
create() ->
mnesia:create_schema([node()]),
mnesia:start(),
mnesia:create_table(ids, [{attributes, record_info(fields, ids)}]),
mnesia:create_table(objects, [{attributes, record_info(fields, objects)}]),
mnesia:create_table(users, [{attributes, record_info(fields, users)}]).
%% @doc Retrieve the next unique ID.
next(Type) ->

View File

@ -27,9 +27,9 @@
%% @doc Start the game server.
start() ->
SPid = spawn(?MODULE, supervisor_init, []),
LPid = spawn(?MODULE, listen, [SPid]),
[{listener, LPid}, {supervisor, SPid}].
SPid = spawn_link(?MODULE, supervisor_init, []),
LPid = spawn_link(?MODULE, listen, [SPid]),
{ok, LPid}.
%% @doc Game processes supervisor initialization.

View File

@ -26,10 +26,10 @@
%% @doc Start the login server. Currently AOTI JP and US only.
start() ->
JPPidE1 = spawn_link(?MODULE, listen, [?LOGIN_PORT_JP_ONE, 10000001]),
JPPidE2 = spawn_link(?MODULE, listen, [?LOGIN_PORT_JP_TWO, 20000001]),
spawn_link(?MODULE, listen, [?LOGIN_PORT_JP_ONE, 10000001]),
spawn_link(?MODULE, listen, [?LOGIN_PORT_JP_TWO, 20000001]),
USPid = spawn_link(?MODULE, listen, [?LOGIN_PORT_US, 30000001]),
[{jp_e1, JPPidE1}, {jp_e2, JPPidE2}, {us, USPid}].
{ok, USPid}.
%% @doc Listen for connections.

View File

@ -25,9 +25,9 @@
%% @doc Start the patch server. Currently supports AOTI US and JP.
start() ->
JPPid = spawn_link(?MODULE, listen, [?PATCH_PORT_JP]),
spawn_link(?MODULE, listen, [?PATCH_PORT_JP]),
USPid = spawn_link(?MODULE, listen, [?PATCH_PORT_US]),
[{jp, JPPid}, {us, USPid}].
{ok, USPid}.
%% @doc Listen for connections.

54
src/egs_sup.erl Normal file
View File

@ -0,0 +1,54 @@
%% @author Loïc Hoguin <essen@dev-extend.eu>
%% @copyright 2010 Loïc Hoguin.
%% @doc Supervisor for the egs application.
%%
%% This file is part of EGS.
%%
%% EGS is free software: you can redistribute it and/or modify
%% it under the terms of the GNU General Public License as published by
%% the Free Software Foundation, either version 3 of the License, or
%% (at your option) any later version.
%%
%% EGS is distributed in the hope that it will be useful,
%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%% GNU General Public License for more details.
%%
%% You should have received a copy of the GNU General Public License
%% along with EGS. If not, see <http://www.gnu.org/licenses/>.
-module(egs_sup).
-behaviour(supervisor).
-export([init/1]). %% Supervisor callbacks.
-export([start_link/0, upgrade/0]). %% Other functions.
%% @spec start_link() -> ServerRet
%% @doc API for starting the supervisor.
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
%% @spec upgrade() -> ok
%% @doc Add processes if necessary.
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.
%% @spec init([]) -> SupervisorTree
%% @doc supervisor callback.
init([]) ->
%% Start egs_cron, egs_game, egs_login, egs_patch. To be replaced by configurable modules.
Processes = [{egs_cron, {egs_cron, start, []}, permanent, 5000, worker, dynamic},
{egs_game, {egs_game, start, []}, permanent, 5000, worker, dynamic},
{egs_login, {egs_login, start, []}, permanent, 5000, worker, dynamic},
{egs_patch, {egs_patch, start, []}, permanent, 5000, worker, dynamic}],
{ok, {{one_for_one, 10, 10}, Processes}}.