diff --git a/src/psu/psu_game.erl b/src/psu/psu_game.erl index c6eadcc..b652277 100644 --- a/src/psu/psu_game.erl +++ b/src/psu/psu_game.erl @@ -140,8 +140,10 @@ process_event({command, Command, Channel, _Data}) -> char_select() -> case psu_proto:packet_recv(get(socket), 5000) of {ok, Orig} -> - {command, Command, _, Data} = psu_proto:packet_parse(Orig), - char_select_handle(Command, Data); + case psu_proto:parse(Orig) of + ignore -> ?MODULE:char_select(); + Event -> char_select_event(Event) + end; {error, timeout} -> psu_proto:send_keepalive(get(socket)), reload, @@ -150,17 +152,11 @@ char_select() -> closed %% exit end. -%% @doc Character selection handler. -char_select_handle(16#020b, << Number:32/little-unsigned-integer, _/bits >>) -> - log("selected character ~b", [Number]), - char_select_load(Number); - -%% @doc Character creation handler. %% @todo Reenable appearance validation whenever things go live. -char_select_handle(16#0d02, << Number:32/little-unsigned-integer, Char/bits >>) -> - log("character creation"), +char_select_event({char_select_create, Slot, CharBin}) -> + log("character creation ~b", [Slot]), %% check for valid character appearance - %~ << _Name:512, RaceID:8, GenderID:8, _TypeID:8, AppearanceBin:776/bits, _/bits >> = Char, + %~ << _Name:512, RaceID:8, GenderID:8, _TypeID:8, AppearanceBin:776/bits, _/bits >> = CharBin, %~ Race = proplists:get_value(RaceID, [{0, human}, {1, newman}, {2, cast}, {3, beast}]), %~ Gender = proplists:get_value(GenderID, [{0, male}, {1, female}]), %~ Appearance = psu_appearance:binary_to_tuple(Race, AppearanceBin), @@ -168,23 +164,21 @@ char_select_handle(16#0d02, << Number:32/little-unsigned-integer, Char/bits >>) %% end of check, continue doing it wrong past that point for now {ok, User} = egs_user_model:read(get(gid)), _ = file:make_dir(io_lib:format("save/~s", [User#egs_user_model.folder])), - file:write_file(io_lib:format("save/~s/~b-character", [User#egs_user_model.folder, Number]), Char), - file:write_file(io_lib:format("save/~s/~b-character.options", [User#egs_user_model.folder, Number]), << 0:128, 4, 0:56 >>), % default 0 to everything except brightness 4 - char_select_load(Number); + file:write_file(io_lib:format("save/~s/~b-character", [User#egs_user_model.folder, Slot]), CharBin), + file:write_file(io_lib:format("save/~s/~b-character.options", [User#egs_user_model.folder, Slot]), << 0:128, 4, 0:56 >>), % default 0 to everything except brightness 4 + ?MODULE:char_select(); -%% @doc Character selection screen request. -char_select_handle(16#0d06, _) -> +char_select_event({char_select_enter, Slot, _BackToPreviousField}) -> + log("selected character ~b", [Slot]), + char_select_load(Slot); + +char_select_event(char_select_request) -> {ok, User} = egs_user_model:read(get(gid)), send_0d03(data_load(User#egs_user_model.folder, 0), data_load(User#egs_user_model.folder, 1), data_load(User#egs_user_model.folder, 2), data_load(User#egs_user_model.folder, 3)), ?MODULE:char_select(); -%% @doc Silently ignore packet 0818. Gives CPU/GPU information. -char_select_handle(16#0818, _) -> - ?MODULE:char_select(); - -%% @doc Unknown command handler. Do nothing. -char_select_handle(Command, _) -> - log("(char_select) dismissed packet ~4.16.0b", [Command]), +char_select_event({command, Command, Channel, _Data}) -> + log("char_select_event: dismissed command ~4.16.0b channel ~b", [Command, Channel]), ?MODULE:char_select(). %% @doc Load the selected character in the start lobby and start the main game's loop. diff --git a/src/psu/psu_proto.erl b/src/psu/psu_proto.erl index 658e1bd..0d2b55d 100644 --- a/src/psu/psu_proto.erl +++ b/src/psu/psu_proto.erl @@ -202,6 +202,26 @@ parse(Size, 16#0110, Channel, Data) -> _ -> log("unknown 0110 EventID ~p", [EventID]) end; +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}; + 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, @@ -638,6 +658,39 @@ parse(Size, 16#0c0f, Channel, Data) -> ?ASSERT_EQ(VarI, 0), {counter_quest_options_request, CounterID}; +%% @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; + %% @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,