diff --git a/README.md b/README.md index ebe59b27..006bf50e 100644 --- a/README.md +++ b/README.md @@ -294,6 +294,7 @@ Some commands only work on the game server and not on the proxy server. The chat * You'll see in-game messages from the server when you take certain actions, like killing an enemy in BB. * You'll see the rare seed value and floor variations when you join a game. * You'll be placed into the highest available slot in lobbies and games instead of the lowest, unless you're joining a BB solo-mode game. + * You'll be able to join games with any PSO version, not only those for which crossplay is normally supported. Be prepared for client crashes and other client-side brokenness if you do this. Please do not submit any issues for broken behaviors in crossplay, unless the situation is explicitly supported (see the "Cross-version play" section above). * The rest of the commands in this section are enabled on the game server. (They are always enabled on the proxy server.) * `$quest ` (game server only): Load a quest by quest number. Can be used to load battle or challenge quests with only one player present. * `$qcall `: Call a quest function on your client. diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 5dab9574..92d7719c 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -1831,32 +1831,68 @@ static void on_09(shared_ptr c, uint16_t, uint32_t, string& data) { abbreviation_for_episode(game->episode), abbreviation_for_difficulty(game->difficulty), abbreviation_for_mode(game->mode), - name_for_section_id(game->section_id)); + abbreviation_for_section_id(game->section_id)); - bool cheats_enabled = game->check_flag(Lobby::Flag::CHEATS_ENABLED); - bool locked = !game->password.empty(); - if (cheats_enabled && locked) { - info += "$C4Locked$C7, $C6cheats on$C7\n"; - } else if (cheats_enabled) { - info += "$C6Cheats on$C7\n"; - } else if (locked) { - info += "$C4Locked$C7\n"; - } + if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) { + vector flags_tokens; + string quest_name; + if (game->check_flag(Lobby::Flag::CHEATS_ENABLED)) { + flags_tokens.emplace_back("$C6C$C7"); + } + if (game->check_flag(Lobby::Flag::PERSISTENT)) { + flags_tokens.emplace_back("$C6P$C7"); + } + if (!game->password.empty()) { + flags_tokens.emplace_back("$C4L$C7"); + } + if (game->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM)) { + flags_tokens.emplace_back("$C8ST$C7"); + } + if (game->check_flag(Lobby::Flag::SPECTATORS_FORBIDDEN)) { + flags_tokens.emplace_back("$C8NS$C7"); + } + if (game->quest) { + flags_tokens.emplace_back(game->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS) ? "$C3JQ$C7" : "$C3Q$C7"); + quest_name = remove_color(game->quest->name); + } else if (game->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS)) { + flags_tokens.emplace_back("$C3JQ$C7"); + } else if (game->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) { + flags_tokens.emplace_back("$C3Q$C7"); + } else if (game->check_flag(Lobby::Flag::BATTLE_IN_PROGRESS)) { + flags_tokens.emplace_back("$C3B$C7"); + } + info += ("Flags: " + join(flags_tokens, ",") + "\n"); + if (!quest_name.empty()) { + info += ("Q: $C6" + quest_name + "$C7\n"); + } + info += string_printf("Version: %s\n", name_for_enum(game->base_version)); - if (game->quest) { - info += (game->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS)) ? "$C6Quest: " : "$C4Quest: "; - info += remove_color(game->quest->name); - info += "\n"; - } else if (game->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS)) { - info += "$C6Quest in progress\n"; - } else if (game->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) { - info += "$C4Quest in progress\n"; - } else if (game->check_flag(Lobby::Flag::BATTLE_IN_PROGRESS)) { - info += "$C4Battle in progress\n"; - } + } else { + bool cheats_enabled = game->check_flag(Lobby::Flag::CHEATS_ENABLED); + bool locked = !game->password.empty(); + if (cheats_enabled && locked) { + info += "$C4Locked$C7, $C6cheats on$C7\n"; + } else if (cheats_enabled) { + info += "$C6Cheats on$C7\n"; + } else if (locked) { + info += "$C4Locked$C7\n"; + } - if (game->check_flag(Lobby::Flag::SPECTATORS_FORBIDDEN)) { - info += "$C4View Battle forbidden\n"; + if (game->quest) { + info += (game->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS)) ? "$C6Quest: " : "$C4Quest: "; + info += remove_color(game->quest->name); + info += "\n"; + } else if (game->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS)) { + info += "$C6Quest in progress\n"; + } else if (game->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) { + info += "$C4Quest in progress\n"; + } else if (game->check_flag(Lobby::Flag::BATTLE_IN_PROGRESS)) { + info += "$C4Battle in progress\n"; + } + + if (game->check_flag(Lobby::Flag::SPECTATORS_FORBIDDEN)) { + info += "$C4View Battle forbidden\n"; + } } strip_trailing_whitespace(info); @@ -2349,7 +2385,7 @@ static void on_10(shared_ptr c, uint16_t, uint32_t, string& data) { send_lobby_message_box(c, "$C6You cannot join this\ngame because it is\nfull."); break; } - if (!game->version_is_allowed(c->version())) { + if (!game->version_is_allowed(c->version()) && !c->config.check_flag(Client::Flag::DEBUG_ENABLED)) { send_lobby_message_box(c, "$C6You cannot join this\ngame because it is\nfor a different\nversion of PSO."); break; } diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index d11c1b8b..6920da8d 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -530,6 +530,7 @@ static void on_sync_joining_player_disp_and_inventory( out_cmd.num_items = xb_cmd->num_items; out_cmd.items = xb_cmd->items; } else if (bb_cmd) { + out_cmd.base.visual.name.encode(bb_cmd->name.decode(c->language()), target->language()); out_cmd.stats = bb_cmd->stats; out_cmd.num_items = bb_cmd->num_items; out_cmd.items = bb_cmd->items; @@ -562,6 +563,7 @@ static void on_sync_joining_player_disp_and_inventory( out_cmd.items = xb_cmd->items; out_cmd.floor = xb_cmd->floor; } else if (bb_cmd) { + out_cmd.base.visual.name.encode(bb_cmd->name.decode(c->language()), target->language()); out_cmd.stats = bb_cmd->stats; out_cmd.num_items = bb_cmd->num_items; out_cmd.items = bb_cmd->items; @@ -597,6 +599,7 @@ static void on_sync_joining_player_disp_and_inventory( out_cmd.items = gc_cmd->items; out_cmd.floor = gc_cmd->floor; } else if (bb_cmd) { + out_cmd.base.visual.name.encode(bb_cmd->name.decode(c->language()), target->language()); out_cmd.stats = bb_cmd->stats; out_cmd.num_items = bb_cmd->num_items; out_cmd.items = bb_cmd->items; @@ -616,6 +619,7 @@ static void on_sync_joining_player_disp_and_inventory( } else { G_SyncPlayerDispAndInventory_BB_6x70 out_cmd; out_cmd.base = *base; + out_cmd.base.visual.name.encode(string_printf("%10" PRIu32, c->license->serial_number), target->language()); out_cmd.name.encode(base->visual.name.decode(c->language()), target->language()); if (v2_cmd) { out_cmd.stats = v2_cmd->stats; diff --git a/src/SendCommands.cc b/src/SendCommands.cc index abc19729..1bcf1438 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -1354,9 +1354,10 @@ void send_game_menu_t( } set, bool (*)(const shared_ptr&, const shared_ptr&)> games(Lobby::compare_shared); + bool client_has_debug = c->config.check_flag(Client::Flag::DEBUG_ENABLED); for (shared_ptr l : s->all_lobbies()) { if (l->is_game() && - l->version_is_allowed(c->version()) && + (client_has_debug || l->version_is_allowed(c->version())) && (l->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM) == is_spectator_team_list) && (!show_tournaments_only || l->tournament_match)) { games.emplace(l); @@ -2667,14 +2668,15 @@ void send_give_experience(shared_ptr c, uint32_t amount) { } void send_set_exp_multiplier(shared_ptr l) { - if (l->base_version != Version::BB_V4) { - throw logic_error("6xDD can only be sent to BB clients"); - } if (!l->is_game()) { throw logic_error("6xDD can only be sent in games (not in lobbies)"); } G_SetEXPMultiplier_BB_6xDD cmd = {{0xDD, sizeof(G_SetEXPMultiplier_BB_6xDD) / 4, (l->mode == GameMode::CHALLENGE) ? 1 : l->base_exp_multiplier}}; - send_command_t(l, 0x60, 0x00, cmd); + for (auto lc : l->clients) { + if (lc && (lc->version() == Version::BB_V4)) { + send_command_t(l, 0x60, 0x00, cmd); + } + } } void send_rare_enemy_index_list(shared_ptr c, const vector& indexes) { diff --git a/src/StaticGameData.cc b/src/StaticGameData.cc index 2b8fe7f3..e0895e2e 100644 --- a/src/StaticGameData.cc +++ b/src/StaticGameData.cc @@ -109,6 +109,9 @@ static const array section_id_to_name = { "Viridia", "Greennill", "Skyly", "Bluefull", "Purplenum", "Pinkal", "Redria", "Oran", "Yellowboze", "Whitill"}; +static const array section_id_to_abbreviation = { + "Vir", "Grn", "Sky", "Blu", "Prp", "Pnk", "Red", "Orn", "Ylw", "Wht"}; + const unordered_map name_to_section_id({ {"viridia", 0}, {"greennill", 1}, @@ -221,6 +224,14 @@ const vector npc_id_to_name({"ninja", "rico", "sonic", "knuckles", "tail const unordered_map name_to_npc_id = { {"ninja", 0}, {"rico", 1}, {"sonic", 2}, {"knuckles", 3}, {"tails", 4}, {"flowen", 5}, {"elly", 6}}; +const char* abbreviation_for_section_id(uint8_t section_id) { + if (section_id < section_id_to_abbreviation.size()) { + return section_id_to_abbreviation[section_id]; + } else { + return ""; + } +} + const char* name_for_section_id(uint8_t section_id) { if (section_id < section_id_to_name.size()) { return section_id_to_name[section_id]; diff --git a/src/StaticGameData.hh b/src/StaticGameData.hh index 5181e6c9..87861eaf 100644 --- a/src/StaticGameData.hh +++ b/src/StaticGameData.hh @@ -40,6 +40,7 @@ extern const std::unordered_map name_to_tech_id; const std::string& name_for_technique(uint8_t tech); uint8_t technique_for_name(const std::string& name); +const char* abbreviation_for_section_id(uint8_t section_id); const char* name_for_section_id(uint8_t section_id); uint8_t section_id_for_name(const std::string& name);