add cross-play options

This commit is contained in:
Martin Michelsen
2023-10-07 20:39:30 -07:00
parent 07b1e9cde3
commit abd87054ac
12 changed files with 143 additions and 85 deletions
+5 -7
View File
@@ -359,11 +359,9 @@ static void server_command_exit(shared_ptr<Client> c, const std::u16string&) {
send_message_box(c, u""); send_message_box(c, u"");
} }
const auto& port_name = version_to_login_port_name.at( const auto& port_name = version_to_login_port_name.at(static_cast<size_t>(c->version()));
static_cast<size_t>(c->version()));
auto s = c->require_server_state(); auto s = c->require_server_state();
send_reconnect(c, s->connect_address_for_client(c), send_reconnect(c, s->connect_address_for_client(c), s->name_to_port_config.at(port_name)->port);
s->name_to_port_config.at(port_name)->port);
} }
} }
@@ -597,7 +595,7 @@ static void server_command_playrec(shared_ptr<Client> c, const std::u16string& a
string file_path = file_path_for_recording(args, c->license->serial_number); string file_path = file_path_for_recording(args, c->license->serial_number);
auto s = c->require_server_state(); auto s = c->require_server_state();
uint32_t flags = Lobby::Flag::NON_V1_ONLY | Lobby::Flag::IS_SPECTATOR_TEAM; uint32_t flags = Lobby::Flag::IS_SPECTATOR_TEAM;
string filename = encode_sjis(args); string filename = encode_sjis(args);
if (filename[0] == '!') { if (filename[0] == '!') {
flags |= Lobby::Flag::START_BATTLE_PLAYER_IMMEDIATELY; flags |= Lobby::Flag::START_BATTLE_PLAYER_IMMEDIATELY;
@@ -614,8 +612,8 @@ static void server_command_playrec(shared_ptr<Client> c, const std::u16string& a
shared_ptr<Episode3::BattleRecord> record(new Episode3::BattleRecord(data)); shared_ptr<Episode3::BattleRecord> record(new Episode3::BattleRecord(data));
shared_ptr<Episode3::BattleRecordPlayer> battle_player( shared_ptr<Episode3::BattleRecordPlayer> battle_player(
new Episode3::BattleRecordPlayer(record, s->game_server->get_base())); new Episode3::BattleRecordPlayer(record, s->game_server->get_base()));
auto game = create_game_generic(s, c, args.c_str(), u"", Episode::EP3, GameMode::NORMAL, auto game = create_game_generic(
0, flags, nullptr, battle_player); s, c, args, u"", Episode::EP3, GameMode::NORMAL, 0, flags, false, nullptr, battle_player);
if (game) { if (game) {
s->change_client_lobby(c, game); s->change_client_lobby(c, game);
c->flags |= Client::Flag::LOADING; c->flags |= Client::Flag::LOADING;
+2 -1
View File
@@ -17,7 +17,8 @@ Lobby::Lobby(shared_ptr<ServerState> s, uint32_t id)
min_level(0), min_level(0),
max_level(0xFFFFFFFF), max_level(0xFFFFFFFF),
next_game_item_id(0x00810000), next_game_item_id(0x00810000),
version(GameVersion::GC), base_version(GameVersion::GC),
allowed_versions(0xFFFF),
section_id(0), section_id(0),
episode(Episode::NONE), episode(Episode::NONE),
mode(GameMode::NORMAL), mode(GameMode::NORMAL),
+14 -3
View File
@@ -27,8 +27,7 @@ struct ServerState;
struct Lobby : public std::enable_shared_from_this<Lobby> { struct Lobby : public std::enable_shared_from_this<Lobby> {
enum Flag { enum Flag {
GAME = 0x00000001, GAME = 0x00000001,
NON_V1_ONLY = 0x00000002, // DC NTE and DCv1 not allowed PERSISTENT = 0x00000002,
PERSISTENT = 0x00000004,
// Flags used only for games // Flags used only for games
CHEATS_ENABLED = 0x00000100, CHEATS_ENABLED = 0x00000100,
@@ -45,6 +44,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
// Flags used only for lobbies // Flags used only for lobbies
PUBLIC = 0x01000000, PUBLIC = 0x01000000,
DEFAULT = 0x02000000, DEFAULT = 0x02000000,
V2_AND_LATER = 0x04000000, // Lobby does not appear on v1
}; };
std::weak_ptr<ServerState> server_state; std::weak_ptr<ServerState> server_state;
@@ -69,7 +69,11 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
parray<le_uint32_t, 0x20> variations; parray<le_uint32_t, 0x20> variations;
// Game config // Game config
GameVersion version; GameVersion base_version;
// Bits in allowed_versions specify who is allowed to join this game. The
// bits are indexed as (1 << version), where version is a value from the
// QuestScriptVersion enum.
uint16_t allowed_versions;
uint8_t section_id; uint8_t section_id;
Episode episode; Episode episode;
GameMode mode; GameMode mode;
@@ -127,6 +131,13 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
return this->episode == Episode::EP3; return this->episode == Episode::EP3;
} }
inline bool version_is_allowed(QuestScriptVersion v) const {
return this->allowed_versions & (1 << static_cast<size_t>(v));
}
inline void allow_version(QuestScriptVersion v) {
this->allowed_versions |= (1 << static_cast<size_t>(v));
}
void reassign_leader_on_client_departure(size_t leaving_client_id); void reassign_leader_on_client_departure(size_t leaving_client_id);
size_t count_clients() const; size_t count_clients() const;
bool any_client_loading() const; bool any_client_loading() const;
+1 -1
View File
@@ -1895,7 +1895,7 @@ int main(int argc, char** argv) {
shared_ptr<DNSServer> dns_server; shared_ptr<DNSServer> dns_server;
if (state->dns_server_port && (behavior != Behavior::REPLAY_LOG)) { if (state->dns_server_port && (behavior != Behavior::REPLAY_LOG)) {
config_log.info("Starting DNS server"); config_log.info("Starting DNS server on port %hu", state->dns_server_port);
dns_server.reset(new DNSServer(base, state->local_address, dns_server.reset(new DNSServer(base, state->local_address,
state->external_address)); state->external_address));
dns_server->listen("", state->dns_server_port); dns_server->listen("", state->dns_server_port);
+3
View File
@@ -27,6 +27,9 @@ void PlayerDispDataDCPCV3::enforce_v2_limits() {
this->visual.char_class = 5; // HUcaseal -> RAcaseal this->visual.char_class = 5; // HUcaseal -> RAcaseal
} }
// V1/V2 has fewer costumes, so substitute them here too
this->visual.costume %= 9;
// If the player is somehow still not a valid class, make them appear as the // If the player is somehow still not a valid class, make them appear as the
// "ninja" NPC // "ninja" NPC
if (this->visual.char_class > 8) { if (this->visual.char_class > 8) {
+64 -29
View File
@@ -185,7 +185,7 @@ static void send_main_menu(shared_ptr<Client> c) {
const auto& l = it.second; const auto& l = it.second;
if (l->is_game()) { if (l->is_game()) {
num_games++; num_games++;
if (l->version == c->version() && if (l->version_is_allowed(c->quest_version()) &&
(!l->is_ep3() == !(c->flags & Client::Flag::IS_EPISODE_3))) { (!l->is_ep3() == !(c->flags & Client::Flag::IS_EPISODE_3))) {
num_compatible_games++; num_compatible_games++;
} }
@@ -1119,11 +1119,8 @@ static bool start_ep3_battle_table_game_if_ready(shared_ptr<Lobby> l, int16_t ta
auto c = game_clients.begin()->second; auto c = game_clients.begin()->second;
auto s = c->require_server_state(); auto s = c->require_server_state();
uint32_t flags = Lobby::Flag::NON_V1_ONLY;
u16string name = tourn ? decode_sjis(tourn->get_name()) : u"<BattleTable>"; u16string name = tourn ? decode_sjis(tourn->get_name()) : u"<BattleTable>";
auto game = create_game_generic( auto game = create_game_generic(s, c, name, u"", Episode::EP3);
s, c, name, u"", Episode::EP3,
GameMode::NORMAL, 0, flags);
if (!game) { if (!game) {
return false; return false;
} }
@@ -1914,10 +1911,9 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, const string& data)
send_lobby_message_box(c, u"$C6You cannot join this\ngame because it is\nfull."); send_lobby_message_box(c, u"$C6You cannot join this\ngame because it is\nfull.");
break; break;
} }
if ((game->version != c->version()) || if (!game->version_is_allowed(c->quest_version()) ||
(!game->is_ep3() != !(c->flags & Client::Flag::IS_EPISODE_3)) || (!game->is_ep3() != !(c->flags & Client::Flag::IS_EPISODE_3)) ||
(!(game->flags & Lobby::Flag::IS_EP3_TRIAL) != !(c->flags & Client::Flag::IS_EP3_TRIAL_EDITION)) || (!(game->flags & Lobby::Flag::IS_EP3_TRIAL) != !(c->flags & Client::Flag::IS_EP3_TRIAL_EDITION))) {
((game->flags & Lobby::Flag::NON_V1_ONLY) && (c->flags & Client::Flag::IS_DC_V1))) {
send_lobby_message_box(c, u"$C6You cannot join this\ngame because it is\nfor a different\nversion of PSO."); send_lobby_message_box(c, u"$C6You cannot join this\ngame because it is\nfor a different\nversion of PSO.");
break; break;
} }
@@ -2375,7 +2371,7 @@ static void on_AC_V3_BB(shared_ptr<Client> c, uint16_t, uint32_t, const string&
if (c->flags & Client::Flag::LOADING_RUNNING_QUEST) { if (c->flags & Client::Flag::LOADING_RUNNING_QUEST) {
c->flags &= ~Client::Flag::LOADING_RUNNING_QUEST; c->flags &= ~Client::Flag::LOADING_RUNNING_QUEST;
if (l->version != GameVersion::BB) { if (l->base_version != GameVersion::BB) {
throw logic_error("joinable quest started on non-BB version"); throw logic_error("joinable quest started on non-BB version");
} }
@@ -2395,7 +2391,7 @@ static void on_AC_V3_BB(shared_ptr<Client> c, uint16_t, uint32_t, const string&
} }
if (send_quest_barrier_if_all_clients_ready(l) && if (send_quest_barrier_if_all_clients_ready(l) &&
(l->version == GameVersion::BB) && (l->base_version == GameVersion::BB) &&
l->map && l->map &&
l->quest) { l->quest) {
auto dat_contents = prs_decompress(*l->quest->dat_contents()); auto dat_contents = prs_decompress(*l->quest->dat_contents());
@@ -3203,6 +3199,7 @@ shared_ptr<Lobby> create_game_generic(
GameMode mode, GameMode mode,
uint8_t difficulty, uint8_t difficulty,
uint32_t flags, uint32_t flags,
bool allow_v1,
shared_ptr<Lobby> watched_lobby, shared_ptr<Lobby> watched_lobby,
shared_ptr<Episode3::BattleRecordPlayer> battle_player) { shared_ptr<Episode3::BattleRecordPlayer> battle_player) {
@@ -3276,7 +3273,54 @@ shared_ptr<Lobby> create_game_generic(
(is_ep3_trial ? Lobby::Flag::IS_EP3_TRIAL : 0) | (is_ep3_trial ? Lobby::Flag::IS_EP3_TRIAL : 0) |
((s->cheat_mode_behavior == ServerState::CheatModeBehavior::ON_BY_DEFAULT) ? Lobby::Flag::CHEATS_ENABLED : 0); ((s->cheat_mode_behavior == ServerState::CheatModeBehavior::ON_BY_DEFAULT) ? Lobby::Flag::CHEATS_ENABLED : 0);
game->password = password; game->password = password;
game->version = c->version();
game->allowed_versions = 0;
switch (c->quest_version()) {
case QuestScriptVersion::DC_NTE:
game->allow_version(QuestScriptVersion::DC_NTE);
break;
case QuestScriptVersion::DC_V1:
game->allow_version(QuestScriptVersion::DC_V1);
game->allow_version(QuestScriptVersion::DC_V2);
if (s->allow_dc_pc_games) {
game->allow_version(QuestScriptVersion::PC_V2);
}
break;
case QuestScriptVersion::DC_V2:
case QuestScriptVersion::PC_V2:
if (allow_v1 && (difficulty <= 2)) {
game->allow_version(QuestScriptVersion::DC_V1);
}
game->allow_version(QuestScriptVersion::DC_V2);
if (s->allow_dc_pc_games) {
game->allow_version(QuestScriptVersion::PC_V2);
}
break;
case QuestScriptVersion::GC_NTE:
game->allow_version(QuestScriptVersion::GC_NTE);
break;
case QuestScriptVersion::GC_V3:
game->allow_version(QuestScriptVersion::GC_V3);
if (s->allow_gc_xb_games) {
game->allow_version(QuestScriptVersion::XB_V3);
}
break;
case QuestScriptVersion::XB_V3:
game->allow_version(QuestScriptVersion::XB_V3);
if (s->allow_gc_xb_games) {
game->allow_version(QuestScriptVersion::GC_V3);
}
break;
case QuestScriptVersion::GC_EP3:
game->allow_version(QuestScriptVersion::GC_EP3);
break;
case QuestScriptVersion::BB_V4:
game->allow_version(QuestScriptVersion::BB_V4);
break;
default:
throw logic_error("invalid quest script version");
}
game->section_id = c->options.override_section_id >= 0 game->section_id = c->options.override_section_id >= 0
? c->options.override_section_id ? c->options.override_section_id
: c->game_data.player()->disp.visual.section_id; : c->game_data.player()->disp.visual.section_id;
@@ -3291,7 +3335,7 @@ shared_ptr<Lobby> create_game_generic(
game->battle_player = battle_player; game->battle_player = battle_player;
battle_player->set_lobby(game); battle_player->set_lobby(game);
} }
if (game->version == GameVersion::BB) { if (game->base_version == GameVersion::BB) {
// TODO: Use appropriate restrictions here if in battle mode // TODO: Use appropriate restrictions here if in battle mode
game->item_creator.reset(new ItemCreator( game->item_creator.reset(new ItemCreator(
s->common_item_set, s->common_item_set,
@@ -3326,7 +3370,7 @@ shared_ptr<Lobby> create_game_generic(
generate_variations(game->variations, game->random_crypt, game->episode, is_solo); generate_variations(game->variations, game->random_crypt, game->episode, is_solo);
} }
if (game->version == GameVersion::BB) { if (game->base_version == GameVersion::BB) {
for (size_t x = 0; x < 4; x++) { for (size_t x = 0; x < 4; x++) {
game->next_item_id[x] = (0x00200000 * x) + 0x00010000; game->next_item_id[x] = (0x00200000 * x) + 0x00010000;
} }
@@ -3382,15 +3426,13 @@ static void on_C1_PC(shared_ptr<Client> c, uint16_t, uint32_t, const string& dat
const auto& cmd = check_size_t<C_CreateGame_PC_C1>(data); const auto& cmd = check_size_t<C_CreateGame_PC_C1>(data);
auto s = c->require_server_state(); auto s = c->require_server_state();
uint32_t flags = Lobby::Flag::NON_V1_ONLY;
GameMode mode = GameMode::NORMAL; GameMode mode = GameMode::NORMAL;
if (cmd.battle_mode) { if (cmd.battle_mode) {
mode = GameMode::BATTLE; mode = GameMode::BATTLE;
} else if (cmd.challenge_mode) { } else if (cmd.challenge_mode) {
mode = GameMode::CHALLENGE; mode = GameMode::CHALLENGE;
} }
auto game = create_game_generic( auto game = create_game_generic(s, c, cmd.name, cmd.password, Episode::EP1, mode, cmd.difficulty);
s, c, cmd.name, cmd.password, Episode::EP1, mode, cmd.difficulty, flags);
if (game) { if (game) {
s->change_client_lobby(c, game); s->change_client_lobby(c, game);
c->flags |= Client::Flag::LOADING; c->flags |= Client::Flag::LOADING;
@@ -3405,8 +3447,7 @@ static void on_0C_C1_E7_EC(shared_ptr<Client> c, uint16_t command, uint32_t, con
const auto& cmd = check_size_t<C_CreateGame_DCNTE<char>>(data); const auto& cmd = check_size_t<C_CreateGame_DCNTE<char>>(data);
u16string name = decode_sjis(cmd.name); u16string name = decode_sjis(cmd.name);
u16string password = decode_sjis(cmd.password); u16string password = decode_sjis(cmd.password);
game = create_game_generic( game = create_game_generic(s, c, name, password);
s, c, name.c_str(), password.c_str(), Episode::EP1, GameMode::NORMAL, 0, 0);
} else { } else {
const auto& cmd = check_size_t<C_CreateGame_DC_V3_0C_C1_Ep3_EC>(data); const auto& cmd = check_size_t<C_CreateGame_DC_V3_0C_C1_Ep3_EC>(data);
@@ -3419,16 +3460,13 @@ static void on_0C_C1_E7_EC(shared_ptr<Client> c, uint16_t command, uint32_t, con
Episode episode = Episode::NONE; Episode episode = Episode::NONE;
uint32_t flags = 0; uint32_t flags = 0;
bool allow_v1 = false;
if (c->version() == GameVersion::DC) { if (c->version() == GameVersion::DC) {
if (cmd.episode) { allow_v1 = (cmd.episode == 0);
flags |= Lobby::Flag::NON_V1_ONLY;
}
episode = Episode::EP1; episode = Episode::EP1;
} else if (client_is_ep3) { } else if (client_is_ep3) {
flags |= Lobby::Flag::NON_V1_ONLY;
episode = Episode::EP3; episode = Episode::EP3;
} else { // XB/GC non-Ep3 } else { // XB/GC non-Ep3
flags |= Lobby::Flag::NON_V1_ONLY;
episode = cmd.episode == 2 ? Episode::EP2 : Episode::EP1; episode = cmd.episode == 2 ? Episode::EP2 : Episode::EP1;
} }
@@ -3464,8 +3502,7 @@ static void on_0C_C1_E7_EC(shared_ptr<Client> c, uint16_t command, uint32_t, con
flags |= Lobby::Flag::IS_SPECTATOR_TEAM; flags |= Lobby::Flag::IS_SPECTATOR_TEAM;
} }
game = create_game_generic( game = create_game_generic(s, c, name, password, episode, mode, cmd.difficulty, flags, allow_v1, watched_lobby);
s, c, name.c_str(), password.c_str(), episode, mode, cmd.difficulty, flags, watched_lobby);
if (game && (game->episode == Episode::EP3)) { if (game && (game->episode == Episode::EP3)) {
game->ep3_ex_result_values = s->ep3_default_ex_values; game->ep3_ex_result_values = s->ep3_default_ex_values;
} }
@@ -3481,7 +3518,6 @@ static void on_C1_BB(shared_ptr<Client> c, uint16_t, uint32_t, const string& dat
const auto& cmd = check_size_t<C_CreateGame_BB_C1>(data); const auto& cmd = check_size_t<C_CreateGame_BB_C1>(data);
auto s = c->require_server_state(); auto s = c->require_server_state();
uint32_t flags = Lobby::Flag::NON_V1_ONLY;
GameMode mode = GameMode::NORMAL; GameMode mode = GameMode::NORMAL;
if (cmd.battle_mode) { if (cmd.battle_mode) {
mode = GameMode::BATTLE; mode = GameMode::BATTLE;
@@ -3508,8 +3544,7 @@ static void on_C1_BB(shared_ptr<Client> c, uint16_t, uint32_t, const string& dat
throw runtime_error("invalid episode number"); throw runtime_error("invalid episode number");
} }
auto game = create_game_generic( auto game = create_game_generic(s, c, cmd.name, cmd.password, episode, mode, cmd.difficulty);
s, c, cmd.name, cmd.password, episode, mode, cmd.difficulty, flags);
if (game) { if (game) {
s->change_client_lobby(c, game); s->change_client_lobby(c, game);
c->flags |= Client::Flag::LOADING; c->flags |= Client::Flag::LOADING;
@@ -3539,7 +3574,7 @@ static void on_6F(shared_ptr<Client> c, uint16_t, uint32_t, const string& data)
c->flags &= (~Client::Flag::LOADING); c->flags &= (~Client::Flag::LOADING);
send_resume_game(l, c); send_resume_game(l, c);
if (l->version == GameVersion::BB) { if (l->base_version == GameVersion::BB) {
send_set_exp_multiplier(l); send_set_exp_multiplier(l);
} }
send_server_time(c); send_server_time(c);
+6 -5
View File
@@ -8,11 +8,12 @@ std::shared_ptr<Lobby> create_game_generic(
std::shared_ptr<ServerState> s, std::shared_ptr<ServerState> s,
std::shared_ptr<Client> c, std::shared_ptr<Client> c,
const std::u16string& name, const std::u16string& name,
const std::u16string& password, const std::u16string& password = u"",
Episode episode, Episode episode = Episode::EP1,
GameMode mode, GameMode mode = GameMode::NORMAL,
uint8_t difficulty, uint8_t difficulty = 0,
uint32_t flags, uint32_t flags = 0,
bool allow_v1 = false,
std::shared_ptr<Lobby> watched_lobby = nullptr, std::shared_ptr<Lobby> watched_lobby = nullptr,
std::shared_ptr<Episode3::BattleRecordPlayer> battle_player = nullptr); std::shared_ptr<Episode3::BattleRecordPlayer> battle_player = nullptr);
+23 -23
View File
@@ -697,7 +697,7 @@ static void on_drop_partial_stack_t(shared_ptr<Client> c, uint8_t command, uint8
if (!l->is_game()) { if (!l->is_game()) {
return; return;
} }
if (l->version == GameVersion::BB) { if (l->base_version == GameVersion::BB) {
return; return;
} }
@@ -738,7 +738,7 @@ static void on_drop_partial_stack(shared_ptr<Client> c, uint8_t command, uint8_t
static void on_drop_partial_stack_bb(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) { static void on_drop_partial_stack_bb(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
auto l = c->require_lobby(); auto l = c->require_lobby();
if (l->version == GameVersion::BB) { if (l->base_version == GameVersion::BB) {
const auto& cmd = check_size_t<G_SplitStackedItem_BB_6xC3>(data, size); const auto& cmd = check_size_t<G_SplitStackedItem_BB_6xC3>(data, size);
if (!l->is_game() || (cmd.header.client_id != c->lobby_client_id)) { if (!l->is_game() || (cmd.header.client_id != c->lobby_client_id)) {
@@ -791,7 +791,7 @@ static void on_buy_shop_item(shared_ptr<Client> c, uint8_t command, uint8_t flag
if (!l->is_game() || (cmd.header.client_id != c->lobby_client_id)) { if (!l->is_game() || (cmd.header.client_id != c->lobby_client_id)) {
return; return;
} }
if (l->version == GameVersion::BB) { if (l->base_version == GameVersion::BB) {
return; return;
} }
@@ -828,7 +828,7 @@ static void on_box_or_enemy_item_drop_t(shared_ptr<Client> c, uint8_t command, u
if (!l->is_game() || (c->lobby_client_id != l->leader_id)) { if (!l->is_game() || (c->lobby_client_id != l->leader_id)) {
return; return;
} }
if (l->version == GameVersion::BB) { if (l->base_version == GameVersion::BB) {
return; return;
} }
@@ -870,7 +870,7 @@ static void on_pick_up_item(shared_ptr<Client> c, uint8_t command, uint8_t flag,
if (!l->is_game()) { if (!l->is_game()) {
return; return;
} }
if (l->version == GameVersion::BB) { if (l->base_version == GameVersion::BB) {
// BB clients should never send this; only the server should send this // BB clients should never send this; only the server should send this
return; return;
} }
@@ -900,7 +900,7 @@ static void on_pick_up_item(shared_ptr<Client> c, uint8_t command, uint8_t flag,
static void on_pick_up_item_request(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) { static void on_pick_up_item_request(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
// This is handled by the server on BB, and by the leader on other versions // This is handled by the server on BB, and by the leader on other versions
auto l = c->require_lobby(); auto l = c->require_lobby();
if (l->version == GameVersion::BB) { if (l->base_version == GameVersion::BB) {
auto& cmd = check_size_t<G_PickUpItemRequest_6x5A>(data, size); auto& cmd = check_size_t<G_PickUpItemRequest_6x5A>(data, size);
if (!l->is_game() || (cmd.header.client_id != c->lobby_client_id)) { if (!l->is_game() || (cmd.header.client_id != c->lobby_client_id)) {
@@ -946,7 +946,7 @@ static void on_equip_unequip_item(shared_ptr<Client> c, uint8_t command, uint8_t
} else { // Unequip } else { // Unequip
c->game_data.player()->inventory.items[index].flags &= 0xFFFFFFF7; c->game_data.player()->inventory.items[index].flags &= 0xFFFFFFF7;
} }
} else if (l->version == GameVersion::BB) { } else if (l->base_version == GameVersion::BB) {
throw logic_error("item tracking not enabled in BB game"); throw logic_error("item tracking not enabled in BB game");
} }
@@ -1021,7 +1021,7 @@ static void on_feed_mag(
// a 6x29 immediately after to destroy the fed item. So on BB, we should // a 6x29 immediately after to destroy the fed item. So on BB, we should
// remove the fed item here, but on other versions, we allow the following // remove the fed item here, but on other versions, we allow the following
// 6x29 command to do that. // 6x29 command to do that.
if (l->version == GameVersion::BB) { if (l->base_version == GameVersion::BB) {
c->game_data.player()->remove_item(cmd.fed_item_id, 1, false); c->game_data.player()->remove_item(cmd.fed_item_id, 1, false);
} }
@@ -1049,7 +1049,7 @@ static void on_open_shop_bb_or_ep3_battle_subs(shared_ptr<Client> c, uint8_t com
} else { } else {
const auto& cmd = check_size_t<G_ShopContentsRequest_BB_6xB5>(data, size); const auto& cmd = check_size_t<G_ShopContentsRequest_BB_6xB5>(data, size);
if ((l->version == GameVersion::BB) && l->is_game()) { if ((l->base_version == GameVersion::BB) && l->is_game()) {
if (!l->item_creator) { if (!l->item_creator) {
throw logic_error("item creator missing from BB game"); throw logic_error("item creator missing from BB game");
} }
@@ -1081,16 +1081,16 @@ static void on_open_shop_bb_or_ep3_battle_subs(shared_ptr<Client> c, uint8_t com
static void on_open_bank_bb_or_card_trade_counter_ep3(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) { static void on_open_bank_bb_or_card_trade_counter_ep3(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
auto l = c->require_lobby(); auto l = c->require_lobby();
if ((l->version == GameVersion::BB) && l->is_game()) { if ((l->base_version == GameVersion::BB) && l->is_game()) {
send_bank(c); send_bank(c);
} else if ((l->version == GameVersion::GC) && l->is_ep3()) { } else if ((l->base_version == GameVersion::GC) && l->is_ep3()) {
forward_subcommand(c, command, flag, data, size); forward_subcommand(c, command, flag, data, size);
} }
} }
static void on_ep3_private_word_select_bb_bank_action(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) { static void on_ep3_private_word_select_bb_bank_action(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
auto l = c->require_lobby(); auto l = c->require_lobby();
if (l->version == GameVersion::BB) { if (l->base_version == GameVersion::BB) {
const auto& cmd = check_size_t<G_BankAction_BB_6xBD>(data, size); const auto& cmd = check_size_t<G_BankAction_BB_6xBD>(data, size);
if (!l->is_game()) { if (!l->is_game()) {
@@ -1141,7 +1141,7 @@ static void on_ep3_private_word_select_bb_bank_action(shared_ptr<Client> c, uint
static void on_sort_inventory_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void* data, size_t size) { static void on_sort_inventory_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void* data, size_t size) {
auto l = c->require_lobby(); auto l = c->require_lobby();
if (l->version == GameVersion::BB) { if (l->base_version == GameVersion::BB) {
const auto& cmd = check_size_t<G_SortInventory_BB_6xC4>(data, size); const auto& cmd = check_size_t<G_SortInventory_BB_6xC4>(data, size);
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
@@ -1178,7 +1178,7 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
// If the game is not BB, forward the request to the leader (if drops are // If the game is not BB, forward the request to the leader (if drops are
// enabled, or just ignore it) instead of generating the item drop command // enabled, or just ignore it) instead of generating the item drop command
if (l->version != GameVersion::BB) { if (l->base_version != GameVersion::BB) {
if (l->flags & Lobby::Flag::DROPS_ENABLED) { if (l->flags & Lobby::Flag::DROPS_ENABLED) {
forward_subcommand(c, command, flag, data, size); forward_subcommand(c, command, flag, data, size);
} }
@@ -1304,7 +1304,7 @@ static void on_set_quest_flag(shared_ptr<Client> c, uint8_t command, uint8_t fla
static void on_enemy_hit(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) { static void on_enemy_hit(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
auto l = c->require_lobby(); auto l = c->require_lobby();
if (l->version == GameVersion::BB) { if (l->base_version == GameVersion::BB) {
const auto& cmd = check_size_t<G_EnemyHitByPlayer_6x0A>(data, size); const auto& cmd = check_size_t<G_EnemyHitByPlayer_6x0A>(data, size);
if (!l->is_game()) { if (!l->is_game()) {
@@ -1333,7 +1333,7 @@ static void on_enemy_hit(shared_ptr<Client> c, uint8_t command, uint8_t flag, co
static void on_charge_attack_bb(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) { static void on_charge_attack_bb(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
auto l = c->require_lobby(); auto l = c->require_lobby();
if (l->version != GameVersion::BB) { if (l->base_version != GameVersion::BB) {
throw runtime_error("BB-only command sent in non-BB game"); throw runtime_error("BB-only command sent in non-BB game");
} }
@@ -1375,7 +1375,7 @@ static void on_steal_exp_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void*
auto s = c->require_server_state(); auto s = c->require_server_state();
auto l = c->require_lobby(); auto l = c->require_lobby();
if (l->version != GameVersion::BB) { if (l->base_version != GameVersion::BB) {
throw runtime_error("BB-only command sent in non-BB game"); throw runtime_error("BB-only command sent in non-BB game");
} }
if (!l->map) { if (!l->map) {
@@ -1413,7 +1413,7 @@ static void on_enemy_killed_bb(shared_ptr<Client> c, uint8_t command, uint8_t fl
auto s = c->require_server_state(); auto s = c->require_server_state();
auto l = c->require_lobby(); auto l = c->require_lobby();
if (l->version != GameVersion::BB) { if (l->base_version != GameVersion::BB) {
throw runtime_error("BB-only command sent in non-BB game"); throw runtime_error("BB-only command sent in non-BB game");
} }
@@ -1574,7 +1574,7 @@ static void on_destroy_ground_item(shared_ptr<Client> c, uint8_t command, uint8_
static void on_identify_item_bb(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) { static void on_identify_item_bb(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
auto l = c->require_lobby(); auto l = c->require_lobby();
if (l->version == GameVersion::BB) { if (l->base_version == GameVersion::BB) {
const auto& cmd = check_size_t<G_IdentifyItemRequest_6xB8>(data, size); const auto& cmd = check_size_t<G_IdentifyItemRequest_6xB8>(data, size);
if (!l->is_game()) { if (!l->is_game()) {
return; return;
@@ -1605,7 +1605,7 @@ static void on_identify_item_bb(shared_ptr<Client> c, uint8_t command, uint8_t f
static void on_accept_identify_item_bb(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) { static void on_accept_identify_item_bb(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
auto l = c->require_lobby(); auto l = c->require_lobby();
if (l->version == GameVersion::BB) { if (l->base_version == GameVersion::BB) {
const auto& cmd = check_size_t<G_AcceptItemIdentification_BB_6xBA>(data, size); const auto& cmd = check_size_t<G_AcceptItemIdentification_BB_6xBA>(data, size);
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
@@ -1630,7 +1630,7 @@ static void on_accept_identify_item_bb(shared_ptr<Client> c, uint8_t command, ui
static void on_sell_item_at_shop_bb(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) { static void on_sell_item_at_shop_bb(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
auto s = c->require_server_state(); auto s = c->require_server_state();
auto l = c->require_lobby(); auto l = c->require_lobby();
if (l->version == GameVersion::BB) { if (l->base_version == GameVersion::BB) {
const auto& cmd = check_size_t<G_SellItemAtShop_BB_6xC0>(data, size); const auto& cmd = check_size_t<G_SellItemAtShop_BB_6xC0>(data, size);
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
@@ -1658,7 +1658,7 @@ static void on_sell_item_at_shop_bb(shared_ptr<Client> c, uint8_t command, uint8
static void on_buy_shop_item_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void* data, size_t size) { static void on_buy_shop_item_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void* data, size_t size) {
auto l = c->require_lobby(); auto l = c->require_lobby();
if (l->version == GameVersion::BB) { if (l->base_version == GameVersion::BB) {
const auto& cmd = check_size_t<G_BuyShopItem_BB_6xB7>(data, size); const auto& cmd = check_size_t<G_BuyShopItem_BB_6xB7>(data, size);
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
throw logic_error("item tracking not enabled in BB game"); throw logic_error("item tracking not enabled in BB game");
@@ -1695,7 +1695,7 @@ static void on_buy_shop_item_bb(shared_ptr<Client> c, uint8_t, uint8_t, const vo
static void on_medical_center_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void*, size_t) { static void on_medical_center_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void*, size_t) {
auto l = c->require_lobby(); auto l = c->require_lobby();
if (l->is_game() && (l->version == GameVersion::BB)) { if (l->is_game() && (l->base_version == GameVersion::BB)) {
c->game_data.player()->remove_meseta(10, false); c->game_data.player()->remove_meseta(10, false);
} }
} }
+5 -13
View File
@@ -1188,15 +1188,7 @@ void send_game_menu_t(
if (!l->is_game()) { if (!l->is_game()) {
continue; continue;
} }
if (l->version != c->version()) { if (!l->version_is_allowed(c->quest_version())) {
continue;
}
bool l_is_ep3 = l->is_ep3();
bool c_is_ep3 = !!(c->flags & Client::Flag::IS_EPISODE_3);
if (l_is_ep3 != c_is_ep3) {
continue;
}
if ((c->flags & Client::Flag::IS_DC_V1) && (l->flags & Lobby::Flag::NON_V1_ONLY)) {
continue; continue;
} }
bool l_is_spectator_team = !!(l->flags & Lobby::Flag::IS_SPECTATOR_TEAM); bool l_is_spectator_team = !!(l->flags & Lobby::Flag::IS_SPECTATOR_TEAM);
@@ -1228,10 +1220,10 @@ void send_game_menu_t(
auto& e = entries.emplace_back(); auto& e = entries.emplace_back();
e.menu_id = MenuID::GAME; e.menu_id = MenuID::GAME;
e.game_id = l->lobby_id; e.game_id = l->lobby_id;
e.difficulty_tag = (l_is_ep3 ? 0x0A : (l->difficulty + 0x22)); e.difficulty_tag = (l->is_ep3() ? 0x0A : (l->difficulty + 0x22));
e.num_players = l->count_clients(); e.num_players = l->count_clients();
if (c->version() == GameVersion::DC) { if (c->version() == GameVersion::DC) {
e.episode = (l->flags & Lobby::Flag::NON_V1_ONLY) ? 1 : 0; e.episode = l->version_is_allowed(QuestScriptVersion::DC_V1) ? 1 : 0;
} else { } else {
e.episode = ((c->version() == GameVersion::BB) ? (l->max_clients << 4) : 0) | episode_num; e.episode = ((c->version() == GameVersion::BB) ? (l->max_clients << 4) : 0) | episode_num;
} }
@@ -1367,7 +1359,7 @@ void send_lobby_list(shared_ptr<Client> c) {
if (!(l->flags & Lobby::Flag::DEFAULT)) { if (!(l->flags & Lobby::Flag::DEFAULT)) {
continue; continue;
} }
if ((l->flags & Lobby::Flag::NON_V1_ONLY) && (c->flags & Client::Flag::IS_DC_V1)) { if ((l->flags & Lobby::Flag::V2_AND_LATER) && (c->flags & Client::Flag::IS_DC_V1)) {
continue; continue;
} }
if (l->is_ep3() && !(c->flags & Client::Flag::IS_EPISODE_3)) { if (l->is_ep3() && !(c->flags & Client::Flag::IS_EPISODE_3)) {
@@ -2182,7 +2174,7 @@ void send_give_experience(shared_ptr<Client> c, uint32_t amount) {
} }
void send_set_exp_multiplier(std::shared_ptr<Lobby> l) { void send_set_exp_multiplier(std::shared_ptr<Lobby> l) {
if (l->version != GameVersion::BB) { if (l->base_version != GameVersion::BB) {
throw logic_error("6xDD can only be sent to BB clients"); throw logic_error("6xDD can only be sent to BB clients");
} }
if (!l->is_game()) { if (!l->is_game()) {
+8 -3
View File
@@ -24,6 +24,8 @@ ServerState::ServerState(const char* config_filename, bool is_replay)
ip_stack_debug(false), ip_stack_debug(false),
allow_unregistered_users(false), allow_unregistered_users(false),
allow_saving(true), allow_saving(true),
allow_dc_pc_games(false),
allow_gc_xb_games(true),
item_tracking_enabled(true), item_tracking_enabled(true),
drops_enabled(true), drops_enabled(true),
ep3_send_function_call_enabled(false), ep3_send_function_call_enabled(false),
@@ -53,7 +55,7 @@ void ServerState::init() {
for (size_t x = 0; x < 20; x++) { for (size_t x = 0; x < 20; x++) {
auto lobby_name = decode_sjis(string_printf("LOBBY%zu", x + 1)); auto lobby_name = decode_sjis(string_printf("LOBBY%zu", x + 1));
bool is_non_v1_only = (x > 9); bool v2_and_later_only = (x > 9);
bool is_ep3_only = (x > 14); bool is_ep3_only = (x > 14);
shared_ptr<Lobby> l = this->create_lobby(); shared_ptr<Lobby> l = this->create_lobby();
@@ -61,7 +63,7 @@ void ServerState::init() {
Lobby::Flag::PUBLIC | Lobby::Flag::PUBLIC |
Lobby::Flag::DEFAULT | Lobby::Flag::DEFAULT |
Lobby::Flag::PERSISTENT | Lobby::Flag::PERSISTENT |
(is_non_v1_only ? Lobby::Flag::NON_V1_ONLY : 0); (v2_and_later_only ? Lobby::Flag::V2_AND_LATER : 0);
l->block = x + 1; l->block = x + 1;
l->name = lobby_name; l->name = lobby_name;
l->max_clients = 12; l->max_clients = 12;
@@ -69,7 +71,7 @@ void ServerState::init() {
l->episode = Episode::EP3; l->episode = Episode::EP3;
} }
if (!is_non_v1_only) { if (!v2_and_later_only) {
this->public_lobby_search_order_v1.emplace_back(l); this->public_lobby_search_order_v1.emplace_back(l);
} }
if (!is_ep3_only) { if (!is_ep3_only) {
@@ -655,6 +657,9 @@ void ServerState::parse_config(const JSON& json, bool is_reload) {
} catch (const out_of_range&) { } catch (const out_of_range&) {
} }
this->allow_dc_pc_games = json.get_bool("AllowDCPCGames", this->allow_dc_pc_games);
this->allow_gc_xb_games = json.get_bool("AllowGCXBGames", this->allow_gc_xb_games);
try { try {
auto v = json.at("LobbyEvent"); auto v = json.at("LobbyEvent");
uint8_t event = v.is_int() ? v.as_int() : event_for_name(v.as_string()); uint8_t event = v.is_int() ? v.as_int() : event_for_name(v.as_string());
+2
View File
@@ -57,6 +57,8 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
bool ip_stack_debug; bool ip_stack_debug;
bool allow_unregistered_users; bool allow_unregistered_users;
bool allow_saving; bool allow_saving;
bool allow_dc_pc_games;
bool allow_gc_xb_games;
bool item_tracking_enabled; bool item_tracking_enabled;
bool drops_enabled; bool drops_enabled;
bool ep3_send_function_call_enabled; bool ep3_send_function_call_enabled;
+10
View File
@@ -503,6 +503,16 @@
// enable Episode 3 USA patches by default; it only does if this option is on. // enable Episode 3 USA patches by default; it only does if this option is on.
// "EnableEpisode3SendFunctionCall": true, // "EnableEpisode3SendFunctionCall": true,
// Whether to allow cross-play for various game versions. DCv1 and DCv2 are
// always allowed to join each other's games (though DCv2 can deny permission
// for DCv1 players to join when creating a game); if AllowDCPCGames is
// enabled, then PC players are allowed in DC games and vice versa. Similarly,
// if AllowGCXBGames is enabled, then GameCube and Xbox players are allowed to
// join each other's games. Note that this behavior is experimental; you are
// likely to encounter bugs in cross-play games, especially in the DC/PC case.
"AllowDCPCGames": false,
"AllowGCXBGames": true,
// By default, the server keeps track of items in all games, even for versions // By default, the server keeps track of items in all games, even for versions
// other than Blue Burst. This enables use of the $what command, as well as // other than Blue Burst. This enables use of the $what command, as well as
// protection against item duplication cheats (the cheater is disconnected // protection against item duplication cheats (the cheater is disconnected