split IS_TRIAL_EDITION into three flags and recognize Ep3 Trial Edition

This commit is contained in:
Martin Michelsen
2023-08-13 19:30:58 -07:00
parent 9b66e07c06
commit 7e55719983
10 changed files with 55 additions and 41 deletions
+2 -1
View File
@@ -495,7 +495,8 @@ static void proxy_command_lobby_event(shared_ptr<ServerState>,
send_text_message(session.client_channel, u"$C6No such lobby event.");
} else {
session.options.override_lobby_event = new_event;
if ((session.version == GameVersion::GC && !(session.newserv_client_config.cfg.flags & Client::Flag::IS_TRIAL_EDITION)) ||
// This command is supported on all V3 versions except Ep1&2 Trial
if ((session.version == GameVersion::GC && !(session.newserv_client_config.cfg.flags & Client::Flag::IS_GC_TRIAL_EDITION)) ||
(session.version == GameVersion::XB) ||
(session.version == GameVersion::BB)) {
session.client_channel.send(0xDA, session.options.override_lobby_event);
+2 -2
View File
@@ -101,7 +101,7 @@ Client::~Client() {
QuestScriptVersion Client::quest_version() const {
switch (this->version()) {
case GameVersion::DC:
if (this->flags & Flag::IS_TRIAL_EDITION) {
if (this->flags & Flag::IS_DC_TRIAL_EDITION) {
return QuestScriptVersion::DC_NTE;
} else if (this->flags & Flag::IS_DC_V1) {
return QuestScriptVersion::DC_V1;
@@ -111,7 +111,7 @@ QuestScriptVersion Client::quest_version() const {
case GameVersion::PC:
return QuestScriptVersion::PC_V2;
case GameVersion::GC:
if (this->flags & Flag::IS_TRIAL_EDITION) {
if (this->flags & Flag::IS_GC_TRIAL_EDITION) {
return QuestScriptVersion::GC_NTE;
} else if (this->flags & Flag::IS_EPISODE_3) {
return QuestScriptVersion::GC_EP3;
+13 -10
View File
@@ -49,16 +49,9 @@ struct ClientOptions {
struct Client {
enum Flag {
// This flag has two meanings. If set on a client with GameVersion::DC, then
// IS_DC_V1 is also set. In this case, the client is DC Network Trial
// Edition, which uses several commands that no other version uses. If this
// flag is set without IS_DC_V1, then the client is GC Episodes 1 & 2 Trial
// Edition, and therefore uses V2 encryption instead of V3 encryption, and
// doesn't support some commands.
// Note that this flag is NOT set for Episode 3 Trial Edition clients, since
// 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,
// Client is DC Network Trial Edition, which is missing a lot of features
// and uses some different command numbers than any other version
IS_DC_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,
@@ -66,6 +59,14 @@ struct Client {
IS_DC_V1_PROTOTYPE = 0x00040000,
// Client is DC v1
IS_DC_V1 = 0x00000010,
// Client is GC Episodes 1&2 Trial Edition, which is much more like PC than
// actual GC Episodes 1&2 - it uses PC encryption and is missing most of the
// features added in Episodes 1&2
IS_GC_TRIAL_EDITION = 0x00200000,
// Client is GC Episode 3 Trial Edition, which is fairly close to the final
// Episode 3 build, but is missing a few commands that we'll have to avoid
// sending
IS_EP3_TRIAL_EDITION = 0x00400000,
// For patch server clients, client is Blue Burst rather than PC
IS_BB_PATCH = 0x00000001,
// After joining a lobby, client will no longer send D6 commands when they
@@ -107,6 +108,8 @@ struct Client {
// Client has received newserv's Episode 3 card definitions, so don't send
// them again
HAS_EP3_CARD_DEFS = 0x00004000,
UNUSED_FLAG_BITS = 0xFF800000,
};
uint64_t id;
+2 -1
View File
@@ -891,8 +891,9 @@ static HandlerResult S_V3_1A_D5(shared_ptr<ServerState>,
static HandlerResult S_V3_BB_DA(shared_ptr<ServerState>,
ProxyServer::LinkedSession& session, uint16_t, uint32_t flag, string&) {
// This command is supported on all V3 versions except Ep1&2 Trial
if ((session.version == GameVersion::GC) &&
(session.newserv_client_config.cfg.flags & Client::Flag::IS_TRIAL_EDITION)) {
(session.newserv_client_config.cfg.flags & Client::Flag::IS_GC_TRIAL_EDITION)) {
return HandlerResult::Type::SUPPRESS;
} else if ((session.options.override_lobby_event >= 0) &&
(static_cast<int16_t>(flag) != session.options.override_lobby_event)) {
+1 -1
View File
@@ -628,7 +628,7 @@ void ProxyServer::LinkedSession::on_error(Channel& ch, short events) {
session->log.info("%s channel connected", is_server_stream ? "Server" : "Client");
if (is_server_stream && (session->options.override_lobby_event >= 0) &&
(((session->version == GameVersion::GC) && !(session->newserv_client_config.cfg.flags & Client::Flag::IS_TRIAL_EDITION)) ||
(((session->version == GameVersion::GC) && !(session->newserv_client_config.cfg.flags & Client::Flag::IS_GC_TRIAL_EDITION)) ||
(session->version == GameVersion::XB) ||
(session->version == GameVersion::BB))) {
session->client_channel.send(0xDA, session->options.override_lobby_event);
+17 -14
View File
@@ -235,7 +235,7 @@ void on_login_complete(shared_ptr<ServerState> s, shared_ptr<Client> c) {
auto team = s->ep3_tournament_index->team_for_serial_number(c->license->serial_number);
auto tourn = team ? team->tournament.lock() : nullptr;
c->ep3_tournament_team = team;
if (!(c->flags & Client::Flag::IS_TRIAL_EDITION)) {
if (!(c->flags & Client::Flag::IS_EP3_TRIAL_EDITION)) {
send_ep3_confirm_tournament_entry(s, c, tourn);
}
}
@@ -302,7 +302,7 @@ static void set_console_client_flags(
c->channel.version = GameVersion::DC;
c->log.info("Game version changed to DC");
} else if (c->version() == GameVersion::GC) {
c->flags |= Client::Flag::IS_TRIAL_EDITION;
c->flags |= Client::Flag::IS_GC_TRIAL_EDITION;
c->log.info("Trial edition flag set");
}
}
@@ -358,7 +358,7 @@ static void on_88_DCNTE(shared_ptr<ServerState> s, shared_ptr<Client> c,
const auto& cmd = check_size_t<C_Login_DCNTE_88>(data);
c->channel.version = GameVersion::DC;
c->flags |= flags_for_version(c->version(), -1);
c->flags |= Client::Flag::IS_DC_V1 | Client::Flag::IS_TRIAL_EDITION;
c->flags |= Client::Flag::IS_DC_V1 | Client::Flag::IS_DC_TRIAL_EDITION;
uint32_t serial_number = stoul(cmd.serial_number, nullptr, 16);
try {
@@ -390,7 +390,7 @@ static void on_8B_DCNTE(shared_ptr<ServerState> s, shared_ptr<Client> c,
const auto& cmd = check_size_t<C_Login_DCNTE_8B>(data, sizeof(C_LoginExtended_DCNTE_8B));
c->channel.version = GameVersion::DC;
c->flags |= flags_for_version(c->version(), -1);
c->flags |= Client::Flag::IS_DC_V1 | Client::Flag::IS_TRIAL_EDITION;
c->flags |= Client::Flag::IS_DC_V1 | Client::Flag::IS_DC_TRIAL_EDITION;
uint32_t serial_number = stoul(cmd.serial_number, nullptr, 16);
try {
@@ -1261,7 +1261,7 @@ static void on_tournament_bracket_updated(
!c->license ||
!serial_numbers.count(c->license->serial_number) ||
c->ep3_tournament_team.expired() ||
(c->flags & Client::Flag::IS_TRIAL_EDITION)) {
(c->flags & Client::Flag::IS_EP3_TRIAL_EDITION)) {
continue;
}
send_ep3_confirm_tournament_entry(s, c, tourn);
@@ -1644,7 +1644,7 @@ static void on_10(shared_ptr<ServerState> s, shared_ptr<Client> c,
// 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))) {
(c->flags & (Client::Flag::IS_DC_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE))) {
send_client_to_lobby_server(s, c);
} else {
send_command(c, 0x97, 0x01);
@@ -1830,7 +1830,7 @@ static void on_10(shared_ptr<ServerState> s, shared_ptr<Client> c,
} else {
// Clear Check Tactics menu so client won't see newserv tournament
// state while logically on another server
if ((c->flags & Client::Flag::IS_EPISODE_3) && !(c->flags & Client::Flag::IS_TRIAL_EDITION)) {
if ((c->flags & Client::Flag::IS_EPISODE_3) && !(c->flags & Client::Flag::IS_EP3_TRIAL_EDITION)) {
send_ep3_confirm_tournament_entry(s, c, nullptr);
}
@@ -1984,7 +1984,7 @@ static void on_10(shared_ptr<ServerState> s, shared_ptr<Client> c,
// check/clear it later.
if ((l->clients[x]->version() != GameVersion::DC) &&
(l->clients[x]->version() != GameVersion::PC) &&
!(l->clients[x]->flags & Client::Flag::IS_TRIAL_EDITION)) {
!(l->clients[x]->flags & Client::Flag::IS_GC_TRIAL_EDITION)) {
l->clients[x]->flags |= Client::Flag::LOADING_QUEST;
l->clients[x]->disconnect_hooks.emplace(QUEST_BARRIER_DISCONNECT_HOOK_NAME, [l]() -> void {
send_quest_barrier_if_all_clients_ready(l);
@@ -2197,7 +2197,7 @@ static void on_A1(shared_ptr<ServerState> s, shared_ptr<Client> c,
static void on_8E_DCNTE(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t command, uint32_t flag, const string& data) {
if (c->flags & Client::Flag::IS_TRIAL_EDITION) {
if (c->flags & Client::Flag::IS_DC_TRIAL_EDITION) {
on_A0(s, c, command, flag, data);
} else {
throw runtime_error("non-DCNTE client sent 8E");
@@ -2206,7 +2206,7 @@ static void on_8E_DCNTE(shared_ptr<ServerState> s, shared_ptr<Client> c,
static void on_8F_DCNTE(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t command, uint32_t flag, const string& data) {
if (c->flags & Client::Flag::IS_TRIAL_EDITION) {
if (c->flags & Client::Flag::IS_DC_TRIAL_EDITION) {
on_A1(s, c, command, flag, data);
} else {
throw runtime_error("non-DCNTE client sent 8F");
@@ -2368,7 +2368,10 @@ static void on_AA(shared_ptr<ServerState> s,
shared_ptr<Client> c, uint16_t, uint32_t, const string& data) {
const auto& cmd = check_size_t<C_SendQuestStatistic_V3_BB_AA>(data);
if (c->flags & Client::Flag::IS_TRIAL_EDITION) {
if (c->version() == GameVersion::DC || c->version() == GameVersion::PC) {
throw runtime_error("pre-V3 client sent update quest stats command");
}
if (c->flags & Client::Flag::IS_GC_TRIAL_EDITION) {
throw runtime_error("trial edition client sent update quest stats command");
}
@@ -3257,7 +3260,7 @@ shared_ptr<Lobby> create_game_generic(
bool is_solo = (game->mode == GameMode::SOLO);
// Generate the map variations
if (game->is_ep3() || (c->version() == GameVersion::DC && (c->flags & (Client::Flag::IS_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE)))) {
if (game->is_ep3() || (c->version() == GameVersion::DC && (c->flags & (Client::Flag::IS_DC_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE)))) {
game->variations.clear(0);
} else {
generate_variations(game->variations, game->random_crypt, game->episode, is_solo);
@@ -3338,7 +3341,7 @@ static void on_0C_C1_E7_EC(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t command, uint32_t, const string& data) {
shared_ptr<Lobby> game;
if (c->version() == GameVersion::DC && (c->flags & (Client::Flag::IS_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE))) {
if (c->version() == GameVersion::DC && (c->flags & (Client::Flag::IS_DC_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);
@@ -3449,7 +3452,7 @@ static void on_C1_BB(shared_ptr<ServerState> s, shared_ptr<Client> c,
static void on_8A(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t, uint32_t, const string& data) {
if ((c->version() == GameVersion::DC) && (c->flags & Client::Flag::IS_TRIAL_EDITION)) {
if ((c->version() == GameVersion::DC) && (c->flags & Client::Flag::IS_DC_TRIAL_EDITION)) {
const auto& cmd = check_size_t<C_ConnectionInfo_DCNTE_8A>(data);
set_console_client_flags(c, cmd.sub_version);
send_command(c, 0x8A, 0x01);
+1 -1
View File
@@ -2044,7 +2044,7 @@ void on_subcommand_multi(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
if (data.empty()) {
throw runtime_error("game command is empty");
}
if (c->version() == GameVersion::DC && (c->flags & (Client::Flag::IS_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE))) {
if (c->version() == GameVersion::DC && (c->flags & (Client::Flag::IS_DC_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, data.data(), data.size());
} else {
+8 -8
View File
@@ -1104,7 +1104,7 @@ void send_menu_t(shared_ptr<Client> c, shared_ptr<const Menu> menu, bool is_info
switch (c->version()) {
case GameVersion::DC:
is_visible &= !(item.flags & MenuItem::Flag::INVISIBLE_ON_DC);
if (c->flags & Client::Flag::IS_TRIAL_EDITION) {
if (c->flags & Client::Flag::IS_DC_TRIAL_EDITION) {
is_visible &= !(item.flags & MenuItem::Flag::INVISIBLE_ON_DCNTE);
}
break;
@@ -1113,7 +1113,7 @@ void send_menu_t(shared_ptr<Client> c, shared_ptr<const Menu> menu, bool is_info
break;
case GameVersion::GC:
is_visible &= !(item.flags & MenuItem::Flag::INVISIBLE_ON_GC);
if (c->flags & Client::Flag::IS_TRIAL_EDITION) {
if (c->flags & Client::Flag::IS_GC_TRIAL_EDITION) {
is_visible &= !(item.flags & MenuItem::Flag::INVISIBLE_ON_GC_TRIAL_EDITION);
}
break;
@@ -1752,7 +1752,7 @@ void send_join_lobby(shared_ptr<Client> c, shared_ptr<Lobby> l) {
send_join_game_t<PlayerLobbyDataPC>(c, l);
break;
case GameVersion::DC:
if (c->flags & (Client::Flag::IS_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE)) {
if (c->flags & (Client::Flag::IS_DC_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE)) {
send_join_game_dc_nte(c, l);
break;
}
@@ -1772,7 +1772,7 @@ void send_join_lobby(shared_ptr<Client> c, shared_ptr<Lobby> l) {
} else {
switch (c->version()) {
case GameVersion::DC:
if (c->flags & (Client::Flag::IS_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE)) {
if (c->flags & (Client::Flag::IS_DC_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE)) {
send_join_lobby_dc_nte(c, l);
} else {
send_join_lobby_t<PlayerLobbyDataDCGC, PlayerDispDataDCPCV3, PlayerRecordsEntry_DC>(c, l);
@@ -1807,7 +1807,7 @@ void send_player_join_notification(shared_ptr<Client> c,
shared_ptr<Lobby> l, shared_ptr<Client> joining_client) {
switch (c->version()) {
case GameVersion::DC:
if (c->flags & (Client::Flag::IS_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE)) {
if (c->flags & (Client::Flag::IS_DC_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE)) {
send_join_lobby_dc_nte(c, l, joining_client);
} else {
send_join_lobby_t<PlayerLobbyDataDCGC, PlayerDispDataDCPCV3, PlayerRecordsEntry_DC>(c, l, joining_client);
@@ -1851,7 +1851,7 @@ void send_self_leave_notification(shared_ptr<Client> c) {
void send_get_player_info(shared_ptr<Client> c) {
if ((c->version() == GameVersion::DC) &&
(c->flags & Client::Flag::IS_TRIAL_EDITION)) {
(c->flags & Client::Flag::IS_DC_TRIAL_EDITION)) {
send_command(c, 0x8D, 0x00);
} else {
send_command(c, 0x95, 0x00);
@@ -2254,7 +2254,7 @@ void send_ep3_confirm_tournament_entry(
shared_ptr<ServerState> s,
shared_ptr<Client> c,
shared_ptr<const Episode3::Tournament> tourn) {
if (c->flags & Client::Flag::IS_TRIAL_EDITION) {
if (c->flags & Client::Flag::IS_EP3_TRIAL_EDITION) {
throw runtime_error("cannot send tournament entry command to Episode 3 Trial Edition client");
}
@@ -2850,7 +2850,7 @@ void send_change_event(shared_ptr<Client> c, uint8_t new_event) {
// This command isn't supported on versions before V3, nor on Trial Edition.
if ((c->version() == GameVersion::DC) ||
(c->version() == GameVersion::PC) ||
(c->flags & Client::Flag::IS_TRIAL_EDITION)) {
(c->flags & Client::Flag::IS_GC_TRIAL_EDITION)) {
return;
}
send_command(c, 0xDA, new_event);
+1 -1
View File
@@ -707,7 +707,7 @@ Proxy session commands:\n\
} else {
session->options.override_lobby_event = event_for_name(command_args);
if ((session->version != GameVersion::DC) &&
(session->version != GameVersion::PC) && (!((session->version == GameVersion::GC) && (session->newserv_client_config.cfg.flags & Client::Flag::IS_TRIAL_EDITION)))) {
(session->version != GameVersion::PC) && (!((session->version == GameVersion::GC) && (session->newserv_client_config.cfg.flags & Client::Flag::IS_GC_TRIAL_EDITION)))) {
session->client_channel.send(0xDA, session->options.override_lobby_event);
}
}
+8 -2
View File
@@ -42,9 +42,9 @@ uint32_t flags_for_version(GameVersion version, int64_t sub_version) {
// TODO: Which other sub_versions of DC v1 and v2 exist?
case 0x20: // DCNTE
// In the case of DCNTE, the IS_TRIAL_EDITION flag is already set when we
// get here, so the remaining flags are the same as DCv1
case 0x21: // DCv1 US
// In the case of DCNTE, the IS_DC_TRIAL_EDITION flag is already set when
// we get here, so the remaining flags are the same as DCv1
return Client::Flag::IS_DC_V1 |
Client::Flag::NO_D6 |
Client::Flag::NO_SEND_FUNCTION_CALL;
@@ -65,6 +65,9 @@ uint32_t flags_for_version(GameVersion version, int64_t sub_version) {
case 0x30: // GC Ep1&2 JP v1.02, at least one version of PSO XB
case 0x31: // GC Ep1&2 US v1.00, GC US v1.01, GC EU v1.00, GC JP v1.00
case 0x34: // GC Ep1&2 JP v1.03
// In the case of GC Trial Edition, the IS_GC_TRIAL_EDITION flag is
// already set when we get here (because the client has used V2 encryption
// instead of V3)
return 0;
case 0x32: // GC Ep1&2 EU 50Hz
case 0x33: // GC Ep1&2 EU 60Hz
@@ -79,8 +82,11 @@ uint32_t flags_for_version(GameVersion version, int64_t sub_version) {
Client::Flag::NO_SEND_FUNCTION_CALL;
case 0x40: // GC Ep3 trial
// TODO: Is this sub_version used for any other Ep3 versions? Is the final
// JP release really 42 and not 40?
return Client::Flag::NO_D6_AFTER_LOBBY |
Client::Flag::IS_EPISODE_3 |
Client::Flag::IS_EP3_TRIAL_EDITION |
Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL |
Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
case 0x42: // GC Ep3 JP