add support for shared serial mechanics
This commit is contained in:
+30
-20
@@ -38,11 +38,11 @@ private:
|
||||
std::string user_msg;
|
||||
};
|
||||
|
||||
static void check_license_flags(shared_ptr<Client> c, uint32_t mask) {
|
||||
static void check_license_flag(shared_ptr<Client> c, License::Flag flag) {
|
||||
if (!c->license) {
|
||||
throw precondition_failed("$C6You are not\nlogged in.");
|
||||
}
|
||||
if ((c->license->flags & mask) != mask) {
|
||||
if (!c->license->check_flag(flag)) {
|
||||
throw precondition_failed("$C6You do not have\npermission to\nrun this command.");
|
||||
}
|
||||
}
|
||||
@@ -66,19 +66,20 @@ static void check_is_ep3(shared_ptr<Client> c, bool is_ep3) {
|
||||
}
|
||||
|
||||
static void check_cheats_enabled(shared_ptr<Lobby> l, shared_ptr<Client> c) {
|
||||
if (!l->check_flag(Lobby::Flag::CHEATS_ENABLED) && !(c->license->flags & License::Flag::CHEAT_ANYWHERE)) {
|
||||
if (!l->check_flag(Lobby::Flag::CHEATS_ENABLED) && !c->license->check_flag(License::Flag::CHEAT_ANYWHERE)) {
|
||||
throw precondition_failed("$C6This command can\nonly be used in\ncheat mode.");
|
||||
}
|
||||
}
|
||||
|
||||
static void check_cheats_allowed(shared_ptr<ServerState> s, shared_ptr<Client> c) {
|
||||
if ((s->cheat_mode_behavior == ServerState::BehaviorSwitch::OFF) && !(c->license->flags & License::Flag::CHEAT_ANYWHERE)) {
|
||||
if ((s->cheat_mode_behavior == ServerState::BehaviorSwitch::OFF) && !c->license->check_flag(License::Flag::CHEAT_ANYWHERE)) {
|
||||
throw precondition_failed("$C6Cheats are disabled\non this server.");
|
||||
}
|
||||
}
|
||||
|
||||
static void check_cheats_allowed(shared_ptr<ServerState> s, shared_ptr<ProxyServer::LinkedSession> ses) {
|
||||
if ((s->cheat_mode_behavior == ServerState::BehaviorSwitch::OFF) && (!ses->license || !(ses->license->flags & License::Flag::CHEAT_ANYWHERE))) {
|
||||
if ((s->cheat_mode_behavior == ServerState::BehaviorSwitch::OFF) &&
|
||||
(!ses->license || !ses->license->check_flag(License::Flag::CHEAT_ANYWHERE))) {
|
||||
throw precondition_failed("$C6Cheats are disabled\non this proxy.");
|
||||
}
|
||||
}
|
||||
@@ -257,13 +258,13 @@ static void proxy_command_lobby_info(shared_ptr<ProxyServer::LinkedSession> ses,
|
||||
}
|
||||
|
||||
static void server_command_ax(shared_ptr<Client> c, const std::string& args) {
|
||||
check_license_flags(c, License::Flag::ANNOUNCE);
|
||||
check_license_flag(c, License::Flag::ANNOUNCE);
|
||||
ax_messages_log.info("%s", args.c_str());
|
||||
}
|
||||
|
||||
static void server_command_announce(shared_ptr<Client> c, const std::string& args) {
|
||||
auto s = c->require_server_state();
|
||||
check_license_flags(c, License::Flag::ANNOUNCE);
|
||||
check_license_flag(c, License::Flag::ANNOUNCE);
|
||||
send_text_message(s, args);
|
||||
}
|
||||
|
||||
@@ -280,7 +281,7 @@ static void proxy_command_arrow(shared_ptr<ProxyServer::LinkedSession> ses, cons
|
||||
}
|
||||
|
||||
static void server_command_debug(shared_ptr<Client> c, const std::string&) {
|
||||
check_license_flags(c, License::Flag::DEBUG);
|
||||
check_license_flag(c, License::Flag::DEBUG);
|
||||
c->config.toggle_flag(Client::Flag::DEBUG_ENABLED);
|
||||
send_text_message_printf(c, "Debug %s", (c->config.check_flag(Client::Flag::DEBUG_ENABLED) ? "enabled" : "disabled"));
|
||||
}
|
||||
@@ -543,7 +544,7 @@ static void server_command_show_material_counts(shared_ptr<Client> c, const std:
|
||||
}
|
||||
|
||||
static void server_command_auction(shared_ptr<Client> c, const std::string&) {
|
||||
check_license_flags(c, License::Flag::DEBUG);
|
||||
check_license_flag(c, License::Flag::DEBUG);
|
||||
auto l = c->require_lobby();
|
||||
if (l->is_game() && l->is_ep3()) {
|
||||
G_InitiateCardAuction_GC_Ep3_6xB5x42 cmd;
|
||||
@@ -746,7 +747,7 @@ static void server_command_cheat(shared_ptr<Client> c, const std::string&) {
|
||||
l->toggle_flag(Lobby::Flag::CHEATS_ENABLED);
|
||||
send_text_message_printf(l, "Cheat mode %s", l->check_flag(Lobby::Flag::CHEATS_ENABLED) ? "enabled" : "disabled");
|
||||
|
||||
if (!(c->license->flags & License::Flag::CHEAT_ANYWHERE)) {
|
||||
if (!c->license->check_flag(License::Flag::CHEAT_ANYWHERE)) {
|
||||
size_t default_min_level = s->default_min_level_for_game(l->base_version, l->episode, l->difficulty);
|
||||
if (l->min_level < default_min_level) {
|
||||
l->min_level = default_min_level;
|
||||
@@ -759,7 +760,7 @@ static void server_command_cheat(shared_ptr<Client> c, const std::string&) {
|
||||
static void server_command_lobby_event(shared_ptr<Client> c, const std::string& args) {
|
||||
auto l = c->require_lobby();
|
||||
check_is_game(l, false);
|
||||
check_license_flags(c, License::Flag::CHANGE_EVENT);
|
||||
check_license_flag(c, License::Flag::CHANGE_EVENT);
|
||||
|
||||
uint8_t new_event = event_for_name(args);
|
||||
if (new_event == 0xFF) {
|
||||
@@ -788,7 +789,7 @@ static void proxy_command_lobby_event(shared_ptr<ProxyServer::LinkedSession> ses
|
||||
}
|
||||
|
||||
static void server_command_lobby_event_all(shared_ptr<Client> c, const std::string& args) {
|
||||
check_license_flags(c, License::Flag::CHANGE_EVENT);
|
||||
check_license_flag(c, License::Flag::CHANGE_EVENT);
|
||||
|
||||
uint8_t new_event = event_for_name(args);
|
||||
if (new_event == 0xFF) {
|
||||
@@ -1043,7 +1044,7 @@ static void server_command_min_level(shared_ptr<Client> c, const std::string& ar
|
||||
size_t new_min_level = stoull(args) - 1;
|
||||
|
||||
auto s = c->require_server_state();
|
||||
bool cheats_allowed = (l->check_flag(Lobby::Flag::CHEATS_ENABLED) || (c->license->flags & License::Flag::CHEAT_ANYWHERE));
|
||||
bool cheats_allowed = (l->check_flag(Lobby::Flag::CHEATS_ENABLED) || c->license->check_flag(License::Flag::CHEAT_ANYWHERE));
|
||||
if (!cheats_allowed) {
|
||||
size_t default_min_level = s->default_min_level_for_game(l->base_version, l->episode, l->difficulty);
|
||||
if (new_min_level < default_min_level) {
|
||||
@@ -1084,7 +1085,8 @@ static void server_command_edit(shared_ptr<Client> c, const std::string& args) {
|
||||
throw precondition_failed("$C6This command cannot\nbe used for your\nversion of PSO.");
|
||||
}
|
||||
|
||||
bool cheats_allowed = ((s->cheat_mode_behavior != ServerState::BehaviorSwitch::OFF) || (c->license->flags & License::Flag::CHEAT_ANYWHERE));
|
||||
bool cheats_allowed = ((s->cheat_mode_behavior != ServerState::BehaviorSwitch::OFF) ||
|
||||
c->license->check_flag(License::Flag::CHEAT_ANYWHERE));
|
||||
|
||||
string encoded_args = tolower(args);
|
||||
vector<string> tokens = split(encoded_args, ' ');
|
||||
@@ -1261,6 +1263,10 @@ static void server_command_bbchar(shared_ptr<Client> c, const std::string& args)
|
||||
}
|
||||
|
||||
static void server_command_savechar(shared_ptr<Client> c, const std::string& args) {
|
||||
if (c->license->check_flag(License::Flag::IS_SHARED_SERIAL)) {
|
||||
send_text_message(c, "$C7This command cannot\nbe used on a shared\nserial number");
|
||||
return;
|
||||
}
|
||||
server_command_bbchar_savechar(c, args, false);
|
||||
}
|
||||
|
||||
@@ -1269,6 +1275,10 @@ static void server_command_loadchar(shared_ptr<Client> c, const std::string& arg
|
||||
send_text_message(c, "$C7This command can only\nbe used on v1 or v2");
|
||||
return;
|
||||
}
|
||||
if (c->license->check_flag(License::Flag::IS_SHARED_SERIAL)) {
|
||||
send_text_message(c, "$C7This command cannot\nbe used on a shared\nserial number");
|
||||
return;
|
||||
}
|
||||
auto l = c->require_lobby();
|
||||
check_is_game(l, false);
|
||||
|
||||
@@ -1314,7 +1324,7 @@ static string name_for_client(shared_ptr<Client> c) {
|
||||
static void server_command_silence(shared_ptr<Client> c, const std::string& args) {
|
||||
auto s = c->require_server_state();
|
||||
auto l = c->require_lobby();
|
||||
check_license_flags(c, License::Flag::SILENCE_USER);
|
||||
check_license_flag(c, License::Flag::SILENCE_USER);
|
||||
|
||||
auto target = s->find_client(&args);
|
||||
if (!target->license) {
|
||||
@@ -1323,7 +1333,7 @@ static void server_command_silence(shared_ptr<Client> c, const std::string& args
|
||||
return;
|
||||
}
|
||||
|
||||
if (target->license->flags & License::Flag::MODERATOR) {
|
||||
if (target->license->check_flag(License::Flag::SILENCE_USER)) {
|
||||
send_text_message(c, "$C6You do not have\nsufficient privileges.");
|
||||
return;
|
||||
}
|
||||
@@ -1337,7 +1347,7 @@ static void server_command_silence(shared_ptr<Client> c, const std::string& args
|
||||
static void server_command_kick(shared_ptr<Client> c, const std::string& args) {
|
||||
auto s = c->require_server_state();
|
||||
auto l = c->require_lobby();
|
||||
check_license_flags(c, License::Flag::KICK_USER);
|
||||
check_license_flag(c, License::Flag::KICK_USER);
|
||||
|
||||
auto target = s->find_client(&args);
|
||||
if (!target->license) {
|
||||
@@ -1346,7 +1356,7 @@ static void server_command_kick(shared_ptr<Client> c, const std::string& args) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (target->license->flags & License::Flag::MODERATOR) {
|
||||
if (target->license->check_flag(License::Flag::KICK_USER)) {
|
||||
send_text_message(c, "$C6You do not have\nsufficient privileges.");
|
||||
return;
|
||||
}
|
||||
@@ -1360,7 +1370,7 @@ static void server_command_kick(shared_ptr<Client> c, const std::string& args) {
|
||||
static void server_command_ban(shared_ptr<Client> c, const std::string& args) {
|
||||
auto s = c->require_server_state();
|
||||
auto l = c->require_lobby();
|
||||
check_license_flags(c, License::Flag::BAN_USER);
|
||||
check_license_flag(c, License::Flag::BAN_USER);
|
||||
|
||||
size_t space_pos = args.find(' ');
|
||||
if (space_pos == string::npos) {
|
||||
@@ -1376,7 +1386,7 @@ static void server_command_ban(shared_ptr<Client> c, const std::string& args) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (target->license->flags & License::Flag::BAN_USER) {
|
||||
if (target->license->check_flag(License::Flag::BAN_USER)) {
|
||||
send_text_message(c, "$C6You do not have\nsufficient privileges.");
|
||||
return;
|
||||
}
|
||||
|
||||
+2
-2
@@ -335,7 +335,7 @@ shared_ptr<const TeamIndex::Team> Client::team() const {
|
||||
}
|
||||
|
||||
bool Client::can_see_quest(shared_ptr<const Quest> q, uint8_t event, uint8_t difficulty, size_t num_players) const {
|
||||
if (this->license && (this->license->flags & License::Flag::DISABLE_QUEST_REQUIREMENTS)) {
|
||||
if (this->license && this->license->check_flag(License::Flag::DISABLE_QUEST_REQUIREMENTS)) {
|
||||
return true;
|
||||
}
|
||||
if (!q->available_expression) {
|
||||
@@ -356,7 +356,7 @@ bool Client::can_see_quest(shared_ptr<const Quest> q, uint8_t event, uint8_t dif
|
||||
}
|
||||
|
||||
bool Client::can_play_quest(shared_ptr<const Quest> q, uint8_t event, uint8_t difficulty, size_t num_players) const {
|
||||
if (this->license && (this->license->flags & License::Flag::DISABLE_QUEST_REQUIREMENTS)) {
|
||||
if (this->license && this->license->check_flag(License::Flag::DISABLE_QUEST_REQUIREMENTS)) {
|
||||
return true;
|
||||
}
|
||||
if (!q->enabled_expression) {
|
||||
|
||||
@@ -4650,7 +4650,7 @@ struct G_SyncEnemyState_6x6B_Entry_Decompressed {
|
||||
uint8_t blue_buff_level = 0;
|
||||
} __packed__;
|
||||
|
||||
// 6x6C: Sync object state (used while loading into game; same header format as 6E)
|
||||
// 6x6C: Sync object state (used while loading into game)
|
||||
// Compressed format is the same as 6x6B.
|
||||
|
||||
// Decompressed format is a list of these
|
||||
@@ -4659,7 +4659,7 @@ struct G_SyncObjectState_6x6C_Entry_Decompressed {
|
||||
le_uint16_t object_index = 0;
|
||||
} __packed__;
|
||||
|
||||
// 6x6D: Sync item state (used while loading into game; same header format as 6E)
|
||||
// 6x6D: Sync item state (used while loading into game)
|
||||
// Internal name: RcvItemCondition
|
||||
// Compressed format is the same as 6x6B.
|
||||
|
||||
|
||||
+60
-17
@@ -3,6 +3,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <phosg/Filesystem.hh>
|
||||
#include <phosg/Hash.hh>
|
||||
#include <phosg/Time.hh>
|
||||
|
||||
#include "License.hh"
|
||||
@@ -165,57 +166,76 @@ void LicenseIndex::remove(uint32_t serial_number) {
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<License> LicenseIndex::verify_v1_v2(uint32_t serial_number, const string& access_key) const {
|
||||
shared_ptr<License> LicenseIndex::verify_v1_v2(
|
||||
uint32_t serial_number,
|
||||
const string& access_key,
|
||||
const string& character_name) const {
|
||||
if (serial_number == 0) {
|
||||
throw no_username();
|
||||
}
|
||||
try {
|
||||
auto& license = this->serial_number_to_license.at(serial_number);
|
||||
if (license->ban_end_time && (license->ban_end_time >= now())) {
|
||||
throw invalid_argument("user is banned");
|
||||
}
|
||||
if (license->check_flag(License::Flag::IS_SHARED_SERIAL)) {
|
||||
return this->create_temporary_license_for_shared_license(license->flags, serial_number, access_key, "", character_name);
|
||||
}
|
||||
if (license->access_key.compare(0, 8, access_key) != 0) {
|
||||
throw incorrect_access_key();
|
||||
}
|
||||
if (license->ban_end_time && (license->ban_end_time >= now())) {
|
||||
throw invalid_argument("user is banned");
|
||||
}
|
||||
return license;
|
||||
} catch (const out_of_range&) {
|
||||
throw missing_license();
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<License> LicenseIndex::verify_gc(uint32_t serial_number, const string& access_key) const {
|
||||
shared_ptr<License> LicenseIndex::verify_gc_no_password(
|
||||
uint32_t serial_number,
|
||||
const string& access_key,
|
||||
const string& character_name) const {
|
||||
if (serial_number == 0) {
|
||||
throw no_username();
|
||||
}
|
||||
try {
|
||||
auto& license = this->serial_number_to_license.at(serial_number);
|
||||
if (license->ban_end_time && (license->ban_end_time >= now())) {
|
||||
throw invalid_argument("user is banned");
|
||||
}
|
||||
if (license->check_flag(License::Flag::IS_SHARED_SERIAL)) {
|
||||
return this->create_temporary_license_for_shared_license(license->flags, serial_number, access_key, "", character_name);
|
||||
}
|
||||
if (license->access_key != access_key) {
|
||||
throw incorrect_access_key();
|
||||
}
|
||||
if (license->ban_end_time && (license->ban_end_time >= now())) {
|
||||
throw invalid_argument("user is banned");
|
||||
}
|
||||
return license;
|
||||
} catch (const out_of_range&) {
|
||||
throw missing_license();
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<License> LicenseIndex::verify_gc(uint32_t serial_number, const string& access_key, const string& password) const {
|
||||
shared_ptr<License> LicenseIndex::verify_gc_with_password(
|
||||
uint32_t serial_number,
|
||||
const string& access_key,
|
||||
const string& password,
|
||||
const string& character_name) const {
|
||||
if (serial_number == 0) {
|
||||
throw no_username();
|
||||
}
|
||||
try {
|
||||
auto& license = this->serial_number_to_license.at(serial_number);
|
||||
if (license->ban_end_time && (license->ban_end_time >= now())) {
|
||||
throw invalid_argument("user is banned");
|
||||
}
|
||||
if (license->check_flag(License::Flag::IS_SHARED_SERIAL)) {
|
||||
return this->create_temporary_license_for_shared_license(license->flags, serial_number, access_key, password, character_name);
|
||||
}
|
||||
if (license->access_key != access_key) {
|
||||
throw incorrect_access_key();
|
||||
}
|
||||
if (license->gc_password != password) {
|
||||
throw incorrect_password();
|
||||
}
|
||||
if (license->ban_end_time && (license->ban_end_time >= now())) {
|
||||
throw invalid_argument("user is banned");
|
||||
}
|
||||
return license;
|
||||
} catch (const out_of_range&) {
|
||||
throw missing_license();
|
||||
@@ -228,15 +248,18 @@ shared_ptr<License> LicenseIndex::verify_xb(const string& gamertag, uint64_t use
|
||||
}
|
||||
try {
|
||||
auto& license = this->xb_gamertag_to_license.at(gamertag);
|
||||
if (license->check_flag(License::Flag::IS_SHARED_SERIAL)) {
|
||||
throw missing_license(); // XB users cannot use shared serials
|
||||
}
|
||||
if (license->ban_end_time && (license->ban_end_time >= now())) {
|
||||
throw invalid_argument("user is banned");
|
||||
}
|
||||
if (license->xb_user_id && (license->xb_user_id != user_id)) {
|
||||
throw incorrect_access_key();
|
||||
}
|
||||
if (license->xb_account_id && (license->xb_account_id != account_id)) {
|
||||
throw incorrect_access_key();
|
||||
}
|
||||
if (license->ban_end_time && (license->ban_end_time >= now())) {
|
||||
throw invalid_argument("user is banned");
|
||||
}
|
||||
return license;
|
||||
} catch (const out_of_range&) {
|
||||
throw missing_license();
|
||||
@@ -249,18 +272,38 @@ shared_ptr<License> LicenseIndex::verify_bb(const string& username, const string
|
||||
}
|
||||
try {
|
||||
auto& license = this->bb_username_to_license.at(username);
|
||||
if (license->bb_password != password) {
|
||||
throw incorrect_password();
|
||||
if (license->check_flag(License::Flag::IS_SHARED_SERIAL)) {
|
||||
throw missing_license(); // BB users cannot use shared serials
|
||||
}
|
||||
if (license->ban_end_time && (license->ban_end_time >= now())) {
|
||||
throw invalid_argument("user is banned");
|
||||
}
|
||||
if (license->bb_password != password) {
|
||||
throw incorrect_password();
|
||||
}
|
||||
return license;
|
||||
} catch (const out_of_range&) {
|
||||
throw missing_license();
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<License> LicenseIndex::create_temporary_license_for_shared_license(
|
||||
uint32_t base_flags,
|
||||
uint32_t serial_number,
|
||||
const string& access_key,
|
||||
const string& password,
|
||||
const string& character_name) const {
|
||||
uint32_t temp_serial_number = fnv1a32(&serial_number, sizeof(serial_number));
|
||||
temp_serial_number = fnv1a32(access_key, temp_serial_number);
|
||||
temp_serial_number = fnv1a32(password, temp_serial_number);
|
||||
temp_serial_number = fnv1a32(character_name, temp_serial_number);
|
||||
auto ret = this->create_temporary_license();
|
||||
ret->serial_number = temp_serial_number & 0x7FFFFFFF;
|
||||
ret->flags = base_flags;
|
||||
ret->set_flag(License::Flag::IS_SHARED_SERIAL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DiskLicenseIndex::DiskLicenseIndex() {
|
||||
struct BinaryLicense {
|
||||
pstring<TextEncoding::ASCII, 0x14> username; // BB username (max. 16 chars; should technically be Unicode)
|
||||
|
||||
+40
-6
@@ -12,22 +12,23 @@ class LicenseIndex;
|
||||
|
||||
class License {
|
||||
public:
|
||||
enum Flag : uint32_t {
|
||||
enum class Flag : uint32_t {
|
||||
// clang-format off
|
||||
KICK_USER = 0x00000001,
|
||||
BAN_USER = 0x00000002,
|
||||
SILENCE_USER = 0x00000004,
|
||||
CHANGE_LOBBY_INFO = 0x00000008,
|
||||
CHANGE_EVENT = 0x00000010,
|
||||
ANNOUNCE = 0x00000020,
|
||||
FREE_JOIN_GAMES = 0x00000040,
|
||||
UNLOCK_GAMES = 0x00000080,
|
||||
DEBUG = 0x01000000,
|
||||
CHEAT_ANYWHERE = 0x02000000,
|
||||
DISABLE_QUEST_REQUIREMENTS = 0x04000000,
|
||||
MODERATOR = 0x00000007,
|
||||
ADMINISTRATOR = 0x000000FF,
|
||||
ROOT = 0x7FFFFFFF,
|
||||
IS_SHARED_SERIAL = 0x80000000,
|
||||
// NOTE: When adding or changing license flags, don't forget to change the
|
||||
// documentation in the shell's help text.
|
||||
|
||||
UNUSED_BITS = 0x78FFFF00,
|
||||
// clang-format on
|
||||
@@ -60,6 +61,22 @@ public:
|
||||
virtual void save() const;
|
||||
virtual void delete_file() const;
|
||||
|
||||
[[nodiscard]] inline bool check_flag(Flag flag) const {
|
||||
return !!(this->flags & static_cast<uint32_t>(flag));
|
||||
}
|
||||
inline void set_flag(Flag flag) {
|
||||
this->flags |= static_cast<uint32_t>(flag);
|
||||
}
|
||||
inline void clear_flag(Flag flag) {
|
||||
this->flags &= (~static_cast<uint32_t>(flag));
|
||||
}
|
||||
inline void toggle_flag(Flag flag) {
|
||||
this->flags ^= static_cast<uint32_t>(flag);
|
||||
}
|
||||
inline void replace_all_flags(Flag mask) {
|
||||
this->flags = static_cast<uint32_t>(mask);
|
||||
}
|
||||
|
||||
std::string str() const;
|
||||
};
|
||||
|
||||
@@ -106,9 +123,19 @@ public:
|
||||
void add(std::shared_ptr<License> l);
|
||||
void remove(uint32_t serial_number);
|
||||
|
||||
std::shared_ptr<License> verify_v1_v2(uint32_t serial_number, const std::string& access_key) const;
|
||||
std::shared_ptr<License> verify_gc(uint32_t serial_number, const std::string& access_key) const;
|
||||
std::shared_ptr<License> verify_gc(uint32_t serial_number, const std::string& access_key, const std::string& password) const;
|
||||
std::shared_ptr<License> verify_v1_v2(
|
||||
uint32_t serial_number,
|
||||
const std::string& access_key,
|
||||
const std::string& character_name) const;
|
||||
std::shared_ptr<License> verify_gc_no_password(
|
||||
uint32_t serial_number,
|
||||
const std::string& access_key,
|
||||
const std::string& character_name) const;
|
||||
std::shared_ptr<License> verify_gc_with_password(
|
||||
uint32_t serial_number,
|
||||
const std::string& access_key,
|
||||
const std::string& password,
|
||||
const std::string& character_name) const;
|
||||
std::shared_ptr<License> verify_xb(const std::string& gamertag, uint64_t user_id, uint64_t account_id) const;
|
||||
std::shared_ptr<License> verify_bb(const std::string& username, const std::string& password) const;
|
||||
|
||||
@@ -116,6 +143,13 @@ protected:
|
||||
std::unordered_map<std::string, std::shared_ptr<License>> bb_username_to_license;
|
||||
std::unordered_map<std::string, std::shared_ptr<License>> xb_gamertag_to_license;
|
||||
std::unordered_map<uint32_t, std::shared_ptr<License>> serial_number_to_license;
|
||||
|
||||
std::shared_ptr<License> create_temporary_license_for_shared_license(
|
||||
uint32_t base_flags,
|
||||
uint32_t serial_number,
|
||||
const std::string& access_key,
|
||||
const std::string& password,
|
||||
const std::string& character_name) const;
|
||||
};
|
||||
|
||||
class DiskLicenseIndex : public LicenseIndex {
|
||||
|
||||
+1
-1
@@ -716,7 +716,7 @@ Lobby::JoinError Lobby::join_error_for_client(std::shared_ptr<Client> c, const s
|
||||
if (this->mode == GameMode::SOLO) {
|
||||
return JoinError::SOLO;
|
||||
}
|
||||
if (!(c->license->flags & License::Flag::FREE_JOIN_GAMES)) {
|
||||
if (!c->license->check_flag(License::Flag::FREE_JOIN_GAMES)) {
|
||||
if (password && !this->password.empty() && (*password != this->password)) {
|
||||
return JoinError::INCORRECT_PASSWORD;
|
||||
}
|
||||
|
||||
+12
-6
@@ -278,7 +278,8 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
ses->channel.version = Version::DC_NTE;
|
||||
ses->log.info("Version changed to DC_NTE");
|
||||
const auto& cmd = check_size_t<C_Login_DCNTE_8B>(data, sizeof(C_LoginExtended_DCNTE_8B));
|
||||
ses->license = s->license_index->verify_v1_v2(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
|
||||
ses->license = s->license_index->verify_v1_v2(
|
||||
stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode(), cmd.name.decode());
|
||||
ses->sub_version = cmd.sub_version;
|
||||
ses->channel.language = cmd.language;
|
||||
ses->character_name = cmd.name.decode(ses->channel.language);
|
||||
@@ -287,7 +288,8 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
ses->channel.version = Version::DC_V1;
|
||||
ses->log.info("Version changed to DC_V1");
|
||||
const auto& cmd = check_size_t<C_LoginV1_DC_93>(data);
|
||||
ses->license = s->license_index->verify_v1_v2(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
|
||||
ses->license = s->license_index->verify_v1_v2(
|
||||
stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode(), cmd.name.decode());
|
||||
ses->sub_version = cmd.sub_version;
|
||||
ses->channel.language = cmd.language;
|
||||
ses->character_name = cmd.name.decode(ses->channel.language);
|
||||
@@ -297,11 +299,13 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
if (cmd.sub_version >= 0x30) {
|
||||
ses->log.info("Version changed to GC_NTE");
|
||||
ses->channel.version = Version::GC_NTE;
|
||||
ses->license = s->license_index->verify_gc(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
|
||||
ses->license = s->license_index->verify_gc_no_password(
|
||||
stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode(), cmd.name.decode());
|
||||
} else { // DC V2
|
||||
ses->log.info("Version changed to DC_V2");
|
||||
ses->channel.version = Version::DC_V2;
|
||||
ses->license = s->license_index->verify_v1_v2(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
|
||||
ses->license = s->license_index->verify_v1_v2(
|
||||
stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode(), cmd.name.decode());
|
||||
}
|
||||
ses->sub_version = cmd.sub_version;
|
||||
ses->channel.language = cmd.language;
|
||||
@@ -319,7 +323,8 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
throw runtime_error("command is not 9D");
|
||||
}
|
||||
const auto& cmd = check_size_t<C_Login_DC_PC_GC_9D>(data, sizeof(C_LoginExtended_PC_9D));
|
||||
ses->license = s->license_index->verify_v1_v2(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
|
||||
ses->license = s->license_index->verify_v1_v2(
|
||||
stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode(), cmd.name.decode());
|
||||
ses->sub_version = cmd.sub_version;
|
||||
ses->channel.language = cmd.language;
|
||||
ses->character_name = cmd.name.decode(ses->channel.language);
|
||||
@@ -332,7 +337,8 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
// We should only get a 9E while the session is unlinked
|
||||
if (command == 0x9E) {
|
||||
const auto& cmd = check_size_t<C_Login_GC_9E>(data, sizeof(C_LoginExtended_GC_9E));
|
||||
ses->license = s->license_index->verify_gc(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
|
||||
ses->license = s->license_index->verify_gc_no_password(
|
||||
stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode(), cmd.name.decode());
|
||||
ses->sub_version = cmd.sub_version;
|
||||
ses->channel.language = cmd.language;
|
||||
ses->character_name = cmd.name.decode(ses->channel.language);
|
||||
|
||||
+14
-15
@@ -57,7 +57,7 @@ static shared_ptr<const Menu> proxy_options_menu_for_client(shared_ptr<const Cli
|
||||
"Block patches", "Disable patches sent\nby the remote server");
|
||||
add_option(ProxyOptionsMenuItemID::SWITCH_ASSIST, Client::Flag::SWITCH_ASSIST_ENABLED,
|
||||
"Switch assist", "Automatically try\nto unlock 2-player\ndoors when you step\non both switches\nsequentially");
|
||||
if ((s->cheat_mode_behavior != ServerState::BehaviorSwitch::OFF) || (c->license->flags & License::Flag::CHEAT_ANYWHERE)) {
|
||||
if ((s->cheat_mode_behavior != ServerState::BehaviorSwitch::OFF) || c->license->check_flag(License::Flag::CHEAT_ANYWHERE)) {
|
||||
if (!is_ep3(c->version())) {
|
||||
add_option(ProxyOptionsMenuItemID::INFINITE_HP, Client::Flag::INFINITE_HP_ENABLED,
|
||||
"Infinite HP", "Enable automatic HP\nrestoration when\nyou are hit by an\nenemy or trap\n\nCannot revive you\nfrom one-hit kills");
|
||||
@@ -397,7 +397,7 @@ static void on_DB_V3(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
|
||||
uint32_t serial_number = stoul(cmd.serial_number.decode(), nullptr, 16);
|
||||
try {
|
||||
auto l = s->license_index->verify_gc(serial_number, cmd.access_key.decode(), cmd.password.decode());
|
||||
auto l = s->license_index->verify_gc_with_password(serial_number, cmd.access_key.decode(), cmd.password.decode(), "");
|
||||
c->set_license(l);
|
||||
send_command(c, 0x9A, 0x02);
|
||||
|
||||
@@ -448,7 +448,7 @@ static void on_88_DCNTE(shared_ptr<Client> c, uint16_t, uint32_t, string& data)
|
||||
|
||||
uint32_t serial_number = stoul(cmd.serial_number.decode(), nullptr, 16);
|
||||
try {
|
||||
shared_ptr<License> l = s->license_index->verify_v1_v2(serial_number, cmd.access_key.decode());
|
||||
shared_ptr<License> l = s->license_index->verify_v1_v2(serial_number, cmd.access_key.decode(), "");
|
||||
c->set_license(l);
|
||||
send_command(c, 0x88, 0x00);
|
||||
|
||||
@@ -493,7 +493,7 @@ static void on_8B_DCNTE(shared_ptr<Client> c, uint16_t, uint32_t, string& data)
|
||||
|
||||
uint32_t serial_number = stoul(cmd.serial_number.decode(), nullptr, 16);
|
||||
try {
|
||||
shared_ptr<License> l = s->license_index->verify_v1_v2(serial_number, cmd.access_key.decode());
|
||||
shared_ptr<License> l = s->license_index->verify_v1_v2(serial_number, cmd.access_key.decode(), cmd.name.decode());
|
||||
c->set_license(l);
|
||||
|
||||
} catch (const LicenseIndex::no_username& e) {
|
||||
@@ -547,7 +547,7 @@ static void on_90_DC(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
|
||||
uint32_t serial_number = stoul(cmd.serial_number.decode(), nullptr, 16);
|
||||
try {
|
||||
shared_ptr<License> l = s->license_index->verify_v1_v2(serial_number, cmd.access_key.decode());
|
||||
shared_ptr<License> l = s->license_index->verify_v1_v2(serial_number, cmd.access_key.decode(), "");
|
||||
c->set_license(l);
|
||||
send_command(c, 0x90, 0x02);
|
||||
|
||||
@@ -604,7 +604,7 @@ static void on_93_DC(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
|
||||
uint32_t serial_number = stoul(cmd.serial_number.decode(), nullptr, 16);
|
||||
try {
|
||||
shared_ptr<License> l = s->license_index->verify_v1_v2(serial_number, cmd.access_key.decode());
|
||||
shared_ptr<License> l = s->license_index->verify_v1_v2(serial_number, cmd.access_key.decode(), cmd.name.decode());
|
||||
c->set_license(l);
|
||||
|
||||
} catch (const LicenseIndex::no_username& e) {
|
||||
@@ -700,7 +700,7 @@ static void on_9A(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
}
|
||||
} else {
|
||||
serial_number = stoul(cmd.serial_number.decode(), nullptr, 16);
|
||||
l = s->license_index->verify_v1_v2(serial_number, cmd.access_key.decode());
|
||||
l = s->license_index->verify_v1_v2(serial_number, cmd.access_key.decode(), "");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -709,7 +709,7 @@ static void on_9A(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
case Version::GC_EP3_NTE:
|
||||
case Version::GC_EP3: {
|
||||
serial_number = stoul(cmd.serial_number.decode(), nullptr, 16);
|
||||
l = s->license_index->verify_gc(serial_number, cmd.access_key.decode());
|
||||
l = s->license_index->verify_gc_no_password(serial_number, cmd.access_key.decode(), "");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -772,13 +772,13 @@ static void on_9C(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
switch (c->version()) {
|
||||
case Version::DC_V2:
|
||||
case Version::PC_V2:
|
||||
l = s->license_index->verify_v1_v2(serial_number, cmd.access_key.decode());
|
||||
l = s->license_index->verify_v1_v2(serial_number, cmd.access_key.decode(), "");
|
||||
break;
|
||||
case Version::GC_NTE:
|
||||
case Version::GC_V3:
|
||||
case Version::GC_EP3_NTE:
|
||||
case Version::GC_EP3:
|
||||
l = s->license_index->verify_gc(serial_number, cmd.access_key.decode(), cmd.password.decode());
|
||||
l = s->license_index->verify_gc_with_password(serial_number, cmd.access_key.decode(), cmd.password.decode(), "");
|
||||
break;
|
||||
default:
|
||||
// TODO: PC_NTE can probably send 9C, but due to the way we've
|
||||
@@ -911,7 +911,7 @@ static void on_9D_9E(shared_ptr<Client> c, uint16_t command, uint32_t, string& d
|
||||
}
|
||||
} else {
|
||||
serial_number = stoul(base_cmd->serial_number.decode(), nullptr, 16);
|
||||
l = s->license_index->verify_v1_v2(serial_number, base_cmd->access_key.decode());
|
||||
l = s->license_index->verify_v1_v2(serial_number, base_cmd->access_key.decode(), base_cmd->name.decode());
|
||||
}
|
||||
break;
|
||||
case Version::GC_NTE:
|
||||
@@ -919,7 +919,7 @@ static void on_9D_9E(shared_ptr<Client> c, uint16_t command, uint32_t, string& d
|
||||
case Version::GC_EP3_NTE:
|
||||
case Version::GC_EP3:
|
||||
serial_number = stoul(base_cmd->serial_number.decode(), nullptr, 16);
|
||||
l = s->license_index->verify_gc(serial_number, base_cmd->access_key.decode());
|
||||
l = s->license_index->verify_gc_no_password(serial_number, base_cmd->access_key.decode(), base_cmd->name.decode());
|
||||
break;
|
||||
default:
|
||||
throw logic_error("unsupported versioned command");
|
||||
@@ -2477,7 +2477,7 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!(c->license->flags & License::Flag::DISABLE_QUEST_REQUIREMENTS)) {
|
||||
if (!c->license->check_flag(License::Flag::DISABLE_QUEST_REQUIREMENTS)) {
|
||||
include_condition = l->quest_include_condition();
|
||||
}
|
||||
}
|
||||
@@ -4015,8 +4015,7 @@ shared_ptr<Lobby> create_game_generic(
|
||||
size_t min_level = s->default_min_level_for_game(c->version(), episode, difficulty);
|
||||
|
||||
auto p = c->character();
|
||||
if (!(c->license->flags & License::Flag::FREE_JOIN_GAMES) &&
|
||||
(min_level > p->disp.stats.level)) {
|
||||
if (!c->license->check_flag(License::Flag::FREE_JOIN_GAMES) && (min_level > p->disp.stats.level)) {
|
||||
// Note: We don't throw here because this is a situation players might
|
||||
// actually encounter while playing the game normally
|
||||
send_lobby_message_box(c, "Your level is too\nlow for this\ndifficulty");
|
||||
|
||||
@@ -1201,7 +1201,7 @@ static void on_received_condition(shared_ptr<Client> c, uint8_t command, uint8_t
|
||||
if (l->is_game()) {
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
if (is_v1_or_v2(c->version()) && (cmd.client_id == c->lobby_client_id)) {
|
||||
bool player_cheats_enabled = l->check_flag(Lobby::Flag::CHEATS_ENABLED) || (c->license->flags & License::Flag::CHEAT_ANYWHERE);
|
||||
bool player_cheats_enabled = l->check_flag(Lobby::Flag::CHEATS_ENABLED) || (c->license->check_flag(License::Flag::CHEAT_ANYWHERE));
|
||||
if (player_cheats_enabled && c->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) {
|
||||
send_remove_conditions(c);
|
||||
}
|
||||
@@ -1216,7 +1216,7 @@ static void on_hit_by_enemy(shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
if (l->is_game() && (cmd.client_id == c->lobby_client_id)) {
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
bool player_cheats_enabled = !is_v1(c->version()) &&
|
||||
(l->check_flag(Lobby::Flag::CHEATS_ENABLED) || (c->license->flags & License::Flag::CHEAT_ANYWHERE));
|
||||
(l->check_flag(Lobby::Flag::CHEATS_ENABLED) || (c->license->check_flag(License::Flag::CHEAT_ANYWHERE)));
|
||||
if (player_cheats_enabled && c->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) {
|
||||
send_player_stats_change(c, PlayerStatsChange::ADD_HP, 2550);
|
||||
}
|
||||
@@ -1230,7 +1230,7 @@ static void on_cast_technique_finished(shared_ptr<Client> c, uint8_t command, ui
|
||||
if (l->is_game() && (cmd.header.client_id == c->lobby_client_id)) {
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
bool player_cheats_enabled = !is_v1(c->version()) &&
|
||||
(l->check_flag(Lobby::Flag::CHEATS_ENABLED) || (c->license->flags & License::Flag::CHEAT_ANYWHERE));
|
||||
(l->check_flag(Lobby::Flag::CHEATS_ENABLED) || (c->license->check_flag(License::Flag::CHEAT_ANYWHERE)));
|
||||
if (player_cheats_enabled && c->config.check_flag(Client::Flag::INFINITE_TP_ENABLED)) {
|
||||
send_player_stats_change(c, PlayerStatsChange::ADD_TP, 255);
|
||||
}
|
||||
|
||||
+1
-1
@@ -1519,7 +1519,7 @@ void send_quest_categories_menu_t(
|
||||
QuestMenuType menu_type,
|
||||
Episode episode) {
|
||||
QuestIndex::IncludeCondition include_condition = nullptr;
|
||||
if (!(c->license->flags & License::Flag::DISABLE_QUEST_REQUIREMENTS)) {
|
||||
if (!c->license->check_flag(License::Flag::DISABLE_QUEST_REQUIREMENTS)) {
|
||||
auto l = c->lobby.lock();
|
||||
include_condition = l ? l->quest_include_condition() : nullptr;
|
||||
}
|
||||
|
||||
+23
-7
@@ -154,7 +154,23 @@ Server commands:\n\
|
||||
gc-password=<password> (GC password)\n\
|
||||
access-key=<access-key> (DC/GC/PC access key)\n\
|
||||
serial=<serial-number> (decimal serial number; required for all licenses)\n\
|
||||
flags=<privilege-mask> (can be normal, mod, admin, root, or numeric)\n\
|
||||
flags=<privilege-mask> (see below)\n\
|
||||
If flags is specified in hex, the meanings of bits are:\n\
|
||||
00000001 = Can kick other users offline\n\
|
||||
00000002 = Can ban other users\n\
|
||||
00000004 = Can silence other users\n\
|
||||
00000010 = Can change lobby events\n\
|
||||
00000020 = Can make server-wide announcements\n\
|
||||
00000040 = Ignores game join restrictions (e.g. level/quest requirements)\n\
|
||||
01000000 = Can use debugging commands\n\
|
||||
02000000 = Can use cheat commands even if cheat mode is disabled\n\
|
||||
04000000 = Can play any quest without progression/flags restrictions\n\
|
||||
80000000 = License is a shared serial (disables Access Key and password\n\
|
||||
checks; players will get Guild Cards based on their player names)\n\
|
||||
There are also shorthands for some general privilege levels:\n\
|
||||
flags=moderator = 00000007\n\
|
||||
flags=admin = 000000FF\n\
|
||||
flags=root = 7FFFFFFF\n\
|
||||
update-license SERIAL-NUMBER PARAMETERS...\n\
|
||||
Update an existing license. <serial-number> specifies which license to\n\
|
||||
update. The options in <parameters> are the same as for the add-license\n\
|
||||
@@ -327,11 +343,11 @@ Proxy session commands:\n\
|
||||
if (mask == "normal") {
|
||||
l->flags = 0;
|
||||
} else if (mask == "mod") {
|
||||
l->flags = License::Flag::MODERATOR;
|
||||
l->replace_all_flags(License::Flag::MODERATOR);
|
||||
} else if (mask == "admin") {
|
||||
l->flags = License::Flag::ADMINISTRATOR;
|
||||
l->replace_all_flags(License::Flag::ADMINISTRATOR);
|
||||
} else if (mask == "root") {
|
||||
l->flags = License::Flag::ROOT;
|
||||
l->replace_all_flags(License::Flag::ROOT);
|
||||
} else {
|
||||
l->flags = stoul(mask, nullptr, 16);
|
||||
}
|
||||
@@ -385,11 +401,11 @@ Proxy session commands:\n\
|
||||
if (mask == "normal") {
|
||||
l->flags = 0;
|
||||
} else if (mask == "mod") {
|
||||
l->flags = License::Flag::MODERATOR;
|
||||
l->replace_all_flags(License::Flag::MODERATOR);
|
||||
} else if (mask == "admin") {
|
||||
l->flags = License::Flag::ADMINISTRATOR;
|
||||
l->replace_all_flags(License::Flag::ADMINISTRATOR);
|
||||
} else if (mask == "root") {
|
||||
l->flags = License::Flag::ROOT;
|
||||
l->replace_all_flags(License::Flag::ROOT);
|
||||
} else {
|
||||
l->flags = stoul(mask, nullptr, 16);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user