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
log ( 0 , " hello (new connection) " ) ,
egs_proto : send_hello ( CSocket ) ,
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
{ ok , Packet } - >
< < _ : 32 , Command : 16 / unsigned - integer , _ / bits > > = Packet ,
process_handle ( Command , CSocket , Version , Packet ) ;
{ 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 } - >
log ( 0 , " recv error, closing " )
end .
%% @doc Game server auth request handler.
process_handle ( 16#020d , CSocket , Version , Packet ) - >
[ { gid , GID } , { auth , Auth } ] = egs_proto : parse_game_auth ( Packet ) ,
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 - >
log ( GID , " good auth, proceed " ) ,
2010-05-26 04:10:14 +08:00
LID = egs_db : next ( lobby ) rem 1024 ,
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 ) ;
_ - >
log ( GID , " bad auth, closing " ) ,
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.
process_handle ( 16#080e , CSocket , _ , Packet ) - >
[ { version , RealVersion } ] = egs_proto : parse_platform_info ( Packet ) ,
? MODULE : process ( CSocket , RealVersion ) ;
%% @doc Unknown command handler. Do nothing.
process_handle ( Command , CSocket , Version , _ ) - >
log ( 0 , io_lib : format ( " (process) dismissed packet ~4.16.0b " , [ Command ] ) ) ,
? 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
{ ok , Packet } - >
< < _ : 32 , Command : 16 / unsigned - integer , _ / bits > > = Packet ,
char_select_handle ( Command , CSocket , GID , Version , Packet ) ;
{ error , timeout } - >
egs_proto : send_keepalive ( CSocket , GID ) ,
reload ,
? MODULE : char_select ( CSocket , GID , Version ) ;
{ error , closed } - >
log ( GID , " recv error, closing " ) ,
egs_db : users_delete ( GID )
end .
%% @doc Character selection handler.
char_select_handle ( 16#020b , CSocket , GID , Version , Packet ) - >
log ( GID , " character selection " ) ,
[ { number , Number } ] = egs_proto : parse_character_select ( Packet ) ,
char_select_load ( CSocket , GID , Version , Number ) ;
%% @doc Character creation handler.
char_select_handle ( 16#0d02 , CSocket , GID , Version , Packet ) - >
log ( GID , " character creation " ) ,
2010-05-14 02:45:17 +08:00
User = egs_db : users_select ( GID ) ,
2010-05-13 12:34:04 +08:00
[ { number , Number } , { char , Char } ] = egs_proto : parse_character_create ( Packet ) ,
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 , _ ) - >
log ( GID , " send character selection screen " ) ,
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 ) ;
%% @doc Unknown command handler. Do nothing.
char_select_handle ( Command , CSocket , GID , Version , _ ) - >
log ( GID , io_lib : format ( " (char_select) dismissed packet ~4.16.0b " , [ Command ] ) ) ,
? 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 .
%% @doc Load the selected character and start the main game's loop.
char_select_load ( CSocket , GID , Version , Number ) - >
User = egs_db : users_select ( GID ) ,
2010-05-14 02:45:17 +08:00
[ { status , _ } , { char , < < Name : 512 / bits , _ / bits > > } | _ ] = char_load ( User #users.folder , Number ) ,
2010-05-13 12:34:04 +08:00
NewRow = User #users { charnumber = Number , charname = Name } ,
egs_db : users_insert ( NewRow ) ,
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 ) .
%% @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-05-23 04:50:44 +08:00
User = OldUser #users { quest = Quest , maptype = MapType , mapnumber = MapNumber , mapentry = MapEntry } ,
2010-05-20 13:47:28 +08:00
egs_db : users_insert ( User ) ,
2010-05-14 02:45:17 +08:00
[ { status , 1 } , { char , Char } , { options , Options } ] = char_load ( User #users.folder , User #users.charnumber ) ,
2010-05-24 17:25:20 +08:00
[ { name , _ } , { quest , QuestFile } , { zone , ZoneFile } , { entries , _ } ] = proplists : get_value ( [ Quest , MapType , MapNumber ] , ? MAPS ,
[ { name , " dammy " } , { quest , " data/lobby/colony.quest.nbl " } , { zone , " data/lobby/colony.zone.nbl " } , { entries , [ ] } ] ) ,
2010-05-13 12:34:04 +08:00
try
2010-05-15 07:53:20 +08:00
% broadcast spawn to other people
lists : foreach ( fun ( Other ) - > Other #users.pid ! { psu_player_spawn , User } end , egs_db : users_select_others ( GID ) ) ,
% load lobby and character
2010-05-13 12:34:04 +08:00
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 ) ,
% 0c00
2010-05-23 04:50:44 +08:00
egs_proto : send_quest ( CSocket , QuestFile ) ,
2010-05-13 12:34:04 +08:00
% 0a05 0111 010d
send_packet_200 ( CSocket , GID ) ,
2010-05-23 04:50:44 +08:00
egs_proto : send_zone ( CSocket , ZoneFile ) ,
egs_proto : send_map ( CSocket , Quest , MapType , MapNumber , MapEntry ) ,
2010-05-13 12:34:04 +08:00
% 100e 020c
egs_proto : send_load_quest ( CSocket , GID ) ,
2010-05-22 09:59:19 +08:00
send_packet_201 ( CSocket , GID , User , Char ) ,
2010-05-15 07:53:20 +08:00
% 0a06
2010-05-15 05:45:03 +08:00
Users = egs_db : users_select_others ( GID ) ,
send_packet_233 ( CSocket , GID , Users ) ,
2010-05-13 12:34:04 +08:00
egs_proto : send_loading_end ( CSocket , GID ) ,
egs_proto : send_camera_center ( CSocket , GID )
catch
_ - >
ssl : close ( CSocket ) ,
log ( GID , " send error, closing " )
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
% broadcast spawn to other people
lists : foreach ( fun ( Other ) - > Other #users.pid ! { psu_player_spawn , User } end , egs_db : users_select_others ( GID ) ) ,
% 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 ) ,
% 0c00
egs_proto : send_quest ( CSocket , QuestFile ) ,
% 0a05 0111 010d
send_packet_200 ( CSocket , GID ) ,
egs_proto : send_zone ( CSocket , ZoneFile ) ,
egs_proto : send_map ( CSocket , Quest , MapType , MapNumber , MapEntry ) ,
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
_ - >
ssl : close ( CSocket ) ,
log ( GID , " send error, closing " )
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-05-24 17:25:20 +08:00
[ { name , _ } , { quest , QuestFile } , { zone , ZoneFile } , { entries , _ } ] = proplists : get_value ( [ Quest , MapType , MapNumber ] , ? MAPS ) ,
2010-05-23 04:50:44 +08:00
try
% 0c00
egs_proto : send_quest ( CSocket , QuestFile ) ,
% 0a05
send_packet_200 ( CSocket , GID ) ,
egs_proto : send_zone ( CSocket , ZoneFile ) ,
egs_proto : send_map ( CSocket , Quest , MapType , MapNumber , MapEntry ) ,
% 100e 020c
send_packet_201 ( CSocket , GID , User , Char ) ,
% 0a06
egs_proto : send_loading_end ( CSocket , GID ) ,
egs_proto : send_camera_center ( CSocket , GID )
catch
_ - >
ssl : close ( CSocket ) ,
log ( GID , " send error, closing " )
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-05-20 13:47:28 +08:00
{ psu_broadcast , Packet } - >
< < A : 192 / bits , _ : 64 , B / bits > > = Packet ,
Broadcast = < < A / binary , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , B / binary > > ,
egs_proto : packet_send ( CSocket , Broadcast ) ,
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 } - >
egs_proto : send_keepalive ( CSocket , GID ) ,
? MODULE : loop ( CSocket , GID , Version , SoFar ) ;
2010-05-21 16:42:47 +08:00
{ psu_player_spawn , PlayerGID } - >
send_spawn ( CSocket , GID , PlayerGID ) ,
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-05-13 12:34:04 +08:00
[ dispatch ( CSocket , GID , Version , P ) | | P < - 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 " ) ,
egs_db : users_delete ( GID ) ,
ssl : close ( CSocket ) .
2010-05-13 12:34:04 +08:00
%% @doc Dispatch the command to the right handler.
dispatch ( CSocket , GID , Version , Packet ) - >
2010-05-20 13:47:28 +08:00
< < _ : 32 , Command : 16 / unsigned - integer , Channel : 8 / little - unsigned - integer , _ / bits > > = Packet ,
case Channel of
1 - >
broadcast ( Command , CSocket , GID , Version , Packet ) ;
_ - >
handle ( Command , CSocket , GID , Version , Packet )
end .
%% @doc Position change broadcast handler. Save the position and then dispatch it.
broadcast ( 16#0503 , _ , GID , _ , Packet ) - >
2010-05-23 04:50:44 +08:00
< < _ : 448 , Coords : 96 / bits , _ : 96 , Quest : 32 / little - unsigned - integer , MapType : 32 / little - unsigned - integer ,
MapNumber : 32 / little - unsigned - integer , MapEntry : 32 / little - unsigned - integer , _ / bits > > = Packet ,
2010-05-20 13:47:28 +08:00
User = egs_db : users_select ( GID ) ,
2010-05-23 04:50:44 +08:00
NewUser = User #users { coords = Coords , quest = Quest , maptype = MapType , mapnumber = MapNumber , mapentry = MapEntry } ,
2010-05-20 13:47:28 +08:00
egs_db : users_insert ( NewUser ) ,
broadcast ( default , ignore , GID , ignore , Packet ) ;
%% @doc Default broadcast handler. Dispatch the packet to everyone (for now).
%% We clean up the packet and use the real GID and LID of the user, disregarding what was sent and possibly tampered with.
%% @todo Don't query the user data everytime! Keep an User instead of a GID probably.
broadcast ( _ , _ , GID , _ , Packet ) - >
< < _ : 32 , A : 64 / bits , _ : 64 , B : 192 / bits , _ : 64 , C / bits > > = Packet ,
case egs_db : users_select ( GID ) of
error - >
ignore ;
Self - >
LID = Self #users.lid ,
SafePacket = < < A / binary , 16#00011300 : 32 , GID : 32 / little - unsigned - integer , B / binary ,
GID : 32 / little - unsigned - integer , LID : 32 / little - unsigned - integer , C / binary > > ,
lists : foreach ( fun ( User ) - > User #users.pid ! { psu_broadcast , SafePacket } end , egs_db : users_select_others ( GID ) )
end .
%% @doc Movement (non-broadcast) handler. Do nothing.
handle ( 16#0102 , _ , _ , _ , _ ) - >
ignore ;
2010-05-13 12:34:04 +08:00
%% @doc Keepalive handler. Do nothing.
handle ( 16#021c , _ , _ , _ , _ ) - >
ignore ;
%% @doc Uni cube handler.
handle ( 16#021d , CSocket , GID , _ , _ ) - >
log ( GID , " uni cube " ) ,
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.
handle ( 16#021f , CSocket , GID , _ , Packet ) - >
case egs_proto : parse_uni_select ( Packet ) of
[ { uni , 0 } ] - >
log ( GID , " uni selection cancelled " ) ;
[ { uni , 16#ffffffff } ] - >
log ( GID , " uni selection (my room) " ) ,
% 0230 0220
2010-05-25 14:50:08 +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-05-15 05:45:03 +08:00
handle ( 16#0304 , _ , GID , Version , Packet ) - >
2010-05-15 23:10:07 +08:00
[ { gid , _ } , { name , ChatName } , { modifiers , ChatModifiers } , { message , ChatMessage } ] = egs_proto : parse_chat ( Version , Packet ) ,
2010-05-13 12:34:04 +08:00
case ChatName of
missing - >
2010-05-15 22:12:00 +08:00
case egs_db : users_select ( GID ) of
error - >
ActualName = ChatName ;
User - >
ActualName = User #users.charname
end ;
2010-05-13 12:34:04 +08:00
_ - >
ActualName = ChatName
end ,
2010-05-24 19:41:06 +08:00
[ LogName | _ ] = re : split ( ActualName , " \\ 0 \\ 0 " , [ { return , binary } ] ) ,
2010-05-24 19:52:45 +08:00
[ TmpMessage | _ ] = re : split ( ChatMessage , " \\ 0 \\ 0 " , [ { return , binary } ] ) ,
LogMessage = re : replace ( TmpMessage , " \\ n " , " " , [ global , { return , binary } ] ) ,
2010-05-24 19:49:00 +08:00
log ( GID , io_lib : format ( " chat from ~s : ~s " , [ [ re : replace ( LogName , " \\ 0 " , " " , [ global , { return , binary } ] ) ] , [ re : replace ( LogMessage , " \\ 0 " , " " , [ global , { return , binary } ] ) ] ] ) ) ,
2010-05-15 23:10:07 +08:00
lists : foreach ( fun ( User ) - > User #users.pid ! { psu_chat , GID , ActualName , ChatModifiers , ChatMessage } end , egs_db : users_select_all ( ) ) ;
2010-05-13 12:34:04 +08:00
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-05-15 05:45:03 +08:00
handle ( 16#0807 , CSocket , GID , _ , Packet ) - >
2010-05-23 04:50:44 +08:00
[ { quest , Quest } , { maptype , MapType } , { mapnumber , MapNumber } , { mapentry , MapEntry } ] = egs_proto : parse_lobby_change ( Packet ) ,
log ( GID , io_lib : format ( " lobby change ( ~b , ~b , ~b , ~b ) " , [ Quest , MapType , MapNumber , MapEntry ] ) ) ,
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.
handle ( 16#0811 , _ , GID , _ , _ ) - >
log ( GID , " dismissed mission counter " ) ;
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 ,
log ( GID , io_lib : format ( " flag handler for ~s " , [ re : replace ( Flag , " \\ 0+ " , " " , [ global , { return , binary } ] ) ] ) ) ,
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-05-15 05:45:03 +08:00
handle ( 16#0d07 , _ , GID , _ , Packet ) - >
2010-05-13 12:34:04 +08:00
log ( GID , " options changes " ) ,
[ { options , Options } ] = egs_proto : parse_options_change ( Packet ) ,
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-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-05-13 12:34:04 +08:00
%% @doc Unknown command handler. Do nothing.
handle ( Command , _ , GID , _ , _ ) - >
log ( GID , io_lib : format ( " (game) dismissed packet ~4.16.0b " , [ Command ] ) ) .
%% @todo Figure out what the packet is.
send_packet_200 ( CSocket , GID ) - >
{ ok , File } = file : read_file ( " p/packet0200.bin " ) ,
< < _ : 288 , After / bits > > = File ,
Packet = < < 16#0200 : 16 , 0 : 208 , GID : 32 / little - unsigned - integer , After / binary > > ,
egs_proto : packet_send ( CSocket , Packet ) .
%% @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-23 04:50:44 +08:00
< < A : 32 / bits , _ : 32 , B : 64 / bits , _ : 32 , C : 32 / bits , _ : 128 , D : 32 / bits , _ : 96 , 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-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-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 ,
D : 32 / bits , Coords : 96 / bits , E / binary , Quest : 32 / little - unsigned - integer , MapType : 32 / little - unsigned - integer , MapNumber : 32 / little - unsigned - integer ,
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-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 ,
Packet = < < 16#1005 : 16 , 0 : 208 , GID : 32 / little - unsigned - integer , 0 : 64 , Before / binary , GID : 32 / little - unsigned - integer , 0 : 64 , Name / binary , After / binary > > ,
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 , _ ) - >
send_packet_233 ( CSocket , GID , egs_db : users_select_others ( GID ) ) .
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 ] ) .