add option to use temporary licenses for NTE versions
This commit is contained in:
@@ -135,6 +135,18 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version)
|
||||
}
|
||||
}
|
||||
|
||||
bool Client::Config::should_update_vs(const Config& other) const {
|
||||
constexpr uint64_t mask = static_cast<uint64_t>(Flag::CLIENT_SIDE_MASK);
|
||||
return ((this->enabled_flags ^ other.enabled_flags) & mask) ||
|
||||
(this->specific_version != other.specific_version) ||
|
||||
(this->override_random_seed != other.override_random_seed) ||
|
||||
(this->override_section_id != other.override_section_id) ||
|
||||
(this->override_lobby_event != other.override_lobby_event) ||
|
||||
(this->override_lobby_number != other.override_lobby_number) ||
|
||||
(this->proxy_destination_address != other.proxy_destination_address) ||
|
||||
(this->proxy_destination_port != other.proxy_destination_port);
|
||||
}
|
||||
|
||||
Client::Client(
|
||||
shared_ptr<Server> server,
|
||||
struct bufferevent* bev,
|
||||
@@ -243,6 +255,23 @@ void Client::set_license(shared_ptr<License> l) {
|
||||
this->license = l;
|
||||
}
|
||||
|
||||
void Client::convert_license_to_temporary_if_nte() {
|
||||
// If the session is a prototype version and the license was created and we
|
||||
// should use a temporary license instead, delete the permanent license and
|
||||
// replace it with a temporary license.
|
||||
auto s = this->require_server_state();
|
||||
if (s->use_temp_licenses_for_prototypes &&
|
||||
this->config.check_flag(Client::Flag::LICENSE_WAS_CREATED) &&
|
||||
is_any_nte(this->version())) {
|
||||
this->log.info("Client is a prototype version and the license was created during this session; converting permanent license to temporary license");
|
||||
s->license_index->remove(this->license->serial_number);
|
||||
auto new_l = s->license_index->create_temporary_license();
|
||||
this->license->delete_file();
|
||||
*new_l = std::move(*this->license);
|
||||
this->set_license(new_l);
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<ServerState> Client::require_server_state() const {
|
||||
auto server = this->server.lock();
|
||||
if (!server) {
|
||||
|
||||
+20
-10
@@ -30,8 +30,15 @@ public:
|
||||
enum class Flag : uint64_t {
|
||||
// clang-format off
|
||||
|
||||
// This mask specifies which flags are sent to the client
|
||||
// TODO: It'd be nice to use a pattern here (e.g. all server-side flags are
|
||||
// in the high bits) but that would require re-recording or manually
|
||||
// rewriting all the tests
|
||||
CLIENT_SIDE_MASK = 0xFFFFFFFFFC0FFFFB,
|
||||
|
||||
// Version-related flags
|
||||
CHECKED_FOR_DC_V1_PROTOTYPE = 0x0000000000000002,
|
||||
LICENSE_WAS_CREATED = 0x0000000000000004, // Server-side only
|
||||
NO_D6_AFTER_LOBBY = 0x0000000000000100,
|
||||
NO_D6 = 0x0000000000000200,
|
||||
FORCE_ENGLISH_LANGUAGE_BB = 0x0000000000000400,
|
||||
@@ -44,20 +51,20 @@ public:
|
||||
USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x0000000000010000,
|
||||
|
||||
// State flags
|
||||
LOADING = 0x0000000000100000,
|
||||
LOADING_QUEST = 0x0000000000200000,
|
||||
LOADING_RUNNING_JOINABLE_QUEST = 0x0000000000400000,
|
||||
LOADING_TOURNAMENT = 0x0000000000800000,
|
||||
IN_INFORMATION_MENU = 0x0000000001000000,
|
||||
AT_WELCOME_MESSAGE = 0x0000000002000000,
|
||||
LOADING = 0x0000000000100000, // Server-side only
|
||||
LOADING_QUEST = 0x0000000000200000, // Server-side only
|
||||
LOADING_RUNNING_JOINABLE_QUEST = 0x0000000000400000, // Server-side only
|
||||
LOADING_TOURNAMENT = 0x0000000000800000, // Server-side only
|
||||
IN_INFORMATION_MENU = 0x0000000001000000, // Server-side only
|
||||
AT_WELCOME_MESSAGE = 0x0000000002000000, // Server-side only
|
||||
SAVE_ENABLED = 0x0000000004000000,
|
||||
HAS_EP3_CARD_DEFS = 0x0000000008000000,
|
||||
HAS_EP3_MEDIA_UPDATES = 0x0000000010000000,
|
||||
USE_OVERRIDE_RANDOM_SEED = 0x0000000020000000,
|
||||
HAS_GUILD_CARD_NUMBER = 0x0000000040000000,
|
||||
AT_BANK_COUNTER = 0x0000000080000000,
|
||||
SHOULD_SEND_ARTIFICIAL_ITEM_STATE = 0x0001000000000000,
|
||||
SHOULD_SEND_ARTIFICIAL_FLAG_STATE = 0x0002000000000000,
|
||||
AT_BANK_COUNTER = 0x0000000080000000, // Server-side only
|
||||
SHOULD_SEND_ARTIFICIAL_ITEM_STATE = 0x0001000000000000, // Server-side only
|
||||
SHOULD_SEND_ARTIFICIAL_FLAG_STATE = 0x0002000000000000, // Server-side only
|
||||
SHOULD_SEND_ENABLE_SAVE = 0x0004000000000000,
|
||||
SWITCH_ASSIST_ENABLED = 0x0000000100000000,
|
||||
|
||||
@@ -99,6 +106,8 @@ public:
|
||||
bool operator==(const Config& other) const = default;
|
||||
bool operator!=(const Config& other) const = default;
|
||||
|
||||
bool should_update_vs(const Config& other) const;
|
||||
|
||||
[[nodiscard]] static inline bool check_flag(uint64_t enabled_flags, Flag flag) {
|
||||
return !!(enabled_flags & static_cast<uint64_t>(flag));
|
||||
}
|
||||
@@ -139,7 +148,7 @@ public:
|
||||
StringWriter w;
|
||||
w.put_u32l(CLIENT_CONFIG_MAGIC);
|
||||
w.put_u32l(this->specific_version);
|
||||
w.put_u64l(this->enabled_flags);
|
||||
w.put_u64l(this->enabled_flags & static_cast<uint64_t>(Flag::CLIENT_SIDE_MASK));
|
||||
w.put_u32l(this->override_random_seed);
|
||||
w.put_u32b(this->proxy_destination_address);
|
||||
w.put_u16l(this->proxy_destination_port);
|
||||
@@ -263,6 +272,7 @@ public:
|
||||
}
|
||||
|
||||
void set_license(std::shared_ptr<License> l);
|
||||
void convert_license_to_temporary_if_nte();
|
||||
|
||||
void sync_config();
|
||||
|
||||
|
||||
+16
-7
@@ -257,6 +257,8 @@ static void send_main_menu(shared_ptr<Client> c) {
|
||||
}
|
||||
|
||||
void on_login_complete(shared_ptr<Client> c) {
|
||||
c->convert_license_to_temporary_if_nte();
|
||||
|
||||
// On BB, this function is called when the data server phase is done (and we
|
||||
// should send the ship select menu), so we don't need to check for it here.
|
||||
switch (c->server_behavior) {
|
||||
@@ -428,6 +430,7 @@ static void on_DB_V3(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
} else {
|
||||
c->config.set_flag(Client::Flag::LICENSE_WAS_CREATED);
|
||||
auto l = s->license_index->create_license();
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = cmd.access_key.decode();
|
||||
@@ -472,6 +475,7 @@ static void on_88_DCNTE(shared_ptr<Client> c, uint16_t, uint32_t, string& data)
|
||||
send_message_box(c, "Incorrect serial number");
|
||||
c->should_disconnect = true;
|
||||
} else {
|
||||
c->config.set_flag(Client::Flag::LICENSE_WAS_CREATED);
|
||||
auto l = s->license_index->create_license();
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = cmd.access_key.decode();
|
||||
@@ -515,6 +519,7 @@ static void on_8B_DCNTE(shared_ptr<Client> c, uint16_t, uint32_t, string& data)
|
||||
send_message_box(c, "Incorrect serial number");
|
||||
c->should_disconnect = true;
|
||||
} else {
|
||||
c->config.set_flag(Client::Flag::LICENSE_WAS_CREATED);
|
||||
auto l = s->license_index->create_license();
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = cmd.access_key.decode();
|
||||
@@ -569,6 +574,7 @@ static void on_90_DC(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
send_command(c, 0x90, 0x03);
|
||||
c->should_disconnect = true;
|
||||
} else {
|
||||
c->config.set_flag(Client::Flag::LICENSE_WAS_CREATED);
|
||||
auto l = s->license_index->create_license();
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = cmd.access_key.decode();
|
||||
@@ -626,6 +632,7 @@ static void on_93_DC(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
} else {
|
||||
c->config.set_flag(Client::Flag::LICENSE_WAS_CREATED);
|
||||
auto l = s->license_index->create_license();
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = cmd.access_key.decode();
|
||||
@@ -743,6 +750,7 @@ static void on_9A(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
c->should_disconnect = true;
|
||||
|
||||
} else if (is_v1_or_v2(c->version())) {
|
||||
c->config.set_flag(Client::Flag::LICENSE_WAS_CREATED);
|
||||
auto l = s->license_index->create_license();
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = cmd.access_key.decode();
|
||||
@@ -805,6 +813,7 @@ static void on_9C(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
} else {
|
||||
c->config.set_flag(Client::Flag::LICENSE_WAS_CREATED);
|
||||
auto l = s->license_index->create_license();
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = cmd.access_key.decode();
|
||||
@@ -942,16 +951,13 @@ static void on_9D_9E(shared_ptr<Client> c, uint16_t command, uint32_t, string& d
|
||||
return;
|
||||
|
||||
} catch (const LicenseIndex::missing_license& e) {
|
||||
// On GC, the client should have sent a different command containing the
|
||||
// password already, which should have created and added a license. So, if
|
||||
// no license exists at this point, disconnect the client even if
|
||||
// unregistered clients are allowed.
|
||||
if (is_v3(c->version()) || !s->allow_unregistered_users || (serial_number == 0)) {
|
||||
if (!s->allow_unregistered_users || (serial_number == 0)) {
|
||||
send_command(c, 0x04, 0x04);
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
|
||||
} else if (is_v1_or_v2(c->version())) {
|
||||
} else if (is_v1_or_v2(c->version()) || is_v3(c->version())) {
|
||||
c->config.set_flag(Client::Flag::LICENSE_WAS_CREATED);
|
||||
auto l = s->license_index->create_license();
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = base_cmd->access_key.decode();
|
||||
@@ -1022,6 +1028,7 @@ static void on_9E_XB(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
return;
|
||||
|
||||
} catch (const LicenseIndex::missing_license& e) {
|
||||
c->config.set_flag(Client::Flag::LICENSE_WAS_CREATED);
|
||||
auto l = s->license_index->create_license();
|
||||
l->serial_number = fnv1a32(xb_gamertag) & 0x7FFFFFFF;
|
||||
l->xb_gamertag = xb_gamertag;
|
||||
@@ -1113,6 +1120,7 @@ static void on_93_BB(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
} else {
|
||||
c->config.set_flag(Client::Flag::LICENSE_WAS_CREATED);
|
||||
auto l = s->license_index->create_license();
|
||||
l->serial_number = fnv1a32(username) & 0x7FFFFFFF;
|
||||
l->bb_username = username;
|
||||
@@ -3034,6 +3042,7 @@ static void on_61_98(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
|
||||
if (c->config.specific_version == 0x33000000) {
|
||||
c->config.specific_version = 0x33534A54; // 3SJT
|
||||
}
|
||||
c->convert_license_to_temporary_if_nte();
|
||||
}
|
||||
cmd = &check_size_t<C_CharacterData_V3_61_98>(data, 0xFFFF);
|
||||
}
|
||||
@@ -5071,7 +5080,7 @@ static void on_04_P(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
} else {
|
||||
|
||||
c->config.set_flag(Client::Flag::LICENSE_WAS_CREATED);
|
||||
auto l = s->license_index->create_license();
|
||||
l->serial_number = fnv1a32(cmd.username.decode()) & 0x7FFFFFFF;
|
||||
l->bb_username = cmd.username.decode();
|
||||
|
||||
+1
-1
@@ -282,7 +282,7 @@ void send_server_init(shared_ptr<Client> c, uint8_t flags) {
|
||||
}
|
||||
|
||||
void send_update_client_config(shared_ptr<Client> c, bool always_send) {
|
||||
if (always_send || (is_v3(c->version()) && (c->config != c->synced_config))) {
|
||||
if (always_send || (is_v3(c->version()) && (c->config.should_update_vs(c->synced_config)))) {
|
||||
switch (c->version()) {
|
||||
case Version::DC_NTE:
|
||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||
|
||||
@@ -39,6 +39,7 @@ ServerState::ServerState(shared_ptr<struct event_base> base, const string& confi
|
||||
ip_stack_debug(false),
|
||||
allow_unregistered_users(false),
|
||||
allow_pc_nte(false),
|
||||
use_temp_licenses_for_prototypes(true),
|
||||
allow_dc_pc_games(false),
|
||||
allow_gc_xb_games(true),
|
||||
allowed_drop_modes_v1_v2_normal(0x1F),
|
||||
@@ -669,6 +670,7 @@ void ServerState::parse_config(const JSON& json, bool is_reload) {
|
||||
this->ip_stack_debug = json.get_bool("IPStackDebug", this->ip_stack_debug);
|
||||
this->allow_unregistered_users = json.get_bool("AllowUnregisteredUsers", this->allow_unregistered_users);
|
||||
this->allow_pc_nte = json.get_bool("AllowPCNTE", this->allow_pc_nte);
|
||||
this->use_temp_licenses_for_prototypes = json.get_bool("UseTemporaryLicensesForPrototypes", this->use_temp_licenses_for_prototypes);
|
||||
this->allowed_drop_modes_v1_v2_normal = json.get_int("AllowedDropModesV1V2Normal", this->allowed_drop_modes_v1_v2_normal);
|
||||
this->allowed_drop_modes_v1_v2_battle = json.get_int("AllowedDropModesV1V2Battle", this->allowed_drop_modes_v1_v2_battle);
|
||||
this->allowed_drop_modes_v1_v2_challenge = json.get_int("AllowedDropModesV1V2Challenge", this->allowed_drop_modes_v1_v2_challenge);
|
||||
|
||||
@@ -75,6 +75,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
bool ip_stack_debug;
|
||||
bool allow_unregistered_users;
|
||||
bool allow_pc_nte;
|
||||
bool use_temp_licenses_for_prototypes;
|
||||
bool allow_dc_pc_games;
|
||||
bool allow_gc_xb_games;
|
||||
uint8_t allowed_drop_modes_v1_v2_normal;
|
||||
|
||||
@@ -29,6 +29,14 @@ const char* name_for_enum<Version>(Version v);
|
||||
template <>
|
||||
Version enum_for_name<Version>(const char* name);
|
||||
|
||||
inline bool is_any_nte(Version version) {
|
||||
return (version == Version::DC_NTE) ||
|
||||
(version == Version::DC_V1_11_2000_PROTOTYPE) ||
|
||||
(version == Version::PC_NTE) ||
|
||||
(version == Version::GC_NTE) ||
|
||||
(version == Version::GC_EP3_NTE);
|
||||
}
|
||||
|
||||
inline bool is_patch(Version version) {
|
||||
return (version == Version::PC_PATCH) || (version == Version::BB_PATCH);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user