2010-05-13 12:34:04 +08:00
% 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.
%
2010-05-15 07:55:23 +08:00
% EGS is distributed in the hope that it will be useful,
2010-05-13 12:34:04 +08:00
% 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
2010-05-15 07:55:23 +08:00
% along with EGS. If not, see <http://www.gnu.org/licenses/>.
2010-05-13 12:34:04 +08:00
- module ( egs_game ) .
- export ( [ start / 0 ] ) . % external
2010-05-23 04:50:44 +08:00
- export ( [ listen / 0 , accept / 1 , process / 2 , char_select / 3 , lobby_load / 6 , loop / 3 , loop / 4 ] ) . % internal
2010-05-13 12:34:04 +08:00
- include ( " include/records.hrl " ) .
- include ( " include/network.hrl " ) .
- include ( " include/maps.hrl " ) .
%% @doc Start the game server.
start ( ) - >
Pid = spawn_link ( ? MODULE , listen , [ ] ) ,
Pid .
%% @doc Listen for connections.
listen ( ) - >
2010-05-21 15:12:11 +08:00
process_flag ( trap_exit , true ) ,
2010-05-13 12:34:04 +08:00
{ ok , LSocket } = ssl : listen ( ? GAME_PORT , ? GAME_LISTEN_OPTIONS ) ,
? MODULE : accept ( LSocket ) .
%% @doc Accept connections.
accept ( LSocket ) - >
case ssl : transport_accept ( LSocket , 5000 ) of
{ ok , CSocket } - >
ssl : ssl_accept ( CSocket ) ,
2010-05-21 15:12:11 +08:00
try
2010-06-04 02:25:52 +08:00
egs_proto : packet_send ( CSocket , < < 16#02020300 : 32 , 0 : 352 > > ) ,
2010-05-21 15:12:11 +08:00
Pid = spawn_link ( ? MODULE , process , [ CSocket , 0 ] ) ,
ssl : controlling_process ( CSocket , Pid )
catch
_ : _ - >
reload
end ;
2010-05-20 06:10:02 +08:00
_ - >
2010-05-13 12:34:04 +08:00
reload
end ,
? MODULE : accept ( LSocket ) .
%% @doc Process the new connections.
%% Send an hello packet, authenticate the user and send him to character select.
process ( CSocket , Version ) - >
case egs_proto : packet_recv ( CSocket , 5000 ) of
2010-06-04 06:09:08 +08:00
{ ok , Orig } - >
< < _ : 32 , Command : 16 / unsigned - integer , _ / bits > > = Orig ,
process_handle ( Command , CSocket , Version , Orig ) ;
2010-05-13 12:34:04 +08:00
{ error , timeout } - >
reload ,
2010-05-15 22:45:25 +08:00
? MODULE : process ( CSocket , Version ) ;
2010-05-13 12:34:04 +08:00
{ error , closed } - >
2010-06-04 05:36:34 +08:00
closed
2010-05-13 12:34:04 +08:00
end .
%% @doc Game server auth request handler.
2010-06-04 06:09:08 +08:00
process_handle ( 16#020d , CSocket , Version , Orig ) - >
2010-06-04 18:04:37 +08:00
< < _ : 352 , GID : 32 / little - unsigned - integer , Auth : 32 / bits , _ / bits > > = Orig ,
2010-05-15 22:12:00 +08:00
case egs_db : users_select ( GID ) of
error - >
log ( GID , " can't find user, closing " ) ,
ssl : close ( CSocket ) ;
User - >
case User #users.auth of
Auth - >
2010-06-04 05:36:34 +08:00
log ( GID , " auth success " ) ,
2010-05-26 04:24:43 +08:00
LID = 1 + egs_db : next ( lobby ) rem 1023 ,
2010-05-21 05:51:13 +08:00
Time = calendar : datetime_to_gregorian_seconds ( calendar : universal_time ( ) ) ,
egs_db : users_insert ( #users { gid = GID , pid = self ( ) , socket = CSocket , auth = success , time = Time , folder = User #users.folder , lid = LID } ) ,
2010-05-15 22:12:00 +08:00
egs_proto : send_flags ( CSocket , GID ) ,
? MODULE : char_select ( CSocket , GID , Version ) ;
_ - >
2010-06-04 05:36:34 +08:00
log ( GID , " quit, auth failed " ) ,
2010-05-15 22:12:00 +08:00
egs_db : users_delete ( GID ) ,
ssl : close ( CSocket )
end
2010-05-13 12:34:04 +08:00
end ;
%% @doc Platform information handler. Obtain the game version.
2010-06-04 06:09:08 +08:00
process_handle ( 16#080e , CSocket , _ , Orig ) - >
2010-06-04 17:26:22 +08:00
< < _ : 416 , Version : 32 / little - unsigned - integer , _ / bits > > = Orig ,
? MODULE : process ( CSocket , Version ) ;
2010-05-13 12:34:04 +08:00
%% @doc Unknown command handler. Do nothing.
process_handle ( Command , CSocket , Version , _ ) - >
2010-06-04 05:36:34 +08:00
log ( 0 , " (process) dismissed packet ~4.16.0b " , [ Command ] ) ,
2010-05-13 12:34:04 +08:00
? MODULE : process ( CSocket , Version ) .
%% @doc Character selection screen loop.
%% The default entry point currently is first floor, near the uni cube.
char_select ( CSocket , GID , Version ) - >
case egs_proto : packet_recv ( CSocket , 5000 ) of
2010-06-04 06:09:08 +08:00
{ ok , Orig } - >
< < _ : 32 , Command : 16 / unsigned - integer , _ / bits > > = Orig ,
char_select_handle ( Command , CSocket , GID , Version , Orig ) ;
2010-05-13 12:34:04 +08:00
{ error , timeout } - >
2010-06-04 23:56:18 +08:00
egs_proto : send_keepalive ( CSocket ) ,
2010-05-13 12:34:04 +08:00
reload ,
? MODULE : char_select ( CSocket , GID , Version ) ;
{ error , closed } - >
2010-06-04 05:36:34 +08:00
log ( GID , " quit " ) ,
2010-05-13 12:34:04 +08:00
egs_db : users_delete ( GID )
end .
%% @doc Character selection handler.
2010-06-04 06:09:08 +08:00
char_select_handle ( 16#020b , CSocket , GID , Version , Orig ) - >
2010-06-04 18:03:37 +08:00
< < _ : 352 , Number : 32 / little - unsigned - integer , _ / bits > > = Orig ,
2010-06-04 22:06:35 +08:00
log ( GID , " selected character ~b " , [ Number ] ) ,
2010-05-13 12:34:04 +08:00
char_select_load ( CSocket , GID , Version , Number ) ;
%% @doc Character creation handler.
2010-06-04 06:09:08 +08:00
char_select_handle ( 16#0d02 , CSocket , GID , Version , Orig ) - >
2010-05-13 12:34:04 +08:00
log ( GID , " character creation " ) ,
2010-05-14 02:45:17 +08:00
User = egs_db : users_select ( GID ) ,
2010-06-04 18:02:10 +08:00
< < _ : 352 , Number : 32 / little - unsigned - integer , Char / bits > > = Orig ,
2010-05-14 02:45:17 +08:00
_ = file : make_dir ( io_lib : format ( " save/ ~s " , [ User #users.folder ] ) ) ,
file : write_file ( io_lib : format ( " save/ ~s / ~b -character " , [ User #users.folder , Number ] ) , Char ) ,
file : write_file ( io_lib : format ( " save/ ~s / ~b -character.options " , [ User #users.folder , Number ] ) , < < 0 : 192 > > ) ,
2010-05-13 12:34:04 +08:00
char_select_load ( CSocket , GID , Version , Number ) ;
%% @doc Character selection screen request.
char_select_handle ( 16#0d06 , CSocket , GID , Version , _ ) - >
2010-05-14 02:45:17 +08:00
User = egs_db : users_select ( GID ) ,
egs_proto : send_character_list ( CSocket , GID ,
char_load ( User #users.folder , 0 ) ,
char_load ( User #users.folder , 1 ) ,
char_load ( User #users.folder , 2 ) ,
char_load ( User #users.folder , 3 ) ) ,
2010-05-13 12:34:04 +08:00
? MODULE : char_select ( CSocket , GID , Version ) ;
2010-06-04 05:45:06 +08:00
%% @doc Silently ignore packet 0818. Gives CPU/GPU information.
char_select_handle ( 16#0818 , CSocket , GID , Version , _ ) - >
? MODULE : char_select ( CSocket , GID , Version ) ;
2010-05-13 12:34:04 +08:00
%% @doc Unknown command handler. Do nothing.
char_select_handle ( Command , CSocket , GID , Version , _ ) - >
2010-06-04 05:36:34 +08:00
log ( GID , " (char_select) dismissed packet ~4.16.0b " , [ Command ] ) ,
2010-05-13 12:34:04 +08:00
? MODULE : char_select ( CSocket , GID , Version ) .
%% @doc Load the given character's data.
2010-05-14 02:45:17 +08:00
char_load ( Folder , Number ) - >
Filename = io_lib : format ( " save/ ~s / ~b -character " , [ Folder , Number ] ) ,
2010-05-13 12:34:04 +08:00
case file : read_file ( Filename ) of
{ ok , Char } - >
{ ok , Options } = file : read_file ( io_lib : format ( " ~s .options " , [ Filename ] ) ) ,
[ { status , 1 } , { char , Char } , { options , Options } ] ;
{ error , _ } - >
[ { status , 0 } , { char , < < 0 : 2208 > > } ]
end .
2010-06-04 21:01:04 +08:00
%% @doc Load the selected character in the start lobby and start the main game's loop.
2010-05-13 12:34:04 +08:00
char_select_load ( CSocket , GID , Version , Number ) - >
User = egs_db : users_select ( GID ) ,
2010-06-04 21:01:04 +08:00
[ { status , 1 } , { char , Char } , { options , Options } ] = char_load ( User #users.folder , Number ) ,
< < Name : 512 / bits , _ / bits > > = Char ,
2010-05-13 12:34:04 +08:00
NewRow = User #users { charnumber = Number , charname = Name } ,
egs_db : users_insert ( NewRow ) ,
2010-06-04 21:01:04 +08:00
egs_proto : send_character_selected ( CSocket , GID , Char , Options ) ,
% 0246
send_packet_0a0a ( CSocket , GID ) ,
2010-06-04 22:25:38 +08:00
send_packet_1006 ( CSocket , GID , 5 ) ,
2010-06-04 21:01:04 +08:00
send_packet_1005 ( CSocket , GID , Char ) ,
2010-06-04 22:25:38 +08:00
send_packet_1006 ( CSocket , GID , 12 ) ,
% 0210
2010-06-04 21:01:04 +08:00
egs_proto : send_universe_info ( CSocket , GID ) ,
2010-06-04 22:47:42 +08:00
egs_proto : send_player_card ( CSocket , GID , Char , Number ) ,
2010-06-04 22:53:32 +08:00
send_packet_1501 ( CSocket , GID ) ,
2010-06-04 23:02:13 +08:00
send_packet_1512 ( CSocket , GID ) ,
% 0303
2010-06-04 21:01:04 +08:00
egs_proto : send_npc_info ( CSocket , GID ) ,
2010-06-05 00:02:26 +08:00
send_packet_021b ( CSocket , GID ) ,
2010-05-23 04:50:44 +08:00
lobby_load ( CSocket , GID , 1100000 , 0 , 1 , 1 ) ,
2010-05-13 12:34:04 +08:00
ssl : setopts ( CSocket , [ { active , true } ] ) ,
? MODULE : loop ( CSocket , GID , Version ) .
2010-05-30 16:34:55 +08:00
%% @doc Load the given map as a mission counter.
counter_load ( CSocket , GID , Quest , MapType , MapNumber , MapEntry ) - >
OldUser = egs_db : users_select ( GID ) ,
User = OldUser #users { quest = Quest , maptype = MapType , mapnumber = MapNumber , mapentry = MapEntry } ,
egs_db : users_insert ( User ) ,
[ { status , 1 } , { char , Char } , { options , _ } ] = char_load ( User #users.folder , User #users.charnumber ) ,
2010-06-01 00:56:38 +08:00
[ { name , AreaName } , { quest , QuestFile } , { zone , ZoneFile } , { entries , _ } ] =
2010-05-30 16:34:55 +08:00
[ { name , " LL counter " } , { quest , " data/lobby/counter.quest.nbl " } , { zone , " data/lobby/counter.zone.nbl " } , { entries , [ ] } ] ,
try
2010-06-02 05:40:32 +08:00
% broadcast unspawn to other people
lists : foreach ( fun ( Other ) - > Other #users.pid ! { psu_player_unspawn , User } end , egs_db : users_select_others_in_area ( OldUser ) ) ,
2010-06-04 02:15:10 +08:00
egs_proto : send_init_quest ( CSocket , GID , 16#7fffffff ) ,
2010-05-30 16:34:55 +08:00
egs_proto : send_quest ( CSocket , QuestFile ) ,
2010-06-05 00:30:57 +08:00
send_packet_0a05 ( CSocket , GID ) ,
% 010d
2010-05-30 16:34:55 +08:00
egs_proto : send_zone_init ( CSocket , GID , counter ) ,
egs_proto : send_zone ( CSocket , ZoneFile ) ,
2010-06-05 01:21:59 +08:00
egs_proto : send_map ( CSocket , MapType , MapNumber , MapEntry ) ,
2010-06-01 00:56:38 +08:00
egs_proto : send_location ( CSocket , GID , Quest , MapType , MapNumber , AreaName ) ,
2010-05-30 16:34:55 +08:00
% 0215 0215 020c 1202 1204 1206 1207
egs_proto : send_load_quest ( CSocket , GID ) ,
send_packet_201 ( CSocket , GID , User , Char ) ,
% 0a06
egs_proto : send_loading_end ( CSocket , GID ) ,
egs_proto : send_camera_center ( CSocket , GID )
catch
_ - >
2010-06-02 05:40:32 +08:00
close ( CSocket , GID )
2010-05-30 16:34:55 +08:00
end .
2010-05-13 12:34:04 +08:00
%% @doc Load the given map as a standard lobby.
2010-05-23 04:50:44 +08:00
lobby_load ( CSocket , GID , Quest , MapType , MapNumber , MapEntry ) - >
2010-05-20 13:47:28 +08:00
OldUser = egs_db : users_select ( GID ) ,
2010-06-01 03:15:39 +08:00
User = OldUser #users { instanceid = undefined , quest = Quest , maptype = MapType , mapnumber = MapNumber , mapentry = MapEntry } ,
2010-05-20 13:47:28 +08:00
egs_db : users_insert ( User ) ,
2010-06-04 21:01:04 +08:00
[ { status , 1 } , { char , Char } , { options , _ } ] = char_load ( User #users.folder , User #users.charnumber ) ,
2010-06-01 00:56:38 +08:00
[ { name , AreaName } , { quest , QuestFile } , { zone , ZoneFile } , { entries , _ } ] = proplists : get_value ( [ Quest , MapType , MapNumber ] , ? MAPS ,
2010-06-01 03:58:20 +08:00
[ { name , " dammy " } , { quest , " data/lobby/colony.quest.nbl " } , { zone , " data/lobby/colony.zone-0.nbl " } , { entries , [ ] } ] ) ,
2010-05-13 12:34:04 +08:00
try
2010-06-02 05:40:32 +08:00
% broadcast spawn and unspawn to other people
lists : foreach ( fun ( Other ) - > Other #users.pid ! { psu_player_unspawn , User } end , egs_db : users_select_others_in_area ( OldUser ) ) ,
lists : foreach ( fun ( Other ) - > Other #users.pid ! { psu_player_spawn , User } end , egs_db : users_select_others_in_area ( User ) ) ,
2010-06-04 02:15:10 +08:00
egs_proto : send_init_quest ( CSocket , GID , Quest ) ,
2010-05-23 04:50:44 +08:00
egs_proto : send_quest ( CSocket , QuestFile ) ,
2010-06-05 00:30:57 +08:00
send_packet_0a05 ( CSocket , GID ) ,
2010-06-05 00:44:41 +08:00
send_packet_0111 ( CSocket , GID ) ,
% 010d
2010-05-30 16:34:55 +08:00
egs_proto : send_zone_init ( CSocket , GID , lobby ) ,
2010-05-23 04:50:44 +08:00
egs_proto : send_zone ( CSocket , ZoneFile ) ,
2010-06-05 01:21:59 +08:00
egs_proto : send_map ( CSocket , MapType , MapNumber , MapEntry ) ,
2010-06-01 00:56:38 +08:00
egs_proto : send_location ( CSocket , GID , Quest , MapType , MapNumber , AreaName ) ,
% 020c
2010-05-13 12:34:04 +08:00
egs_proto : send_load_quest ( CSocket , GID ) ,
2010-05-22 09:59:19 +08:00
send_packet_201 ( CSocket , GID , User , Char ) ,
2010-06-01 00:50:24 +08:00
send_packet_0a06 ( CSocket , GID ) ,
2010-06-02 05:40:32 +08:00
send_packet_233 ( CSocket , GID , egs_db : users_select_others_in_area ( User ) ) ,
2010-05-13 12:34:04 +08:00
egs_proto : send_loading_end ( CSocket , GID ) ,
egs_proto : send_camera_center ( CSocket , GID )
catch
_ - >
2010-06-02 05:40:32 +08:00
close ( CSocket , GID )
2010-05-13 12:34:04 +08:00
end .
2010-05-30 16:34:55 +08:00
%% @doc Load the given map as a mission.
2010-06-04 02:15:10 +08:00
%% @todo One of the silenced packets enable a 04xx command sent by the client and related to enemies sync. Probably 1202.
%% @todo Maybe the quest doesn't work right because of the lack of this odd checksum-like value.
2010-05-30 16:34:55 +08:00
mission_load ( CSocket , GID , Quest , MapType , MapNumber , MapEntry ) - >
OldUser = egs_db : users_select ( GID ) ,
2010-06-01 03:15:39 +08:00
User = OldUser #users { instanceid = GID , quest = Quest , maptype = MapType , mapnumber = MapNumber , mapentry = MapEntry } ,
2010-05-30 16:34:55 +08:00
egs_db : users_insert ( User ) ,
[ { status , 1 } , { char , Char } , { options , _ } ] = char_load ( User #users.folder , User #users.charnumber ) ,
2010-06-01 00:56:38 +08:00
[ { name , AreaName } , { quest , QuestFile } , { zone , ZoneFile } , { entries , _ } ] = proplists : get_value ( [ Quest , MapType , MapNumber ] , ? MAPS ) ,
2010-05-30 16:34:55 +08:00
try
2010-06-04 02:15:10 +08:00
egs_proto : send_init_quest ( CSocket , GID , Quest ) ,
2010-05-30 16:34:55 +08:00
egs_proto : send_quest ( CSocket , QuestFile ) ,
2010-06-05 00:30:57 +08:00
% 0215
send_packet_0a05 ( CSocket , GID ) ,
% 010d
2010-05-30 16:34:55 +08:00
egs_proto : send_zone_init ( CSocket , GID , mission ) ,
egs_proto : send_zone ( CSocket , ZoneFile ) ,
2010-06-05 01:21:59 +08:00
egs_proto : send_map ( CSocket , MapType , MapNumber , MapEntry ) ,
2010-06-01 00:56:38 +08:00
egs_proto : send_location ( CSocket , GID , Quest , MapType , MapNumber , AreaName ) ,
% 0215 0215
2010-05-30 16:34:55 +08:00
egs_proto : send_trial_start ( CSocket , GID ) ,
2010-06-01 00:50:24 +08:00
% 020c
2010-05-30 16:34:55 +08:00
% mandatory packet to make enemies appear
{ ok , < < _ : 32 , Packet / bits > > } = file : read_file ( " p/packet1202.bin " ) ,
egs_proto : packet_send ( CSocket , Packet ) ,
% 1204 1206 1207
egs_proto : send_load_quest ( CSocket , GID ) ,
send_packet_201 ( CSocket , GID , User , Char ) ,
2010-06-01 00:50:24 +08:00
send_packet_0a06 ( CSocket , GID ) ,
2010-05-30 16:34:55 +08:00
egs_proto : send_loading_end ( CSocket , GID ) ,
egs_proto : send_camera_center ( CSocket , GID )
catch
_ - >
2010-06-02 05:40:32 +08:00
close ( CSocket , GID )
2010-05-30 16:34:55 +08:00
end .
2010-05-25 14:50:08 +08:00
%% @doc Load the given map as a player room.
%% Always load the same room that isn't this player's room for now.
%% @todo Load 'Your room' correctly.
myroom_load ( CSocket , GID , Quest , MapType , MapNumber , MapEntry ) - >
OldUser = egs_db : users_select ( GID ) ,
User = OldUser #users { quest = Quest , maptype = MapType , mapnumber = MapNumber , mapentry = MapEntry } ,
egs_db : users_insert ( User ) ,
[ { status , 1 } , { char , Char } , { options , Options } ] = char_load ( User #users.folder , User #users.charnumber ) ,
[ { name , _ } , { quest , QuestFile } , { zone , ZoneFile } , { entries , _ } ] =
[ { name , " dammy " } , { quest , " data/rooms/test.quest.nbl " } , { zone , " data/rooms/test.zone.nbl " } , { entries , [ ] } ] ,
try
2010-06-02 05:40:32 +08:00
% broadcast spawn and unspawn to other people
lists : foreach ( fun ( Other ) - > Other #users.pid ! { psu_player_unspawn , User } end , egs_db : users_select_others_in_area ( OldUser ) ) ,
lists : foreach ( fun ( Other ) - > Other #users.pid ! { psu_player_spawn , User } end , egs_db : users_select_others_in_area ( User ) ) ,
2010-05-25 14:50:08 +08:00
% load lobby and character
egs_proto : send_character_selected ( CSocket , GID , Char , Options ) ,
% 0246 0a0a 1006
send_packet_1005 ( CSocket , GID , Char ) ,
% 1006 0210
egs_proto : send_universe_info ( CSocket , GID ) ,
egs_proto : send_player_card ( CSocket , GID , Char ) ,
% 1501 1512 0303
egs_proto : send_npc_info ( CSocket , GID ) ,
2010-06-04 02:15:10 +08:00
egs_proto : send_init_quest ( CSocket , GID , Quest ) ,
2010-05-25 14:50:08 +08:00
egs_proto : send_quest ( CSocket , QuestFile ) ,
2010-06-05 00:30:57 +08:00
send_packet_0a05 ( CSocket , GID ) ,
2010-06-05 00:44:41 +08:00
send_packet_0111 ( CSocket , GID ) ,
% 010d
2010-05-30 16:34:55 +08:00
egs_proto : send_zone_init ( CSocket , GID , myroom ) ,
2010-05-25 14:50:08 +08:00
egs_proto : send_zone ( CSocket , ZoneFile ) ,
2010-06-05 01:21:59 +08:00
egs_proto : send_map ( CSocket , MapType , MapNumber , MapEntry ) ,
2010-05-25 14:50:08 +08:00
myroom_send_packet ( CSocket , " p/packet1332.bin " ) ,
% 130e(a) 130e(b) 1202 1204 1206
egs_proto : send_load_quest ( CSocket , GID ) ,
myroom_send_packet ( CSocket , " p/packet1309.bin " ) ,
% 130a(removing moved the pm from shop to normal spot) 1318
send_packet_201 ( CSocket , GID , User , Char ) ,
% 0a06 0233
egs_proto : send_loading_end ( CSocket , GID ) ,
egs_proto : send_camera_center ( CSocket , GID )
catch
_ - >
2010-06-02 05:40:32 +08:00
close ( CSocket , GID )
2010-05-25 14:50:08 +08:00
end .
myroom_send_packet ( CSocket , Filename ) - >
{ ok , < < _ : 32 , File / bits > > } = file : read_file ( Filename ) ,
egs_proto : packet_send ( CSocket , File ) .
2010-05-23 04:50:44 +08:00
%% @doc Load the given map as a spaceport.
spaceport_load ( CSocket , GID , Quest , MapType , MapNumber , MapEntry ) - >
OldUser = egs_db : users_select ( GID ) ,
User = OldUser #users { quest = Quest , maptype = MapType , mapnumber = MapNumber , mapentry = MapEntry } ,
egs_db : users_insert ( User ) ,
[ { status , 1 } , { char , Char } , { options , _ } ] = char_load ( User #users.folder , User #users.charnumber ) ,
2010-06-01 00:56:38 +08:00
[ { name , AreaName } , { quest , QuestFile } , { zone , ZoneFile } , { entries , _ } ] = proplists : get_value ( [ Quest , MapType , MapNumber ] , ? MAPS ) ,
2010-05-23 04:50:44 +08:00
try
2010-06-02 05:40:32 +08:00
% broadcast unspawn to other people
lists : foreach ( fun ( Other ) - > Other #users.pid ! { psu_player_unspawn , User } end , egs_db : users_select_others_in_area ( OldUser ) ) ,
2010-06-04 02:15:10 +08:00
egs_proto : send_init_quest ( CSocket , GID , Quest ) ,
2010-05-23 04:50:44 +08:00
egs_proto : send_quest ( CSocket , QuestFile ) ,
2010-06-05 00:30:57 +08:00
send_packet_0a05 ( CSocket , GID ) ,
2010-05-30 16:34:55 +08:00
egs_proto : send_zone_init ( CSocket , GID , spaceport ) ,
2010-05-23 04:50:44 +08:00
egs_proto : send_zone ( CSocket , ZoneFile ) ,
2010-06-05 01:21:59 +08:00
egs_proto : send_map ( CSocket , MapType , MapNumber , MapEntry ) ,
2010-06-01 00:56:38 +08:00
egs_proto : send_location ( CSocket , GID , Quest , MapType , MapNumber , AreaName ) ,
% 020c
2010-05-23 04:50:44 +08:00
send_packet_201 ( CSocket , GID , User , Char ) ,
% 0a06
egs_proto : send_loading_end ( CSocket , GID ) ,
egs_proto : send_camera_center ( CSocket , GID )
catch
_ - >
2010-06-02 05:40:32 +08:00
close ( CSocket , GID )
2010-05-23 04:50:44 +08:00
end .
2010-05-17 10:53:23 +08:00
%% @doc Alias for the game main's loop when the buffer is empty.
loop ( CSocket , GID , Version ) - >
loop ( CSocket , GID , Version , < < > > ) .
2010-05-13 12:34:04 +08:00
%% @doc Game's main loop.
2010-05-17 10:53:23 +08:00
loop ( CSocket , GID , Version , SoFar ) - >
2010-05-13 12:34:04 +08:00
receive
2010-06-04 06:09:08 +08:00
{ psu_broadcast , Orig } - >
< < A : 64 / bits , _ : 32 , B : 96 / bits , _ : 64 , C / bits > > = Orig ,
Packet = < < A / binary , 16#00011300 : 32 , B / binary , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , C / binary > > ,
egs_proto : packet_send ( CSocket , Packet ) ,
2010-05-17 10:53:23 +08:00
? MODULE : loop ( CSocket , GID , Version , SoFar ) ;
2010-05-15 23:10:07 +08:00
{ psu_chat , ChatGID , ChatName , ChatModifiers , ChatMessage } - >
egs_proto : send_chat ( CSocket , Version , ChatGID , ChatName , ChatModifiers , ChatMessage ) ,
2010-05-17 10:53:23 +08:00
? MODULE : loop ( CSocket , GID , Version , SoFar ) ;
2010-05-20 17:35:25 +08:00
{ psu_keepalive } - >
2010-06-04 23:56:18 +08:00
egs_proto : send_keepalive ( CSocket ) ,
2010-05-20 17:35:25 +08:00
? MODULE : loop ( CSocket , GID , Version , SoFar ) ;
2010-06-02 05:40:32 +08:00
{ psu_player_spawn , Spawn } - >
send_spawn ( CSocket , GID , Spawn ) ,
? MODULE : loop ( CSocket , GID , Version , SoFar ) ;
{ psu_player_unspawn , Spawn } - >
send_unspawn ( CSocket , GID , Spawn ) ,
2010-05-17 10:53:23 +08:00
? MODULE : loop ( CSocket , GID , Version , SoFar ) ;
2010-05-13 12:34:04 +08:00
{ ssl , _ , Data } - >
2010-05-17 10:53:23 +08:00
{ Packets , Rest } = egs_proto : packet_split ( < < SoFar / bits , Data / bits > > ) ,
2010-06-04 06:09:08 +08:00
[ dispatch ( CSocket , GID , Version , Orig ) | | Orig < - Packets ] ,
2010-05-17 10:53:23 +08:00
? MODULE : loop ( CSocket , GID , Version , Rest ) ;
2010-05-13 12:34:04 +08:00
{ ssl_closed , _ } - >
2010-05-21 16:42:47 +08:00
close ( CSocket , GID ) ;
2010-05-13 12:34:04 +08:00
{ ssl_error , _ , _ } - >
2010-05-21 16:42:47 +08:00
close ( CSocket , GID ) ;
2010-05-13 12:34:04 +08:00
_ - >
2010-05-17 10:53:23 +08:00
? MODULE : loop ( CSocket , GID , Version , SoFar )
2010-05-13 12:34:04 +08:00
after 1000 - >
reload ,
2010-05-17 10:53:23 +08:00
? MODULE : loop ( CSocket , GID , Version , SoFar )
2010-05-13 12:34:04 +08:00
end .
2010-05-21 16:42:47 +08:00
%% @doc Close the connection for the given user.
close ( CSocket , GID ) - >
log ( GID , " quit " ) ,
2010-06-02 05:40:32 +08:00
User = egs_db : users_select ( GID ) ,
lists : foreach ( fun ( Other ) - > Other #users.pid ! { psu_player_unspawn , User } end , egs_db : users_select_others_in_area ( User ) ) ,
2010-05-21 16:42:47 +08:00
egs_db : users_delete ( GID ) ,
ssl : close ( CSocket ) .
2010-05-13 12:34:04 +08:00
%% @doc Dispatch the command to the right handler.
2010-06-04 06:10:54 +08:00
%% Command 0b05 uses the channel for something else. Conflicts could occur. Better to just ignore it anyway.
2010-05-13 12:34:04 +08:00
2010-06-04 06:09:08 +08:00
dispatch ( CSocket , GID , Version , Orig ) - >
< < _ : 32 , Command : 16 / unsigned - integer , Channel : 8 / little - unsigned - integer , _ / bits > > = Orig ,
2010-05-26 11:35:31 +08:00
case [ Command , Channel ] of
[ 16#0b05 , _ ] - >
2010-06-04 06:10:54 +08:00
ignore ;
2010-05-26 11:35:31 +08:00
[ _ , 1 ] - >
2010-06-04 06:09:08 +08:00
broadcast ( Command , GID , Orig ) ;
2010-05-20 13:47:28 +08:00
_ - >
2010-06-04 06:09:08 +08:00
handle ( Command , CSocket , GID , Version , Orig )
2010-05-20 13:47:28 +08:00
end .
%% @doc Position change broadcast handler. Save the position and then dispatch it.
2010-06-04 06:09:08 +08:00
broadcast ( 16#0503 , GID , Orig ) - >
2010-05-26 16:47:30 +08:00
LID = 0 , % TODO: handle the LID correctly
2010-05-26 23:11:11 +08:00
< < 100 : 32 / little - unsigned - integer , 16#050301 : 24 / unsigned - integer , _ : 72 , GID : 32 / little - unsigned - integer , _ : 192 ,
2010-05-26 17:27:51 +08:00
GID : 32 / little - unsigned - integer , LID : 32 / little - unsigned - integer , Direction : 32 / bits , Coords : 96 / bits , _ : 96 ,
2010-05-26 23:11:11 +08:00
Quest : 32 / little - unsigned - integer , MapType : 32 / little - unsigned - integer , MapNumber : 32 / little - unsigned - integer ,
2010-06-04 06:09:08 +08:00
MapEntry : 32 / little - unsigned - integer , _ : 32 > > = Orig ,
2010-05-20 13:47:28 +08:00
User = egs_db : users_select ( GID ) ,
2010-05-26 15:55:55 +08:00
NewUser = User #users { direction = Direction , coords = Coords , quest = Quest , maptype = MapType , mapnumber = MapNumber , mapentry = MapEntry } ,
2010-05-20 13:47:28 +08:00
egs_db : users_insert ( NewUser ) ,
2010-06-04 06:09:08 +08:00
broadcast ( default , GID , Orig ) ;
2010-05-20 13:47:28 +08:00
2010-05-26 16:10:14 +08:00
%% @doc Stand still broadcast handler. Save the position and then dispatch it.
2010-06-04 06:09:08 +08:00
broadcast ( 16#0514 , GID , Orig ) - >
2010-05-26 16:10:14 +08:00
< < _ : 320 , _ : 96 , Direction : 32 / bits , Coords : 96 / bits , Quest : 32 / little - unsigned - integer , MapType : 32 / little - unsigned - integer ,
2010-06-04 06:09:08 +08:00
MapNumber : 32 / little - unsigned - integer , MapEntry : 32 / little - unsigned - integer , _ / bits > > = Orig ,
2010-05-26 16:10:14 +08:00
User = egs_db : users_select ( GID ) ,
NewUser = User #users { direction = Direction , coords = Coords , quest = Quest , maptype = MapType , mapnumber = MapNumber , mapentry = MapEntry } ,
egs_db : users_insert ( NewUser ) ,
2010-06-04 06:09:08 +08:00
broadcast ( default , GID , Orig ) ;
2010-05-26 16:10:14 +08:00
2010-05-26 05:09:08 +08:00
%% @doc Default broadcast handler. Dispatch the command to everyone.
%% We clean up the command and use the real GID and LID of the user, disregarding what was sent and possibly tampered with.
2010-05-26 11:35:31 +08:00
%% Only a handful of commands are allowed to broadcast. An user tampering with it would get disconnected instantly.
2010-05-20 13:47:28 +08:00
%% @todo Don't query the user data everytime! Keep an User instead of a GID probably.
2010-06-04 06:09:08 +08:00
broadcast ( Command , GID , Orig )
2010-05-26 05:09:08 +08:00
when Command =:= 16#0101 ;
Command =:= 16#0102 ;
Command =:= 16#0104 ;
Command =:= 16#0107 ;
Command =:= 16#010f ;
Command =:= 16#050f ;
Command =:= default - >
2010-06-04 06:09:08 +08:00
< < _ : 32 , A : 64 / bits , _ : 64 , B : 192 / bits , _ : 64 , C / bits > > = Orig ,
2010-05-20 13:47:28 +08:00
case egs_db : users_select ( GID ) of
error - >
ignore ;
Self - >
LID = Self #users.lid ,
2010-06-04 06:09:08 +08:00
Packet = < < A / binary , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , B / binary ,
2010-05-20 13:47:28 +08:00
GID : 32 / little - unsigned - integer , LID : 32 / little - unsigned - integer , C / binary > > ,
2010-06-04 06:09:08 +08:00
lists : foreach ( fun ( User ) - > User #users.pid ! { psu_broadcast , Packet } end , egs_db : users_select_others_in_area ( Self ) )
2010-05-20 13:47:28 +08:00
end .
%% @doc Movement (non-broadcast) handler. Do nothing.
handle ( 16#0102 , _ , _ , _ , _ ) - >
ignore ;
2010-05-13 12:34:04 +08:00
2010-06-02 01:37:02 +08:00
%% @doc Weapon equip, unequip, item drop, and more... handler. Do what we can.
%% Melee uses a format similar to: AAAA--BBCCCC----DDDDDDDDEE----FF with
%% AAAA the attack sound effect, BB the range, CCCC and DDDDDDDD unknown but related to angular range or similar, EE number of targets and FF the model.
%% Bullets and tech weapons formats are unknown but likely use a slightly different format.
2010-06-01 00:50:24 +08:00
%% @todo Others probably want to see that you changed your weapon.
2010-06-02 01:37:02 +08:00
%% @todo Apparently B is always ItemID+1. Not sure why.
%% @todo Currently use a separate file for the data sent for the weapons.
2010-06-01 00:50:24 +08:00
handle ( 16#0105 , CSocket , GID , _ , Orig ) - >
2010-06-02 01:37:02 +08:00
< < _ : 384 , A : 32 / little - unsigned - integer , ItemID : 8 , Action : 8 , B : 16 , C : 32 / little - unsigned - integer , _ / bits > > = Orig ,
case Action of
1 - > % equip weapon
log ( GID , " 0105 - equip weapon " ) ,
{ ok , File } = file : read_file ( " p/packet0105_1.bin " ) ,
Packet = < < 16#01050300 : 32 , 0 : 64 , GID : 32 / little - unsigned - integer , 0 : 64 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer ,
0 : 64 , GID : 32 / little - unsigned - integer , A : 32 / little - unsigned - integer , ItemID , Action , B : 16 / little - unsigned - integer , C : 32 / little - unsigned - integer ,
File / binary > > ,
egs_proto : packet_send ( CSocket , Packet ) ;
2 - > % unequip weapon
log ( GID , " 0105 - unequip weapon " ) ,
Packet = < < 16#01050300 : 32 , 0 : 64 , GID : 32 / little - unsigned - integer , 0 : 64 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer ,
0 : 64 , GID : 32 / little - unsigned - integer , A : 32 / little - unsigned - integer , ItemID , Action , B : 16 / little - unsigned - integer , C : 32 / little - unsigned - integer > > ,
egs_proto : packet_send ( CSocket , Packet ) ;
_ - >
log ( GID , " 0105 - ignored " ) ,
ignored
end ;
2010-06-01 00:50:24 +08:00
2010-06-04 18:23:18 +08:00
%% @doc Shop listing request. Currently return the normal item shop for everything.
%% @todo Return the other shops appropriately.
2010-06-03 02:05:50 +08:00
handle ( 16#010a , CSocket , GID , _ , Orig ) - >
< < _ : 384 , A : 32 / little - unsigned - integer , B : 32 / little - unsigned - integer , C : 32 / little - unsigned - integer > > = Orig ,
2010-06-04 05:36:34 +08:00
log ( GID , " shop listing request ( ~b , ~b , ~b ) " , [ A , B , C ] ) ,
2010-06-03 02:05:50 +08:00
{ ok , File } = file : read_file ( " p/itemshop.bin " ) ,
Packet = < < 16#010a0300 : 32 , 0 : 64 , GID : 32 / little - unsigned - integer , 0 : 64 , 16#00011300 : 32 ,
GID : 32 / little - unsigned - integer , 0 : 64 , GID : 32 / little - unsigned - integer , 0 : 32 , File / binary > > ,
egs_proto : packet_send ( CSocket , Packet ) ;
2010-06-04 05:36:34 +08:00
%% @doc Character death, and more, handler. Abort mission and redirect to 4th floor for now.
2010-06-01 00:50:24 +08:00
%% @todo Recover from death correctly.
handle ( 16#0110 , CSocket , GID , _ , _ ) - >
2010-06-04 05:36:34 +08:00
log ( GID , " death (and more) " ) ,
2010-06-01 00:50:24 +08:00
lobby_load ( CSocket , GID , 1100000 , 0 , 4 , 6 ) ;
2010-05-13 12:34:04 +08:00
%% @doc Uni cube handler.
2010-06-04 05:36:34 +08:00
handle ( 16#021d , CSocket , _ , _ , _ ) - >
2010-05-13 12:34:04 +08:00
egs_proto : send_universe_cube ( CSocket ) ;
%% @doc Uni selection handler.
%% When selecting 'Your room', load first floor for now.
%% When selecting 'Reload', load first floor.
%% @todo Load 'Your room' correctly.
2010-06-04 06:09:08 +08:00
handle ( 16#021f , CSocket , GID , _ , Orig ) - >
2010-06-04 17:23:15 +08:00
< < _ : 352 , Uni : 32 / little - unsigned - integer , _ / bits > > = Orig ,
case Uni of
0 - > % cancelled uni selection
2010-06-04 05:36:34 +08:00
ignore ;
2010-06-04 17:23:15 +08:00
16#ffffffff - >
2010-05-13 12:34:04 +08:00
log ( GID , " uni selection (my room) " ) ,
% 0230 0220
2010-05-26 17:45:33 +08:00
myroom_load ( CSocket , GID , 1120000 , 0 , 423 , 0 ) ;
2010-05-13 12:34:04 +08:00
_ - >
log ( GID , " uni selection (reload) " ) ,
% 0230 0220
2010-05-23 04:50:44 +08:00
lobby_load ( CSocket , GID , 1100000 , 0 , 1 , 1 )
2010-05-13 12:34:04 +08:00
end ;
%% @doc Shortcut changes handler. Do nothing.
%% @todo Save it.
handle ( 16#0302 , _ , GID , _ , _ ) - >
log ( GID , " dismissed shortcut changes " ) ;
2010-05-20 13:47:28 +08:00
%% @doc Chat broadcast handler. Dispatch the message to everyone (for now).
2010-05-13 12:34:04 +08:00
%% We must take extra precautions to handle different versions of the game correctly.
%% @todo Only broadcast to people in the same map.
2010-06-04 06:09:08 +08:00
handle ( 16#0304 , _ , GID , Version , Orig ) - >
2010-06-04 18:18:12 +08:00
case Version of
0 - > % AOTI v2.000
< < _ : 416 , Modifiers : 128 / bits , Message / bits > > = Orig ,
User = egs_db : users_select ( GID ) ,
Name = User #users.charname ;
_ - > % Above
< < _ : 416 , Modifiers : 128 / bits , Name : 512 / bits , Message / bits > > = Orig
2010-05-13 12:34:04 +08:00
end ,
2010-06-04 18:18:12 +08:00
[ LogName | _ ] = re : split ( Name , " \\ 0 \\ 0 " , [ { return , binary } ] ) ,
[ TmpMessage | _ ] = re : split ( Message , " \\ 0 \\ 0 " , [ { return , binary } ] ) ,
2010-05-24 19:52:45 +08:00
LogMessage = re : replace ( TmpMessage , " \\ n " , " " , [ global , { return , binary } ] ) ,
2010-06-04 05:36:34 +08:00
log ( GID , " chat from ~s : ~s " , [ [ re : replace ( LogName , " \\ 0 " , " " , [ global , { return , binary } ] ) ] , [ re : replace ( LogMessage , " \\ 0 " , " " , [ global , { return , binary } ] ) ] ] ) ,
2010-06-04 18:18:12 +08:00
lists : foreach ( fun ( User ) - > User #users.pid ! { psu_chat , GID , Name , Modifiers , Message } end , egs_db : users_select_all ( ) ) ;
2010-05-13 12:34:04 +08:00
2010-06-01 00:50:24 +08:00
%% @todo Handle this packet. Ignore for now.
handle ( 16#0402 , _ , _ , _ , _ ) - >
ignore ;
2010-05-23 04:50:44 +08:00
%% @doc Map change handler.
2010-05-25 14:50:08 +08:00
%% Spaceports and my room are handled differently than normal lobbies.
%% @todo Load 'Your room' correctly.
2010-05-13 12:34:04 +08:00
2010-06-04 06:09:08 +08:00
handle ( 16#0807 , CSocket , GID , _ , Orig ) - >
2010-06-04 18:07:30 +08:00
< < _ : 352 , Quest : 32 / little - unsigned - integer , MapType : 16 / little - unsigned - integer ,
MapNumber : 16 / little - unsigned - integer , MapEntry : 16 / little - unsigned - integer , _ / bits > > = Orig ,
2010-06-04 05:36:34 +08:00
log ( GID , " lobby change ( ~b , ~b , ~b , ~b ) " , [ Quest , MapType , MapNumber , MapEntry ] ) ,
2010-05-23 04:50:44 +08:00
case { Quest , MapType , MapNumber , MapEntry } of
{ 1104000 , 0 , 900 , 0 } - >
spaceport_load ( CSocket , GID , Quest , MapType , MapNumber , MapEntry ) ;
2010-05-25 14:50:08 +08:00
{ 1120000 , _ , _ , _ } - >
myroom_load ( CSocket , GID , Quest , MapType , 423 , MapEntry ) ;
2010-05-23 04:50:44 +08:00
_ - >
lobby_load ( CSocket , GID , Quest , MapType , MapNumber , MapEntry )
end ;
2010-05-13 12:34:04 +08:00
2010-05-24 19:10:05 +08:00
%% @doc Mission counter handler.
2010-06-04 06:09:08 +08:00
handle ( 16#0811 , CSocket , GID , _ , Orig ) - >
2010-06-04 18:07:30 +08:00
< < _ : 352 , Quest : 32 / little - unsigned - integer , MapType : 16 / little - unsigned - integer ,
MapNumber : 16 / little - unsigned - integer , MapEntry : 16 / little - unsigned - integer , _ / bits > > = Orig ,
2010-06-04 05:36:34 +08:00
log ( GID , " mission counter ( ~b , ~b , ~b , ~b ) " , [ Quest , MapType , MapNumber , MapEntry ] ) ,
2010-05-30 16:34:55 +08:00
counter_load ( CSocket , GID , Quest , MapType , MapNumber , MapEntry ) ;
2010-06-02 18:56:33 +08:00
%% @doc Start mission handler. Packet contains the selected mission number.
2010-05-30 16:34:55 +08:00
%% @todo Load more than one mission.
2010-06-04 02:25:52 +08:00
handle ( 16#0c01 , CSocket , GID , _ , _ ) - >
2010-05-30 16:34:55 +08:00
Packet = < < 16#0c020300 : 32 , 0 : 160 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 96 > > ,
egs_proto : packet_send ( CSocket , Packet ) ,
mission_load ( CSocket , GID , 1000013 , 0 , 1121 , 0 ) ; % load test mission!
%% @doc Counter quests files request handler? Send huge number of quest files.
%% @todo Handle correctly.
handle ( 16#0c05 , CSocket , _ , _ , _ ) - >
{ ok , < < _ : 32 , Packet / bits > > } = file : read_file ( " p/packet0c06.bin " ) ,
egs_proto : packet_send ( CSocket , Packet ) ;
%% @doc Counter available mission list request handler.
%% @todo Temporarily allow rare mission and LL all difficulties to all players.
handle ( 16#0c0f , CSocket , GID , _ , _ ) - >
Packet = < < 16#0c100300 : 32 , 0 : 32 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 64 ,
16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 64 , 16#01a92800 : 32 , 3 , 3 , 0 ,
3 , 3 , 3 , 3 , 0 , 0 : 40 , 3 , 3 , 3 , 3 , 3 , 0 : 176 > > ,
egs_proto : packet_send ( CSocket , Packet ) ;
2010-05-24 19:10:05 +08:00
2010-05-24 19:30:36 +08:00
%% @doc Set flag handler. Associate a new flag with the character.
%% Just reply with a success value for now.
%% @todo God save the flags.
2010-05-22 10:32:14 +08:00
handle ( 16#0d04 , CSocket , GID , _ , Orig ) - >
2010-05-24 19:30:36 +08:00
< < _ : 352 , Flag : 128 / bits , A : 16 / bits , _ : 8 , B / bits > > = Orig ,
2010-06-04 05:36:34 +08:00
log ( GID , " flag handler for ~s " , [ re : replace ( Flag , " \\ 0+ " , " " , [ global , { return , binary } ] ) ] ) ,
2010-05-24 19:30:36 +08:00
Packet = < < 16#0d040300 : 32 , 0 : 160 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 64 , Flag / binary , A / binary , 1 , B / binary > > ,
2010-05-22 10:32:14 +08:00
egs_proto : packet_send ( CSocket , Packet ) ;
2010-05-13 12:34:04 +08:00
%% @doc Options changes handler.
2010-06-04 06:09:08 +08:00
handle ( 16#0d07 , _ , GID , _ , Orig ) - >
2010-05-13 12:34:04 +08:00
log ( GID , " options changes " ) ,
2010-06-04 18:08:10 +08:00
< < _ : 352 , Options / bits > > = Orig ,
2010-05-13 12:34:04 +08:00
User = egs_db : users_select ( GID ) ,
2010-05-14 02:45:17 +08:00
file : write_file ( io_lib : format ( " save/ ~s / ~b -character.options " , [ User #users.folder , User #users.charnumber ] ) , Options ) ;
2010-05-13 12:34:04 +08:00
2010-06-01 00:50:24 +08:00
%% @doc Hit handler.
%% @todo Finish the work on it.
2010-06-02 02:11:22 +08:00
%% @todo First value at 2C is the number of hits. We don't need to know it though.
%% @todo We should later send this into one 0e07 packet rather than many.
2010-06-01 00:50:24 +08:00
handle ( 16#0e00 , CSocket , GID , _ , Orig ) - >
2010-06-02 02:11:22 +08:00
< < _ : 448 , Data / bits > > = Orig ,
handle_hits ( CSocket , GID , Data ) ;
2010-06-01 00:50:24 +08:00
2010-05-20 06:09:03 +08:00
%% @doc Lobby event handler. Handle chairs!
2010-05-18 01:47:49 +08:00
%% Apparently used for elevator, sit on chairs, and more?
2010-05-20 13:57:01 +08:00
%% @todo Handle more than sit on chair.
2010-05-17 10:56:02 +08:00
2010-05-20 06:09:03 +08:00
handle ( 16#0f0a , CSocket , GID , _ , Orig ) - >
< < _ : 448 , A : 32 / little - unsigned - integer , _ : 64 , B : 32 / little - unsigned - integer , _ / bits > > = Orig ,
Packet = < < 16#1211 : 16 , 0 : 176 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 64 , A : 32 / little - unsigned - integer , B : 32 / little - unsigned - integer , 8 : 32 / little - unsigned - integer , 0 : 32 > > ,
egs_proto : packet_send ( CSocket , Packet ) ,
log ( GID , " lobby event (can only chair so far) " ) ;
2010-05-17 10:56:02 +08:00
2010-06-03 02:49:19 +08:00
%% @doc Party information recap request.
2010-06-02 02:26:42 +08:00
%% @todo Handle when the party already exists! And stop doing it wrong.
handle ( 16#1705 , CSocket , GID , _ , _ ) - >
User = egs_db : users_select ( GID ) ,
CharName = User #users.charname ,
Packet = < < 16#17060300 : 32 , 0 : 160 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 64 ,
16#00000300 : 32 , 16#d5c0faff : 32 , 0 : 64 , CharName / binary , 16#78000000 : 32 , 16#01010000 : 32 ,
0 : 1536 , 16#0100c800 : 32 , 16#0601010a : 32 , 16#ffffffff : 32 , 0 : 32 > > ,
egs_proto : packet_send ( CSocket , Packet ) ;
2010-06-03 02:49:19 +08:00
%% @doc Mission selected handler. Send the currently selected mission.
%% @todo Probably need to dispatch that info to other party members in the same counter.
handle ( 16#1707 , _ , _ , _ , _ ) - >
ignore ;
2010-06-02 02:26:42 +08:00
%% @doc Party settings request handler. Item distribution is random for now.
%% @todo Handle correctly.
handle ( 16#1709 , CSocket , GID , _ , _ ) - >
Packet = < < 16#170a0300 : 32 , 0 : 160 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 64 , 16#01010c08 : 32 > > ,
egs_proto : packet_send ( CSocket , Packet ) ;
2010-05-30 16:34:55 +08:00
%% @doc Counter initialization handler?
%% @todo Handle correctly.
handle ( 16#1710 , CSocket , GID , _ , _ ) - >
Packet = < < 16#17110300 : 32 , 0 : 160 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 96 > > ,
egs_proto : packet_send ( CSocket , Packet ) ;
2010-06-03 02:05:50 +08:00
%% @doc Dialog request handler. Do what we can.
%% @todo Handle correctly.
handle ( 16#1a01 , CSocket , GID , _ , Orig ) - >
< < _ : 384 , A : 8 , B : 8 , _ : 16 , C : 8 , _ / bits > > = Orig ,
file : write_file ( " 1a01.bin " , Orig ) ,
case B of
0 - >
case A of
0 - >
case C of
2 - >
log ( GID , " Lumilass? " ) ,
Command = < < 16#1a030300 : 32 > > ,
{ ok , File } = file : read_file ( " p/lumilassA.bin " ) ,
Packet = < < 0 : 32 , File / binary > > ;
3 - >
log ( GID , " PP cube? " ) ,
Command = < < 16#1a040300 : 32 > > ,
{ ok , File } = file : read_file ( " p/ppcube.bin " ) ,
Packet = < < 0 : 32 , File / binary > > ;
_ - >
Command = < < 16#1a020300 : 32 > > ,
Packet = < < > > ,
log ( GID , " unhandled 1a01 B A C " )
end ;
80 - >
log ( GID , " NPC dialog request? " ) ,
Command = < < 16#1a020300 : 32 > > ,
Packet = < < 0 : 32 , 16#11001100 : 32 , 16#03000900 : 32 > > ;
90 - >
log ( GID , " unknown 1a01 90 " ) ,
Command = < < 16#1a020300 : 32 > > ,
Packet = < < 0 : 32 , 16#05000100 : 32 , 16#04000500 : 32 > > ;
91 - >
log ( GID , " unknown 1a01 91 " ) ,
Command = < < 16#1a020300 : 32 > > ,
Packet = < < 0 : 32 , 16#05000500 : 32 , 16#04000700 : 32 > > ;
92 - >
log ( GID , " unknown 1a01 92 " ) ,
Command = < < 16#1a020300 : 32 > > ,
Packet = < < 0 : 32 , 16#05000800 : 32 , 16#04000000 : 32 > > ;
93 - >
log ( GID , " unknown 1a01 93 " ) ,
Command = < < 16#1a020300 : 32 > > ,
Packet = < < 0 : 32 , 16#05001200 : 32 , 16#04000000 : 32 > > ;
_ - >
Command = < < 16#1a020300 : 32 > > ,
Packet = < < > > ,
log ( GID , " unhandled 1a01 in A case " )
end ;
2 - >
log ( GID , " unknown 1a01 B " ) ,
Command = < < 16#1a020300 : 32 > > ,
Packet = < < 0 : 32 , 16#00000100 : 32 , 0 : 32 > > ;
_ - >
Command = < < 16#1a020300 : 32 > > ,
Packet = < < > > ,
log ( GID , " unhandled 1a01 in B case " )
end ,
egs_proto : packet_send ( CSocket , < < Command / binary , 0 : 160 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 64 , Packet / binary > > ) ;
2010-05-13 12:34:04 +08:00
%% @doc Unknown command handler. Do nothing.
handle ( Command , _ , GID , _ , _ ) - >
2010-06-04 05:36:34 +08:00
log ( GID , " dismissed packet ~4.16.0b " , [ Command ] ) .
2010-05-13 12:34:04 +08:00
2010-06-02 02:11:22 +08:00
%% @doc Handle all hits received.
%% @todo Finish the work on it.
handle_hits ( _ , _ , < < > > ) - >
ok ;
handle_hits ( CSocket , GID , Data ) - >
< < A : 224 / bits , B : 128 / bits , _ : 288 / bits , Rest / bits > > = Data ,
PlayerHP = 4401 ,
TargetHP = 0 ,
Damage = 58008 ,
Packet = < < 16#0e070300 : 32 , 0 : 160 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 64 ,
1 : 32 / little - unsigned - integer , 16#01050000 : 32 , Damage : 32 / little - unsigned - integer ,
A / binary , 0 : 64 , PlayerHP : 32 / little - unsigned - integer , 0 : 32 , 16#01000200 : 32 ,
0 : 32 , TargetHP : 32 , 0 : 32 , B / binary , 16#04320000 : 32 , 16#80000000 : 32 , 16#26030000 : 32 , 16#89068d00 : 32 , 16#0c1c0105 : 32 > > ,
egs_proto : packet_send ( CSocket , Packet ) ,
handle_hits ( CSocket , GID , Rest ) .
2010-05-13 12:34:04 +08:00
%% @todo Figure out what the other things are.
2010-05-22 09:59:19 +08:00
send_packet_201 ( CSocket , GID , User , Char ) - >
2010-05-23 04:50:44 +08:00
Quest = User #users.quest ,
2010-05-22 09:59:19 +08:00
MapType = User #users.maptype ,
MapNumber = User #users.mapnumber ,
MapEntry = User #users.mapentry ,
2010-05-15 07:53:20 +08:00
CharGID = User #users.gid ,
CharLID = User #users.lid ,
2010-05-13 12:34:04 +08:00
{ ok , File } = file : read_file ( " p/packet0201.bin " ) ,
2010-05-23 04:50:44 +08:00
< < _ : 96 , A : 32 / bits , _ : 96 , B : 32 / bits , _ : 256 , D : 32 / bits , _ : 2656 , After / bits > > = File ,
2010-05-15 07:53:20 +08:00
Packet = < < 16#0201 : 16 , 0 : 48 , A / binary , CharGID : 32 / little - unsigned - integer , 0 : 64 , B / binary , GID : 32 / little - unsigned - integer ,
2010-05-23 04:50:44 +08:00
0 : 64 , CharLID : 32 / little - unsigned - integer , CharGID : 32 / little - unsigned - integer , 0 : 96 , D / binary , Quest : 32 / little - unsigned - integer ,
2010-05-25 14:50:08 +08:00
MapType : 32 / little - unsigned - integer , MapNumber : 32 / little - unsigned - integer , MapEntry : 32 / little - unsigned - integer , 0 : 192 , Quest : 32 / little - unsigned - integer ,
MapType : 32 / little - unsigned - integer , MapNumber : 32 / little - unsigned - integer , MapEntry : 32 / little - unsigned - integer , Char / binary , After / binary > > ,
2010-05-13 12:34:04 +08:00
egs_proto : packet_send ( CSocket , Packet ) .
2010-05-15 05:45:03 +08:00
%% @todo Figure out what the other things are.
send_packet_233 ( CSocket , GID , Users ) - >
NbUsers = length ( Users ) ,
case NbUsers of
0 - >
ignore ;
_ - >
Header = < < 16#02330300 : 32 , 0 : 32 , 16#00001200 : 32 , GID : 32 / little - unsigned - integer , 0 : 64 , 16#00011300 : 32 ,
GID : 32 / little - unsigned - integer , 0 : 64 , NbUsers : 32 / little - unsigned - integer > > ,
Contents = build_packet_233_contents ( Users ) ,
Packet = < < Header / binary , Contents / binary > > ,
egs_proto : packet_send ( CSocket , Packet )
end .
build_packet_233_contents ( [ ] ) - >
< < > > ;
build_packet_233_contents ( Users ) - >
[ User | Rest ] = Users ,
{ ok , File } = file : read_file ( " p/player.bin " ) ,
2010-05-26 15:55:55 +08:00
< < A : 32 / bits , _ : 32 , B : 64 / bits , _ : 32 , C : 32 / bits , _ : 256 , E : 64 / bits , _ : 2336 , F / bits > > = File ,
2010-05-15 05:45:03 +08:00
{ ok , CharFile } = file : read_file ( io_lib : format ( " save/ ~s / ~b -character " , [ User #users.folder , User #users.charnumber ] ) ) ,
CharGID = User #users.gid ,
LID = User #users.lid ,
2010-05-16 01:31:48 +08:00
case User #users.coords of % TODO: temporary? undefined handling
undefined - >
2010-05-26 15:55:55 +08:00
Direction = < < 0 : 32 > > ,
2010-05-17 10:54:35 +08:00
Coords = < < 0 : 96 > > ,
2010-05-23 04:50:44 +08:00
Quest = 1100000 ,
2010-05-22 09:59:19 +08:00
MapType = 0 ,
MapNumber = 1 ,
MapEntry = 0 ;
2010-05-16 01:31:48 +08:00
_ - >
2010-05-26 15:55:55 +08:00
Direction = User #users.direction ,
2010-05-17 10:54:35 +08:00
Coords = User #users.coords ,
2010-05-23 04:50:44 +08:00
Quest = User #users.quest ,
2010-05-22 09:59:19 +08:00
MapType = User #users.maptype ,
MapNumber = User #users.mapnumber ,
MapEntry = User #users.mapentry
2010-05-16 01:31:48 +08:00
end ,
Chunk = < < A / binary , CharGID : 32 / little - unsigned - integer , B / binary , LID : 16 / little - unsigned - integer , 16#0100 : 16 , C / binary ,
2010-05-23 04:50:44 +08:00
Quest : 32 / little - unsigned - integer , MapType : 32 / little - unsigned - integer , MapNumber : 32 / little - unsigned - integer , MapEntry : 32 / little - unsigned - integer ,
2010-05-26 15:55:55 +08:00
Direction : 32 / bits , Coords : 96 / bits , E / binary , Quest : 32 / little - unsigned - integer , MapType : 32 / little - unsigned - integer , MapNumber : 32 / little - unsigned - integer ,
2010-05-23 04:50:44 +08:00
MapEntry : 32 / little - unsigned - integer , CharFile / binary , F / binary > > ,
2010-05-15 05:45:03 +08:00
Next = build_packet_233_contents ( Rest ) ,
< < Chunk / binary , Next / binary > > .
2010-06-05 00:44:41 +08:00
%% @todo Possibly related to 010d. Just send seemingly safe values.
send_packet_0111 ( CSocket , GID ) - >
Packet = < < 16#01110300 : 32 , 0 : 64 , GID : 32 / little - unsigned - integer , 0 : 64 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 64 ,
GID : 32 / little - unsigned - integer , 0 : 32 , 6 : 32 / little - unsigned - integer , 0 : 32 > > ,
egs_proto : packet_send ( CSocket , Packet ) .
2010-06-05 00:02:26 +08:00
%% @todo End of character loading. Just send it.
send_packet_021b ( CSocket , GID ) - >
Packet = < < 16#021b0300 : 32 , 0 : 160 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 64 > > ,
egs_proto : packet_send ( CSocket , Packet ) .
2010-06-05 00:30:57 +08:00
%% @todo Inventory related. No idea what it does.
send_packet_0a05 ( CSocket , GID ) - >
Packet = < < 16#0a050300 : 32 , 0 : 160 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 64 > > ,
egs_proto : packet_send ( CSocket , Packet ) .
2010-06-01 00:50:24 +08:00
%% @todo Inventory related. Figure out everything in this packet and handle it correctly.
send_packet_0a06 ( CSocket , GID ) - >
{ ok , < < _ : 32 , A : 96 / bits , _ : 32 , B : 96 / bits , _ : 32 , C : 1440 / bits , _ : 32 , D / bits > > } = file : read_file ( " p/packet0a06.bin " ) ,
egs_proto : packet_send ( CSocket , < < A / binary , GID : 32 / little - unsigned - integer , B / binary , GID : 32 / little - unsigned - integer , C / binary , GID : 32 / little - unsigned - integer , D / binary > > ) .
%% @todo Inventory. Figure out everything in this packet and handle it correctly.
send_packet_0a0a ( CSocket , GID ) - >
{ ok , < < _ : 32 , A : 224 / bits , _ : 32 , B / bits > > } = file : read_file ( " p/packet0a0a.bin " ) ,
egs_proto : packet_send ( CSocket , < < A / binary , GID : 32 / little - unsigned - integer , B / binary > > ) .
2010-05-13 12:34:04 +08:00
%% @todo Figure out what the packet is.
send_packet_1005 ( CSocket , GID , Char ) - >
{ ok , File } = file : read_file ( " p/packet1005.bin " ) ,
< < _ : 352 , Before : 160 / bits , _ : 608 , After / bits > > = File ,
< < Name : 512 / bits , _ / bits > > = Char ,
2010-06-04 22:32:34 +08:00
Packet = < < 16#10050300 : 32 , 0 : 160 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 64 , Before / binary , GID : 32 / little - unsigned - integer , 0 : 64 , Name / binary , After / binary > > ,
2010-05-13 12:34:04 +08:00
egs_proto : packet_send ( CSocket , Packet ) .
2010-06-04 22:25:38 +08:00
%% @todo Figure out what the packet is.
send_packet_1006 ( CSocket , GID , N ) - >
Packet = < < 16#10060300 : 32 , 0 : 160 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 64 , N : 32 / little - unsigned - integer > > ,
egs_proto : packet_send ( CSocket , Packet ) .
2010-06-04 22:53:32 +08:00
%% @todo Send an empty partner card list.
send_packet_1501 ( CSocket , GID ) - >
Packet = < < 16#15010300 : 32 , 0 : 160 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 96 > > ,
egs_proto : packet_send ( CSocket , Packet ) .
2010-06-04 23:02:13 +08:00
%% @todo Send an empty blacklist.
send_packet_1512 ( CSocket , GID ) - >
Packet = < < 16#15120300 : 32 , 0 : 160 , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 46144 > > ,
egs_proto : packet_send ( CSocket , Packet ) .
2010-05-15 07:53:20 +08:00
%% @todo Figure out what the other things are and do it right.
%% @todo Temporarily send 233 until the correct process is figured out.
%% Should be something along the lines of 203 201 204.
send_spawn ( CSocket , GID , _ ) - >
2010-06-02 05:40:32 +08:00
send_packet_233 ( CSocket , GID , egs_db : users_select_others_in_area ( egs_db : users_select ( GID ) ) ) .
%% @doc Send a character unspawn notification.
%% @todo It's probably right but who knows...
send_unspawn ( CSocket , GID , Spawn ) - >
PlayerGID = Spawn #users.gid ,
PlayerLID = Spawn #users.lid ,
Packet = < < 16#02040300 : 32 , 0 : 32 , 16#00001200 : 32 , PlayerGID : 32 / little - unsigned - integer , 0 : 64 ,
16#00011300 : 32 , GID : 32 / little - unsigned - integer , 0 : 64 , PlayerGID : 32 / little - unsigned - integer ,
PlayerLID : 32 / little - unsigned - integer , 5 : 32 / little - unsigned - integer > > ,
egs_proto : packet_send ( CSocket , Packet ) .
2010-05-15 07:53:20 +08:00
2010-05-13 12:34:04 +08:00
%% @doc Log message to the console.
log ( GID , Message ) - >
io : format ( " game ( ~.10b ): ~s ~n " , [ GID , Message ] ) .
2010-06-04 05:36:34 +08:00
log ( GID , Message , Format ) - >
RealMessage = io_lib : format ( Message , Format ) ,
log ( GID , RealMessage ) .