2010-08-20 05:43:19 +08:00
%% @author Lo<4C> c Hoguin <essen@dev-extend.eu>
%% @copyright 2010 Lo<4C> c Hoguin.
%% @doc Independent implementation of the PSU protocol.
%%
%% This file is part of EGS.
%%
%% EGS is free software: you can redistribute it and/or modify
2010-09-04 06:09:06 +08:00
%% it under the terms of the GNU Affero General Public License as
%% published by the Free Software Foundation, either version 3 of the
%% License, or (at your option) any later version.
2010-08-20 05:43:19 +08:00
%%
%% 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
2010-09-04 06:09:06 +08:00
%% GNU Affero General Public License for more details.
2010-08-20 05:43:19 +08:00
%%
2010-09-04 06:09:06 +08:00
%% You should have received a copy of the GNU Affero General Public License
2010-08-20 05:43:19 +08:00
%% along with EGS. If not, see <http://www.gnu.org/licenses/>.
2010-05-13 12:34:04 +08:00
2010-08-19 07:53:18 +08:00
- module ( psu_proto ) .
2010-05-13 12:34:04 +08:00
- compile ( export_all ) .
2010-08-30 00:31:23 +08:00
- include ( " include/records.hrl " ) .
2010-08-20 05:43:19 +08:00
%% @doc Log the message.
log ( Msg ) - >
io : format ( " ~p ~s ~n " , [ get ( gid ) , Msg ] ) .
%% @spec log(Msg, FmtVars) -> ok
%% @doc Format and log the message.
log ( Msg , FmtVars ) - >
FmtMsg = io_lib : format ( Msg , FmtVars ) ,
log ( FmtMsg ) .
%% @spec assert() -> ok
%% @doc Log a detailed message when the function is called.
2010-08-25 07:42:37 +08:00
- define ( ASSERT ( ) , log ( " assert error in module ~p on line ~p " , [ ? MODULE , ? LINE ] ) ) .
2010-08-20 05:43:19 +08:00
%% @spec assert(A, B) -> ok
%% @doc Log a detailed message when the assertion A =:= B fails.
2010-08-25 07:42:37 +08:00
- define ( ASSERT_EQ ( A , B ) , if A =:= B - > ok ; true - > log ( " assert error in module ~p on line ~p " , [ ? MODULE , ? LINE ] ) end ) .
2010-08-20 05:43:19 +08:00
%% @spec parse(Packet) -> Result
%% @doc Parse the packet and return a result accordingly.
parse ( < < Size : 32 / little , Command : 16 , Channel : 8 , _ Unknown : 8 , Data / bits > > ) - >
parse ( Size , Command , Channel , Data ) .
2010-08-28 00:24:53 +08:00
%% @todo Maybe we shouldn't ignore it?
%% @todo VarI is probably animation state related and defines what the player is doing.
parse ( Size , 16#0102 , 2 , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , _ FromGID : 32 / little , VarC : 32 / little , VarD : 32 / little ,
VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , _ TargetGID : 32 / little , _ TargetLID : 32 / little ,
_ VarI : 8 , _ IntDir : 24 / little , VarJ : 32 / little , _ X : 32 / little - float , _ Y : 32 / little - float , _ Z : 32 / little - float ,
_ QuestID : 32 / little , _ ZoneID : 32 / little , _ MapID : 32 / little , _ EntryID : 32 / little , VarK : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 92 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarJ , 0 ) ,
? ASSERT_EQ ( VarK , 0 ) ,
ignore ;
2010-08-20 05:43:19 +08:00
%% @todo One of the missing events is probably learning a new PA.
parse ( Size , 16#0105 , Channel , Data ) - >
2010-08-21 05:22:24 +08:00
< < _ LID : 16 / little , _ VarB : 16 / little , VarC : 32 / little , _ FromGID : 32 / little , VarD : 32 / little , VarE : 32 / little , TypeID : 32 / little , GID : 32 / little ,
2010-08-30 05:09:06 +08:00
VarF : 32 / little , VarG : 32 / little , TargetGID : 32 / little , TargetLID : 32 / little , ItemIndex : 8 , EventID : 8 , _ PAIndex : 8 , VarH : 8 , VarI : 32 / little , Rest / bits > > = Data ,
2010-08-20 05:43:19 +08:00
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( TypeID , 0 ) ,
? ASSERT_EQ ( GID , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
Event = case EventID of
1 - > item_equip ;
2 - > item_unequip ;
3 - > ignore ; %% @todo item_link_pa;
4 - > ignore ; %% @todo item_unlink_pa;
5 - > item_drop ;
2010-09-05 21:32:51 +08:00
7 - > ignore ; %% @todo item_learn_pa;
2010-08-20 05:43:19 +08:00
8 - > ignore ; %% @todo item_use;
2010-09-29 05:06:27 +08:00
9 - > item_set_trap ;
2010-08-20 05:43:19 +08:00
18 - > ignore ; %% @todo item_unlearn_pa;
_ - > log ( " unknown 0105 EventID ~p " , [ EventID ] )
end ,
case Event of
item_drop - >
? ASSERT_EQ ( Size , 76 ) ,
< < _ Quantity : 32 / little , _ PosX : 32 / little - float , _ PosY : 32 / little - float , _ PosZ : 32 / little - float > > = Rest ,
2010-08-30 05:09:06 +08:00
%~ {Event, ItemIndex, Quantity, ...};
2010-08-20 05:43:19 +08:00
ignore ;
ignore - >
? ASSERT_EQ ( Size , 60 ) ,
ignore ;
_ - >
? ASSERT_EQ ( Size , 60 ) ,
2010-08-30 05:09:06 +08:00
{ Event , ItemIndex , TargetGID , TargetLID , VarH , VarI }
2010-08-20 05:43:19 +08:00
end ;
2010-08-23 02:33:16 +08:00
parse ( Size , 16#010a , Channel , Data ) - >
< < HeaderLID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little ,
2010-09-29 23:46:56 +08:00
_ GID : 32 / little , BodyLID : 32 / little , EventID : 16 / little , QuantityOrColor : 8 , VarK : 8 , Param : 16 / bits , VarL : 16 > > = Data ,
2010-08-23 02:33:16 +08:00
? ASSERT_EQ ( Size , 60 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
? ASSERT_EQ ( HeaderLID , BodyLID ) ,
case EventID of
1 - >
< < ShopID : 16 / little > > = Param ,
2010-09-29 23:46:56 +08:00
? ASSERT_EQ ( QuantityOrColor , 0 ) ,
2010-08-23 02:33:16 +08:00
? ASSERT_EQ ( VarK , 0 ) ,
? ASSERT_EQ ( VarL , 0 ) ,
{ npc_shop_enter , ShopID } ;
2 - >
2010-09-05 01:01:35 +08:00
< < ShopItemIndex : 16 / little > > = Param ,
2010-09-29 23:46:56 +08:00
? ASSERT_EQ ( QuantityOrColor , VarK ) ,
2010-08-23 02:33:16 +08:00
? ASSERT_EQ ( VarL , 0 ) ,
2010-09-29 23:46:56 +08:00
{ npc_shop_buy , ShopItemIndex , QuantityOrColor } ;
2010-08-23 02:33:16 +08:00
3 - >
2010-09-05 01:01:35 +08:00
< < InventoryItemIndex : 8 , _ Unknown : 8 > > = Param ,
2010-08-23 02:33:16 +08:00
? ASSERT_EQ ( VarK , 0 ) ,
? ASSERT_EQ ( VarL , 0 ) ,
2010-09-29 23:46:56 +08:00
{ npc_shop_sell , InventoryItemIndex , QuantityOrColor } ;
2010-09-05 21:32:51 +08:00
4 - > ignore ; %% @todo npc_shop_gift_wrap
2010-08-23 02:33:16 +08:00
5 - >
< < ShopID : 16 / little > > = Param ,
2010-09-29 23:46:56 +08:00
? ASSERT_EQ ( QuantityOrColor , 0 ) ,
2010-08-23 02:33:16 +08:00
? ASSERT_EQ ( VarK , 0 ) ,
? ASSERT_EQ ( VarL , 0 ) ,
{ npc_shop_leave , ShopID } ;
6 - > ? ASSERT ( ) , ignore
end ;
2010-08-27 01:21:57 +08:00
%% @todo We probably want to check some of those values and save the others. It's mostly harmless though, ignore for now.
%% @todo We also probably should send the spawn to everyone in response to this command rather than on area_change.
parse ( Size , 16#010b , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , HeaderGID : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little ,
2010-10-06 23:54:25 +08:00
BodyGID : 32 / little , _ PartyPosOrLID : 32 / little , VarJ : 16 / little , _ IntDir : 16 / little , _ X : 32 / little - float , _ Y : 32 / little - float , _ Z : 32 / little - float ,
2010-08-27 01:21:57 +08:00
VarK : 32 / little , VarL : 32 / little , _ QuestID : 32 / little , _ ZoneID : 32 / little , _ MapID : 32 / little , _ EntryID : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 92 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
? ASSERT_EQ ( VarJ , 0 ) ,
? ASSERT_EQ ( VarK , 0 ) ,
? ASSERT_EQ ( VarL , 0 ) ,
? ASSERT_EQ ( HeaderGID , BodyGID ) ,
ignore ; %% @todo player_enter_area
2010-08-21 06:23:17 +08:00
parse ( Size , 16#0110 , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , HeaderGID : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little ,
VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , BodyGID : 32 / little , _ PartyPosOrLID : 32 / little , EventID : 32 / little , Param : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 60 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( HeaderGID , BodyGID ) ,
case EventID of
1 - > ? ASSERT_EQ ( Param , 0 ) , ? ASSERT ( ) , ignore ;
2 - > ? ASSERT_EQ ( Param , 0 ) , player_type_capabilities_request ;
3 - > ignore ; %% @todo {player_type_change, Param};
4 - > ? ASSERT_EQ ( Param , 0 ) , ignore ; %% @todo (related to npc death)
6 - > ? ASSERT_EQ ( Param , 0 ) , ignore ; %% @todo
7 - > ? ASSERT_EQ ( Param , 0 ) , player_death ;
8 - > ? ASSERT_EQ ( Param , 0 ) , player_death_return_to_lobby ;
9 - > ? ASSERT_EQ ( Param , 10 ) , ignore ; %% @todo
10 - > ignore ; %% @todo {player_online_status_change, Param};
_ - > log ( " unknown 0110 EventID ~p " , [ EventID ] )
end ;
2010-09-05 01:59:06 +08:00
parse ( Size , 16#020b , Channel , Data ) - >
< < LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little ,
VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , Slot : 32 / little , VarJ : 8 , BackToPreviousField : 8 , VarK : 16 / little > > = Data ,
? ASSERT_EQ ( Size , 52 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( LID , 16#ffff ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
? ASSERT_EQ ( VarJ , 0 ) ,
? ASSERT_EQ ( VarK , 0 ) ,
AtomBackToPreviousField = if BackToPreviousField =:= 0 - > false ; true - > true end ,
{ char_select_enter , Slot , AtomBackToPreviousField } ;
2010-09-04 04:14:50 +08:00
parse ( Size , 16#020d , Channel , Data ) - >
< < LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little ,
VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , AuthGID : 32 / little , AuthKey : 32 / bits , VarJ : 32 / little , VarK : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 60 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( LID , 16#ffff ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
? ASSERT_EQ ( VarJ , 0 ) ,
? ASSERT_EQ ( VarK , 0 ) ,
{ system_key_auth_request , AuthGID , AuthKey } ;
2010-09-19 23:31:46 +08:00
parse ( Size , 16#0217 , Channel , Data ) - >
< < LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 44 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( LID , 16#ffff ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
system_game_server_request ;
2010-09-19 23:51:42 +08:00
parse ( Size , 16#0219 , Channel , Data ) - >
< < LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little ,
VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , UsernameBlob : 192 / bits , PasswordBlob : 192 / bits , _ VarJ : 32 / little , _ VarK : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 100 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( LID , 16#ffff ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
[ Username | _ ] = re : split ( UsernameBlob , " \\ 0 " ) ,
[ Password | _ ] = re : split ( PasswordBlob , " \\ 0 " ) ,
{ system_login_auth_request , Username , Password } ;
2010-08-27 00:04:56 +08:00
parse ( Size , 16#021c , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 44 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
2010-09-19 01:57:55 +08:00
char_load_complete ;
2010-08-27 00:04:56 +08:00
2010-08-21 05:22:24 +08:00
parse ( Size , 16#021d , Channel , Data ) - >
2010-08-21 21:40:17 +08:00
< < _ LID : 16 / little , VarB : 16 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little ,
2010-08-21 05:22:24 +08:00
VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , VarJ : 32 / little , _ EntryID : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 48 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
? ASSERT_EQ ( VarJ , 0 ) ,
unicube_request ;
parse ( Size , 16#021f , Channel , Data ) - >
2010-08-21 21:40:17 +08:00
< < _ LID : 16 / little , VarB : 16 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little ,
2010-08-21 05:22:24 +08:00
VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , VarJ : 32 / little , UniID : 32 / little , EntryID : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 52 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
? ASSERT_EQ ( VarJ , 0 ) ,
Selection = case UniID of
0 - > cancel ;
_ - > UniID
end ,
{ unicube_select , Selection , EntryID } ;
2010-09-20 00:41:06 +08:00
%% @doc Seems to be exactly the same as 023f, except Channel, and that it's used for JP clients.
parse ( Size , 16#0226 , Channel , Data ) - >
< < LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little ,
VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , Page : 8 , Language : 8 , VarJ : 16 / little > > = Data ,
? ASSERT_EQ ( Size , 48 ) ,
? ASSERT_EQ ( Channel , 3 ) ,
? ASSERT_EQ ( LID , 16#ffff ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
? ASSERT_EQ ( VarJ , 0 ) ,
{ system_motd_request , Page , language_integer_to_atom ( Language ) } ;
2010-09-20 01:27:52 +08:00
%% @doc Whether the MOTD was accepted. Safely ignored.
parse ( Size , 16#0227 , Channel , Data ) - >
< < LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little ,
VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , _ AcceptMOTD : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 48 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( LID , 16#ffff ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
ignore ; %% {system_motd_accept, true|false (AcceptMOTD)};
2010-09-20 00:41:06 +08:00
%% @doc Seems to be exactly the same as 0226, except Channel, and that it's used for US clients.
parse ( Size , 16#023f , Channel , Data ) - >
< < LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little ,
VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , Page : 8 , Language : 8 , VarJ : 16 / little > > = Data ,
? ASSERT_EQ ( Size , 48 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( LID , 16#ffff ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
? ASSERT_EQ ( VarJ , 0 ) ,
{ system_motd_request , Page , language_integer_to_atom ( Language ) } ;
2010-08-23 20:45:47 +08:00
parse ( _ Size , 16#0304 , Channel , Data ) - >
< < _ LID : 16 / little , VarB : 16 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little ,
VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , VarJ : 32 / little , FromTypeID : 32 , FromGID : 32 / little ,
VarK : 32 / little , VarL : 32 / little , ChatType : 8 , ChatCutIn : 8 , ChatCutInAngle : 8 , ChatMsgLength : 8 ,
2010-09-02 05:53:26 +08:00
ChatChannel : 8 , ChatCharacterType : 8 , VarN : 8 , _ VarO : 8 , FromName : 512 / bits , ChatMsg / bits > > = Data ,
2010-08-23 20:45:47 +08:00
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
? ASSERT_EQ ( VarJ , 0 ) ,
? ASSERT_EQ ( VarK , 0 ) ,
? ASSERT_EQ ( VarL , 0 ) ,
? ASSERT_EQ ( VarN , 0 ) ,
Modifiers = { chat_modifiers , ChatType , ChatCutIn , ChatCutInAngle , ChatMsgLength , ChatChannel , ChatCharacterType } ,
{ chat , FromTypeID , FromGID , FromName , Modifiers , ChatMsg } ;
2010-09-26 00:52:33 +08:00
%% @doc Probably safely ignored. _AreaNb is apparently replied with the same value sent by 0205, the one after EntryID.
2010-08-27 00:40:38 +08:00
parse ( Size , 16#0806 , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little ,
2010-09-26 00:52:33 +08:00
VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , _ AreaNb : 32 / little > > = Data ,
2010-08-27 00:40:38 +08:00
? ASSERT_EQ ( Size , 48 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
ignore ;
2010-08-21 21:40:17 +08:00
parse ( Size , 16#0807 , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little ,
2010-09-13 05:42:36 +08:00
QuestID : 32 / little , ZoneID : 16 / little , MapID : 16 / little , EntryID : 16 / little , _ AreaChangeNb : 16 / little , PartyPos : 32 / little > > = Data ,
2010-08-21 21:40:17 +08:00
? ASSERT_EQ ( Size , 60 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
2010-09-13 05:42:36 +08:00
{ area_change , QuestID , ZoneID , MapID , EntryID , PartyPos } ;
2010-08-21 21:40:17 +08:00
2010-09-26 00:52:33 +08:00
%% @doc Probably safely ignored. _AreaNb is apparently replied with the same value sent by 0208.
2010-08-27 00:23:37 +08:00
parse ( Size , 16#0808 , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little ,
2010-09-26 00:52:33 +08:00
VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , _ AreaNb : 32 / little > > = Data ,
2010-08-27 00:23:37 +08:00
? ASSERT_EQ ( Size , 48 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
ignore ;
2010-08-29 19:41:53 +08:00
%% @todo Check that _Rest is full of 0s.
parse ( Size , 16#080c , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little ,
VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , VarJ : 32 / little , NPCid : 16 / little ,
_ VarK : 16 / little , VarL : 32 / little , VarM : 32 / little , VarN : 16 / little , _ Var0 : 16 / little , _ Rest / bits > > = Data ,
? ASSERT_EQ ( Size , 648 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
? ASSERT_EQ ( VarJ , 16#ffffffff ) ,
? ASSERT_EQ ( VarL , 16#ffffffff ) ,
? ASSERT_EQ ( VarM , 16#ffffffff ) ,
? ASSERT_EQ ( VarN , 0 ) ,
{ npc_force_invite , NPCid } ;
2010-08-27 00:16:15 +08:00
%% @doc This command should be safely ignored. Probably indicates that a non-mission area change was successful.
parse ( Size , 16#080d , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 44 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
ignore ;
2010-09-04 07:50:12 +08:00
%% @todo Make sure the Language field is the right one.
parse ( Size , 16#080e , Channel , Data ) - >
< < LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little ,
2010-09-20 01:19:09 +08:00
VarJ : 8 , Language : 8 , VarK : 8 , Entrance : 8 , Platform : 8 , VarM : 24 / little , Revision : 8 , Minor : 4 , _ VarN : 12 , Major : 4 , _ VarO : 4 , VarP : 32 / little , VarQ : 32 / little , VarR : 32 / little > > = Data ,
2010-09-04 07:50:12 +08:00
? ASSERT_EQ ( Size , 68 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( LID , 16#ffff ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
? ASSERT_EQ ( VarJ , 0 ) ,
? ASSERT_EQ ( VarK , 1 ) ,
? ASSERT_EQ ( VarM , 0 ) ,
? ASSERT_EQ ( VarP , 0 ) ,
? ASSERT_EQ ( VarQ , 0 ) ,
? ASSERT_EQ ( VarR , 0 ) ,
AtomPlatform = case Platform of
0 - > ps2 ;
1 - > pc ;
_ - > log ( " unknown 080e Platform ~p " , [ Platform ] ) , unknown
end ,
Version = Major * 1000000 + Minor * 1000 + Revision ,
2010-09-20 01:19:09 +08:00
{ system_client_version_info , Entrance , language_integer_to_atom ( Language ) , AtomPlatform , Version } ;
2010-09-04 07:50:12 +08:00
2010-09-03 05:48:27 +08:00
%% @todo Find out what it's really doing!
parse ( Size , 16#080f , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little ,
VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , _ PartyPos : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 48 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
ignore ;
2010-08-21 22:52:09 +08:00
parse ( Size , 16#0811 , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little ,
_ CounterType : 8 , VarJ : 8 , FromZoneID : 16 / little , FromMapID : 16 / little , FromEntryID : 16 / little , CounterID : 32 / little , VarK : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 60 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
? ASSERT_EQ ( VarJ , 41 ) ,
? ASSERT_EQ ( VarK , 16#ffffffff ) ,
{ counter_enter , CounterID , FromZoneID , FromMapID , FromEntryID } ;
2010-08-21 22:59:01 +08:00
parse ( Size , 16#0812 , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 44 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
counter_leave ;
2010-08-21 23:08:21 +08:00
parse ( Size , 16#0813 , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little ,
VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , VarJ : 32 / little , NPCid : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 52 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
? ASSERT_EQ ( VarJ , 16#ffffffff ) ,
{ npc_invite , NPCid } ;
2010-08-27 00:09:53 +08:00
%% @doc This command should be safely ignored. Probably indicates that a mission area change was successful.
parse ( Size , 16#0814 , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 44 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
ignore ;
2010-08-27 00:08:07 +08:00
%% @doc This command should be safely ignored. Probably indicates that a non-mission area change was successful.
parse ( Size , 16#0815 , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 44 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
ignore ;
2010-09-05 03:00:55 +08:00
parse ( Size , 16#0818 , Channel , Data ) - >
< < LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little ,
VarH : 32 / little , VarI : 32 / little , BinGPU : 512 / bits , BinCPU : 384 / bits , _ VarJ : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 160 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( LID , 16#ffff ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
[ _ StringCPU | _ ] = re : split ( BinCPU , " \\ 0 " , [ { return , binary } ] ) ,
[ _ StringGPU | _ ] = re : split ( BinGPU , " \\ 0 " , [ { return , binary } ] ) ,
ignore ; %% @todo {system_client_hardware_info, StringGPU, StringCPU}; worth logging?
2010-08-23 06:10:12 +08:00
parse ( Size , 16#0a10 , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little ,
VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , ItemID : 32 > > = Data ,
? ASSERT_EQ ( Size , 48 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
{ item_description_request , ItemID } ;
2010-08-20 05:43:19 +08:00
parse ( Size , 16#0b05 , _ Channel , _ Data ) - >
? ASSERT_EQ ( Size , 8 ) ,
ignore ;
2010-08-21 23:24:38 +08:00
parse ( Size , 16#0c01 , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little ,
VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , QuestID : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 48 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
{ mission_start , QuestID } ;
2010-08-21 23:42:31 +08:00
parse ( Size , 16#0c05 , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little ,
VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , CounterID : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 48 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
{ counter_quest_files_request , CounterID } ;
2010-08-21 23:59:59 +08:00
%% @doc On official, Price = Rate x 200.
parse ( Size , 16#0c07 , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little ,
VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , _ QuestID : 32 / little , _ Rate : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 52 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
lobby_transport_request ;
2010-08-27 00:40:59 +08:00
%% @doc This command should be safely ignored. Probably indicates that a mission area change was successful.
parse ( Size , 16#0c0d , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 44 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
ignore ;
2010-08-22 00:08:32 +08:00
parse ( Size , 16#0c0e , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 44 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
mission_abort ;
2010-08-22 00:16:21 +08:00
parse ( Size , 16#0c0f , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little ,
VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , CounterID : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 48 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
{ counter_quest_options_request , CounterID } ;
2010-09-05 01:59:06 +08:00
%% @todo Return a tuple rather than a binary!
%% @todo Parse and validate the data here rather than in psu_game.
parse ( Size , 16#0d02 , Channel , Data ) - >
< < VarA : 32 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little ,
VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , Slot : 32 / little , CharBin / bits > > = Data ,
? ASSERT_EQ ( Size , 324 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
{ char_select_create , Slot , CharBin } ;
parse ( Size , 16#0d06 , Channel , Data ) - >
< < VarA : 32 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 44 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
char_select_request ;
2010-08-27 03:27:49 +08:00
%% @todo Return a tuple rather than a binary!
parse ( Size , 16#0d07 , Channel , Data ) - >
< < VarA : 32 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little ,
TextDisplaySpeed : 8 , Sound : 8 , MusicVolume : 8 , SoundEffectVolume : 8 , Vibration : 8 , RadarMapDisplay : 8 ,
CutInDisplay : 8 , MainMenuCursorPosition : 8 , VarJ : 8 , Camera3rdY : 8 , Camera3rdX : 8 , Camera1stY : 8 , Camera1stX : 8 ,
Controller : 8 , WeaponSwap : 8 , LockOn : 8 , Brightness : 8 , FunctionKeySetting : 8 , _ VarK : 8 , ButtonDetailDisplay : 8 , VarL : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 68 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
? ASSERT_EQ ( VarJ , 0 ) ,
? ASSERT_EQ ( VarL , 0 ) ,
%% Make sure the options are valid.
true = TextDisplaySpeed =< 1 ,
true = Sound =< 1 ,
true = MusicVolume =< 9 ,
true = SoundEffectVolume =< 9 ,
true = Vibration =< 1 ,
true = RadarMapDisplay =< 1 ,
true = CutInDisplay =< 1 ,
true = MainMenuCursorPosition =< 1 ,
true = Camera3rdY =< 1 ,
true = Camera3rdX =< 1 ,
true = Camera1stY =< 1 ,
true = Camera1stX =< 1 ,
true = Controller =< 1 ,
true = WeaponSwap =< 1 ,
true = LockOn =< 1 ,
true = Brightness =< 4 ,
true = FunctionKeySetting =< 1 ,
true = ButtonDetailDisplay =< 2 ,
%% Options are considered safe past this point.
Options = { options , TextDisplaySpeed , Sound , MusicVolume , SoundEffectVolume , Vibration , RadarMapDisplay ,
CutInDisplay , MainMenuCursorPosition , Camera3rdY , Camera3rdX , Camera1stY , Camera1stX ,
Controller , WeaponSwap , LockOn , Brightness , FunctionKeySetting , ButtonDetailDisplay } ,
{ player_options_change , psu_characters : options_tuple_to_binary ( Options ) } ; %% @todo {player_options_change, Options};
2010-08-26 20:41:35 +08:00
%% @todo Many unknown vars in the command header.
parse ( Size , 16#0e00 , Channel , Data ) - >
< < _ UnknownVars : 288 / bits , NbHits : 32 / little , _ PartyPosOrLID : 32 / little , _ HitCommandNb : 32 / little , Hits / bits > > = Data ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( Size , 56 + NbHits * 80 ) ,
{ hits , parse_hits ( Hits , [ ] ) } ;
2010-08-25 07:42:37 +08:00
parse ( Size , 16#0f0a , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little ,
VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , BlockID : 16 / little , ListNb : 16 / little ,
ObjectNb : 16 / little , _ MapID : 16 / little , ObjectID : 16 / little , VarJ : 16 / little , ObjectTargetID : 32 / little ,
2010-09-24 08:33:22 +08:00
ObjectType : 16 / little , VarK : 16 / little , ObjectBaseTargetID : 16 / little , VarL : 16 / little , PartyPosOrLID : 32 / little ,
2010-08-25 07:42:37 +08:00
VarN : 32 / little , VarO : 32 / little , VarP : 32 / little , VarQ : 32 / little , VarR : 32 / little , VarS : 32 / little ,
VarT : 32 / little , VarU : 32 / little , ObjectType2 : 16 / little , EventID : 8 , VarV : 8 , VarW : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 112 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
? ASSERT_EQ ( VarK , 0 ) ,
? ASSERT_EQ ( VarP , 16#ffffffff ) ,
? ASSERT_EQ ( VarQ , 16#ffffffff ) ,
? ASSERT_EQ ( VarR , 16#ffffffff ) ,
? ASSERT_EQ ( VarS , 0 ) ,
? ASSERT_EQ ( VarT , 0 ) ,
? ASSERT_EQ ( VarU , 0 ) ,
? ASSERT_EQ ( ObjectType , ObjectType2 ) ,
case [ ObjectType , EventID ] of
[ 5 , 13 ] - >
? ASSERT_EQ ( VarN , 16#ffffffff ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
? ASSERT_EQ ( VarW , 0 ) ,
{ object_switch_on , ObjectID } ;
[ 5 , 14 ] - >
? ASSERT_EQ ( VarN , 16#ffffffff ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
? ASSERT_EQ ( VarW , 0 ) ,
{ object_switch_off , ObjectID } ;
[ 9 , 20 ] - >
%% @todo We probably need to handle it for Airboard Rally.
ignore ; %% object_sensor_trigger
[ 14 , 0 ] - >
? ASSERT_EQ ( ObjectID , 16#ffff ) ,
? ASSERT_EQ ( ObjectTargetID , 16#ffffffff ) ,
? ASSERT_EQ ( ObjectBaseTargetID , 16#ffff ) ,
? ASSERT_EQ ( VarN , 16#ffffffff ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
{ object_warp_take , BlockID , ListNb , ObjectNb } ;
[ 22 , 12 ] - >
? ASSERT_EQ ( VarJ , 134 ) ,
? ASSERT_EQ ( ObjectTargetID , 16#ffffffff ) ,
? ASSERT_EQ ( ObjectBaseTargetID , 16#ffff ) ,
? ASSERT_EQ ( VarL , 116 ) ,
? ASSERT_EQ ( VarN , 16#ffffffff ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
? ASSERT_EQ ( VarW , 0 ) ,
{ object_key_console_enable , ObjectID } ;
[ 22 , 23 ] - >
? ASSERT_EQ ( VarJ , 134 ) ,
? ASSERT_EQ ( ObjectTargetID , 16#ffffffff ) ,
? ASSERT_EQ ( ObjectBaseTargetID , 16#ffff ) ,
? ASSERT_EQ ( VarN , 16#ffffffff ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
? ASSERT_EQ ( VarW , 0 ) ,
{ object_key_console_init , ObjectID } ;
[ 22 , 24 ] - >
? ASSERT_EQ ( VarJ , 134 ) ,
? ASSERT_EQ ( ObjectTargetID , 16#ffffffff ) ,
? ASSERT_EQ ( ObjectBaseTargetID , 16#ffff ) ,
? ASSERT_EQ ( VarL , 116 ) ,
? ASSERT_EQ ( VarN , 16#ffffffff ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
? ASSERT_EQ ( VarW , 0 ) ,
{ object_key_console_open_gate , ObjectID } ;
[ 31 , 12 ] - >
? ASSERT_EQ ( VarJ , 134 ) ,
? ASSERT_EQ ( ObjectTargetID , 16#ffffffff ) ,
? ASSERT_EQ ( ObjectBaseTargetID , 16#ffff ) ,
? ASSERT_EQ ( VarN , 16#ffffffff ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
? ASSERT_EQ ( VarW , 0 ) ,
{ object_key_enable , ObjectID } ;
[ 48 , 4 ] - >
? ASSERT_EQ ( VarJ , 134 ) ,
? ASSERT_EQ ( VarL , 116 ) ,
? ASSERT_EQ ( VarN , 16#ffffffff ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
? ASSERT_EQ ( VarW , 0 ) ,
{ object_boss_gate_enter , ObjectID } ;
[ 48 , 5 ] - >
? ASSERT_EQ ( VarJ , 134 ) ,
? ASSERT_EQ ( VarL , 116 ) ,
? ASSERT_EQ ( VarN , 16#ffffffff ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
? ASSERT_EQ ( VarW , 0 ) ,
{ object_boss_gate_leave , ObjectID } ;
[ 48 , 6 ] - >
? ASSERT_EQ ( VarJ , 134 ) ,
? ASSERT_EQ ( VarL , 116 ) ,
? ASSERT_EQ ( VarN , 16#ffffffff ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
? ASSERT_EQ ( VarW , 0 ) ,
{ object_boss_gate_activate , ObjectID } ;
[ 48 , 7 ] - >
? ASSERT_EQ ( VarJ , 134 ) ,
? ASSERT_EQ ( VarL , 116 ) ,
? ASSERT_EQ ( VarN , 16#ffffffff ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
? ASSERT_EQ ( VarW , 0 ) ,
? ASSERT ( ) ,
ignore ; %% @todo object_boss_gate_???
[ 49 , 3 ] - >
? ASSERT_EQ ( VarJ , 134 ) ,
? ASSERT_EQ ( ObjectTargetID , 16#ffffffff ) ,
? ASSERT_EQ ( ObjectBaseTargetID , 16#ffff ) ,
? ASSERT_EQ ( VarL , 116 ) ,
? ASSERT_EQ ( VarN , 16#ffffffff ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
? ASSERT_EQ ( VarW , 0 ) ,
{ object_crystal_activate , ObjectID } ;
2010-09-24 08:33:22 +08:00
[ 50 , 9 ] - >
%% @todo Make NPC characters be healed too. This would use VarN and VarO as PartyPosOrLID, and VarV would be > 1.
? ASSERT_EQ ( VarJ , 134 ) ,
? ASSERT_EQ ( ObjectTargetID , 16#ffffffff ) ,
? ASSERT_EQ ( ObjectBaseTargetID , 16#ffff ) ,
? ASSERT_EQ ( VarL , 116 ) ,
? ASSERT_EQ ( VarN , 16#ffffffff ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
? ASSERT_EQ ( VarW , 0 ) ,
{ object_healing_pad_tick , [ PartyPosOrLID ] } ;
2010-08-25 07:42:37 +08:00
[ 51 , 1 ] - >
? ASSERT_EQ ( VarL , 116 ) ,
? ASSERT_EQ ( ObjectTargetID , VarN ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
? ASSERT_EQ ( VarW , 0 ) ,
2010-09-14 01:36:08 +08:00
{ object_goggle_target_activate , ObjectID } ;
2010-08-25 07:42:37 +08:00
[ 56 , 25 ] - >
? ASSERT_EQ ( VarN , 16#ffffffff ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
? ASSERT_EQ ( VarW , 0 ) ,
{ object_chair_sit , ObjectTargetID } ;
[ 56 , 26 ] - >
? ASSERT_EQ ( VarN , 16#ffffffff ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
? ASSERT_EQ ( VarW , 0 ) ,
{ object_chair_stand , ObjectTargetID } ;
[ 57 , 12 ] - >
? ASSERT_EQ ( VarJ , 134 ) ,
? ASSERT_EQ ( ObjectTargetID , 16#ffffffff ) ,
? ASSERT_EQ ( ObjectBaseTargetID , 16#ffff ) ,
? ASSERT_EQ ( VarL , 116 ) ,
? ASSERT_EQ ( VarN , 16#ffffffff ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
{ object_vehicle_boost_enable , ObjectID } ;
[ 57 , 28 ] - >
? ASSERT_EQ ( VarJ , 134 ) ,
? ASSERT_EQ ( ObjectTargetID , 16#ffffffff ) ,
? ASSERT_EQ ( ObjectBaseTargetID , 16#ffff ) ,
? ASSERT_EQ ( VarL , 116 ) ,
? ASSERT_EQ ( VarN , 16#ffffffff ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
? ASSERT_EQ ( VarW , 0 ) ,
{ object_vehicle_boost_respawn , ObjectID } ;
[ 71 , 27 ] - >
? ASSERT_EQ ( VarJ , 134 ) ,
? ASSERT_EQ ( ObjectTargetID , VarN ) ,
? ASSERT_EQ ( VarO , 16#ffffffff ) ,
? ASSERT_EQ ( VarV , 1 ) ,
? ASSERT_EQ ( VarW , 0 ) ,
? ASSERT ( ) ,
ignore ; %% @todo object_trap(3rd)_???
_ - > %% Unhandled actions.
log ( " unknown 0f0a ObjectType ~p EventID ~p " , [ ObjectType , EventID ] ) ,
ignore
end ;
2010-09-03 05:48:27 +08:00
parse ( Size , 16#1007 , Channel , Data ) - >
< < VarA : 32 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little ,
VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , PartyPos : 32 / little , _ Name : 512 / bits > > = Data ,
? ASSERT_EQ ( Size , 112 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
{ party_remove_member , PartyPos } ;
2010-10-22 05:55:01 +08:00
parse ( Size , 16#1701 , Channel , Data ) - >
< < VarA : 32 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little ,
VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , VarJ : 32 / little , VarK : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 52 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
? ASSERT_EQ ( VarJ , 0 ) ,
? ASSERT_EQ ( VarK , 16#ffffffff ) ,
counter_join_party_request ;
2010-08-23 05:00:43 +08:00
parse ( Size , 16#1705 , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 44 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
counter_party_info_request ;
2010-08-27 01:31:45 +08:00
%% @todo Currently selected quest. Probably need to broadcast it to other players in the party.
parse ( Size , 16#1707 , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little ,
VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , _ QuestID : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 48 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
ignore ; %% @todo {counter_quest_selection, QuestID}
2010-08-23 05:08:01 +08:00
parse ( Size , 16#1709 , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 44 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
counter_party_options_request ;
2010-08-27 02:37:53 +08:00
parse ( Size , 16#170b , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 44 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
counter_background_locations_request ;
2010-08-22 00:39:15 +08:00
parse ( Size , 16#1710 , Channel , Data ) - >
< < _ LID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little ,
VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little , CounterID : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 48 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
{ counter_options_request , CounterID } ;
2010-08-23 01:15:08 +08:00
parse ( Size , 16#1a01 , Channel , Data ) - >
< < HeaderLID : 16 / little , VarA : 16 / little , VarB : 32 / little , VarC : 32 / little , VarD : 32 / little , VarE : 32 / little , VarF : 32 / little , VarG : 32 / little , VarH : 32 / little , VarI : 32 / little ,
BodyLID : 32 / little , ShopID : 32 / little , EventID : 32 / little , VarJ : 32 / little , VarK : 32 / little > > = Data ,
? ASSERT_EQ ( Size , 64 ) ,
? ASSERT_EQ ( Channel , 2 ) ,
? ASSERT_EQ ( VarA , 0 ) ,
? ASSERT_EQ ( VarB , 0 ) ,
? ASSERT_EQ ( VarC , 0 ) ,
? ASSERT_EQ ( VarD , 0 ) ,
? ASSERT_EQ ( VarE , 0 ) ,
? ASSERT_EQ ( VarF , 0 ) ,
? ASSERT_EQ ( VarG , 0 ) ,
? ASSERT_EQ ( VarH , 0 ) ,
? ASSERT_EQ ( VarI , 0 ) ,
? ASSERT_EQ ( HeaderLID , BodyLID ) ,
case EventID of
0 - > ? ASSERT_EQ ( VarJ , 0 ) , { npc_shop_request , ShopID } ;
2 - >
? ASSERT_EQ ( ShopID , 0 ) ,
? ASSERT_EQ ( VarJ , 0 ) ,
lumilass_options_request ;
3 - >
? ASSERT_EQ ( ShopID , 0 ) ,
? ASSERT_EQ ( VarJ , 0 ) ,
ppcube_request ;
4 - > ? ASSERT_EQ ( ShopID , 0 ) , ignore ; %% @todo ppcube_recharge_all
5 - > ? ASSERT_EQ ( ShopID , 0 ) , ignore ; %% @todo ppcube_recharge_one
6 - >
? ASSERT_EQ ( ShopID , 0 ) ,
? ASSERT_EQ ( VarJ , 0 ) ,
2010-09-04 22:06:46 +08:00
? ASSERT ( ) , ignore ; %% @todo put_on_outfit
2010-08-23 01:15:08 +08:00
7 - >
? ASSERT_EQ ( ShopID , 0 ) ,
2010-09-04 22:06:46 +08:00
? ASSERT ( ) , ignore ; %% @todo remove_outfit
2010-08-23 01:15:08 +08:00
9 - >
? ASSERT_EQ ( ShopID , 0 ) ,
? ASSERT_EQ ( VarJ , 0 ) ,
? ASSERT_EQ ( VarK , 0 ) ,
player_type_availability_request ;
_ - > log ( " unknown 1a01 EventID ~p " , [ EventID ] )
end ;
2010-09-19 01:57:55 +08:00
%% @doc Unknown command,
parse ( _ Size , Command , Channel , _ Data ) - >
{ command , Command , Channel } .
2010-08-20 05:43:19 +08:00
2010-08-26 20:41:35 +08:00
%% @todo Many unknown vars in the hit values.
parse_hits ( < < > > , Acc ) - >
lists : reverse ( Acc ) ;
parse_hits ( Hits , Acc ) - >
< < A : 224 / bits , B : 128 / bits , _ C : 128 / bits , _ D : 160 / bits , Rest / bits > > = Hits ,
< < _ PosX1 : 32 / little - float , _ PosY1 : 32 / little - float , _ PosZ1 : 32 / little - float , FromTargetID : 32 / little , ToTargetID : 32 / little , _ AEnd1 : 32 , _ AEnd2 : 32 > > = A ,
%~ << Stuff2:32, PosX2:32/little-float, PosY2:32/little-float, PosZ2:32/little-float >> = B, %% player
%~ << Stuff3:32, PosX3:32/little-float, PosY3:32/little-float, PosZ3:32/little-float >> = C, %% target
%~ << D1:32, D2:32, D3:32, D4:32, D5:32 >> = D,
parse_hits ( Rest , [ { hit , FromTargetID , ToTargetID , A , B } | Acc ] ) .
2010-08-20 05:43:19 +08:00
2010-09-03 01:02:39 +08:00
%% @doc Send character appearance and other information.
%% @todo Probably don't pattern match the data like this...
2011-02-27 00:00:41 +08:00
send_010d ( CharUser , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2011-02-13 07:35:05 +08:00
CharGID = CharUser #users.gid ,
2011-02-13 00:53:58 +08:00
CharLID = CharUser #users.lid ,
2010-09-03 01:02:39 +08:00
< < _ : 640 , CharBin / bits > > = psu_characters : character_user_to_binary ( CharUser ) ,
2011-02-20 02:45:50 +08:00
packet_send ( Socket , < < 16#010d0300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little ,
2010-10-06 23:54:25 +08:00
0 : 64 , 1 : 32 / little , 0 : 32 , 16#00000300 : 32 , 16#ffff0000 : 32 , 0 : 32 , CharGID : 32 / little ,
0 : 192 , CharGID : 32 / little , CharLID : 32 / little , 16#ffffffff : 32 , CharBin / binary > > ) .
2010-09-03 01:02:39 +08:00
2010-09-24 03:23:59 +08:00
%% @doc Trigger a character-related event.
2011-02-27 00:00:41 +08:00
send_0111 ( CharUser , EventID , Client ) - >
send_0111 ( CharUser , EventID , 0 , Client ) .
send_0111 ( #users { gid = CharGID , lid = CharLID } , EventID , Param , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2010-09-24 03:23:59 +08:00
packet_send ( Socket , < < 16#01110300 : 32 , DestLID : 16 / little , 0 : 48 , CharGID : 32 / little , 0 : 64 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 ,
CharGID : 32 / little , CharLID : 32 / little , EventID : 32 / little , Param : 32 / little > > ) .
2010-09-23 22:09:13 +08:00
%% @doc Update the character level, blastbar, luck and money information.
2011-02-27 00:00:41 +08:00
send_0115 ( CharUser , Client ) - >
send_0115 ( CharUser , 16#ffffffff , Client ) .
send_0115 ( #users { gid = CharGID , lid = CharLID , character = Character } , EnemyTargetID , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2010-09-23 22:09:13 +08:00
packet_send ( Socket , < < 16#01150300 : 32 , DestLID : 16 / little , 0 : 48 , CharGID : 32 / little , 0 : 64 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 ,
CharGID : 32 / little , CharLID : 32 / little , EnemyTargetID : 32 / little , ( build_char_level ( Character ) ) / binary > > ) .
%% @todo Handle class levels.
build_char_level ( #characters { type = Type , mainlevel = #level { number = Level , exp = EXP } , blastbar = BlastBar , luck = Luck , money = Money , playtime = PlayTime } ) - >
ClassesBin = case Type of
npc - >
< < 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 ,
16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 ,
16#4e4f4630 : 32 , 16#08000000 : 32 , 0 : 32 , 0 : 32 , 16#4e454e44 : 32 > > ;
_ - >
< < 0 : 160 ,
16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 ,
16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 , 16#01000000 : 32 > >
end ,
< < Level : 32 / little , BlastBar : 16 / little , Luck : 8 , 0 : 40 , EXP : 32 / little , 0 : 32 , Money : 32 / little , PlayTime : 32 / little , ClassesBin / binary > > .
2010-09-24 00:30:50 +08:00
%% @doc Revive player with optional SEs.
%% @todo SEs.
2011-02-27 00:00:41 +08:00
send_0117 ( #users { gid = CharGID , lid = CharLID , character = #characters { currenthp = HP } } , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2010-09-24 00:30:50 +08:00
SE = < < 0 : 64 > > ,
packet_send ( Socket , < < 16#01170300 : 32 , DestLID : 16 / little , 0 : 48 , CharGID : 32 / little , 0 : 64 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 ,
CharGID : 32 / little , CharLID : 32 / little , SE / binary , HP : 32 / little , 0 : 32 > > ) .
2010-09-26 01:22:13 +08:00
%% @doc Send the zone initialization command.
%% @todo Handle NbPlayers properly. There's more than 1 player!
2011-02-27 00:00:41 +08:00
send_0200 ( ZoneID , ZoneType , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2010-09-26 01:22:13 +08:00
Var = case ZoneType of
mission - > < < 16#06000500 : 32 , 16#01000000 : 32 , 0 : 64 , 16#00040000 : 32 , 16#00010000 : 32 , 16#00140000 : 32 > > ;
myroom - > < < 16#06000000 : 32 , 16#02000000 : 32 , 0 : 64 , 16#40000000 : 32 , 16#00010000 : 32 , 16#00010000 : 32 > > ;
_ - > < < 16#00040000 : 32 , 0 : 160 , 16#00140000 : 32 > >
end ,
2011-02-20 02:45:50 +08:00
packet_send ( Socket , < < 16#02000300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 ,
DestLID : 16 / little , ZoneID : 16 / little , 1 : 32 / little , 16#ffffffff : 32 , Var / binary , 16#ffffffff : 32 , 16#ffffffff : 32 > > ) .
2010-09-26 01:22:13 +08:00
2010-09-02 23:43:25 +08:00
%% @doc Send character location, appearance and other information.
2011-02-27 00:00:41 +08:00
send_0201 ( CharUser , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2011-02-13 00:53:58 +08:00
[ CharTypeID , GameVersion ] = case ( CharUser #users.character ) #characters.type of
2010-09-02 23:43:25 +08:00
npc - > [ 16#00001d00 , 255 ] ;
_ - > [ 16#00001200 , 0 ]
end ,
2011-02-13 07:35:05 +08:00
CharGID = CharUser #users.gid ,
2010-09-02 23:43:25 +08:00
CharBin = psu_characters : character_user_to_binary ( CharUser ) ,
IsGM = 0 ,
OnlineStatus = 0 ,
2011-02-20 02:45:50 +08:00
packet_send ( Socket , < < 16#02010300 : 32 , DestLID : 16 / little , 0 : 16 , CharTypeID : 32 , CharGID : 32 / little ,
2010-10-06 23:54:25 +08:00
0 : 64 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , CharBin / binary , IsGM : 8 , 0 : 8 , OnlineStatus : 8 , GameVersion : 8 , 0 : 608 > > ) .
2010-09-02 23:43:25 +08:00
2010-09-04 03:36:38 +08:00
%% @doc Hello command. Sent when a client connects to the game or login server.
%% @todo Can contain an error message if 0:1024 is setup similar to this: 0:32, 3:32/little, 0:48, Len:16/little, Error/binary, 0:Padding.
2011-02-27 00:00:41 +08:00
send_0202 ( #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2010-09-24 01:05:42 +08:00
packet_send ( Socket , < < 16#020203bf : 32 , DestLID : 16 / little , 0 : 272 , DestGID : 32 / little , 0 : 1024 > > ) .
2010-09-04 03:36:38 +08:00
2010-10-22 10:04:21 +08:00
%% @doc Spawn a player with the given GID and LID.
2011-02-27 00:00:41 +08:00
send_0203 ( #users { gid = CharGID , lid = CharLID } , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2011-02-20 02:45:50 +08:00
packet_send ( Socket , < < 16#02030300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 ,
2010-10-22 10:04:21 +08:00
DestGID : 32 / little , 0 : 64 , CharGID : 32 / little , CharLID : 32 / little > > ) .
2010-10-22 04:31:06 +08:00
%% @doc Unspawn the given character.
%% @todo The last 4 bytes are probably the number of players remaining in the zone.
2011-02-27 00:00:41 +08:00
send_0204 ( User , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2011-02-13 00:53:58 +08:00
CharTypeID = case ( User #users.character ) #characters.type of
2010-10-22 04:31:06 +08:00
npc - > 16#00001d00 ;
_ - > 16#00001200
end ,
2011-02-13 07:35:05 +08:00
#users { gid = CharGID , lid = CharLID } = User ,
2011-02-20 02:45:50 +08:00
packet_send ( Socket , < < 16#02040300 : 32 , DestLID : 16 / little , 0 : 16 , CharTypeID : 32 , CharGID : 32 / little , 0 : 64 ,
2010-10-22 04:31:06 +08:00
16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , CharGID : 32 / little , CharLID : 32 / little , 100 : 32 / little > > ) .
2010-08-30 01:09:01 +08:00
%% @doc Make the client load a new map.
2011-02-27 00:00:41 +08:00
send_0205 ( CharUser , IsSeasonal , #client { socket = Socket , gid = DestGID , lid = DestLID , areanb = AreaNb } ) - >
2011-02-13 06:28:56 +08:00
#users { lid = CharLID , area = { _ QuestID , ZoneID , MapID } , entryid = EntryID } = CharUser ,
2010-09-22 09:10:53 +08:00
packet_send ( Socket , < < 16#02050300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 ,
2010-10-22 10:04:21 +08:00
16#ffffffff : 32 , ZoneID : 32 / little , MapID : 32 / little , EntryID : 32 / little , AreaNb : 32 / little , CharLID : 16 / little , 0 : 8 , IsSeasonal : 8 > > ) .
2010-08-30 01:09:01 +08:00
2010-09-25 22:18:33 +08:00
%% @doc Indicate to the client that loading should finish.
2011-02-27 00:00:41 +08:00
send_0208 ( #client { socket = Socket , gid = DestGID , lid = DestLID , areanb = AreaNb } ) - >
2011-02-20 02:45:50 +08:00
packet_send ( Socket , < < 16#02080300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , AreaNb : 32 / little > > ) .
2010-09-25 22:18:33 +08:00
2010-08-30 04:21:23 +08:00
%% @todo No idea what this one does. For unknown reasons it uses channel 2.
2010-09-24 01:05:42 +08:00
%% @todo Handle the DestLID properly?
2011-02-27 00:00:41 +08:00
send_020c ( #client { socket = Socket } ) - >
2010-09-22 09:15:02 +08:00
packet_send ( Socket , < < 16#020c0200 : 32 , 16#ffff0000 : 32 , 0 : 256 > > ) .
2010-08-30 04:21:23 +08:00
2010-08-30 01:22:02 +08:00
%% @doc Send the quest file to be loaded by the client.
2010-09-24 01:05:42 +08:00
%% @todo Handle the DestLID properly?
2011-02-27 00:00:41 +08:00
send_020e ( QuestData , #client { socket = Socket } ) - >
2010-11-08 02:45:35 +08:00
Size = byte_size ( QuestData ) ,
packet_send ( Socket , < < 16#020e0300 : 32 , 16#ffff : 16 , 0 : 272 , Size : 32 / little , 0 : 32 , QuestData / binary , 0 : 32 > > ) .
2010-08-30 01:22:02 +08:00
2010-09-25 21:57:15 +08:00
%% @doc Send the zone file to be loaded.
2011-02-27 00:00:41 +08:00
send_020f ( ZoneData , SetID , SeasonID , #client { socket = Socket } ) - >
2010-11-28 06:03:04 +08:00
Size = byte_size ( ZoneData ) ,
packet_send ( Socket , < < 16#020f0300 : 32 , 16#ffff : 16 , 0 : 272 , SetID , SeasonID , 0 : 16 , Size : 32 / little , ZoneData / binary > > ) .
2010-09-25 21:57:15 +08:00
2010-09-24 00:44:56 +08:00
%% @doc Send the current UNIX time.
2011-02-27 00:00:41 +08:00
send_0210 ( #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2010-09-24 00:44:56 +08:00
UnixTime = calendar : datetime_to_gregorian_seconds ( calendar : now_to_universal_time ( now ( ) ) )
- calendar : datetime_to_gregorian_seconds ( { { 1970 , 1 , 1 } , { 0 , 0 , 0 } } ) ,
packet_send ( Socket , < < 16#02100300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 96 , UnixTime : 32 / little > > ) .
2010-08-30 04:15:32 +08:00
%% @todo No idea what this is doing.
2011-02-27 00:00:41 +08:00
send_0215 ( UnknownValue , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2010-09-22 09:44:49 +08:00
packet_send ( Socket , < < 16#02150300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , UnknownValue : 32 / little > > ) .
2010-08-30 04:15:32 +08:00
2010-09-20 06:53:06 +08:00
%% @doc Send the game server's IP and port that the client requested.
2011-02-27 00:00:41 +08:00
send_0216 ( IP , Port , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2010-09-24 01:05:42 +08:00
packet_send ( Socket , < < 16#02160300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00000f00 : 32 , DestGID : 32 / little , 0 : 64 , IP / binary , Port : 16 / little , 0 : 16 > > ) .
2010-09-20 06:53:06 +08:00
2010-09-24 01:05:42 +08:00
%% @doc End of character loading.
2011-02-27 00:00:41 +08:00
send_021b ( #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2010-09-24 00:53:58 +08:00
packet_send ( Socket , < < 16#021b0300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 > > ) .
2010-10-21 23:00:30 +08:00
%% @doc Send the list of available universes.
2011-02-27 00:00:41 +08:00
send_021e ( Universes , #client { socket = Socket } ) - >
2010-10-21 23:00:30 +08:00
NbUnis = length ( Universes ) ,
UnisBin = build_021e_uni ( Universes , [ ] ) ,
packet_send ( Socket , < < 16#021e0300 : 32 , 0 : 288 , NbUnis : 32 / little , UnisBin / binary > > ) .
build_021e_uni ( [ ] , Acc ) - >
iolist_to_binary ( lists : reverse ( Acc ) ) ;
build_021e_uni ( [ { _ UniID , { myroom , Name , NbPlayers , _ MaxPlayers } } | Tail ] , Acc ) - >
Padding = 8 * ( 44 - byte_size ( Name ) ) ,
Bin = < < 16#ffffffff : 32 , NbPlayers : 16 / little , 0 : 16 , Name / binary , 0 : Padding > > ,
build_021e_uni ( Tail , [ Bin | Acc ] ) ;
build_021e_uni ( [ { UniID , { universe , Name , NbPlayers , _ MaxPlayers } } | Tail ] , Acc ) - >
Padding = 8 * ( 32 - byte_size ( Name ) ) ,
PopString = lists : flatten ( io_lib : format ( " ~5b " , [ NbPlayers ] ) ) ,
PopString2 = < < < < X : 8 , 0 : 8 > > | | X < - PopString > > ,
Bin = < < UniID : 32 / little , NbPlayers : 16 / little , 643 : 16 / little , Name / binary , 0 : Padding , PopString2 / binary , 0 : 16 > > ,
build_021e_uni ( Tail , [ Bin | Acc ] ) .
%% @doc Send the current universe info along with the current level cap.
2011-02-27 00:00:41 +08:00
send_0222 ( UniID , #client { socket = Socket , gid = DestGID } ) - >
2010-10-21 23:00:30 +08:00
{ _ Type , Name , NbPlayers , MaxPlayers } = egs_universes : read ( UniID ) ,
Padding = 8 * ( 44 - byte_size ( Name ) ) ,
LevelCap = egs_conf : read ( level_cap ) ,
packet_send ( Socket , < < 16#02220300 : 32 , 16#ffff : 16 , 0 : 16 , 16#00001200 : 32 , DestGID : 32 / little , 0 : 64 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 ,
UniID : 32 / little , NbPlayers : 16 / little , MaxPlayers : 16 / little , Name / binary , 0 : Padding , LevelCap : 32 / little > > ) .
2010-09-23 07:48:15 +08:00
%% @doc Send the auth key, or, in case of failure, a related error message.
2011-02-27 00:00:41 +08:00
send_0223 ( AuthGID , AuthKey , #client { socket = Socket , gid = DestGID } ) - >
2010-09-23 07:48:15 +08:00
packet_send ( Socket , < < 16#02230300 : 32 , 0 : 160 , 16#00000f00 : 32 , DestGID : 32 / little , 0 : 64 , AuthGID : 32 / little , AuthKey : 32 / bits > > ) .
2011-02-27 00:00:41 +08:00
send_0223 ( ErrorMsg , #client { socket = Socket , gid = DestGID } ) - >
2010-09-23 07:48:15 +08:00
Length = byte_size ( ErrorMsg ) div 2 + 2 ,
packet_send ( Socket , < < 16#02230300 : 32 , 0 : 160 , 16#00000f00 : 32 , DestGID : 32 / little , 0 : 128 , 3 : 32 / little , 0 : 48 , Length : 16 / little , ErrorMsg / binary , 0 : 16 > > ) .
2010-09-23 08:17:13 +08:00
%% @doc Send a MOTD page.
2011-02-27 00:00:41 +08:00
send_0225 ( MOTD , CurrentPage , #client { socket = Socket , lid = DestLID } ) - >
2010-09-23 08:17:13 +08:00
Tokens = re : split ( MOTD , " \n . " ) ,
Msg = < < < < Line / binary , " \n " , 0 > > | | Line < - lists : sublist ( Tokens , 1 + CurrentPage * 15 , 15 ) > > ,
NbPages = 1 + length ( Tokens ) div 15 ,
Length = byte_size ( Msg ) div 2 + 2 ,
packet_send ( Socket , < < 16#02250300 : 32 , DestLID : 16 / little , 0 : 272 , NbPages : 8 , CurrentPage : 8 , Length : 16 / little , Msg / binary , 0 : 16 > > ) .
2010-10-09 08:07:02 +08:00
%% @doc Display a notice on the player's screen.
%% There are four types of notices: dialog, top, scroll and timeout.
%% * dialog: A dialog in the center of the screen, which can be OK'd by players.
%% * top: Horizontal scroll on top of the screen, traditionally used for server-wide messages.
%% * scroll: Vertical scroll on the right of the screen, traditionally used for rare missions obtention messages.
%% * timeout: A dialog in the center of the screen that disappears after Duration seconds.
2011-02-27 00:00:41 +08:00
send_0228 ( Type , Duration , Message , #client { socket = Socket , gid = DestGID } ) - >
2010-10-09 08:07:02 +08:00
TypeInt = case Type of dialog - > 0 ; top - > 1 ; scroll - > 2 ; timeout - > 3 end ,
UCS2Message = < < < < X : 8 , 0 : 8 > > | | X < - Message > > ,
packet_send ( Socket , < < 16#02280300 : 32 , 16#ffff : 16 , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 ,
TypeInt : 32 / little , Duration : 32 / little , UCS2Message / binary , 0 : 16 > > ) .
2011-02-27 00:38:07 +08:00
%% @todo No idea!
%% @todo This packet hasn't been reviewed at all yet.
send_022c ( A , B , #client { socket = Socket , gid = DestGID } ) - >
packet_send ( Socket , < < 16#022c0300 : 32 , 0 : 160 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , A : 16 / little , B : 16 / little > > ) .
2010-10-20 00:37:05 +08:00
%% @todo Not sure. Sent when going to or from room. Possibly when changing universes too?
2011-02-27 00:00:41 +08:00
send_0230 ( #client { socket = Socket , gid = DestGID } ) - >
2010-10-20 00:37:05 +08:00
packet_send ( Socket , < < 16#02300300 : 32 , 16#ffff : 16 , 0 : 16 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 > > ) .
2010-09-23 04:28:27 +08:00
%% @doc Forward the player to a website. The website will open when the player closes the game. Used for login issues mostly.
2011-02-27 00:00:41 +08:00
send_0231 ( URL , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2010-09-23 04:28:27 +08:00
URLBin = list_to_binary ( URL ) ,
Length = byte_size ( URLBin ) + 1 ,
Padding = 8 * ( 512 - Length - 1 ) ,
packet_send ( Socket , < < 16#02310300 : 32 , DestLID : 16 / little , 0 : 16 , 16#00000f00 : 32 , DestGID : 32 / little , 0 : 64 ,
16#00000f00 : 32 , DestGID : 32 / little , 0 : 64 , Length : 32 / little , URLBin / binary , 0 : Padding > > ) .
2011-02-20 02:45:50 +08:00
%% @doc Send the list of players already spawned in the zone when entering it.
2011-02-27 00:00:41 +08:00
send_0233 ( Users , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2010-10-22 08:35:16 +08:00
NbUsers = length ( Users ) ,
Bin = build_0233_users ( Users , [ ] ) ,
2011-02-20 02:45:50 +08:00
packet_send ( Socket , < < 16#02330300 : 32 , DestLID : 16 / little , 0 : 16 , 16#00001200 : 32 , DestGID : 32 / little , 0 : 64 ,
2010-10-22 08:35:16 +08:00
16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , NbUsers : 32 / little , Bin / binary , 0 : 608 > > ) .
build_0233_users ( [ ] , Acc ) - >
iolist_to_binary ( lists : reverse ( Acc ) ) ;
build_0233_users ( [ User | Tail ] , Acc ) - >
Bin = psu_characters : character_user_to_binary ( User ) ,
build_0233_users ( Tail , [ < < Bin / binary , 0 : 32 > > | Acc ] ) .
2010-09-26 01:29:47 +08:00
%% @doc Start the zone handling: load the zone file and the objects sent separately.
2011-02-27 00:00:41 +08:00
send_0236 ( #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2011-02-20 02:45:50 +08:00
packet_send ( Socket , < < 16#02360300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 > > ) .
2010-09-26 01:29:47 +08:00
2010-10-20 00:12:25 +08:00
%% @doc Chat message.
2011-02-27 00:00:41 +08:00
send_0304 ( FromGID , ChatTypeID , ChatGID , ChatName , ChatModifiers , ChatMessage , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2010-10-20 00:12:25 +08:00
{ chat_modifiers , ChatType , ChatCutIn , ChatCutInAngle , ChatMsgLength , ChatChannel , ChatCharacterType } = ChatModifiers ,
2011-02-20 02:45:50 +08:00
packet_send ( Socket , < < 16#03040300 : 32 , DestLID : 16 / little , 0 : 16 , 16#00011300 : 32 , FromGID : 32 / little , 0 : 64 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 ,
2010-10-20 00:12:25 +08:00
ChatTypeID : 32 , ChatGID : 32 / little , 0 : 64 , ChatType : 8 , ChatCutIn : 8 , ChatCutInAngle : 8 , ChatMsgLength : 8 ,
ChatChannel : 8 , ChatCharacterType : 8 , 0 : 16 , ChatName / binary , ChatMessage / binary > > ) .
2010-08-30 01:09:01 +08:00
%% @todo Inventory related. Doesn't seem to do anything.
2011-02-27 00:00:41 +08:00
send_0a05 ( #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2011-02-20 02:45:50 +08:00
packet_send ( Socket , < < 16#0a050300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 > > ) .
2010-10-02 10:17:23 +08:00
%% @doc Send the list of ItemUUID for the items in the inventory.
2011-02-27 00:00:41 +08:00
send_0a06 ( CharUser , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2011-02-13 00:53:58 +08:00
Len = length ( ( CharUser #users.character ) #characters.inventory ) ,
2010-10-02 10:17:23 +08:00
UUIDs = lists : seq ( 1 , Len ) ,
Bin = iolist_to_binary ( [ < < N : 32 / little > > | | N < - UUIDs ] ) ,
Blanks = lists : seq ( 1 , 60 - Len ) ,
Bin2 = iolist_to_binary ( [ < < 16#ffffffff : 32 > > | | _ N < - Blanks ] ) ,
2011-02-20 02:45:50 +08:00
packet_send ( Socket , < < 16#0a060300 : 32 , DestLID : 16 / little , 0 : 48 , DestGID : 32 / little , 0 : 64 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , Bin / binary , Bin2 / binary > > ) .
2010-08-30 01:09:01 +08:00
2010-09-25 09:27:07 +08:00
%% @doc Send an item's description.
2011-02-27 00:00:41 +08:00
send_0a11 ( ItemID , ItemDesc , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2010-09-25 09:27:07 +08:00
Length = 1 + byte_size ( ItemDesc ) div 2 ,
packet_send ( Socket , < < 16#0a110300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 ,
ItemID : 32 , Length : 32 / little , ItemDesc / binary , 0 : 16 > > ) .
2010-08-30 00:31:23 +08:00
%% @doc Quest init.
%% @todo When first entering a zone it seems LID should be set to ffff apparently.
2011-02-27 00:00:41 +08:00
send_0c00 ( CharUser , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2011-02-13 06:28:56 +08:00
#users { area = { QuestID , _ ZoneID , _ MapID } } = CharUser ,
2010-09-22 10:19:04 +08:00
packet_send ( Socket , < < 16#0c000300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , QuestID : 32 / little ,
2010-08-30 00:31:23 +08:00
16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 ,
16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 ,
16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 ,
16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 > > ) .
2011-02-27 01:03:55 +08:00
%% @todo Figure out last 4 bytes!
%% @todo This packet hasn't been reviewed at all yet.
send_0c02 ( #client { socket = Socket , gid = DestGID } ) - >
packet_send ( Socket , < < 16#0c020300 : 32 , 0 : 160 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , 0 : 32 > > ) .
2010-10-14 02:09:34 +08:00
%% @doc Send the huge pack of quest files available in the counter.
2011-02-27 00:00:41 +08:00
send_0c06 ( Pack , #client { socket = Socket } ) - >
2011-02-20 07:18:14 +08:00
packet_send ( Socket , < < 16#0c060300 : 32 , 0 : 288 , 1 : 32 / little , Pack / binary > > ) .
2010-10-14 02:09:34 +08:00
2010-10-22 04:56:18 +08:00
%% @doc Reply that the player is allowed to use the lobby transport. Always allow.
2011-02-27 00:00:41 +08:00
send_0c08 ( #client { socket = Socket , gid = DestGID } ) - >
2010-10-22 04:56:18 +08:00
packet_send ( Socket , < < 16#0c080300 : 32 , 16#ffff : 16 , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 96 > > ) .
2011-02-27 01:09:07 +08:00
%% @doc Send the trial start notification.
%% @todo This packet hasn't been reviewed at all yet.
send_0c09 ( #client { socket = Socket , gid = DestGID } ) - >
packet_send ( Socket , < < 16#0c090300 : 32 , 0 : 160 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , 0 : 64 > > ) .
2010-10-14 02:09:34 +08:00
%% @doc Send the counter's mission options (0 = invisible, 2 = disabled, 3 = available).
2011-02-27 00:00:41 +08:00
send_0c10 ( Options , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2010-10-14 02:09:34 +08:00
Size = byte_size ( Options ) ,
2011-02-20 02:45:50 +08:00
packet_send ( Socket , < < 16#0c100300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , 1 , 0 , Size : 16 / little , Options / binary > > ) .
2010-10-14 02:09:34 +08:00
2010-10-22 01:39:11 +08:00
%% @doc Send the general data and flags for the selected character.
%% @todo Handle bitflags and value flags properly.
2011-02-27 00:00:41 +08:00
send_0d01 ( Character , #client { socket = Socket , gid = DestGID } ) - >
2010-10-22 01:39:11 +08:00
CharBin = psu_characters : character_tuple_to_binary ( Character ) ,
OptionsBin = psu_characters : options_tuple_to_binary ( Character #characters.options ) ,
packet_send ( Socket , < < 16#0d010300 : 32 , 16#ffff : 16 , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , CharBin / binary ,
16#ffbbef1c : 32 , 16#f8ff0700 : 32 , 16#fc810916 : 32 , 16#7802134c : 32 , 16#b0c0040f : 32 , 16#7cf0e583 : 32 ,
16#b7bce0c6 : 32 , 16#7ff8f963 : 32 , 16#3fd7ffff : 32 , 16#fff7ffff : 32 , 16#f3ff63e0 : 32 , 16#1fe00000 : 32 ,
0 : 7744 , OptionsBin / binary > > ) .
%% @doc Send the flags list. This is the whole list of available values, not the character's.
2010-09-04 05:26:29 +08:00
%% Sent without fragmentation on official for unknown reasons. Do the same here.
2011-02-27 00:00:41 +08:00
send_0d05 ( #client { socket = Socket , gid = DestGID } ) - >
2010-09-04 05:26:29 +08:00
{ ok , Flags } = file : read_file ( " p/flags.bin " ) ,
Packet = < < 16#0d050300 : 32 , 0 : 32 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , Flags / binary > > ,
Size = 4 + byte_size ( Packet ) ,
2010-09-22 10:21:48 +08:00
ssl : send ( Socket , < < Size : 32 / little , Packet / binary > > ) .
2010-08-30 00:31:23 +08:00
2010-10-22 00:20:43 +08:00
%% @doc Send the client's own player's party information, on the bottom left of the screen.
%% @todo Location and the 20 bytes following sometimes have values, not sure why; when joining a party maybe?
2011-02-27 00:00:41 +08:00
send_1005 ( Character , #client { socket = Socket , gid = DestGID } ) - >
2010-10-22 00:20:43 +08:00
#characters { name = Name , mainlevel = #level { number = Level } , currenthp = CurrentHP , maxhp = MaxHP } = Character ,
Location = < < 0 : 512 > > ,
packet_send ( Socket , < < 16#10050300 : 32 , 16#ffff : 16 , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 ,
16#00000100 : 32 , 0 : 32 , 16#ffffffff : 32 , 0 : 32 , 16#00011200 : 32 , DestGID : 32 / little , 0 : 64 ,
Name / binary , Level : 8 , 0 : 16 , 1 : 8 , 16#01010000 : 32 , 0 : 32 , Location / binary ,
16#ffffffff : 32 , 0 : 96 , 16#ffffffff : 32 , 0 : 64 , CurrentHP : 32 / little , MaxHP : 32 / little , 0 : 640 ,
16#0100ffff : 32 , 16#0000ff00 : 32 , 16#ffff0000 : 32 , 0 : 640 , 16#ffffffff : 32 , 0 : 768 ,
16#0100ffff : 32 , 16#0000ff00 : 32 , 16#ffff0000 : 32 , 0 : 640 , 16#ffffffff : 32 , 0 : 768 ,
16#0100ffff : 32 , 16#0000ff00 : 32 , 16#ffff0000 : 32 , 0 : 640 , 16#ffffffff : 32 , 0 : 768 ,
16#0100ffff : 32 , 16#0000ff00 : 32 , 16#ffff0000 : 32 , 0 : 640 , 16#ffffffff : 32 , 0 : 768 ,
16#0100ffff : 32 , 16#0000ff00 : 32 , 16#ffff0000 : 32 , 0 : 640 , 16#ffffffff : 32 , 0 : 448 ,
16#ffffffff : 32 , 0 : 32 , 16#ff020000 : 32 , 16#ffff0000 : 32 , 16#ffff0000 : 32 , 16#ffff0000 : 32 ,
16#ffff0000 : 32 , 16#ffff0000 : 32 , 16#ffff0000 : 32 , 0 : 3680 > > ) .
2010-09-24 01:55:03 +08:00
%% @doc Party-related events.
2011-02-27 00:00:41 +08:00
send_1006 ( EventID , Client ) - >
send_1006 ( EventID , 0 , Client ) .
send_1006 ( EventID , PartyPos , #client { socket = Socket , gid = DestGID } ) - >
2010-09-24 01:55:03 +08:00
packet_send ( Socket , < < 16#10060300 : 32 , 16#ffff : 16 , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , EventID : 8 , PartyPos : 8 , 0 : 16 > > ) .
2010-09-26 03:46:04 +08:00
%% @doc Send the player's current location.
%% @todo Handle PartyPos.
%% @todo Receive the AreaName as UCS2 directly to allow for color codes and the like.
%% @todo Handle TargetLID probably (right after the padding).
2010-11-09 05:03:16 +08:00
%% @todo Do counters even have a name?
2011-02-27 00:00:41 +08:00
send_100e ( CounterID , AreaName , #client { socket = Socket , gid = DestGID } ) - >
2010-09-26 03:46:04 +08:00
PartyPos = 0 ,
UCS2Name = < < < < X : 8 , 0 : 8 > > | | X < - AreaName > > ,
Padding = 8 * ( 64 - byte_size ( UCS2Name ) ) ,
CounterType = if CounterID =:= 16#ffffffff - > 2 ; true - > 1 end ,
packet_send ( Socket , < < 16#100e0300 : 32 , 16#ffffffbf : 32 , 0 : 128 , 16#00011300 : 32 , DestGID : 32 , 0 : 64 ,
1 , PartyPos , 0 : 48 , 16#ffffff7f : 32 , UCS2Name / binary , 0 : Padding , 0 : 32 , CounterID : 32 / little , CounterType : 32 / little > > ) .
2011-02-27 00:00:41 +08:00
send_100e ( { QuestID , ZoneID , MapID } , EntryID , AreaName , #client { socket = Socket , gid = DestGID } ) - >
2010-09-26 03:46:04 +08:00
PartyPos = 0 ,
UCS2Name = < < < < X : 8 , 0 : 8 > > | | X < - AreaName > > ,
Padding = 8 * ( 64 - byte_size ( UCS2Name ) ) ,
packet_send ( Socket , < < 16#100e0300 : 32 , 16#ffffffbf : 32 , 0 : 128 , 16#00011300 : 32 , DestGID : 32 , 0 : 64 ,
1 , PartyPos , ZoneID : 16 / little , MapID : 16 / little , EntryID : 16 / little , QuestID : 32 / little ,
UCS2Name / binary , 0 : Padding , 0 : 32 , 16#ffffffff : 32 , 0 : 32 > > ) .
2011-02-27 01:15:53 +08:00
%% @todo No idea. Also the 2 PartyPos in the built packet more often than not match, but sometimes don't? That's probably because one is PartyPos and the other is LID or something.
%% @todo This packet hasn't been reviewed at all yet.
send_100f ( NPCid , PartyPos , #client { socket = Socket , gid = DestGID } ) - >
packet_send ( Socket , < < 16#100f0300 : 32 , 0 : 160 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , NPCid : 16 / little , 1 , PartyPos : 8 , PartyPos : 32 / little > > ) .
2011-02-27 01:25:04 +08:00
%% @doc Send the mission's quest file when starting a new mission.
%% @todo Handle correctly. 0:32 is actually a missing value. Value before that is unknown too.
%% @todo This packet hasn't been reviewed at all yet.
send_1015 ( QuestID , #client { socket = Socket , gid = DestGID } ) - >
QuestData = egs_quests_db : quest_nbl ( QuestID ) ,
Size = byte_size ( QuestData ) ,
packet_send ( Socket , < < 16#10150300 : 32 , 0 : 160 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , QuestID : 32 / little , 16#01010000 : 32 , 0 : 32 , Size : 32 / little , QuestData / binary > > ) .
2011-02-27 01:27:31 +08:00
%% @todo No idea.
%% @todo This packet hasn't been reviewed at all yet.
send_101a ( NPCid , PartyPos , #client { socket = Socket , gid = DestGID } ) - >
packet_send ( Socket , < < 16#101a0300 : 32 , 0 : 160 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , NPCid : 16 / little , PartyPos : 16 / little , 16#ffffffff : 32 > > ) .
2010-09-24 06:06:14 +08:00
%% @doc Mission start related.
2011-02-27 00:00:41 +08:00
send_1020 ( #client { socket = Socket , gid = DestGID } ) - >
2010-09-24 06:06:14 +08:00
packet_send ( Socket , < < 16#10200300 : 32 , 16#ffff : 16 , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 > > ) .
2010-09-24 05:16:24 +08:00
%% @doc Update HP in the party members information on the left.
%% @todo Handle PartyPos. Probably only pass HP later.
2011-02-27 00:00:41 +08:00
send_1022 ( #users { character = #characters { currenthp = HP } } , #client { socket = Socket , gid = DestGID } ) - >
2010-09-24 05:16:24 +08:00
PartyPos = 0 ,
packet_send ( Socket , < < 16#10220300 : 32 , 16#ffff : 16 , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , HP : 32 / little , PartyPos : 32 / little > > ) .
2011-02-27 01:30:40 +08:00
%% @todo Boss related command.
%% @todo This packet hasn't been reviewed at all yet.
send_110e ( Data , #client { socket = Socket , gid = DestGID } ) - >
packet_send ( Socket , < < 16#110e0300 : 32 , 16#ffff : 16 , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , Data / binary , 0 : 32 , 5 : 16 / little , 12 : 16 / little , 0 : 32 , 260 : 32 / little > > ) .
2011-02-27 01:36:28 +08:00
%% @todo Boss related command.
%% @todo This packet hasn't been reviewed at all yet.
send_1113 ( Data , #client { socket = Socket , gid = DestGID } ) - >
packet_send ( Socket , < < 16#11130300 : 32 , 16#ffff : 16 , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , Data / binary > > ) .
2011-02-27 01:39:04 +08:00
%% @todo Figure out what this packet does. Sane values for counter and missions for now.
%% @todo This packet hasn't been reviewed at all yet.
send_1202 ( #client { socket = Socket , gid = DestGID } ) - >
packet_send ( Socket , < < 16#12020300 : 32 , 16#ffff : 16 , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , 0 : 32 , 16#10000000 : 32 , 0 : 64 , 16#14000000 : 32 , 0 : 32 > > ) .
2010-09-26 07:59:40 +08:00
%% @todo Always the same value, no idea what it's for.
2011-02-27 00:00:41 +08:00
send_1204 ( #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2011-02-20 02:45:50 +08:00
packet_send ( Socket , < < 16#12040300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 96 , 16#20000000 : 32 , 0 : 256 > > ) .
2010-09-26 07:59:40 +08:00
2011-02-27 01:44:42 +08:00
%% @doc Object events response?
%% @todo Not sure what Value does exactly. It's either 0 or 1.
%% @todo This packet hasn't been reviewed at all yet.
send_1205 ( EventID , BlockID , Value , #client { socket = Socket , gid = DestGID } ) - >
packet_send ( Socket , < < 16#12050300 : 32 , 16#ffff : 16 , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , EventID , BlockID , 0 : 16 , Value , 0 : 24 > > ) .
2011-02-27 04:28:07 +08:00
%% @todo Figure out what this packet does. Sane values for counter and missions for now.
%% @todo This packet hasn't been reviewed at all yet.
send_1206 ( #client { socket = Socket , gid = DestGID } ) - >
packet_send ( Socket , < < 16#12060300 : 32 , 16#ffff : 16 , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , 0 : 32 , 16#80020000 : 32 , 0 : 5120 > > ) .
2011-02-27 04:32:35 +08:00
%% @todo Figure out what this packet does. Sane values for counter and missions for now.
%% @todo This packet hasn't been reviewed at all yet.
send_1207 ( #client { socket = Socket , gid = DestGID } ) - >
Chunk = < < 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 16#ffffffff : 32 , 0 : 224 , 16#0000ffff : 32 , 16#ff000000 : 32 , 16#64000a00 : 32 > > ,
packet_send ( Socket , < < 16#12070300 : 32 , 16#ffff : 16 , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 ,
Chunk / binary , Chunk / binary , Chunk / binary , Chunk / binary , Chunk / binary , Chunk / binary > > ) .
2010-10-22 05:30:26 +08:00
%% @doc Send the player's partner card.
%% @todo Handle the LID and comment properly.
2011-02-27 00:00:41 +08:00
send_1500 ( Character , #client { socket = Socket , gid = DestGID } ) - >
2010-10-22 05:30:26 +08:00
#characters { slot = Slot , name = Name , race = Race , gender = Gender , class = Class , appearance = Appearance } = Character ,
case Appearance of
#flesh_appearance { voicetype = VoiceType , voicepitch = VoicePitch } - > ok ;
#metal_appearance { voicetype = VoiceType , voicepitch = VoicePitch } - > ok
end ,
RaceBin = psu_characters : race_atom_to_binary ( Race ) ,
GenderBin = psu_characters : gender_atom_to_binary ( Gender ) ,
ClassBin = psu_characters : class_atom_to_binary ( Class ) ,
Comment = < < 0 : 2816 > > ,
packet_send ( Socket , < < 16#15000300 : 32 , 16#ffff : 16 , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 ,
Name / binary , RaceBin : 8 , GenderBin : 8 , ClassBin : 8 , VoiceType : 8 , VoicePitch : 8 , 0 : 24 ,
DestGID : 32 / little , 0 : 224 , Comment / binary , 1 , 4 , 1 , Slot , 0 : 64 > > ) .
2010-10-22 05:55:01 +08:00
%% @doc Send the list of parties to join.
%% @todo Handle lists of parties.
%% @todo Probably has to handle a LID here, although it should always be 0.
2011-02-27 00:00:41 +08:00
send_1701 ( #client { socket = Socket , gid = DestGID } ) - >
2010-10-22 05:55:01 +08:00
packet_send ( Socket , < < 16#17010300 : 32 , 0 : 160 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 96 > > ) .
2010-10-19 06:20:44 +08:00
%% @doc Send the background to use for the counter.
2011-02-27 00:00:41 +08:00
send_1711 ( Bg , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2011-02-20 02:45:50 +08:00
packet_send ( Socket , < < 16#17110300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 64 , Bg : 8 , 0 : 24 > > ) .
2010-10-19 06:20:44 +08:00
2010-10-06 23:51:28 +08:00
%% @doc NPC shop request reply.
2011-02-27 00:00:41 +08:00
send_1a02 ( A , B , C , D , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2011-02-20 02:45:50 +08:00
packet_send ( Socket , < < 16#1a020300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 96 ,
2010-10-06 23:51:28 +08:00
A : 16 / little , B : 16 / little , C : 16 / little , D : 16 / little > > ) .
2010-10-08 09:45:01 +08:00
%% @doc Lumilass available hairstyles/headtypes handler.
2011-02-27 00:00:41 +08:00
send_1a03 ( CharUser , #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2010-10-08 09:45:01 +08:00
{ ok , Conf } = file : consult ( " priv/lumilass.conf " ) ,
2011-02-13 00:53:58 +08:00
Character = CharUser #users.character ,
2010-10-08 09:45:01 +08:00
NbHeadtypes = proplists : get_value ( { headtypes , Character #characters.gender , Character #characters.race } , Conf , 0 ) ,
HairstylesList = proplists : get_value ( { hairstyles , Character #characters.gender } , Conf ) ,
NbHairstyles = length ( HairstylesList ) ,
HairstylesBin = iolist_to_binary ( [ < < N : 32 > > | | N < - HairstylesList ] ) ,
2011-02-20 02:45:50 +08:00
packet_send ( Socket , < < 16#1a030300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 96 ,
2010-10-08 09:45:01 +08:00
NbHairstyles : 32 / little , NbHeadtypes : 32 / little , 0 : 416 , HairstylesBin / binary , 0 : 32 > > ) .
2010-09-24 04:33:53 +08:00
%% @doc Available types handler. Enable all 16 types.
2011-02-27 00:00:41 +08:00
send_1a07 ( #client { socket = Socket , gid = DestGID , lid = DestLID } ) - >
2010-09-24 04:33:53 +08:00
packet_send ( Socket , < < 16#1a070300 : 32 , DestLID : 16 / little , 0 : 144 , 16#00011300 : 32 , DestGID : 32 / little , 0 : 160 ,
16#01010101 : 32 , 16#01010101 : 32 , 16#01010101 : 32 , 16#01010101 : 32 > > ) .
2010-09-20 00:41:06 +08:00
%% Utility functions.
2010-08-30 00:31:23 +08:00
2010-09-20 00:41:06 +08:00
%% @doc Return the language as an atom from its integer value.
2010-09-26 08:00:50 +08:00
%% @todo Identify which of the english languages is american and which is uk.
2010-09-20 00:41:06 +08:00
language_integer_to_atom ( 0 ) - > japanese ;
2010-09-27 23:24:09 +08:00
language_integer_to_atom ( 1 ) - > american_english ;
language_integer_to_atom ( 2 ) - > british_english ;
2010-09-20 00:41:06 +08:00
language_integer_to_atom ( 3 ) - > french ;
language_integer_to_atom ( 4 ) - > german ;
2010-09-27 23:24:09 +08:00
language_integer_to_atom ( 5 ) - > spanish ;
language_integer_to_atom ( 6 ) - > italian ;
language_integer_to_atom ( 7 ) - > korean ;
language_integer_to_atom ( 8 ) - > simplified_chinese ;
language_integer_to_atom ( 9 ) - > traditional_chinese ;
2010-09-20 00:41:06 +08:00
language_integer_to_atom ( Language ) - > log ( " unknown 080e Language ~p " , [ Language ] ) .
2010-08-30 00:31:23 +08:00
2010-05-13 12:34:04 +08:00
%% @doc Prepare a packet. Return the real size and padding at the end.
packet_prepare ( Packet ) - >
Size = 4 + byte_size ( Packet ) ,
case Size rem 4 of
2010-09-24 04:33:53 +08:00
0 - > { ok , Size , < < > > } ;
2 - > { ok , Size + 2 , < < 0 : 16 > > } ;
_ - > { error , badarg }
2010-05-13 12:34:04 +08:00
end .
%% @doc Send a packet. The packet argument must not contain the size field.
packet_send ( CSocket , Packet ) - >
{ ok , Size , Padding } = packet_prepare ( Packet ) ,
2010-10-06 23:54:25 +08:00
packet_send ( CSocket , < < Size : 32 / little , Packet / binary , Padding / binary > > , Size ) .
2010-05-13 12:34:04 +08:00
%% @doc Send a normal command.
packet_send ( CSocket , Packet , Size ) when Size =< 16#4000 - >
ssl : send ( CSocket , Packet ) ;
%% @doc Send a fragmented command when size is too big.
packet_send ( CSocket , Packet , Size ) - >
packet_fragment_send ( CSocket , Packet , Size , 0 ) .
%% @doc Send the last chunk of a fragmented command.
packet_fragment_send ( CSocket , Packet , Size , Current ) when Size - Current =< 16#4000 - >
FragmentSize = 16#10 + byte_size ( Packet ) ,
2010-10-06 23:54:25 +08:00
Fragment = < < FragmentSize : 32 / little , 16#0b030000 : 32 , Size : 32 / little , Current : 32 / little , Packet / binary > > ,
2010-05-13 12:34:04 +08:00
ssl : send ( CSocket , Fragment ) ;
%% @doc Send another chunk of a fragmented command.
packet_fragment_send ( CSocket , Packet , Size , Current ) - >
< < Chunk : 131072 / bits , Rest / bits > > = Packet ,
2010-10-06 23:54:25 +08:00
Fragment = < < 16#10400000 : 32 , 16#0b030000 : 32 , Size : 32 / little , Current : 32 / little , Chunk / binary > > ,
2010-05-13 12:34:04 +08:00
ssl : send ( CSocket , Fragment ) ,
packet_fragment_send ( CSocket , Rest , Size , Current + 16#4000 ) .
2010-06-04 23:56:18 +08:00
%% @doc Keepalive. Just send an empty packet, the game doesn't really care.
%% @todo If there's an actual keepalive command, use it instead.
send_keepalive ( CSocket ) - >
Packet = < < 0 : 32 > > ,
2010-05-13 12:34:04 +08:00
packet_send ( CSocket , Packet ) .