support DC NTE and DCv1 Dec 2000 prototype
This commit is contained in:
@@ -83,7 +83,8 @@ Current known issues / missing features / things to do:
|
||||
newserv supports several versions of PSO. Specifically:
|
||||
| Version | Login | Lobbies | Games | Proxy |
|
||||
|----------------|--------------|--------------|--------------|--------------|
|
||||
| DC Trial | Partial (4) | No | No | No |
|
||||
| DC Trial | Yes (4) | Yes (4) | Yes (4) | No |
|
||||
| DC Prototype | Yes (4) | Yes (4) | Yes (4) | No |
|
||||
| DC V1 | Yes (1) | Yes | Yes | Yes |
|
||||
| DC V2 | Yes (1) | Yes | Yes | Yes |
|
||||
| PC | Yes | Yes | Yes | Yes |
|
||||
@@ -100,7 +101,7 @@ newserv supports several versions of PSO. Specifically:
|
||||
1. *DC support has only been tested with the US versions of PSO DC. Other versions probably don't work, but will be easy to add support for. Please submit a GitHub issue if you have a non-US DC version, and can provide a log from a connection attempt.*
|
||||
2. *newserv's implementations of these versions are based on disassembly of the client executables and have never been tested.*
|
||||
3. *Some basic features are not implemented in Blue Burst games, so the games are not very playable. A lot of work has to be done to get BB games to a playable state.*
|
||||
4. *Support for PSO Dreamcast Trial Edition is very incomplete and probably never will be complete. This is really just exploring a curiosity that sheds some light on early network engineering done by Sega, not an actual attempt at supporting this version of the game.*
|
||||
4. *Support for PSO Dreamcast Trial Edition and the December 2000 prototype is somewhat incomplete and probably never will be complete. These versions are rather unstable and seem to crash often, but it's not obvious whether it's because they're prototypes or because newserv sends data they can't hendle.*
|
||||
|
||||
## Setup
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -59,7 +59,7 @@ I 40469 2023-05-26 10:40:55 - [Commands] Received from C-1 (version=DC command=9
|
||||
I 40469 2023-05-26 10:40:55 - [C-1] Game version changed to DC
|
||||
I 40469 2023-05-26 10:40:55 - [Commands] Sending to C-1 (version=DC command=04 flag=00)
|
||||
0000 | 04 00 2C 00 00 00 01 00 77 77 77 77 39 98 AC 82 | , wwww9
|
||||
0010 | 0E 89 2A 49 14 02 02 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0010 | 0E 89 2A 49 14 02 0A 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
I 40469 2023-05-26 10:40:55 - [Commands] Sending to C-1 (version=DC command=07 flag=05)
|
||||
0000 | 07 04 90 00 11 00 00 11 FF FF FF FF 04 00 41 6C | Al
|
||||
@@ -84,7 +84,7 @@ I 40469 2023-05-26 10:40:58 - [Commands] Sending to C-1 (version=DC command=97 f
|
||||
0000 | 97 01 04 00 |
|
||||
I 40469 2023-05-26 10:40:58 - [Commands] Sending to C-1 (version=DC command=04 flag=00)
|
||||
0000 | 04 00 2C 00 00 00 01 00 77 77 77 77 39 98 AC 82 | , wwww9
|
||||
0010 | 0E 89 2A 49 14 06 02 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0010 | 0E 89 2A 49 14 06 0A 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
I 40469 2023-05-26 10:40:58 - [Commands] Received from C-1 (version=DC command=B1 flag=00)
|
||||
0000 | B1 00 04 00 |
|
||||
@@ -133,6 +133,38 @@ I 40469 2023-05-26 10:40:59 - [Commands] Sending to C-2 (version=DC command=04 f
|
||||
0000 | 04 00 2C 00 00 00 01 00 77 77 77 77 39 98 AC 82 | , wwww9
|
||||
0010 | 0E 89 2A 49 14 02 00 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
I 40469 2023-05-26 10:40:59 - [Commands] Sending to C-2 (version=DC command=04 flag=00)
|
||||
0000 | 90 01 04 00 |
|
||||
I 40469 2023-05-26 10:40:59 - [Commands] Received from C-2 (version=DC command=61 flag=01)
|
||||
0000 | 92 00 A4 00 00 00 13 2B 64 B2 2C B2 21 00 00 00 | +d , !
|
||||
0010 | 00 01 00 00 38 41 30 46 32 39 36 45 00 00 00 00 | 8A0F296E
|
||||
0020 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0030 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0040 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0050 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0060 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0070 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0080 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0090 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
00A0 | 00 00 00 00 |
|
||||
I 40469 2023-05-26 10:40:59 - [Commands] Sending to C-2 (version=DC command=04 flag=00)
|
||||
0000 | 92 01 04 00 |
|
||||
I 40469 2023-05-26 10:40:59 - [Commands] Received from C-2 (version=GC command=93 flag=00)
|
||||
0000 | 93 00 B0 00 00 00 01 00 77 77 77 77 00 00 13 2B | wwww +
|
||||
0010 | 64 B2 2C B2 21 00 00 00 00 01 00 00 37 37 37 37 | d , ! 7777
|
||||
0020 | 37 37 37 37 00 00 00 00 00 00 00 00 00 31 31 31 | 7777 111
|
||||
0030 | 31 31 31 31 31 00 00 00 00 00 00 00 00 00 38 41 | 11111 8A
|
||||
0040 | 30 46 32 39 36 45 00 00 00 00 00 00 00 00 00 00 | 0F296E
|
||||
0050 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0060 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0070 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0080 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0090 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 54 61 | Ta
|
||||
00A0 | 6C 69 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | li
|
||||
I 40469 2023-05-26 10:40:59 - [Commands] Sending to C-2 (version=DC command=04 flag=00)
|
||||
0000 | 04 00 2C 00 00 00 01 00 77 77 77 77 39 98 AC 82 | , wwww9
|
||||
0010 | 0E 89 2A 49 14 02 08 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
I 40469 2023-05-26 10:40:59 - [Commands] Sending to C-2 (version=DC command=83 flag=0A)
|
||||
0000 | 83 0A 7C 00 33 00 00 33 01 00 00 00 00 00 00 00 | | 3 3
|
||||
0010 | 33 00 00 33 02 00 00 00 00 00 00 00 33 00 00 33 | 3 3 3 3
|
||||
@@ -1195,67 +1227,3 @@ I 40469 2023-05-26 10:42:49 - [Commands] Sending to C-2 (Tali) (version=DC comma
|
||||
I 40469 2023-05-26 10:42:49 - [Commands] Sending to C-2 (Tali) (version=DC command=19 flag=00)
|
||||
0000 | 19 00 0C 00 0A 00 00 04 EC 13 00 00 |
|
||||
I 40469 2023-05-26 10:42:49 - [Server] Client disconnected: C-2 on fd 38
|
||||
I 40469 2023-05-26 10:42:49 - [C-2] Deleted
|
||||
I 40469 2023-05-26 10:42:49 - [C-3] Created
|
||||
I 40469 2023-05-26 10:42:49 - [Server] Client connected: C-3 on fd 38 via 7 (T-5100-DC-console-login-login_server)
|
||||
I 40469 2023-05-26 10:42:49 - [Commands] Sending to C-3 (version=GC command=17 flag=00)
|
||||
0000 | 17 00 00 01 44 72 65 61 6D 43 61 73 74 20 50 6F | DreamCast Po
|
||||
0010 | 72 74 20 4D 61 70 2E 20 43 6F 70 79 72 69 67 68 | rt Map. Copyrigh
|
||||
0020 | 74 20 53 45 47 41 20 45 6E 74 65 72 70 72 69 73 | t SEGA Enterpris
|
||||
0030 | 65 73 2E 20 31 39 39 39 00 00 00 00 00 00 00 00 | es. 1999
|
||||
0040 | 00 00 00 00 B3 33 1D 1F 6A B1 A8 97 54 68 69 73 | 3 j This
|
||||
0050 | 20 73 65 72 76 65 72 20 69 73 20 69 6E 20 6E 6F | server is in no
|
||||
0060 | 20 77 61 79 20 61 66 66 69 6C 69 61 74 65 64 2C | way affiliated,
|
||||
0070 | 20 73 70 6F 6E 73 6F 72 65 64 2C 20 6F 72 20 73 | sponsored, or s
|
||||
0080 | 75 70 70 6F 72 74 65 64 20 62 79 20 53 45 47 41 | upported by SEGA
|
||||
0090 | 20 45 6E 74 65 72 70 72 69 73 65 73 20 6F 72 20 | Enterprises or
|
||||
00A0 | 53 4F 4E 49 43 54 45 41 4D 2E 20 54 68 65 20 70 | SONICTEAM. The p
|
||||
00B0 | 72 65 63 65 64 69 6E 67 20 6D 65 73 73 61 67 65 | receding message
|
||||
00C0 | 20 65 78 69 73 74 73 20 6F 6E 6C 79 20 74 6F 20 | exists only to
|
||||
00D0 | 72 65 6D 61 69 6E 20 63 6F 6D 70 61 74 69 62 6C | remain compatibl
|
||||
00E0 | 65 20 77 69 74 68 20 70 72 6F 67 72 61 6D 73 20 | e with programs
|
||||
00F0 | 74 68 61 74 20 65 78 70 65 63 74 20 69 74 2E 00 | that expect it.
|
||||
I 40469 2023-05-26 10:42:49 - [Commands] Received from C-3 (version=GC command=90 flag=00)
|
||||
0000 | 90 00 28 00 37 37 37 37 37 37 37 37 00 00 00 00 | ( 77777777
|
||||
0010 | 00 00 00 00 00 31 31 31 31 31 31 31 31 00 00 00 | 11111111
|
||||
0020 | 00 00 00 00 00 00 00 00 |
|
||||
I 40469 2023-05-26 10:42:49 - [Commands] Sending to C-3 (version=DC command=90 flag=02)
|
||||
0000 | 90 02 04 00 |
|
||||
I 40469 2023-05-26 10:42:49 - [Commands] Received from C-3 (version=DC command=93 flag=00)
|
||||
0000 | 93 00 14 01 00 00 01 00 77 77 77 77 00 00 13 2B | wwww +
|
||||
0010 | 64 B2 2C B2 21 00 00 00 00 01 00 00 37 37 37 37 | d , ! 7777
|
||||
0020 | 37 37 37 37 00 00 00 00 00 00 00 00 00 31 31 31 | 7777 111
|
||||
0030 | 31 31 31 31 31 00 00 00 00 00 00 00 00 00 38 41 | 11111 8A
|
||||
0040 | 30 46 32 39 36 45 00 00 00 00 00 00 00 00 00 00 | 0F296E
|
||||
0050 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0060 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0070 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0080 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0090 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 54 61 | Ta
|
||||
00A0 | 6C 69 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | li
|
||||
00B0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
00D0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
00E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
00F0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0100 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0110 | 00 00 00 00 |
|
||||
I 40469 2023-05-26 10:42:49 - [C-3] Game version changed to DC
|
||||
I 40469 2023-05-26 10:42:49 - [Commands] Sending to C-3 (version=DC command=04 flag=00)
|
||||
0000 | 04 00 2C 00 00 00 01 00 77 77 77 77 39 98 AC 82 | , wwww9
|
||||
0010 | 0E 89 2A 49 14 02 02 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
I 40469 2023-05-26 10:42:49 - [Commands] Sending to C-3 (version=DC command=07 flag=05)
|
||||
0000 | 07 04 90 00 11 00 00 11 FF FF FF FF 04 00 41 6C | Al
|
||||
0010 | 65 78 61 6E 64 72 69 61 00 00 00 00 00 00 00 00 | exandria
|
||||
0020 | 11 00 00 11 11 22 22 11 04 0F 47 6F 20 74 6F 20 | "" Go to
|
||||
0030 | 6C 6F 62 62 79 00 00 00 00 00 00 00 11 00 00 11 | lobby
|
||||
0040 | 11 44 44 11 04 0F 44 6F 77 6E 6C 6F 61 64 20 71 | DD Download q
|
||||
0050 | 75 65 73 74 73 00 00 00 11 00 00 11 11 88 88 11 | uests
|
||||
0060 | 04 0F 44 69 73 63 6F 6E 6E 65 63 74 00 00 00 00 | Disconnect
|
||||
0070 | 00 00 00 00 11 00 00 11 11 99 99 11 04 0F 43 6C | Cl
|
||||
0080 | 65 61 72 20 6C 69 63 65 6E 73 65 00 00 00 00 00 | ear license
|
||||
I 40469 2023-05-26 10:42:51 - [Commands] Received from C-3 (version=DC command=10 flag=00)
|
||||
0000 | 10 00 0C 00 11 00 00 11 11 88 88 11 |
|
||||
I 40469 2023-05-26 10:42:51 - [Server] Client disconnected: C-3 on fd 38
|
||||
I 40469 2023-05-26 10:42:51 - [C-3] Deleted
|
||||
|
||||
Reference in New Issue
Block a user