Convert the login server to a supervisor-compatible module.

This commit is contained in:
Loïc Hoguin 2010-07-18 12:54:59 +02:00
parent 982b821415
commit b85826c06a
7 changed files with 57 additions and 56 deletions

View File

@ -10,7 +10,7 @@
egs_cron,
egs_db,
egs_game,
egs_login,
psu_login,
psu_patch,
egs_proto,
psu_appearance,

View File

@ -51,7 +51,9 @@ 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},
{psu_login_jp1, {psu_login, start_link, [?LOGIN_PORT_JP_ONE, 10000001]}, permanent, 5000, worker, dynamic},
{psu_login_jp2, {psu_login, start_link, [?LOGIN_PORT_JP_TWO, 20000001]}, permanent, 5000, worker, dynamic},
{psu_login_us, {psu_login, start_link, [?LOGIN_PORT_US, 30000001]}, permanent, 5000, worker, dynamic},
{psu_patch_jp, {psu_patch, start_link, [?PATCH_PORT_JP]}, permanent, 5000, worker, dynamic},
{psu_patch_us, {psu_patch, start_link, [?PATCH_PORT_US]}, permanent, 5000, worker, dynamic}],
{ok, {{one_for_one, 10, 10}, Processes}}.

View File

@ -1,77 +1,75 @@
% 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 Process login requests.
%%
%% 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_login).
-export([start/0]). % external
-export([listen/2, accept/2, process/2, loop/2]). % internal
-module(psu_login).
-export([start_link/2]). %% External.
-export([listen/2, accept/2, process/2, loop/2]). %% Internal.
-include("include/records.hrl").
-include("include/network.hrl").
%% @doc Start the login server. Currently AOTI JP and US only.
-define(OPTIONS, [binary, {active, false}, {certfile, "priv/ssl/servercert.pem"}, {keyfile, "priv/ssl/serverkey.pem"}, {password, "alpha"}]).
start() ->
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]),
{ok, USPid}.
%% @spec start_link(Port) -> {ok,Pid::pid()}
%% @doc Start the PSU login server for inclusion in a supervisor tree.
start_link(Port, SessionID) ->
Pid = spawn(?MODULE, listen, [Port, SessionID]),
{ok, Pid}.
%% @spec listen(Port, SessionID) -> ok
%% @doc Listen for connections.
listen(Port, SessionID) ->
process_flag(trap_exit, true),
{ok, LSocket} = ssl:listen(Port, ?LOGIN_LISTEN_OPTIONS),
{ok, LSocket} = ssl:listen(Port, ?OPTIONS),
?MODULE:accept(LSocket, SessionID).
%% @spec accept(LSocket, SessionID) -> ok
%% @doc Accept connections.
accept(LSocket, SessionID) ->
case ssl:transport_accept(LSocket, 5000) of
{ok, CSocket} ->
% in the future, modulo to avoid conflicts future with real GIDs
%~ NextID = (SessionID + 1) rem 1000000,
%% in the future, modulo to avoid conflicts future with real GIDs
%% NextID = (SessionID + 1) rem 1000000,
NextID = SessionID + 1,
ssl:ssl_accept(CSocket),
spawn_link(?MODULE, process, [CSocket, SessionID]);
_ ->
spawn(?MODULE, process, [CSocket, SessionID]);
{error, _Reason} ->
NextID = SessionID,
reload
end,
?MODULE:accept(LSocket, NextID).
%% @spec process(CSocket, SessionID) -> ok
%% @doc Process the new connections. Send an hello packet and start the loop.
process(CSocket, SessionID) ->
log(SessionID, "hello"),
egs_proto:packet_send(CSocket, << 16#02020300:32, 0:288, SessionID:32/little-unsigned-integer >>),
?MODULE:loop(CSocket, SessionID).
%% @spec loop(CSocket, SessionID) -> ok
%% @doc Main loop for the login server.
loop(CSocket, SessionID) ->
case egs_proto:packet_recv(CSocket, 5000) of
{ok, Orig} ->
<< _:32, Command:16/unsigned-integer, _/bits >> = Orig,
case handle(Command, CSocket, SessionID, Orig) of
closed ->
ignore;
_ ->
?MODULE:loop(CSocket, SessionID)
closed -> ok;
_ -> ?MODULE:loop(CSocket, SessionID)
end;
{error, timeout} ->
reload,
@ -81,8 +79,11 @@ loop(CSocket, SessionID) ->
egs_db:users_delete(SessionID)
end.
%% @doc Game server info request handler.
%% @spec handle(Command, CSocket, SessionID, Orig) -> ok | closed
%% @doc Login server client commands handler.
%% Game server info request handler.
%% @todo Remove the dependency on network.hrl
handle(16#0217, CSocket, SessionID, _) ->
log(SessionID, "forward to game server"),
IP = ?GAME_IP,
@ -92,12 +93,11 @@ handle(16#0217, CSocket, SessionID, _) ->
ssl:close(CSocket),
closed;
%% @doc Authentication request handler. Currently always succeed.
%% Use the temporary session ID as the GID for now.
%% Use username and password as a folder name for saving character data.
%% Authentication request handler. Currently always succeed.
%% Use the temporary session ID as the GID for now.
%% Use username and password as a folder name for saving character data.
%% @todo Handle real GIDs whenever there's real authentication. GID is the second SessionID in the reply.
%% @todo Apparently it's possible to ask a question in the reply here. Used for free course on JP.
handle(16#0219, CSocket, SessionID, Orig) ->
<< _:352, UsernameBlob:192/bits, PasswordBlob:192/bits, _/bits >> = Orig,
Username = re:replace(UsernameBlob, "\\0", "", [global, {return, binary}]),
@ -110,35 +110,34 @@ handle(16#0219, CSocket, SessionID, Orig) ->
Packet = << 16#02230300:32, 0:192, SessionID:32/little-unsigned-integer, 0:64, SessionID:32/little-unsigned-integer, Auth:32/bits >>,
egs_proto:packet_send(CSocket, Packet);
%% @doc MOTD request handler. Handles both forms of MOTD requests, US and JP. Page number starts at 0.
%% MOTD request handler. Handles both forms of MOTD requests, US and JP. Page number starts at 0.
%% @todo Currently ignore the language and send the same MOTD file to everyone. Language is 8 bits next to Page.
%% @todo Use a normal ASCII file rather than an UCS2 one?
handle(Command, CSocket, SessionID, Orig) when Command =:= 16#0226; Command =:= 16#023f ->
<< _:352, Page:8/little-unsigned-integer, _/bits >> = Orig,
log(SessionID, "send MOTD page ~.10b", [Page + 1]),
{ok, File} = file:read_file("conf/motd.txt"),
{ok, File} = file:read_file("priv/psu_login/motd.txt"),
Tokens = re:split(File, "\n."),
MOTD = << << Line/binary, "\n", 0 >> || Line <- lists:sublist(Tokens, 1 + Page * 15, 15) >>,
NbPages = 1 + length(Tokens) div 15,
Packet = << 16#0225:16, 0:304, NbPages:8, Page:8, 16#8200:16/unsigned-integer, MOTD/binary, 0:16 >>,
egs_proto:packet_send(CSocket, Packet);
%% @doc Silently ignore packets 0227 and 080e.
%% Silently ignore packets 0227 and 080e.
handle(Command, _, _, _) when Command =:= 16#0227; Command =:= 16#080e ->
ignore;
%% @doc Unknown command handler. Print a log message about it.
%% Unknown command handler. Print a log message about it.
handle(Command, _, SessionID, _) ->
log(SessionID, "dismissed packet ~4.16.0b", [Command]).
%% @spec log(SessionID, Message) -> ok
%% @doc Log message to the console.
log(SessionID, Message) ->
io:format("login (~.10b): ~s~n", [SessionID, Message]).
%% @spec log(SessionID, Message, Format) -> ok
%% @doc Format a message and log it to the console.
log(SessionID, Message, Format) ->
RealMessage = io_lib:format(Message, Format),
log(SessionID, RealMessage).

View File

@ -24,7 +24,7 @@
-define(OPTIONS, [binary, {send_timeout, 5000}, {packet, 0}, {active, false}, {reuseaddr, true}]).
%% @spec start_link(Port) -> {ok,Pid::pid()}
%% @doc Starts the patch server for inclusion in a supervisor tree.
%% @doc Start the PSU patch server for inclusion in a supervisor tree.
start_link(Port) ->
Pid = spawn(?MODULE, listen, [Port]),
{ok, Pid}.