support DC NTE and DCv1 Dec 2000 prototype
This commit is contained in:
@@ -58,6 +58,11 @@ struct Client {
|
||||
// that version is similar enough to the release version of Episode 3 that
|
||||
// newserv does not have to change its behavior at all.
|
||||
IS_TRIAL_EDITION = 0x00002000,
|
||||
// A 90 01 command has been sent (which proto will send a 93 in response to,
|
||||
// and actual DCv1 will send a 92)
|
||||
CHECKED_FOR_DC_V1_PROTOTYPE = 0x00080000,
|
||||
// Client is DC v1 prototype
|
||||
IS_DC_V1_PROTOTYPE = 0x00040000,
|
||||
// Client is DC v1
|
||||
IS_DC_V1 = 0x00000010,
|
||||
// For patch server clients, client is Blue Burst rather than PC
|
||||
|
||||
+40
-17
@@ -1149,6 +1149,15 @@ struct S_JoinGame {
|
||||
uint8_t unused3 = 0;
|
||||
} __packed__;
|
||||
|
||||
struct S_JoinGame_DCNTE_64 {
|
||||
uint8_t client_id;
|
||||
uint8_t leader_id;
|
||||
uint8_t disable_udp;
|
||||
uint8_t unused;
|
||||
parray<le_uint32_t, 0x20> variations;
|
||||
parray<PlayerLobbyDataDCGC, 4> lobby_data;
|
||||
} __packed__;
|
||||
|
||||
struct S_JoinGame_PC_64 : S_JoinGame<PlayerLobbyDataPC, PlayerDispDataDCPCV3> {
|
||||
} __packed__;
|
||||
struct S_JoinGame_DC_GC_64 : S_JoinGame<PlayerLobbyDataDCGC, PlayerDispDataDCPCV3> {
|
||||
@@ -1177,9 +1186,14 @@ struct S_JoinGame_BB_64 : S_JoinGame<PlayerLobbyDataBB, PlayerDispDataBB> {
|
||||
// command (described above), and the players already in the game receive a 65
|
||||
// command containing only the joining player's data.
|
||||
|
||||
// Header flag = entry count (always 1 for 65 and 68; up to 0x0C for 67)
|
||||
template <typename LobbyDataT, typename DispDataT>
|
||||
struct S_JoinLobby {
|
||||
struct LobbyFlags_DCNTE {
|
||||
uint8_t client_id = 0;
|
||||
uint8_t leader_id = 0;
|
||||
uint8_t disable_udp = 1;
|
||||
uint8_t unused = 0;
|
||||
} __packed__;
|
||||
|
||||
struct LobbyFlags {
|
||||
uint8_t client_id = 0;
|
||||
uint8_t leader_id = 0;
|
||||
uint8_t disable_udp = 1;
|
||||
@@ -1189,6 +1203,12 @@ struct S_JoinLobby {
|
||||
uint8_t event = 0;
|
||||
uint8_t unknown_a2 = 0;
|
||||
le_uint32_t unused = 0;
|
||||
} __packed__;
|
||||
|
||||
// Header flag = entry count (always 1 for 65 and 68; up to 0x0C for 67)
|
||||
template <typename LobbyFlagsT, typename LobbyDataT, typename DispDataT>
|
||||
struct S_JoinLobby {
|
||||
LobbyFlagsT lobby_flags;
|
||||
struct Entry {
|
||||
LobbyDataT lobby_data;
|
||||
PlayerInventory inventory;
|
||||
@@ -1202,26 +1222,22 @@ struct S_JoinLobby {
|
||||
return offsetof(S_JoinLobby, entries) + used_entries * sizeof(Entry);
|
||||
}
|
||||
} __packed__;
|
||||
|
||||
struct S_JoinLobby_DCNTE_65_67_68
|
||||
: S_JoinLobby<LobbyFlags_DCNTE, PlayerLobbyDataDCGC, PlayerDispDataDCPCV3> {
|
||||
} __packed__;
|
||||
struct S_JoinLobby_PC_65_67_68
|
||||
: S_JoinLobby<PlayerLobbyDataPC, PlayerDispDataDCPCV3> {
|
||||
: S_JoinLobby<LobbyFlags, PlayerLobbyDataPC, PlayerDispDataDCPCV3> {
|
||||
} __packed__;
|
||||
struct S_JoinLobby_DC_GC_65_67_68_Ep3_EB
|
||||
: S_JoinLobby<PlayerLobbyDataDCGC, PlayerDispDataDCPCV3> {
|
||||
: S_JoinLobby<LobbyFlags, PlayerLobbyDataDCGC, PlayerDispDataDCPCV3> {
|
||||
} __packed__;
|
||||
struct S_JoinLobby_BB_65_67_68
|
||||
: S_JoinLobby<PlayerLobbyDataBB, PlayerDispDataBB> {
|
||||
: S_JoinLobby<LobbyFlags, PlayerLobbyDataBB, PlayerDispDataBB> {
|
||||
} __packed__;
|
||||
|
||||
struct S_JoinLobby_XB_65_67_68 {
|
||||
uint8_t client_id = 0;
|
||||
uint8_t leader_id = 0;
|
||||
uint8_t disable_udp = 1;
|
||||
uint8_t lobby_number = 0;
|
||||
uint8_t block_number = 0;
|
||||
uint8_t unknown_a1 = 0;
|
||||
uint8_t event = 0;
|
||||
uint8_t unknown_a2 = 0;
|
||||
parray<uint8_t, 4> unknown_a3;
|
||||
LobbyFlags lobby_flags;
|
||||
parray<le_uint32_t, 6> unknown_a4;
|
||||
struct Entry {
|
||||
PlayerLobbyDataXB lobby_data;
|
||||
@@ -1516,7 +1532,10 @@ struct C_LoginExtended_DCNTE_8B : C_Login_DCNTE_8B {
|
||||
struct C_LoginV1_DC_PC_V3_90 {
|
||||
ptext<char, 0x11> serial_number;
|
||||
ptext<char, 0x11> access_key;
|
||||
parray<uint8_t, 2> unused;
|
||||
// Note: There is a bug in the Japanese and prototype versions of DCv1 that
|
||||
// cause the client to send this command despite its size not being a
|
||||
// multiple of 4. This is fixed in later versions, so we handle both cases in
|
||||
// the receive handler.
|
||||
} __packed__;
|
||||
|
||||
// 90 (S->C): License verification result (V3)
|
||||
@@ -2280,13 +2299,17 @@ struct S_ChoiceSearchEntry_PC_BB_C0 : S_ChoiceSearchEntry<le_uint16_t, char16_t>
|
||||
// Internal name: SndCreateGame
|
||||
|
||||
template <typename CharT>
|
||||
struct C_CreateGame {
|
||||
struct C_CreateGame_DCNTE {
|
||||
// menu_id and item_id are only used for the E7 (create spectator team) form
|
||||
// of this command
|
||||
le_uint32_t menu_id;
|
||||
le_uint32_t item_id;
|
||||
ptext<CharT, 0x10> name;
|
||||
ptext<CharT, 0x10> password;
|
||||
} __packed__;
|
||||
|
||||
template <typename CharT>
|
||||
struct C_CreateGame : C_CreateGame_DCNTE<CharT> {
|
||||
uint8_t difficulty = 0; // 0-3 (always 0 on Episode 3)
|
||||
uint8_t battle_mode = 0; // 0 or 1 (always 0 on Episode 3)
|
||||
// Note: Episode 3 uses the challenge mode flag for view battle permissions.
|
||||
|
||||
@@ -1323,8 +1323,8 @@ static HandlerResult S_65_67_68_EB(shared_ptr<ServerState>,
|
||||
bool modified = false;
|
||||
|
||||
size_t num_replacements = 0;
|
||||
session.lobby_client_id = cmd.client_id;
|
||||
update_leader_id(session, cmd.leader_id);
|
||||
session.lobby_client_id = cmd.lobby_flags.client_id;
|
||||
update_leader_id(session, cmd.lobby_flags.leader_id);
|
||||
for (size_t x = 0; x < flag; x++) {
|
||||
size_t index = cmd.entries[x].lobby_data.client_id;
|
||||
if (index >= session.lobby_players.size()) {
|
||||
@@ -1354,11 +1354,11 @@ static HandlerResult S_65_67_68_EB(shared_ptr<ServerState>,
|
||||
}
|
||||
|
||||
if (session.options.override_lobby_event >= 0) {
|
||||
cmd.event = session.options.override_lobby_event;
|
||||
cmd.lobby_flags.event = session.options.override_lobby_event;
|
||||
modified = true;
|
||||
}
|
||||
if (session.options.override_lobby_number >= 0) {
|
||||
cmd.lobby_number = session.options.override_lobby_number;
|
||||
cmd.lobby_flags.lobby_number = session.options.override_lobby_number;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
|
||||
+82
-54
@@ -471,7 +471,7 @@ static void on_8B_DCNTE(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
|
||||
static void on_90_DC(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
uint16_t, uint32_t, const string& data) {
|
||||
const auto& cmd = check_size_t<C_LoginV1_DC_PC_V3_90>(data);
|
||||
const auto& cmd = check_size_t<C_LoginV1_DC_PC_V3_90>(data, sizeof(C_LoginV1_DC_PC_V3_90), 0xFFFF);
|
||||
c->channel.version = GameVersion::DC;
|
||||
c->flags |= flags_for_version(c->version(), -1);
|
||||
c->flags |= Client::Flag::IS_DC_V1;
|
||||
@@ -504,6 +504,10 @@ static void on_90_DC(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
static void on_92_DC(shared_ptr<ServerState>, shared_ptr<Client> c,
|
||||
uint16_t, uint32_t, const string& data) {
|
||||
check_size_t<C_RegisterV1_DC_92>(data);
|
||||
// It appears that in response to 90 01, the DCv1 prototype sends 93 rather
|
||||
// than 92, so we use the presence of a 92 command to determine that the
|
||||
// client is actually DCv1 and not the prototype.
|
||||
c->flags = (c->flags & ~Client::Flag::IS_DC_V1_PROTOTYPE) | Client::Flag::CHECKED_FOR_DC_V1_PROTOTYPE;
|
||||
send_command(c, 0x92, 0x01);
|
||||
}
|
||||
|
||||
@@ -546,7 +550,18 @@ static void on_93_DC(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
|
||||
send_update_client_config(c);
|
||||
|
||||
on_login_complete(s, c);
|
||||
// The first time we receive a 93 from a DC client, we set this flag and send
|
||||
// a 92. The IS_DC_V1_PROTOTYPE flag will be removed if the client sends a 92
|
||||
// command (which it seems the prototype never does). This is why we always
|
||||
// respond with 90 01 here - that's the only case where actual DCv1 sends a
|
||||
// 92 command. The IS_DC_V1_PROTOTYPE flag will be removed if the client does
|
||||
// indeed send a 92.
|
||||
if (!(c->flags & Client::Flag::CHECKED_FOR_DC_V1_PROTOTYPE)) {
|
||||
send_command(c, 0x90, 0x01);
|
||||
c->flags |= (Client::Flag::CHECKED_FOR_DC_V1_PROTOTYPE | Client::Flag::IS_DC_V1_PROTOTYPE);
|
||||
} else {
|
||||
on_login_complete(s, c);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_9A(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
@@ -1673,9 +1688,10 @@ static void on_10(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
c->should_send_to_lobby_server = true;
|
||||
if (!(c->flags & Client::Flag::SAVE_ENABLED)) {
|
||||
c->flags |= Client::Flag::SAVE_ENABLED;
|
||||
// DC NTE crashes if it receives a 97 command, so we instead do the
|
||||
// redirect immediately
|
||||
if ((c->version() == GameVersion::DC) && (c->flags & Client::Flag::IS_TRIAL_EDITION)) {
|
||||
// DC NTE and the v1 prototype crash if they receive a 97 command,
|
||||
// so we instead do the redirect immediately
|
||||
if ((c->version() == GameVersion::DC) &&
|
||||
(c->flags & (Client::Flag::IS_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE))) {
|
||||
send_client_to_lobby_server(s, c);
|
||||
} else {
|
||||
send_command(c, 0x97, 0x01);
|
||||
@@ -3223,7 +3239,7 @@ shared_ptr<Lobby> create_game_generic(
|
||||
bool is_solo = (game->mode == GameMode::SOLO);
|
||||
|
||||
// Generate the map variations
|
||||
if (game->is_ep3()) {
|
||||
if (game->is_ep3() || (c->version() == GameVersion::DC && (c->flags & (Client::Flag::IS_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE)))) {
|
||||
game->variations.clear(0);
|
||||
} else {
|
||||
generate_variations(game->variations, game->random, game->episode, is_solo);
|
||||
@@ -3307,60 +3323,72 @@ static void on_C1_PC(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
|
||||
static void on_0C_C1_E7_EC(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
uint16_t command, uint32_t, const string& data) {
|
||||
const auto& cmd = check_size_t<C_CreateGame_DC_V3_0C_C1_Ep3_EC>(data);
|
||||
|
||||
// Only allow E7/EC from Ep3 clients
|
||||
bool client_is_ep3 = !!(c->flags & Client::Flag::IS_EPISODE_3);
|
||||
if (((command & 0xF0) == 0xE0) != client_is_ep3) {
|
||||
throw runtime_error("invalid command");
|
||||
}
|
||||
shared_ptr<Lobby> game;
|
||||
if (c->version() == GameVersion::DC && (c->flags & (Client::Flag::IS_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE))) {
|
||||
const auto& cmd = check_size_t<C_CreateGame_DCNTE<char>>(data);
|
||||
u16string name = decode_sjis(cmd.name);
|
||||
u16string password = decode_sjis(cmd.password);
|
||||
game = create_game_generic(
|
||||
s, c, name.c_str(), password.c_str(), Episode::EP1, GameMode::NORMAL, 0, 0);
|
||||
|
||||
Episode episode = Episode::NONE;
|
||||
uint32_t flags = 0;
|
||||
if (c->version() == GameVersion::DC) {
|
||||
if (cmd.episode) {
|
||||
} else {
|
||||
const auto& cmd = check_size_t<C_CreateGame_DC_V3_0C_C1_Ep3_EC>(data);
|
||||
|
||||
// Only allow E7/EC from Ep3 clients
|
||||
bool client_is_ep3 = !!(c->flags & Client::Flag::IS_EPISODE_3);
|
||||
if (((command & 0xF0) == 0xE0) != client_is_ep3) {
|
||||
throw runtime_error("invalid command");
|
||||
}
|
||||
|
||||
Episode episode = Episode::NONE;
|
||||
uint32_t flags = 0;
|
||||
if (c->version() == GameVersion::DC) {
|
||||
if (cmd.episode) {
|
||||
flags |= Lobby::Flag::NON_V1_ONLY;
|
||||
}
|
||||
episode = Episode::EP1;
|
||||
} else if (client_is_ep3) {
|
||||
flags |= Lobby::Flag::NON_V1_ONLY;
|
||||
episode = Episode::EP3;
|
||||
} else { // XB/GC non-Ep3
|
||||
flags |= Lobby::Flag::NON_V1_ONLY;
|
||||
episode = cmd.episode == 2 ? Episode::EP2 : Episode::EP1;
|
||||
}
|
||||
episode = Episode::EP1;
|
||||
} else if (client_is_ep3) {
|
||||
flags |= Lobby::Flag::NON_V1_ONLY;
|
||||
episode = Episode::EP3;
|
||||
} else { // XB/GC non-Ep3
|
||||
flags |= Lobby::Flag::NON_V1_ONLY;
|
||||
episode = cmd.episode == 2 ? Episode::EP2 : Episode::EP1;
|
||||
|
||||
u16string name = decode_sjis(cmd.name);
|
||||
u16string password = decode_sjis(cmd.password);
|
||||
|
||||
GameMode mode = GameMode::NORMAL;
|
||||
if (cmd.battle_mode) {
|
||||
mode = GameMode::BATTLE;
|
||||
}
|
||||
if (cmd.challenge_mode) {
|
||||
if (client_is_ep3) {
|
||||
flags |= Lobby::Flag::SPECTATORS_FORBIDDEN;
|
||||
} else {
|
||||
mode = GameMode::CHALLENGE;
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<Lobby> watched_lobby;
|
||||
if (command == 0xE7) {
|
||||
if (cmd.menu_id != MenuID::GAME) {
|
||||
throw runtime_error("incorrect menu ID");
|
||||
}
|
||||
watched_lobby = s->find_lobby(cmd.item_id);
|
||||
if (watched_lobby->flags & Lobby::Flag::SPECTATORS_FORBIDDEN) {
|
||||
send_lobby_message_box(c, u"$C6This game does not\nallow spectators");
|
||||
return;
|
||||
}
|
||||
flags |= Lobby::Flag::IS_SPECTATOR_TEAM;
|
||||
}
|
||||
|
||||
game = create_game_generic(
|
||||
s, c, name.c_str(), password.c_str(), episode, mode, cmd.difficulty,
|
||||
flags, watched_lobby);
|
||||
}
|
||||
|
||||
u16string name = decode_sjis(cmd.name);
|
||||
u16string password = decode_sjis(cmd.password);
|
||||
|
||||
GameMode mode = GameMode::NORMAL;
|
||||
if (cmd.battle_mode) {
|
||||
mode = GameMode::BATTLE;
|
||||
}
|
||||
if (cmd.challenge_mode) {
|
||||
if (client_is_ep3) {
|
||||
flags |= Lobby::Flag::SPECTATORS_FORBIDDEN;
|
||||
} else {
|
||||
mode = GameMode::CHALLENGE;
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<Lobby> watched_lobby;
|
||||
if (command == 0xE7) {
|
||||
if (cmd.menu_id != MenuID::GAME) {
|
||||
throw runtime_error("incorrect menu ID");
|
||||
}
|
||||
watched_lobby = s->find_lobby(cmd.item_id);
|
||||
if (watched_lobby->flags & Lobby::Flag::SPECTATORS_FORBIDDEN) {
|
||||
send_lobby_message_box(c, u"$C6This game does not\nallow spectators");
|
||||
return;
|
||||
}
|
||||
flags |= Lobby::Flag::IS_SPECTATOR_TEAM;
|
||||
}
|
||||
|
||||
auto game = create_game_generic(
|
||||
s, c, name.c_str(), password.c_str(), episode, mode, cmd.difficulty,
|
||||
flags, watched_lobby);
|
||||
if (game) {
|
||||
s->change_client_lobby(c, game);
|
||||
c->flags |= Client::Flag::LOADING;
|
||||
|
||||
@@ -1618,12 +1618,17 @@ void on_subcommand(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
|
||||
if (data.empty()) {
|
||||
throw runtime_error("game command is empty");
|
||||
}
|
||||
uint8_t which = static_cast<uint8_t>(data[0]);
|
||||
auto fn = subcommand_handlers[which];
|
||||
if (fn) {
|
||||
fn(s, l, c, command, flag, data);
|
||||
if (c->version() == GameVersion::DC && (c->flags & (Client::Flag::IS_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE))) {
|
||||
// TODO: We should convert these to non-trial formats and vice versa
|
||||
forward_subcommand(l, c, command, flag, std::move(data));
|
||||
} else {
|
||||
on_unimplemented(s, l, c, command, flag, data);
|
||||
uint8_t which = static_cast<uint8_t>(data[0]);
|
||||
auto fn = subcommand_handlers[which];
|
||||
if (fn) {
|
||||
fn(s, l, c, command, flag, data);
|
||||
} else {
|
||||
on_unimplemented(s, l, c, command, flag, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ void ReplaySession::check_for_password(shared_ptr<const Event> ev) const {
|
||||
} else if (header.command == 0x04) {
|
||||
check_ak(check_size_t<C_LegacyLogin_PC_V3_04>(cmd_data, cmd_size).access_key);
|
||||
} else if (header.command == 0x90) {
|
||||
check_ak(check_size_t<C_LoginV1_DC_PC_V3_90>(cmd_data, cmd_size).access_key);
|
||||
check_ak(check_size_t<C_LoginV1_DC_PC_V3_90>(cmd_data, cmd_size, sizeof(C_LoginV1_DC_PC_V3_90), 0xFFFF).access_key);
|
||||
} else if (header.command == 0x93) {
|
||||
const auto& cmd = check_size_t<C_LoginV1_DC_93>(cmd_data, cmd_size,
|
||||
sizeof(C_LoginV1_DC_93), sizeof(C_LoginExtendedV1_DC_93));
|
||||
|
||||
+97
-10
@@ -27,6 +27,7 @@ const unordered_set<uint32_t> v2_crypt_initial_client_commands({
|
||||
0x00260088, // (17) DCNTE license check
|
||||
0x00B0008B, // (02) DCNTE login
|
||||
0x0114008B, // (02) DCNTE extended login
|
||||
0x00260090, // (17) DCv1 prototype and JP license check
|
||||
0x00280090, // (17) DCv1 license check
|
||||
0x00B00093, // (02) DCv1 login
|
||||
0x01140093, // (02) DCv1 extended login
|
||||
@@ -1477,6 +1478,33 @@ void send_join_game_t(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
send_command(c, 0x64, player_count, data);
|
||||
}
|
||||
|
||||
void send_join_game_dc_nte(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
if (l->flags & Lobby::Flag::IS_SPECTATOR_TEAM) {
|
||||
throw runtime_error("DC NTE players cannot join spectator teams");
|
||||
}
|
||||
|
||||
S_JoinGame_DCNTE_64 cmd;
|
||||
cmd.client_id = c->lobby_client_id;
|
||||
cmd.leader_id = l->leader_id;
|
||||
cmd.disable_udp = 0x01;
|
||||
cmd.variations = l->variations;
|
||||
|
||||
size_t player_count = 0;
|
||||
for (size_t x = 0; x < 4; x++) {
|
||||
if (l->clients[x]) {
|
||||
cmd.lobby_data[x].player_tag = 0x00010000;
|
||||
cmd.lobby_data[x].guild_card = l->clients[x]->license->serial_number;
|
||||
cmd.lobby_data[x].client_id = l->clients[x]->lobby_client_id;
|
||||
cmd.lobby_data[x].name = l->clients[x]->game_data.player()->disp.name;
|
||||
player_count++;
|
||||
} else {
|
||||
cmd.lobby_data[x].clear();
|
||||
}
|
||||
}
|
||||
|
||||
send_command_t(c, 0x64, player_count, cmd);
|
||||
}
|
||||
|
||||
template <typename LobbyDataT, typename DispDataT>
|
||||
void send_join_lobby_t(shared_ptr<Client> c, shared_ptr<Lobby> l,
|
||||
shared_ptr<Client> joining_client = nullptr) {
|
||||
@@ -1510,16 +1538,16 @@ void send_join_lobby_t(shared_ptr<Client> c, shared_ptr<Lobby> l,
|
||||
}
|
||||
}
|
||||
|
||||
S_JoinLobby<LobbyDataT, DispDataT> cmd;
|
||||
cmd.client_id = c->lobby_client_id;
|
||||
cmd.leader_id = l->leader_id;
|
||||
cmd.disable_udp = 0x01;
|
||||
cmd.lobby_number = lobby_type;
|
||||
cmd.block_number = l->block;
|
||||
cmd.unknown_a1 = 0;
|
||||
cmd.event = l->event;
|
||||
cmd.unknown_a2 = 0;
|
||||
cmd.unused = 0;
|
||||
S_JoinLobby<LobbyFlags, LobbyDataT, DispDataT> cmd;
|
||||
cmd.lobby_flags.client_id = c->lobby_client_id;
|
||||
cmd.lobby_flags.leader_id = l->leader_id;
|
||||
cmd.lobby_flags.disable_udp = 0x01;
|
||||
cmd.lobby_flags.lobby_number = lobby_type;
|
||||
cmd.lobby_flags.block_number = l->block;
|
||||
cmd.lobby_flags.unknown_a1 = 0;
|
||||
cmd.lobby_flags.event = l->event;
|
||||
cmd.lobby_flags.unknown_a2 = 0;
|
||||
cmd.lobby_flags.unused = 0;
|
||||
|
||||
vector<shared_ptr<Client>> lobby_clients;
|
||||
if (joining_client) {
|
||||
@@ -1549,6 +1577,50 @@ void send_join_lobby_t(shared_ptr<Client> c, shared_ptr<Lobby> l,
|
||||
send_command(c, command, used_entries, &cmd, cmd.size(used_entries));
|
||||
}
|
||||
|
||||
void send_join_lobby_dc_nte(shared_ptr<Client> c, shared_ptr<Lobby> l,
|
||||
shared_ptr<Client> joining_client = nullptr) {
|
||||
uint8_t command;
|
||||
if (l->is_game()) {
|
||||
if (joining_client) {
|
||||
command = 0x65;
|
||||
} else {
|
||||
throw logic_error("send_join_lobby_dc_nte should not be used for primary game join command");
|
||||
}
|
||||
} else {
|
||||
command = joining_client ? 0x68 : 0x67;
|
||||
}
|
||||
|
||||
S_JoinLobby_DCNTE_65_67_68 cmd;
|
||||
cmd.lobby_flags.client_id = c->lobby_client_id;
|
||||
cmd.lobby_flags.leader_id = l->leader_id;
|
||||
cmd.lobby_flags.disable_udp = 0x01;
|
||||
|
||||
vector<shared_ptr<Client>> lobby_clients;
|
||||
if (joining_client) {
|
||||
lobby_clients.emplace_back(joining_client);
|
||||
} else {
|
||||
for (auto lc : l->clients) {
|
||||
if (lc) {
|
||||
lobby_clients.emplace_back(lc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t used_entries = 0;
|
||||
for (const auto& lc : lobby_clients) {
|
||||
auto& e = cmd.entries[used_entries++];
|
||||
e.lobby_data.player_tag = 0x00010000;
|
||||
e.lobby_data.guild_card = lc->license->serial_number;
|
||||
e.lobby_data.client_id = lc->lobby_client_id;
|
||||
e.lobby_data.name = lc->game_data.player()->disp.name;
|
||||
e.inventory = lc->game_data.player()->inventory;
|
||||
e.disp = convert_player_disp_data<PlayerDispDataDCPCV3>(lc->game_data.player()->disp);
|
||||
e.disp.enforce_v2_limits();
|
||||
}
|
||||
|
||||
send_command(c, command, used_entries, &cmd, cmd.size(used_entries));
|
||||
}
|
||||
|
||||
void send_join_lobby(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
if (l->is_game()) {
|
||||
switch (c->version()) {
|
||||
@@ -1556,6 +1628,11 @@ void send_join_lobby(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
send_join_game_t<PlayerLobbyDataPC, PlayerDispDataDCPCV3>(c, l);
|
||||
break;
|
||||
case GameVersion::DC:
|
||||
if (c->flags & (Client::Flag::IS_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE)) {
|
||||
send_join_game_dc_nte(c, l);
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case GameVersion::GC:
|
||||
send_join_game_t<PlayerLobbyDataDCGC, PlayerDispDataDCPCV3>(c, l);
|
||||
break;
|
||||
@@ -1574,6 +1651,11 @@ void send_join_lobby(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
send_join_lobby_t<PlayerLobbyDataPC, PlayerDispDataDCPCV3>(c, l);
|
||||
break;
|
||||
case GameVersion::DC:
|
||||
if (c->flags & (Client::Flag::IS_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE)) {
|
||||
send_join_lobby_dc_nte(c, l);
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case GameVersion::GC:
|
||||
send_join_lobby_t<PlayerLobbyDataDCGC, PlayerDispDataDCPCV3>(c, l);
|
||||
break;
|
||||
@@ -1603,6 +1685,11 @@ void send_player_join_notification(shared_ptr<Client> c,
|
||||
send_join_lobby_t<PlayerLobbyDataPC, PlayerDispDataDCPCV3>(c, l, joining_client);
|
||||
break;
|
||||
case GameVersion::DC:
|
||||
if (c->flags & (Client::Flag::IS_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE)) {
|
||||
send_join_lobby_dc_nte(c, l, joining_client);
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case GameVersion::GC:
|
||||
send_join_lobby_t<PlayerLobbyDataDCGC, PlayerDispDataDCPCV3>(c, l, joining_client);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user