diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index 4d3d2996..7c08538f 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -362,6 +362,14 @@ static void check_is_game(shared_ptr l, bool is_game) { } } +static void check_is_ep3(shared_ptr c, bool is_ep3) { + if (!!(c->flags & ClientFlag::Episode3Games) != is_ep3) { + throw precondition_failed(is_ep3 ? + u"$C6This command can only\nbe used in Episode 3." : + u"$C6This command cannot\nbe used in Episode 3."); + } +} + static void check_cheats_enabled(shared_ptr l) { if (!(l->flags & LobbyFlag::CheatsEnabled)) { throw precondition_failed(u"$C6This command can\nonly be used in\ncheat mode."); @@ -817,6 +825,14 @@ static void command_warp(shared_ptr, shared_ptr l, send_warp(c, area); } +static void command_song(shared_ptr, shared_ptr, + shared_ptr c, const char16_t* args) { + check_is_ep3(c, true); + + uint32_t song = stoul(encode_sjis(args), nullptr, 0); + send_ep3_change_music(c, song); +} + static void command_infinite_hp(shared_ptr, shared_ptr l, shared_ptr c, const char16_t*) { check_is_game(l, true); @@ -890,10 +906,11 @@ static const unordered_map chat_commands({ {u"item" , {command_item , u"Usage:\nitem "}}, {u"kick" , {command_kick , u"Usage:\nkick "}}, {u"li" , {command_lobby_info , u"Usage:\nli"}}, - {u"password" , {command_password , u"Usage:\nlock [password]\nomit password to\nunlock game"}}, {u"maxlevel" , {command_max_level , u"Usage:\nmax_level "}}, {u"minlevel" , {command_min_level , u"Usage:\nmin_level "}}, + {u"password" , {command_password , u"Usage:\nlock [password]\nomit password to\nunlock game"}}, {u"silence" , {command_silence , u"Usage:\nsilence "}}, + {u"song" , {command_song , u"Usage:\nsong "}}, {u"type" , {command_lobby_type , u"Usage:\ntype "}}, {u"warp" , {command_warp , u"Usage:\nwarp "}}, }); diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 2fbe148d..30cf62f8 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -463,18 +463,27 @@ void process_server_time_request(shared_ptr, shared_ptr c, void process_ep3_jukebox(shared_ptr s, shared_ptr c, uint16_t command, uint32_t, uint16_t size, const void* data) { - struct Cmd { - uint32_t unknown[3]; // should be FFFFFFFF 00000000 + struct InputCmd { + uint32_t transaction_num; + uint32_t value; + uint32_t unknown_token; }; - check_size(size, sizeof(Cmd)); - const auto* cmd = reinterpret_cast(data); + struct OutputCmd { + uint32_t remaining_meseta; + uint32_t unknown; + uint32_t unknown_token; + }; + check_size(size, sizeof(InputCmd)); + const auto* in_cmd = reinterpret_cast(data); + + OutputCmd out_cmd = {1000000, 0x80E8, in_cmd->unknown_token}; auto l = s->find_lobby(c->lobby_id); if (!l || !(l->flags & LobbyFlag::Episode3)) { return; } - send_command(l, command, 0x03, cmd); + send_command(c, command, 0x03, &out_cmd, sizeof(out_cmd)); } void process_ep3_menu_challenge(shared_ptr, shared_ptr c, diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index d4f34692..e6dff8e4 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -869,6 +869,15 @@ static void process_subcommand_forward_check_size_game(shared_ptr, forward_subcommand(l, c, command, flag, p, count); } +static void process_subcommand_forward_check_size_ep3_lobby(shared_ptr, + shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, + const PSOSubcommand* p, size_t count) { + if (!(l->flags & LobbyFlag::Episode3) || l->is_game() || (p->byte[1] != count)) { + return; + } + forward_subcommand(l, c, command, flag, p, count); +} + static void process_subcommand_invalid(shared_ptr, shared_ptr, shared_ptr, uint8_t command, uint8_t flag, const PSOSubcommand* p, size_t count) { @@ -951,7 +960,7 @@ subcommand_handler_t subcommand_handlers[0x100] = { process_subcommand_drop_item, process_subcommand_unimplemented, process_subcommand_forward_check_size, - process_subcommand_forward_check_size_game, + process_subcommand_forward_check_size, process_subcommand_unimplemented, process_subcommand_hit_by_monster, // 30 @@ -1106,7 +1115,7 @@ subcommand_handler_t subcommand_handlers[0x100] = { process_subcommand_unimplemented, process_subcommand_bank_action, process_subcommand_unimplemented, - process_subcommand_unimplemented, + process_subcommand_forward_check_size_ep3_lobby, // C0 process_subcommand_unimplemented, process_subcommand_unimplemented, diff --git a/src/SendCommands.cc b/src/SendCommands.cc index d1738166..57160a31 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -1847,7 +1847,6 @@ void send_player_stats_change(shared_ptr l, shared_ptr c, send_command(l, 0x60, 0x00, subs); } -// sends a player to the given area. void send_warp(shared_ptr c, uint32_t area) { PSOSubcommand cmds[2]; cmds[0].byte[0] = 0x94; @@ -1858,6 +1857,35 @@ void send_warp(shared_ptr c, uint32_t area) { send_command(c, 0x62, c->lobby_client_id, cmds, 8); } +void send_ep3_change_music(shared_ptr c, uint32_t song) { + PSOSubcommand cmds[2]; + cmds[0].byte[0] = 0xBF; + cmds[0].byte[1] = 0x02; + cmds[0].byte[2] = c->lobby_client_id; + cmds[0].byte[3] = 0x00; + cmds[1].dword = song; + send_command(c, 0x60, 0x00, cmds, 8); +} + +void send_set_player_visibility(shared_ptr l, shared_ptr c, + bool visible) { + PSOSubcommand cmd; + cmd.byte[0] = visible ? 0x23 : 0x22; + cmd.byte[1] = 0x01; + cmd.byte[2] = c->lobby_client_id; + cmd.byte[3] = 0x00; + send_command(l, 0x60, 0x00, &cmd, 4); +} + +void send_revive_player(shared_ptr l, shared_ptr c) { + PSOSubcommand cmd; + cmd.byte[0] = 0x31; + cmd.byte[1] = 0x01; + cmd.byte[2] = c->lobby_client_id; + cmd.byte[3] = 0x00; + send_command(l, 0x60, 0x00, &cmd, 4); +} + //////////////////////////////////////////////////////////////////////////////// diff --git a/src/SendCommands.hh b/src/SendCommands.hh index 65d3e30b..0ad5e9f7 100644 --- a/src/SendCommands.hh +++ b/src/SendCommands.hh @@ -158,6 +158,11 @@ void send_player_stats_change(std::shared_ptr l, std::shared_ptr PlayerStatsChange which, uint32_t amount); void send_warp(std::shared_ptr c, uint32_t area); +void send_ep3_change_music(std::shared_ptr c, uint32_t song); +void send_set_player_visibility(std::shared_ptr l, + std::shared_ptr c, bool visible); +void send_revive_player(std::shared_ptr l, std::shared_ptr c); + void send_drop_item(std::shared_ptr l, const ItemData& item, bool from_enemy, uint8_t area, float x, float y, uint16_t request_id); void send_drop_stacked_item(std::shared_ptr l, const ItemData& item,