add support for shared serial mechanics

This commit is contained in:
Martin Michelsen
2024-02-01 21:28:35 -08:00
parent ef89699d59
commit 50f3ebca5e
11 changed files with 188 additions and 80 deletions
+30 -20
View File
@@ -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
View File
@@ -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) {
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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");
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}