implement Episode 3 meseta
This commit is contained in:
+18
-17
@@ -37,11 +37,11 @@ private:
|
||||
std::u16string user_msg;
|
||||
};
|
||||
|
||||
static void check_privileges(shared_ptr<Client> c, uint64_t mask) {
|
||||
static void check_license_flags(shared_ptr<Client> c, uint32_t mask) {
|
||||
if (!c->license) {
|
||||
throw precondition_failed(u"$C6You are not\nlogged in.");
|
||||
}
|
||||
if ((c->license->privileges & mask) != mask) {
|
||||
if ((c->license->flags & mask) != mask) {
|
||||
throw precondition_failed(u"$C6You do not have\npermission to\nrun this command.");
|
||||
}
|
||||
}
|
||||
@@ -206,14 +206,14 @@ static void proxy_command_lobby_info(shared_ptr<ProxyServer::LinkedSession> ses,
|
||||
}
|
||||
|
||||
static void server_command_ax(shared_ptr<Client> c, const std::u16string& args) {
|
||||
check_privileges(c, Privilege::ANNOUNCE);
|
||||
check_license_flags(c, License::Flag::ANNOUNCE);
|
||||
string message = encode_sjis(args);
|
||||
ax_messages_log.info("%s", message.c_str());
|
||||
}
|
||||
|
||||
static void server_command_announce(shared_ptr<Client> c, const std::u16string& args) {
|
||||
auto s = c->require_server_state();
|
||||
check_privileges(c, Privilege::ANNOUNCE);
|
||||
check_license_flags(c, License::Flag::ANNOUNCE);
|
||||
send_text_message(s, args);
|
||||
}
|
||||
|
||||
@@ -230,14 +230,14 @@ static void proxy_command_arrow(shared_ptr<ProxyServer::LinkedSession> ses, cons
|
||||
}
|
||||
|
||||
static void server_command_debug(shared_ptr<Client> c, const std::u16string&) {
|
||||
check_privileges(c, Privilege::DEBUG);
|
||||
check_license_flags(c, License::Flag::DEBUG);
|
||||
c->options.debug = !c->options.debug;
|
||||
send_text_message_printf(c, "Debug %s",
|
||||
c->options.debug ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
static void server_command_auction(shared_ptr<Client> c, const std::u16string&) {
|
||||
check_privileges(c, Privilege::DEBUG);
|
||||
check_license_flags(c, License::Flag::DEBUG);
|
||||
auto l = c->require_lobby();
|
||||
if (l->is_game() && l->is_ep3()) {
|
||||
G_InitiateCardAuction_GC_Ep3_6xB5x42 cmd;
|
||||
@@ -330,7 +330,7 @@ static void proxy_command_patch(shared_ptr<ProxyServer::LinkedSession> ses, cons
|
||||
}
|
||||
|
||||
static void server_command_persist(shared_ptr<Client> c, const std::u16string&) {
|
||||
check_privileges(c, Privilege::DEBUG);
|
||||
check_license_flags(c, License::Flag::DEBUG);
|
||||
auto l = c->require_lobby();
|
||||
if (l->flags & Lobby::Flag::DEFAULT) {
|
||||
send_text_message(c, u"$C6Default lobbies\ncannot be marked\ntemporary");
|
||||
@@ -464,7 +464,7 @@ static void server_command_cheat(shared_ptr<Client> c, const std::u16string&) {
|
||||
static void server_command_lobby_event(shared_ptr<Client> c, const std::u16string& args) {
|
||||
auto l = c->require_lobby();
|
||||
check_is_game(l, false);
|
||||
check_privileges(c, Privilege::CHANGE_EVENT);
|
||||
check_license_flags(c, License::Flag::CHANGE_EVENT);
|
||||
|
||||
uint8_t new_event = event_for_name(args);
|
||||
if (new_event == 0xFF) {
|
||||
@@ -496,7 +496,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::u16string& args) {
|
||||
check_privileges(c, Privilege::CHANGE_EVENT);
|
||||
check_license_flags(c, License::Flag::CHANGE_EVENT);
|
||||
|
||||
uint8_t new_event = event_for_name(args);
|
||||
if (new_event == 0xFF) {
|
||||
@@ -844,7 +844,7 @@ static void server_command_convert_char_to_bb(shared_ptr<Client> c, const std::u
|
||||
}
|
||||
|
||||
try {
|
||||
s->license_manager->verify_bb(tokens[0].c_str(), tokens[1].c_str());
|
||||
s->license_index->verify_bb(tokens[0].c_str(), tokens[1].c_str());
|
||||
} catch (const exception& e) {
|
||||
send_text_message_printf(c, "$C6Login failed: %s", e.what());
|
||||
return;
|
||||
@@ -876,7 +876,7 @@ static string name_for_client(shared_ptr<Client> c) {
|
||||
static void server_command_silence(shared_ptr<Client> c, const std::u16string& args) {
|
||||
auto s = c->require_server_state();
|
||||
auto l = c->require_lobby();
|
||||
check_privileges(c, Privilege::SILENCE_USER);
|
||||
check_license_flags(c, License::Flag::SILENCE_USER);
|
||||
|
||||
auto target = s->find_client(&args);
|
||||
if (!target->license) {
|
||||
@@ -885,7 +885,7 @@ static void server_command_silence(shared_ptr<Client> c, const std::u16string& a
|
||||
return;
|
||||
}
|
||||
|
||||
if (target->license->privileges & Privilege::MODERATOR) {
|
||||
if (target->license->flags & License::Flag::MODERATOR) {
|
||||
send_text_message(c, u"$C6You do not have\nsufficient privileges.");
|
||||
return;
|
||||
}
|
||||
@@ -899,7 +899,7 @@ static void server_command_silence(shared_ptr<Client> c, const std::u16string& a
|
||||
static void server_command_kick(shared_ptr<Client> c, const std::u16string& args) {
|
||||
auto s = c->require_server_state();
|
||||
auto l = c->require_lobby();
|
||||
check_privileges(c, Privilege::KICK_USER);
|
||||
check_license_flags(c, License::Flag::KICK_USER);
|
||||
|
||||
auto target = s->find_client(&args);
|
||||
if (!target->license) {
|
||||
@@ -908,7 +908,7 @@ static void server_command_kick(shared_ptr<Client> c, const std::u16string& args
|
||||
return;
|
||||
}
|
||||
|
||||
if (target->license->privileges & Privilege::MODERATOR) {
|
||||
if (target->license->flags & License::Flag::MODERATOR) {
|
||||
send_text_message(c, u"$C6You do not have\nsufficient privileges.");
|
||||
return;
|
||||
}
|
||||
@@ -922,7 +922,7 @@ static void server_command_kick(shared_ptr<Client> c, const std::u16string& args
|
||||
static void server_command_ban(shared_ptr<Client> c, const std::u16string& args) {
|
||||
auto s = c->require_server_state();
|
||||
auto l = c->require_lobby();
|
||||
check_privileges(c, Privilege::BAN_USER);
|
||||
check_license_flags(c, License::Flag::BAN_USER);
|
||||
|
||||
u16string args_str(args);
|
||||
size_t space_pos = args_str.find(L' ');
|
||||
@@ -939,7 +939,7 @@ static void server_command_ban(shared_ptr<Client> c, const std::u16string& args)
|
||||
return;
|
||||
}
|
||||
|
||||
if (target->license->privileges & Privilege::BAN_USER) {
|
||||
if (target->license->flags & License::Flag::BAN_USER) {
|
||||
send_text_message(c, u"$C6You do not have\nsufficient privileges.");
|
||||
return;
|
||||
}
|
||||
@@ -963,7 +963,8 @@ static void server_command_ban(shared_ptr<Client> c, const std::u16string& args)
|
||||
usecs *= 60 * 60 * 24 * 365;
|
||||
}
|
||||
|
||||
s->license_manager->ban_until(target->license->serial_number, now() + usecs);
|
||||
target->license->ban_end_time = now() + usecs;
|
||||
target->license->save();
|
||||
send_message_box(target, u"$C6You were banned by a moderator.");
|
||||
target->should_disconnect = true;
|
||||
string target_name = name_for_client(target);
|
||||
|
||||
+2
-2
@@ -147,11 +147,11 @@ QuestScriptVersion Client::quest_version() const {
|
||||
}
|
||||
}
|
||||
|
||||
void Client::set_license(shared_ptr<const License> l) {
|
||||
void Client::set_license(shared_ptr<License> l) {
|
||||
this->license = l;
|
||||
this->game_data.guild_card_number = this->license->serial_number;
|
||||
if (this->version() == GameVersion::BB) {
|
||||
this->game_data.bb_username = this->license->username;
|
||||
this->game_data.bb_username = this->license->bb_username;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -122,7 +122,7 @@ struct Client : public std::enable_shared_from_this<Client> {
|
||||
PrefixedLogger log;
|
||||
|
||||
// License & account
|
||||
std::shared_ptr<const License> license;
|
||||
std::shared_ptr<License> license;
|
||||
|
||||
// Note: these fields are included in the client config. On GC, the client
|
||||
// config can be up to 0x20 bytes; on BB it can be 0x28 bytes. We don't use
|
||||
@@ -191,7 +191,7 @@ struct Client : public std::enable_shared_from_this<Client> {
|
||||
}
|
||||
QuestScriptVersion quest_version() const;
|
||||
|
||||
void set_license(std::shared_ptr<const License> l);
|
||||
void set_license(std::shared_ptr<License> l);
|
||||
|
||||
std::shared_ptr<ServerState> require_server_state() const;
|
||||
std::shared_ptr<Lobby> require_lobby() const;
|
||||
|
||||
@@ -2209,8 +2209,8 @@ struct S_RankUpdate_GC_Ep3_B7 {
|
||||
// without modifying it.
|
||||
le_uint32_t rank = 0;
|
||||
ptext<char, 0x0C> rank_text; // Encrypted (with encrypt_challenge_rank_text)
|
||||
le_uint32_t meseta = 0;
|
||||
le_uint32_t max_meseta = 0;
|
||||
le_uint32_t current_meseta = 0;
|
||||
le_uint32_t total_meseta_earned = 0;
|
||||
le_uint32_t unlocked_jukebox_songs = 0xFFFFFFFF;
|
||||
} __packed__;
|
||||
|
||||
@@ -2315,15 +2315,15 @@ struct S_UpdateMediaHeader_GC_Ep3_B9 {
|
||||
// sent by client)
|
||||
// 04 = unknown (C->S; request_token must match the last token sent by client)
|
||||
|
||||
struct C_Meseta_GC_Ep3_BA {
|
||||
struct C_MesetaTransaction_GC_Ep3_BA {
|
||||
le_uint32_t transaction_num = 0;
|
||||
le_uint32_t value = 0;
|
||||
le_uint32_t request_token = 0;
|
||||
} __packed__;
|
||||
|
||||
struct S_Meseta_GC_Ep3_BA {
|
||||
le_uint32_t remaining_meseta = 0;
|
||||
le_uint32_t total_meseta_awarded = 0;
|
||||
struct S_MesetaTransaction_GC_Ep3_BA {
|
||||
le_uint32_t current_meseta = 0;
|
||||
le_uint32_t total_meseta_earned = 0;
|
||||
le_uint32_t request_token = 0; // Should match the token sent by the client
|
||||
} __packed__;
|
||||
|
||||
|
||||
+153
-157
@@ -10,103 +10,172 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
License::License()
|
||||
License::License(const JSON& json)
|
||||
: serial_number(0),
|
||||
privileges(0),
|
||||
ban_end_time(0) {}
|
||||
flags(0),
|
||||
ban_end_time(0),
|
||||
ep3_current_meseta(0),
|
||||
ep3_total_meseta_earned(0) {
|
||||
this->serial_number = json.get_int("SerialNumber");
|
||||
this->access_key = json.get_string("AccessKey", "");
|
||||
this->gc_password = json.get_string("GCPassword", "");
|
||||
this->bb_username = json.get_string("BBUsername", "");
|
||||
this->bb_password = json.get_string("BBPassword", "");
|
||||
this->flags = json.get_int("Flags", 0);
|
||||
this->ban_end_time = json.get_int("BanEndTime", 0);
|
||||
this->ep3_current_meseta = json.get_int("Ep3CurrentMeseta", 0);
|
||||
this->ep3_total_meseta_earned = json.get_int("Ep3TotalMesetaEarned", 0);
|
||||
}
|
||||
|
||||
JSON License::json() const {
|
||||
return JSON::dict({
|
||||
{"SerialNumber", this->serial_number},
|
||||
{"AccessKey", this->access_key},
|
||||
{"GCPassword", this->gc_password},
|
||||
{"BBUsername", this->bb_username},
|
||||
{"BBPassword", this->bb_password},
|
||||
{"Flags", this->flags},
|
||||
{"BanEndTime", this->ban_end_time},
|
||||
{"Ep3CurrentMeseta", this->ep3_current_meseta},
|
||||
{"Ep3TotalMesetaEarned", this->ep3_total_meseta_earned},
|
||||
});
|
||||
}
|
||||
|
||||
void License::save() const {
|
||||
if (!(this->flags & License::Flag::TEMPORARY)) {
|
||||
auto json = this->json();
|
||||
string json_data = json.serialize(JSON::SerializeOption::FORMAT | JSON::SerializeOption::HEX_INTEGERS);
|
||||
string filename = string_printf("system/licenses/%010" PRIu32 ".json", this->serial_number);
|
||||
save_file(filename, json_data);
|
||||
}
|
||||
}
|
||||
|
||||
void License::delete_file() const {
|
||||
string filename = string_printf("system/licenses/%010" PRIu32 ".json", this->serial_number);
|
||||
remove(filename.c_str());
|
||||
}
|
||||
|
||||
string License::str() const {
|
||||
string ret = string_printf("License(serial_number=%" PRIu32, this->serial_number);
|
||||
if (!this->username.empty()) {
|
||||
ret += ", username=";
|
||||
ret += this->username;
|
||||
}
|
||||
if (!this->bb_password.empty()) {
|
||||
ret += ", bb-password=";
|
||||
ret += this->bb_password;
|
||||
}
|
||||
vector<string> tokens;
|
||||
tokens.emplace_back(string_printf("serial_number=%010" PRIu32 "/%08" PRIX32, this->serial_number, this->serial_number));
|
||||
if (!this->access_key.empty()) {
|
||||
ret += ", access-key=";
|
||||
ret += this->access_key;
|
||||
tokens.emplace_back("access_key=" + this->access_key);
|
||||
}
|
||||
if (!this->gc_password.empty()) {
|
||||
ret += ", gc-password=";
|
||||
ret += this->gc_password;
|
||||
tokens.emplace_back("gc_password=" + this->gc_password);
|
||||
}
|
||||
ret += string_printf(", privileges=%" PRIu32, this->privileges);
|
||||
if (!this->bb_username.empty()) {
|
||||
tokens.emplace_back("bb_username=" + this->bb_username);
|
||||
}
|
||||
if (!this->bb_password.empty()) {
|
||||
tokens.emplace_back("bb_password=" + this->bb_password);
|
||||
}
|
||||
tokens.emplace_back(string_printf("flags=%08" PRIX32, this->flags));
|
||||
if (this->ban_end_time) {
|
||||
ret += string_printf(", banned-until=%" PRIu64, this->ban_end_time);
|
||||
tokens.emplace_back(string_printf("ban_end_time=%016" PRIX64, this->ban_end_time));
|
||||
}
|
||||
return ret + ")";
|
||||
if (this->ep3_current_meseta) {
|
||||
tokens.emplace_back(string_printf("ep3_current_meseta=%" PRIu32, this->ep3_current_meseta));
|
||||
}
|
||||
if (this->ep3_total_meseta_earned) {
|
||||
tokens.emplace_back(string_printf("ep3_total_meseta_earned=%" PRIu32, this->ep3_total_meseta_earned));
|
||||
}
|
||||
return "[License: " + join(tokens, ", ") + "]";
|
||||
}
|
||||
|
||||
LicenseManager::LicenseManager()
|
||||
: filename(""),
|
||||
autosave(false) {}
|
||||
struct BinaryLicense {
|
||||
ptext<char, 0x14> username; // BB username (max. 16 chars; should technically be Unicode)
|
||||
ptext<char, 0x14> bb_password; // BB password (max. 16 chars)
|
||||
uint32_t serial_number; // PC/GC serial number. MUST BE PRESENT FOR BB LICENSES TOO; this is also the player's guild card number.
|
||||
ptext<char, 0x10> access_key; // PC/GC access key. (to log in using PC on a GC license, just enter the first 8 characters of the GC access key)
|
||||
ptext<char, 0x0C> gc_password; // GC password
|
||||
uint32_t privileges; // privilege level
|
||||
uint64_t ban_end_time; // end time of ban (zero = not banned)
|
||||
} __attribute__((packed));
|
||||
|
||||
LicenseManager::LicenseManager(const string& filename)
|
||||
: filename(filename),
|
||||
autosave(true) {
|
||||
try {
|
||||
auto licenses = load_vector_file<License>(this->filename);
|
||||
for (const auto& read_license : licenses) {
|
||||
shared_ptr<License> license(new License(read_license));
|
||||
LicenseIndex::LicenseIndex() : autosave(true) {
|
||||
if (!isdir("system/licenses")) {
|
||||
mkdir("system/licenses", 0755);
|
||||
}
|
||||
|
||||
// Before the temporary flag existed, licenses with root privileges would
|
||||
// have the temporary flag set. To migrate these, explicitly unset the
|
||||
// flag for all licenses loaded from the license file.
|
||||
license->privileges &= ~Privilege::TEMPORARY;
|
||||
|
||||
uint32_t serial_number = license->serial_number;
|
||||
this->bb_username_to_license.emplace(license->username, license);
|
||||
this->serial_number_to_license.emplace(serial_number, license);
|
||||
// Convert binary licenses to JSON licenses and save them
|
||||
if (isfile("system/licenses.nsi")) {
|
||||
auto bin_licenses = load_vector_file<BinaryLicense>("system/licenses.nsi");
|
||||
for (const auto& bin_license : bin_licenses) {
|
||||
// Only add licenses from the binary file if there isn't a JSON version of
|
||||
// the same license
|
||||
try {
|
||||
this->get(bin_license.serial_number);
|
||||
} catch (const missing_license&) {
|
||||
License license;
|
||||
license.serial_number = bin_license.serial_number;
|
||||
license.access_key = bin_license.access_key;
|
||||
license.gc_password = bin_license.gc_password;
|
||||
license.bb_username = bin_license.username;
|
||||
license.bb_password = bin_license.bb_password;
|
||||
license.flags = bin_license.privileges & (~License::Flag::TEMPORARY);
|
||||
license.ban_end_time = bin_license.ban_end_time;
|
||||
license.ep3_current_meseta = 0;
|
||||
license.ep3_total_meseta_earned = 0;
|
||||
license.save();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (const cannot_open_file&) {
|
||||
license_log.warning("File %s does not exist; no licenses are registered",
|
||||
this->filename.c_str());
|
||||
::remove("system/licenses.nsi");
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseManager::save() const {
|
||||
if (this->filename.empty()) {
|
||||
throw logic_error("license manager has no filename; cannot save");
|
||||
}
|
||||
auto f = fopen_unique(this->filename, "wb");
|
||||
for (const auto& it : this->serial_number_to_license) {
|
||||
if (it.second->privileges & Privilege::TEMPORARY) {
|
||||
continue;
|
||||
for (const auto& item : list_directory("system/licenses")) {
|
||||
if (ends_with(item, ".json")) {
|
||||
JSON json = JSON::parse(load_file("system/licenses/" + item));
|
||||
shared_ptr<License> license(new License(json));
|
||||
this->add(license);
|
||||
}
|
||||
fwritex(f.get(), it.second.get(), sizeof(License));
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseManager::set_autosave(bool autosave) {
|
||||
void LicenseIndex::set_autosave(bool autosave) {
|
||||
this->autosave = autosave;
|
||||
}
|
||||
|
||||
shared_ptr<const License> LicenseManager::verify_pc(uint32_t serial_number,
|
||||
const string& access_key) const {
|
||||
try {
|
||||
auto& license = this->serial_number_to_license.at(serial_number);
|
||||
if (!license->access_key.eq_n(access_key, 8)) {
|
||||
throw incorrect_access_key();
|
||||
}
|
||||
size_t LicenseIndex::count() const {
|
||||
return this->serial_number_to_license.size();
|
||||
}
|
||||
|
||||
if (license->ban_end_time && (license->ban_end_time >= now())) {
|
||||
throw invalid_argument("user is banned");
|
||||
}
|
||||
return license;
|
||||
shared_ptr<License> LicenseIndex::get(uint32_t serial_number) const {
|
||||
try {
|
||||
return this->serial_number_to_license.at(serial_number);
|
||||
} catch (const out_of_range&) {
|
||||
throw missing_license();
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<const License> LicenseManager::verify_gc(uint32_t serial_number,
|
||||
const string& access_key) const {
|
||||
vector<shared_ptr<License>> LicenseIndex::all() const {
|
||||
vector<shared_ptr<License>> ret;
|
||||
ret.reserve(this->serial_number_to_license.size());
|
||||
for (const auto& it : this->serial_number_to_license) {
|
||||
ret.emplace_back(it.second);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void LicenseIndex::add(shared_ptr<License> l) {
|
||||
this->serial_number_to_license[l->serial_number] = l;
|
||||
if (!l->bb_username.empty()) {
|
||||
this->bb_username_to_license[l->bb_username] = l;
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseIndex::remove(uint32_t serial_number) {
|
||||
auto l = this->serial_number_to_license.at(serial_number);
|
||||
this->serial_number_to_license.erase(l->serial_number);
|
||||
if (!l->bb_username.empty()) {
|
||||
this->bb_username_to_license.erase(l->bb_username);
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<License> LicenseIndex::verify_v1_v2(uint32_t serial_number, const string& access_key) const {
|
||||
try {
|
||||
auto& license = this->serial_number_to_license.at(serial_number);
|
||||
if (!license->access_key.eq_n(access_key, 12)) {
|
||||
if (license->access_key.compare(0, 8, access_key) != 0) {
|
||||
throw incorrect_access_key();
|
||||
}
|
||||
if (license->ban_end_time && (license->ban_end_time >= now())) {
|
||||
@@ -118,11 +187,25 @@ shared_ptr<const License> LicenseManager::verify_gc(uint32_t serial_number,
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<const License> LicenseManager::verify_gc(uint32_t serial_number,
|
||||
const string& access_key, const string& password) const {
|
||||
shared_ptr<License> LicenseIndex::verify_gc(uint32_t serial_number, const string& access_key) const {
|
||||
try {
|
||||
auto& license = this->serial_number_to_license.at(serial_number);
|
||||
if (!license->access_key.eq_n(access_key, 12)) {
|
||||
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 {
|
||||
try {
|
||||
auto& license = this->serial_number_to_license.at(serial_number);
|
||||
if (license->access_key != access_key) {
|
||||
throw incorrect_access_key();
|
||||
}
|
||||
if (license->gc_password != password) {
|
||||
@@ -137,14 +220,12 @@ shared_ptr<const License> LicenseManager::verify_gc(uint32_t serial_number,
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<const License> LicenseManager::verify_bb(const string& username,
|
||||
const string& password) const {
|
||||
shared_ptr<License> LicenseIndex::verify_bb(const string& username, const string& password) const {
|
||||
try {
|
||||
auto& license = this->bb_username_to_license.at(username);
|
||||
if (license->bb_password != password) {
|
||||
throw incorrect_password();
|
||||
}
|
||||
|
||||
if (license->ban_end_time && (license->ban_end_time >= now())) {
|
||||
throw invalid_argument("user is banned");
|
||||
}
|
||||
@@ -153,88 +234,3 @@ shared_ptr<const License> LicenseManager::verify_bb(const string& username,
|
||||
throw missing_license();
|
||||
}
|
||||
}
|
||||
|
||||
size_t LicenseManager::count() const {
|
||||
return this->serial_number_to_license.size();
|
||||
}
|
||||
|
||||
void LicenseManager::ban_until(uint32_t serial_number, uint64_t end_time) {
|
||||
this->serial_number_to_license.at(serial_number)->ban_end_time = end_time;
|
||||
if (this->autosave) {
|
||||
this->save();
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<const License> LicenseManager::get(uint32_t serial_number) const {
|
||||
try {
|
||||
return this->serial_number_to_license.at(serial_number);
|
||||
} catch (const out_of_range&) {
|
||||
throw missing_license();
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseManager::add(shared_ptr<License> l) {
|
||||
this->serial_number_to_license[l->serial_number] = l;
|
||||
if (!l->username.empty()) {
|
||||
this->bb_username_to_license[l->username] = l;
|
||||
}
|
||||
if (this->autosave) {
|
||||
this->save();
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseManager::remove(uint32_t serial_number) {
|
||||
auto l = this->serial_number_to_license.at(serial_number);
|
||||
this->serial_number_to_license.erase(l->serial_number);
|
||||
if (!l->username.empty()) {
|
||||
this->bb_username_to_license.erase(l->username);
|
||||
}
|
||||
if (this->autosave) {
|
||||
this->save();
|
||||
}
|
||||
}
|
||||
|
||||
vector<License> LicenseManager::snapshot() const {
|
||||
vector<License> ret;
|
||||
for (auto it : this->serial_number_to_license) {
|
||||
ret.emplace_back(*it.second);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
shared_ptr<License> LicenseManager::create_license_pc(
|
||||
uint32_t serial_number, const string& access_key, bool temporary) {
|
||||
shared_ptr<License> l(new License());
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = access_key;
|
||||
if (temporary) {
|
||||
l->privileges |= Privilege::TEMPORARY;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
shared_ptr<License> LicenseManager::create_license_gc(
|
||||
uint32_t serial_number, const string& access_key, const string& password,
|
||||
bool temporary) {
|
||||
shared_ptr<License> l(new License());
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = access_key;
|
||||
l->gc_password = password;
|
||||
if (temporary) {
|
||||
l->privileges |= Privilege::TEMPORARY;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
shared_ptr<License> LicenseManager::create_license_bb(
|
||||
uint32_t serial_number, const string& username, const string& password,
|
||||
bool temporary) {
|
||||
shared_ptr<License> l(new License());
|
||||
l->serial_number = serial_number;
|
||||
l->username = username;
|
||||
l->bb_password = password;
|
||||
if (temporary) {
|
||||
l->privileges |= Privilege::TEMPORARY;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
+63
-66
@@ -1,96 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <phosg/JSON.hh>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "Text.hh"
|
||||
|
||||
enum Privilege {
|
||||
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,
|
||||
|
||||
MODERATOR = 0x00000007,
|
||||
ADMINISTRATOR = 0x0000003F,
|
||||
ROOT = 0x7FFFFFFF,
|
||||
|
||||
TEMPORARY = 0x80000000,
|
||||
};
|
||||
class LicenseIndex;
|
||||
|
||||
struct License {
|
||||
ptext<char, 0x14> username; // BB username (max. 16 chars; should technically be Unicode)
|
||||
ptext<char, 0x14> bb_password; // BB password (max. 16 chars)
|
||||
uint32_t serial_number; // PC/GC serial number. MUST BE PRESENT FOR BB LICENSES TOO; this is also the player's guild card number.
|
||||
ptext<char, 0x10> access_key; // PC/GC access key. (to log in using PC on a GC license, just enter the first 8 characters of the GC access key)
|
||||
ptext<char, 0x0C> gc_password; // GC password
|
||||
uint32_t privileges; // privilege level
|
||||
uint64_t ban_end_time; // end time of ban (zero = not banned)
|
||||
enum 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,
|
||||
MODERATOR = 0x00000007,
|
||||
ADMINISTRATOR = 0x000000FF,
|
||||
ROOT = 0x010000FF,
|
||||
TEMPORARY = 0x80000000,
|
||||
|
||||
License();
|
||||
std::string str() const;
|
||||
} __attribute__((packed));
|
||||
UNUSED_BITS = 0x7EFFFF00,
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
class incorrect_password : public std::invalid_argument {
|
||||
public:
|
||||
incorrect_password() : invalid_argument("incorrect password") {}
|
||||
};
|
||||
uint32_t serial_number = 0;
|
||||
std::string access_key;
|
||||
std::string gc_password;
|
||||
std::string bb_username;
|
||||
std::string bb_password;
|
||||
|
||||
class incorrect_access_key : public std::invalid_argument {
|
||||
public:
|
||||
incorrect_access_key() : invalid_argument("incorrect access key") {}
|
||||
};
|
||||
uint32_t flags = 0;
|
||||
uint64_t ban_end_time = 0; // 0 = not banned
|
||||
|
||||
class missing_license : public std::invalid_argument {
|
||||
public:
|
||||
missing_license() : invalid_argument("missing license") {}
|
||||
};
|
||||
uint32_t ep3_current_meseta = 0;
|
||||
uint32_t ep3_total_meseta_earned = 0;
|
||||
|
||||
class LicenseManager {
|
||||
public:
|
||||
LicenseManager();
|
||||
explicit LicenseManager(const std::string& filename);
|
||||
~LicenseManager() = default;
|
||||
License() = default;
|
||||
explicit License(const JSON& json);
|
||||
|
||||
JSON json() const;
|
||||
void save() const;
|
||||
void delete_file() const;
|
||||
|
||||
std::string str() const;
|
||||
};
|
||||
|
||||
class LicenseIndex {
|
||||
public:
|
||||
class incorrect_password : public std::invalid_argument {
|
||||
public:
|
||||
incorrect_password() : invalid_argument("incorrect password") {}
|
||||
};
|
||||
|
||||
class incorrect_access_key : public std::invalid_argument {
|
||||
public:
|
||||
incorrect_access_key() : invalid_argument("incorrect access key") {}
|
||||
};
|
||||
|
||||
class missing_license : public std::invalid_argument {
|
||||
public:
|
||||
missing_license() : invalid_argument("missing license") {}
|
||||
};
|
||||
|
||||
LicenseIndex();
|
||||
~LicenseIndex() = default;
|
||||
|
||||
void set_autosave(bool autosave);
|
||||
|
||||
std::shared_ptr<const License> verify_pc(uint32_t serial_number,
|
||||
const std::string& access_key) const;
|
||||
std::shared_ptr<const License> verify_gc(uint32_t serial_number,
|
||||
const std::string& access_key) const;
|
||||
std::shared_ptr<const License> verify_gc(uint32_t serial_number,
|
||||
const std::string& access_key, const std::string& password) const;
|
||||
std::shared_ptr<const License> verify_bb(const std::string& username,
|
||||
const std::string& password) const;
|
||||
void ban_until(uint32_t serial_number, uint64_t seconds);
|
||||
|
||||
size_t count() const;
|
||||
std::shared_ptr<License> get(uint32_t serial_number) const;
|
||||
std::vector<std::shared_ptr<License>> all() const;
|
||||
|
||||
std::shared_ptr<const License> get(uint32_t serial_number) const;
|
||||
void add(std::shared_ptr<License> l);
|
||||
void remove(uint32_t serial_number);
|
||||
std::vector<License> snapshot() const;
|
||||
|
||||
static std::shared_ptr<License> create_license_pc(
|
||||
uint32_t serial_number, const std::string& access_key, bool temporary);
|
||||
static std::shared_ptr<License> create_license_gc(
|
||||
uint32_t serial_number, const std::string& access_key,
|
||||
const std::string& password, bool temporary);
|
||||
static std::shared_ptr<License> create_license_bb(
|
||||
uint32_t serial_number, const std::string& username,
|
||||
const std::string& password, bool temporary);
|
||||
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_bb(const std::string& username, const std::string& password) const;
|
||||
|
||||
protected:
|
||||
std::string filename;
|
||||
bool autosave;
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<License>> bb_username_to_license;
|
||||
|
||||
@@ -12,7 +12,6 @@ PrefixedLogger config_log("[Config] ", LogLevel::USE_DEFAULT);
|
||||
PrefixedLogger dns_server_log("[DNSServer] ", LogLevel::USE_DEFAULT);
|
||||
PrefixedLogger function_compiler_log("[FunctionCompiler] ", LogLevel::USE_DEFAULT);
|
||||
PrefixedLogger ip_stack_simulator_log("[IPStackSimulator] ", LogLevel::USE_DEFAULT);
|
||||
PrefixedLogger license_log("[LicenseManager] ", LogLevel::USE_DEFAULT);
|
||||
PrefixedLogger lobby_log("", LogLevel::USE_DEFAULT);
|
||||
PrefixedLogger patch_index_log("[PatchFileIndex] ", LogLevel::USE_DEFAULT);
|
||||
PrefixedLogger player_data_log("", LogLevel::USE_DEFAULT);
|
||||
@@ -39,7 +38,6 @@ void set_log_levels_from_json(const JSON& json) {
|
||||
set_log_level_from_json(dns_server_log, json, "DNSServer");
|
||||
set_log_level_from_json(function_compiler_log, json, "FunctionCompiler");
|
||||
set_log_level_from_json(ip_stack_simulator_log, json, "IPStackSimulator");
|
||||
set_log_level_from_json(license_log, json, "LicenseManager");
|
||||
set_log_level_from_json(lobby_log, json, "Lobbies");
|
||||
set_log_level_from_json(patch_index_log, json, "PatchFileIndex");
|
||||
set_log_level_from_json(player_data_log, json, "PlayerData");
|
||||
|
||||
@@ -11,7 +11,6 @@ extern PrefixedLogger config_log;
|
||||
extern PrefixedLogger dns_server_log;
|
||||
extern PrefixedLogger function_compiler_log;
|
||||
extern PrefixedLogger ip_stack_simulator_log;
|
||||
extern PrefixedLogger license_log;
|
||||
extern PrefixedLogger lobby_log;
|
||||
extern PrefixedLogger patch_index_log;
|
||||
extern PrefixedLogger player_data_log;
|
||||
|
||||
@@ -1176,8 +1176,8 @@ static HandlerResult S_G_B7(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t
|
||||
if (ses->newserv_client_config.cfg.flags & Client::Flag::IS_EPISODE_3) {
|
||||
if (ses->options.ep3_infinite_meseta) {
|
||||
auto& cmd = check_size_t<S_RankUpdate_GC_Ep3_B7>(data);
|
||||
if (cmd.meseta != 1000000) {
|
||||
cmd.meseta = 1000000;
|
||||
if (cmd.current_meseta != 1000000) {
|
||||
cmd.current_meseta = 1000000;
|
||||
return HandlerResult::Type::MODIFIED;
|
||||
}
|
||||
}
|
||||
@@ -1272,9 +1272,9 @@ static HandlerResult S_B_EF(shared_ptr<ProxyServer::LinkedSession>, uint16_t, ui
|
||||
|
||||
static HandlerResult S_G_BA(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t, string& data) {
|
||||
if (ses->options.ep3_infinite_meseta) {
|
||||
auto& cmd = check_size_t<S_Meseta_GC_Ep3_BA>(data);
|
||||
if (cmd.remaining_meseta != 1000000) {
|
||||
cmd.remaining_meseta = 1000000;
|
||||
auto& cmd = check_size_t<S_MesetaTransaction_GC_Ep3_BA>(data);
|
||||
if (cmd.current_meseta != 1000000) {
|
||||
cmd.current_meseta = 1000000;
|
||||
return HandlerResult::Type::MODIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
+16
-13
@@ -258,7 +258,7 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
auto s = server->state;
|
||||
|
||||
bool should_close_unlinked_session = false;
|
||||
shared_ptr<const License> license;
|
||||
shared_ptr<License> license;
|
||||
uint32_t sub_version = 0;
|
||||
uint8_t language = 1; // Default = English
|
||||
string character_name;
|
||||
@@ -272,7 +272,7 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
// anything else, disconnect
|
||||
if (command == 0x93) {
|
||||
const auto& cmd = check_size_t<C_LoginV1_DC_93>(data);
|
||||
license = s->license_manager->verify_pc(
|
||||
license = s->license_index->verify_v1_v2(
|
||||
stoul(cmd.serial_number, nullptr, 16), cmd.access_key);
|
||||
sub_version = cmd.sub_version;
|
||||
language = cmd.language;
|
||||
@@ -281,7 +281,7 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
client_config.cfg.flags |= Client::Flag::IS_DC_V1;
|
||||
} else if (command == 0x9D) {
|
||||
const auto& cmd = check_size_t<C_Login_DC_PC_GC_9D>(data, sizeof(C_LoginExtended_DC_GC_9D));
|
||||
license = s->license_manager->verify_pc(
|
||||
license = s->license_index->verify_v1_v2(
|
||||
stoul(cmd.serial_number, nullptr, 16), cmd.access_key);
|
||||
sub_version = cmd.sub_version;
|
||||
language = cmd.language;
|
||||
@@ -297,7 +297,7 @@ 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));
|
||||
license = s->license_manager->verify_pc(
|
||||
license = s->license_index->verify_v1_v2(
|
||||
stoul(cmd.serial_number, nullptr, 16), cmd.access_key);
|
||||
sub_version = cmd.sub_version;
|
||||
language = cmd.language;
|
||||
@@ -311,7 +311,7 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
throw runtime_error("command is not 9E");
|
||||
}
|
||||
const auto& cmd = check_size_t<C_Login_GC_9E>(data, sizeof(C_LoginExtended_GC_9E));
|
||||
license = s->license_manager->verify_gc(
|
||||
license = s->license_index->verify_gc(
|
||||
stoul(cmd.serial_number, nullptr, 16), cmd.access_key);
|
||||
sub_version = cmd.sub_version;
|
||||
language = cmd.language;
|
||||
@@ -329,15 +329,18 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
}
|
||||
const auto& cmd = check_size_t<C_Login_BB_93>(data);
|
||||
try {
|
||||
license = s->license_manager->verify_bb(
|
||||
license = s->license_index->verify_bb(
|
||||
cmd.username, cmd.password);
|
||||
} catch (const missing_license&) {
|
||||
} catch (const LicenseIndex::missing_license&) {
|
||||
if (!s->allow_unregistered_users) {
|
||||
throw;
|
||||
}
|
||||
shared_ptr<License> l = LicenseManager::create_license_bb(
|
||||
fnv1a32(cmd.username) & 0x7FFFFFFF, cmd.username, cmd.password, true);
|
||||
s->license_manager->add(l);
|
||||
shared_ptr<License> l(new License());
|
||||
l->serial_number = fnv1a32(cmd.username) & 0x7FFFFFFF;
|
||||
l->bb_username = cmd.username;
|
||||
l->bb_password = cmd.password;
|
||||
l->flags |= License::Flag::TEMPORARY;
|
||||
s->license_index->add(l);
|
||||
license = l;
|
||||
}
|
||||
login_command_bb = std::move(data);
|
||||
@@ -484,7 +487,7 @@ ProxyServer::LinkedSession::LinkedSession(
|
||||
shared_ptr<ProxyServer> server,
|
||||
uint16_t local_port,
|
||||
GameVersion version,
|
||||
shared_ptr<const License> license,
|
||||
shared_ptr<License> license,
|
||||
const ClientConfigBB& newserv_client_config)
|
||||
: LinkedSession(server, license->serial_number, local_port, version) {
|
||||
this->license = license;
|
||||
@@ -500,7 +503,7 @@ ProxyServer::LinkedSession::LinkedSession(
|
||||
shared_ptr<ProxyServer> server,
|
||||
uint16_t local_port,
|
||||
GameVersion version,
|
||||
std::shared_ptr<const License> license,
|
||||
std::shared_ptr<License> license,
|
||||
const struct sockaddr_storage& next_destination)
|
||||
: LinkedSession(server, license->serial_number, local_port, version) {
|
||||
this->license = license;
|
||||
@@ -821,7 +824,7 @@ shared_ptr<ProxyServer::LinkedSession> ProxyServer::get_session_by_name(
|
||||
}
|
||||
|
||||
shared_ptr<ProxyServer::LinkedSession> ProxyServer::create_licensed_session(
|
||||
shared_ptr<const License> l, uint16_t local_port, GameVersion version,
|
||||
shared_ptr<License> l, uint16_t local_port, GameVersion version,
|
||||
const ClientConfigBB& newserv_client_config) {
|
||||
shared_ptr<LinkedSession> session(new LinkedSession(
|
||||
this->shared_from_this(), local_port, version, l, newserv_client_config));
|
||||
|
||||
+4
-4
@@ -38,7 +38,7 @@ public:
|
||||
|
||||
std::unique_ptr<struct event, void (*)(struct event*)> timeout_event;
|
||||
|
||||
std::shared_ptr<const License> license;
|
||||
std::shared_ptr<License> license;
|
||||
|
||||
Channel client_channel;
|
||||
Channel server_channel;
|
||||
@@ -120,13 +120,13 @@ public:
|
||||
std::shared_ptr<ProxyServer> server,
|
||||
uint16_t local_port,
|
||||
GameVersion version,
|
||||
std::shared_ptr<const License> license,
|
||||
std::shared_ptr<License> license,
|
||||
const ClientConfigBB& newserv_client_config);
|
||||
LinkedSession(
|
||||
std::shared_ptr<ProxyServer> server,
|
||||
uint16_t local_port,
|
||||
GameVersion version,
|
||||
std::shared_ptr<const License> license,
|
||||
std::shared_ptr<License> license,
|
||||
const struct sockaddr_storage& next_destination);
|
||||
LinkedSession(
|
||||
std::shared_ptr<ProxyServer> server,
|
||||
@@ -171,7 +171,7 @@ public:
|
||||
std::shared_ptr<LinkedSession> get_session();
|
||||
std::shared_ptr<LinkedSession> get_session_by_name(const std::string& name);
|
||||
std::shared_ptr<LinkedSession> create_licensed_session(
|
||||
std::shared_ptr<const License> l,
|
||||
std::shared_ptr<License> l,
|
||||
uint16_t local_port,
|
||||
GameVersion version,
|
||||
const ClientConfigBB& newserv_client_config);
|
||||
|
||||
+155
-98
@@ -121,7 +121,7 @@ static bool send_enable_send_function_call_if_applicable(shared_ptr<Client> c) {
|
||||
auto s = c->require_server_state();
|
||||
if (function_compiler_available() &&
|
||||
(c->flags & Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL)) {
|
||||
if (s->episode_3_send_function_call_enabled) {
|
||||
if (s->ep3_send_function_call_enabled) {
|
||||
send_quest_buffer_overflow(c);
|
||||
} else {
|
||||
c->flags |= Client::Flag::NO_SEND_FUNCTION_CALL;
|
||||
@@ -279,6 +279,10 @@ void on_login_complete(shared_ptr<Client> c) {
|
||||
c->game_data.should_update_play_time = true;
|
||||
}
|
||||
|
||||
if (c->flags & Client::Flag::IS_EPISODE_3) {
|
||||
send_ep3_rank_update(c);
|
||||
}
|
||||
|
||||
send_lobby_list(c);
|
||||
send_get_player_info(c);
|
||||
}
|
||||
@@ -327,30 +331,32 @@ static void on_DB_V3(shared_ptr<Client> c, uint16_t, uint32_t, const string& dat
|
||||
|
||||
uint32_t serial_number = stoul(cmd.serial_number, nullptr, 16);
|
||||
try {
|
||||
auto l = s->license_manager->verify_gc(serial_number, cmd.access_key,
|
||||
cmd.password);
|
||||
auto l = s->license_index->verify_gc(serial_number, cmd.access_key, cmd.password);
|
||||
c->set_license(l);
|
||||
send_command(c, 0x9A, 0x02);
|
||||
|
||||
} catch (const incorrect_access_key& e) {
|
||||
} catch (const LicenseIndex::incorrect_access_key& e) {
|
||||
send_command(c, 0x9A, 0x03);
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
|
||||
} catch (const incorrect_password& e) {
|
||||
} catch (const LicenseIndex::incorrect_password& e) {
|
||||
send_command(c, 0x9A, 0x07);
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
|
||||
} catch (const missing_license& e) {
|
||||
} catch (const LicenseIndex::missing_license& e) {
|
||||
if (!s->allow_unregistered_users) {
|
||||
send_command(c, 0x9A, 0x04);
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
} else {
|
||||
auto l = LicenseManager::create_license_gc(serial_number, cmd.access_key,
|
||||
cmd.password, true);
|
||||
s->license_manager->add(l);
|
||||
shared_ptr<License> l(new License());
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = cmd.access_key;
|
||||
l->gc_password = cmd.password;
|
||||
l->flags |= License::Flag::TEMPORARY;
|
||||
s->license_index->add(l);
|
||||
c->set_license(l);
|
||||
send_command(c, 0x9A, 0x02);
|
||||
}
|
||||
@@ -367,23 +373,24 @@ static void on_88_DCNTE(shared_ptr<Client> c, uint16_t, uint32_t, const string&
|
||||
|
||||
uint32_t serial_number = stoul(cmd.serial_number, nullptr, 16);
|
||||
try {
|
||||
shared_ptr<const License> l = s->license_manager->verify_pc(
|
||||
serial_number, cmd.access_key);
|
||||
shared_ptr<License> l = s->license_index->verify_v1_v2(serial_number, cmd.access_key);
|
||||
c->set_license(l);
|
||||
send_command(c, 0x88, 0x00);
|
||||
|
||||
} catch (const incorrect_access_key& e) {
|
||||
} catch (const LicenseIndex::incorrect_access_key& e) {
|
||||
send_message_box(c, u"Incorrect access key");
|
||||
c->should_disconnect = true;
|
||||
|
||||
} catch (const missing_license& e) {
|
||||
} catch (const LicenseIndex::missing_license& e) {
|
||||
if (!s->allow_unregistered_users) {
|
||||
send_message_box(c, u"Incorrect serial number");
|
||||
c->should_disconnect = true;
|
||||
} else {
|
||||
auto l = LicenseManager::create_license_pc(
|
||||
serial_number, cmd.access_key, true);
|
||||
s->license_manager->add(l);
|
||||
shared_ptr<License> l(new License());
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = cmd.access_key;
|
||||
l->flags |= License::Flag::TEMPORARY;
|
||||
s->license_index->add(l);
|
||||
c->set_license(l);
|
||||
send_command(c, 0x88, 0x00);
|
||||
}
|
||||
@@ -400,25 +407,25 @@ static void on_8B_DCNTE(shared_ptr<Client> c, uint16_t, uint32_t, const string&
|
||||
|
||||
uint32_t serial_number = stoul(cmd.serial_number, nullptr, 16);
|
||||
try {
|
||||
shared_ptr<const License> l = s->license_manager->verify_pc(
|
||||
shared_ptr<License> l = s->license_index->verify_v1_v2(
|
||||
serial_number, cmd.access_key);
|
||||
c->set_license(l);
|
||||
// send_command(c, 0x8B, 0x01);
|
||||
|
||||
} catch (const incorrect_access_key& e) {
|
||||
} catch (const LicenseIndex::incorrect_access_key& e) {
|
||||
send_message_box(c, u"Incorrect access key");
|
||||
c->should_disconnect = true;
|
||||
|
||||
} catch (const missing_license& e) {
|
||||
} catch (const LicenseIndex::missing_license& e) {
|
||||
if (!s->allow_unregistered_users) {
|
||||
send_message_box(c, u"Incorrect serial number");
|
||||
c->should_disconnect = true;
|
||||
} else {
|
||||
auto l = LicenseManager::create_license_pc(
|
||||
serial_number, cmd.access_key, true);
|
||||
s->license_manager->add(l);
|
||||
shared_ptr<License> l(new License());
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = cmd.access_key;
|
||||
l->flags |= License::Flag::TEMPORARY;
|
||||
s->license_index->add(l);
|
||||
c->set_license(l);
|
||||
// send_command(c, 0x8B, 0x01);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,23 +452,24 @@ static void on_90_DC(shared_ptr<Client> c, uint16_t, uint32_t, const string& dat
|
||||
|
||||
uint32_t serial_number = stoul(cmd.serial_number, nullptr, 16);
|
||||
try {
|
||||
shared_ptr<const License> l = s->license_manager->verify_pc(
|
||||
serial_number, cmd.access_key);
|
||||
shared_ptr<License> l = s->license_index->verify_v1_v2(serial_number, cmd.access_key);
|
||||
c->set_license(l);
|
||||
send_command(c, 0x90, 0x02);
|
||||
|
||||
} catch (const incorrect_access_key& e) {
|
||||
} catch (const LicenseIndex::incorrect_access_key& e) {
|
||||
send_command(c, 0x90, 0x03);
|
||||
c->should_disconnect = true;
|
||||
|
||||
} catch (const missing_license& e) {
|
||||
} catch (const LicenseIndex::missing_license& e) {
|
||||
if (!s->allow_unregistered_users) {
|
||||
send_command(c, 0x90, 0x03);
|
||||
c->should_disconnect = true;
|
||||
} else {
|
||||
auto l = LicenseManager::create_license_pc(
|
||||
serial_number, cmd.access_key, true);
|
||||
s->license_manager->add(l);
|
||||
shared_ptr<License> l(new License());
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = cmd.access_key;
|
||||
l->flags |= License::Flag::TEMPORARY;
|
||||
s->license_index->add(l);
|
||||
c->set_license(l);
|
||||
send_command(c, 0x90, 0x01);
|
||||
}
|
||||
@@ -485,24 +493,25 @@ static void on_93_DC(shared_ptr<Client> c, uint16_t, uint32_t, const string& dat
|
||||
|
||||
uint32_t serial_number = stoul(cmd.serial_number, nullptr, 16);
|
||||
try {
|
||||
shared_ptr<const License> l = s->license_manager->verify_pc(
|
||||
serial_number, cmd.access_key);
|
||||
shared_ptr<License> l = s->license_index->verify_v1_v2(serial_number, cmd.access_key);
|
||||
c->set_license(l);
|
||||
|
||||
} catch (const incorrect_access_key& e) {
|
||||
} catch (const LicenseIndex::incorrect_access_key& e) {
|
||||
send_message_box(c, u"Incorrect access key");
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
|
||||
} catch (const missing_license& e) {
|
||||
} catch (const LicenseIndex::missing_license& e) {
|
||||
if (!s->allow_unregistered_users) {
|
||||
send_message_box(c, u"Incorrect serial number");
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
} else {
|
||||
auto l = LicenseManager::create_license_pc(
|
||||
serial_number, cmd.access_key, true);
|
||||
s->license_manager->add(l);
|
||||
shared_ptr<License> l(new License());
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = cmd.access_key;
|
||||
l->flags |= License::Flag::TEMPORARY;
|
||||
s->license_index->add(l);
|
||||
c->set_license(l);
|
||||
}
|
||||
}
|
||||
@@ -538,14 +547,14 @@ static void on_9A(shared_ptr<Client> c, uint16_t, uint32_t, const string& data)
|
||||
|
||||
uint32_t serial_number = stoul(cmd.serial_number, nullptr, 16);
|
||||
try {
|
||||
shared_ptr<const License> l;
|
||||
shared_ptr<License> l;
|
||||
switch (c->version()) {
|
||||
case GameVersion::DC:
|
||||
case GameVersion::PC:
|
||||
l = s->license_manager->verify_pc(serial_number, cmd.access_key);
|
||||
l = s->license_index->verify_v1_v2(serial_number, cmd.access_key);
|
||||
break;
|
||||
case GameVersion::GC:
|
||||
l = s->license_manager->verify_gc(serial_number, cmd.access_key);
|
||||
l = s->license_index->verify_gc(serial_number, cmd.access_key);
|
||||
break;
|
||||
case GameVersion::XB:
|
||||
throw runtime_error("xbox licenses are not implemented");
|
||||
@@ -556,17 +565,17 @@ static void on_9A(shared_ptr<Client> c, uint16_t, uint32_t, const string& data)
|
||||
c->set_license(l);
|
||||
send_command(c, 0x9A, 0x02);
|
||||
|
||||
} catch (const incorrect_access_key& e) {
|
||||
} catch (const LicenseIndex::incorrect_access_key& e) {
|
||||
send_command(c, 0x9A, 0x03);
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
|
||||
} catch (const incorrect_password& e) {
|
||||
} catch (const LicenseIndex::incorrect_password& e) {
|
||||
send_command(c, 0x9A, 0x07);
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
|
||||
} catch (const missing_license& e) {
|
||||
} catch (const LicenseIndex::missing_license& e) {
|
||||
// On V3, the client should have sent a different command containing the
|
||||
// password already, which should have created and added a temporary
|
||||
// license. So, if no license exists at this point, disconnect the client
|
||||
@@ -577,8 +586,11 @@ static void on_9A(shared_ptr<Client> c, uint16_t, uint32_t, const string& data)
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
} else if ((c->version() == GameVersion::DC) || (c->version() == GameVersion::PC)) {
|
||||
l = LicenseManager::create_license_pc(serial_number, cmd.access_key, true);
|
||||
s->license_manager->add(l);
|
||||
shared_ptr<License> l(new License());
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = cmd.access_key;
|
||||
l->flags |= License::Flag::TEMPORARY;
|
||||
s->license_index->add(l);
|
||||
c->set_license(l);
|
||||
send_command(c, 0x9A, 0x02);
|
||||
} else {
|
||||
@@ -595,14 +607,14 @@ static void on_9C(shared_ptr<Client> c, uint16_t, uint32_t, const string& data)
|
||||
|
||||
uint32_t serial_number = stoul(cmd.serial_number, nullptr, 16);
|
||||
try {
|
||||
shared_ptr<const License> l;
|
||||
shared_ptr<License> l;
|
||||
switch (c->version()) {
|
||||
case GameVersion::DC:
|
||||
case GameVersion::PC:
|
||||
l = s->license_manager->verify_pc(serial_number, cmd.access_key);
|
||||
l = s->license_index->verify_v1_v2(serial_number, cmd.access_key);
|
||||
break;
|
||||
case GameVersion::GC:
|
||||
l = s->license_manager->verify_gc(serial_number, cmd.access_key,
|
||||
l = s->license_index->verify_gc(serial_number, cmd.access_key,
|
||||
cmd.password);
|
||||
break;
|
||||
case GameVersion::XB:
|
||||
@@ -614,35 +626,25 @@ static void on_9C(shared_ptr<Client> c, uint16_t, uint32_t, const string& data)
|
||||
c->set_license(l);
|
||||
send_command(c, 0x9C, 0x01);
|
||||
|
||||
} catch (const incorrect_password& e) {
|
||||
} catch (const LicenseIndex::incorrect_password& e) {
|
||||
send_command(c, 0x9C, 0x00);
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
|
||||
} catch (const missing_license& e) {
|
||||
} catch (const LicenseIndex::missing_license& e) {
|
||||
if (!s->allow_unregistered_users) {
|
||||
send_command(c, 0x9C, 0x00);
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
} else {
|
||||
shared_ptr<License> l;
|
||||
switch (c->version()) {
|
||||
case GameVersion::DC:
|
||||
case GameVersion::PC:
|
||||
l = LicenseManager::create_license_pc(serial_number, cmd.access_key,
|
||||
true);
|
||||
break;
|
||||
case GameVersion::GC:
|
||||
l = LicenseManager::create_license_gc(serial_number, cmd.access_key,
|
||||
cmd.password, true);
|
||||
break;
|
||||
case GameVersion::XB:
|
||||
throw runtime_error("xbox licenses are not implemented");
|
||||
break;
|
||||
default:
|
||||
throw logic_error("unsupported versioned command");
|
||||
shared_ptr<License> l(new License());
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = cmd.access_key;
|
||||
if (c->version() == GameVersion::GC) {
|
||||
l->gc_password = cmd.password;
|
||||
}
|
||||
s->license_manager->add(l);
|
||||
l->flags |= License::Flag::TEMPORARY;
|
||||
s->license_index->add(l);
|
||||
c->set_license(l);
|
||||
send_command(c, 0x9C, 0x01);
|
||||
}
|
||||
@@ -714,7 +716,7 @@ static void on_9D_9E(shared_ptr<Client> c, uint16_t command, uint32_t, const str
|
||||
// the client to crash.
|
||||
if (base_cmd->unused1 == 0x5F5CA297) {
|
||||
c->flags &= ~(Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL | Client::Flag::NO_SEND_FUNCTION_CALL);
|
||||
} else if (!s->episode_3_send_function_call_enabled &&
|
||||
} else if (!s->ep3_send_function_call_enabled &&
|
||||
(c->flags & Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL)) {
|
||||
c->flags &= ~Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL;
|
||||
c->flags |= Client::Flag::NO_SEND_FUNCTION_CALL;
|
||||
@@ -722,14 +724,14 @@ static void on_9D_9E(shared_ptr<Client> c, uint16_t command, uint32_t, const str
|
||||
|
||||
uint32_t serial_number = stoul(base_cmd->serial_number, nullptr, 16);
|
||||
try {
|
||||
shared_ptr<const License> l;
|
||||
shared_ptr<License> l;
|
||||
switch (c->version()) {
|
||||
case GameVersion::DC:
|
||||
case GameVersion::PC:
|
||||
l = s->license_manager->verify_pc(serial_number, base_cmd->access_key);
|
||||
l = s->license_index->verify_v1_v2(serial_number, base_cmd->access_key);
|
||||
break;
|
||||
case GameVersion::GC:
|
||||
l = s->license_manager->verify_gc(serial_number, base_cmd->access_key);
|
||||
l = s->license_index->verify_gc(serial_number, base_cmd->access_key);
|
||||
break;
|
||||
case GameVersion::XB:
|
||||
throw runtime_error("xbox licenses are not implemented");
|
||||
@@ -739,17 +741,17 @@ static void on_9D_9E(shared_ptr<Client> c, uint16_t command, uint32_t, const str
|
||||
}
|
||||
c->set_license(l);
|
||||
|
||||
} catch (const incorrect_access_key& e) {
|
||||
} catch (const LicenseIndex::incorrect_access_key& e) {
|
||||
send_command(c, 0x04, 0x03);
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
|
||||
} catch (const incorrect_password& e) {
|
||||
} catch (const LicenseIndex::incorrect_password& e) {
|
||||
send_command(c, 0x04, 0x06);
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
|
||||
} catch (const missing_license& e) {
|
||||
} catch (const LicenseIndex::missing_license& e) {
|
||||
// On V3, the client should have sent a different command containing the
|
||||
// password already, which should have created and added a temporary
|
||||
// license. So, if no license exists at this point, disconnect the client
|
||||
@@ -760,8 +762,11 @@ static void on_9D_9E(shared_ptr<Client> c, uint16_t command, uint32_t, const str
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
} else if ((c->version() == GameVersion::DC) || (c->version() == GameVersion::PC)) {
|
||||
l = LicenseManager::create_license_pc(serial_number, base_cmd->access_key, true);
|
||||
s->license_manager->add(l);
|
||||
shared_ptr<License> l(new License());
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = base_cmd->access_key;
|
||||
l->flags |= License::Flag::TEMPORARY;
|
||||
s->license_index->add(l);
|
||||
c->set_license(l);
|
||||
} else {
|
||||
throw runtime_error("unsupported game version");
|
||||
@@ -788,25 +793,28 @@ static void on_93_BB(shared_ptr<Client> c, uint16_t, uint32_t, const string& dat
|
||||
c->flags |= flags_for_version(c->version(), -1);
|
||||
|
||||
try {
|
||||
auto l = s->license_manager->verify_bb(cmd.username, cmd.password);
|
||||
auto l = s->license_index->verify_bb(cmd.username, cmd.password);
|
||||
c->set_license(l);
|
||||
|
||||
} catch (const incorrect_password& e) {
|
||||
} catch (const LicenseIndex::incorrect_password& e) {
|
||||
u16string message = u"Login failed: " + decode_sjis(e.what());
|
||||
send_message_box(c, message.c_str());
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
|
||||
} catch (const missing_license& e) {
|
||||
} catch (const LicenseIndex::missing_license& e) {
|
||||
if (!s->allow_unregistered_users) {
|
||||
u16string message = u"Login failed: " + decode_sjis(e.what());
|
||||
send_message_box(c, message.c_str());
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
} else {
|
||||
shared_ptr<License> l = LicenseManager::create_license_bb(
|
||||
fnv1a32(cmd.username) & 0x7FFFFFFF, cmd.username, cmd.password, true);
|
||||
s->license_manager->add(l);
|
||||
shared_ptr<License> l(new License());
|
||||
l->serial_number = fnv1a32(cmd.username) & 0x7FFFFFFF;
|
||||
l->bb_username = cmd.username;
|
||||
l->bb_password = cmd.password;
|
||||
l->flags |= License::Flag::TEMPORARY;
|
||||
s->license_index->add(l);
|
||||
c->set_license(l);
|
||||
}
|
||||
}
|
||||
@@ -892,11 +900,29 @@ static void on_B1(shared_ptr<Client> c, uint16_t, uint32_t, const string& data)
|
||||
}
|
||||
|
||||
static void on_BA_Ep3(shared_ptr<Client> c, uint16_t command, uint32_t, const string& data) {
|
||||
const auto& in_cmd = check_size_t<C_Meseta_GC_Ep3_BA>(data);
|
||||
const auto& in_cmd = check_size_t<C_MesetaTransaction_GC_Ep3_BA>(data);
|
||||
auto s = c->require_server_state();
|
||||
auto l = c->lobby.lock();
|
||||
bool is_lobby = l && !l->is_game();
|
||||
|
||||
uint32_t meseta = s->ep3_infinite_meseta ? 1000000 : 0;
|
||||
S_Meseta_GC_Ep3_BA out_cmd = {meseta, meseta, in_cmd.request_token};
|
||||
uint32_t current_meseta, total_meseta_earned;
|
||||
if (s->ep3_infinite_meseta) {
|
||||
current_meseta = 1000000;
|
||||
total_meseta_earned = 1000000;
|
||||
} else if (is_lobby && s->ep3_jukebox_is_free) {
|
||||
current_meseta = c->license->ep3_current_meseta;
|
||||
total_meseta_earned = c->license->ep3_total_meseta_earned;
|
||||
} else {
|
||||
if (c->license->ep3_current_meseta < in_cmd.value) {
|
||||
throw runtime_error("meseta overdraft not allowed");
|
||||
}
|
||||
c->license->ep3_current_meseta -= in_cmd.value;
|
||||
c->license->save();
|
||||
current_meseta = c->license->ep3_current_meseta;
|
||||
total_meseta_earned = c->license->ep3_total_meseta_earned;
|
||||
}
|
||||
|
||||
S_MesetaTransaction_GC_Ep3_BA out_cmd = {current_meseta, total_meseta_earned, in_cmd.request_token};
|
||||
send_command(c, command, 0x03, &out_cmd, sizeof(out_cmd));
|
||||
}
|
||||
|
||||
@@ -1321,14 +1347,41 @@ static void on_CA_Ep3(shared_ptr<Client> c, uint16_t, uint32_t, const string& da
|
||||
auto tourn = l->tournament_match->tournament.lock();
|
||||
tourn->print_bracket(stderr);
|
||||
|
||||
shared_ptr<Episode3::Tournament::Team> winner_team;
|
||||
shared_ptr<Episode3::Tournament::Team> loser_team;
|
||||
if (winner_team_id == 0) {
|
||||
l->tournament_match->set_winner_team(l->tournament_match->preceding_a->winner_team);
|
||||
winner_team = l->tournament_match->preceding_a->winner_team;
|
||||
loser_team = l->tournament_match->preceding_b->winner_team;
|
||||
} else if (winner_team_id == 1) {
|
||||
l->tournament_match->set_winner_team(l->tournament_match->preceding_b->winner_team);
|
||||
winner_team = l->tournament_match->preceding_b->winner_team;
|
||||
loser_team = l->tournament_match->preceding_a->winner_team;
|
||||
} else {
|
||||
throw logic_error("invalid winner team id");
|
||||
}
|
||||
send_ep3_tournament_match_result(l);
|
||||
l->tournament_match->set_winner_team(winner_team);
|
||||
|
||||
uint32_t meseta_reward = 0;
|
||||
auto& round_rewards = loser_team->has_any_human_players()
|
||||
? s->ep3_defeat_player_meseta_rewards
|
||||
: s->ep3_defeat_com_meseta_rewards;
|
||||
meseta_reward = (l->tournament_match->round_num - 1 < round_rewards.size())
|
||||
? round_rewards[l->tournament_match->round_num - 1]
|
||||
: round_rewards.back();
|
||||
if (l->tournament_match == tourn->get_final_match()) {
|
||||
meseta_reward += s->ep3_final_round_meseta_bonus;
|
||||
}
|
||||
for (const auto& player : winner_team->players) {
|
||||
if (player.is_human()) {
|
||||
auto winner_c = player.client.lock();
|
||||
if (winner_c) {
|
||||
winner_c->license->ep3_current_meseta += meseta_reward;
|
||||
winner_c->license->ep3_total_meseta_earned += meseta_reward;
|
||||
winner_c->license->save();
|
||||
send_ep3_rank_update(winner_c);
|
||||
}
|
||||
}
|
||||
}
|
||||
send_ep3_tournament_match_result(l, meseta_reward);
|
||||
|
||||
on_tournament_bracket_updated(s, tourn);
|
||||
l->ep3_server->tournament_match_result_sent = true;
|
||||
@@ -1863,7 +1916,7 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, const string& data)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(c->license->privileges & Privilege::FREE_JOIN_GAMES)) {
|
||||
if (!(c->license->flags & License::Flag::FREE_JOIN_GAMES)) {
|
||||
if (!game->password.empty() && (password != game->password)) {
|
||||
send_lobby_message_box(c, u"$C6Incorrect password.");
|
||||
break;
|
||||
@@ -2679,7 +2732,7 @@ static void on_00E3_BB(shared_ptr<Client> c, uint16_t, uint32_t, const string& d
|
||||
|
||||
ClientGameData temp_gd;
|
||||
temp_gd.guild_card_number = c->license->serial_number;
|
||||
temp_gd.bb_username = c->license->username;
|
||||
temp_gd.bb_username = c->license->bb_username;
|
||||
temp_gd.bb_player_index = cmd.player_index;
|
||||
|
||||
try {
|
||||
@@ -3162,7 +3215,7 @@ shared_ptr<Lobby> create_game_generic(
|
||||
throw runtime_error("invalid episode");
|
||||
}
|
||||
|
||||
if (!(c->license->privileges & Privilege::FREE_JOIN_GAMES) &&
|
||||
if (!(c->license->flags & License::Flag::FREE_JOIN_GAMES) &&
|
||||
(min_level > c->game_data.player()->disp.stats.level)) {
|
||||
// Note: We don't throw here because this is a situation players might
|
||||
// actually encounter while playing the game normally
|
||||
@@ -3773,25 +3826,29 @@ static void on_04_P(shared_ptr<Client> c, uint16_t, uint32_t, const string& data
|
||||
auto s = c->require_server_state();
|
||||
|
||||
try {
|
||||
auto l = s->license_manager->verify_bb(cmd.username, cmd.password);
|
||||
auto l = s->license_index->verify_bb(cmd.username, cmd.password);
|
||||
c->set_license(l);
|
||||
|
||||
} catch (const incorrect_password& e) {
|
||||
} catch (const LicenseIndex::incorrect_password& e) {
|
||||
u16string message = u"Login failed: " + decode_sjis(e.what());
|
||||
send_message_box(c, message.c_str());
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
|
||||
} catch (const missing_license& e) {
|
||||
} catch (const LicenseIndex::missing_license& e) {
|
||||
if (!s->allow_unregistered_users) {
|
||||
u16string message = u"Login failed: " + decode_sjis(e.what());
|
||||
send_message_box(c, message.c_str());
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
} else {
|
||||
shared_ptr<License> l = LicenseManager::create_license_bb(
|
||||
fnv1a32(cmd.username) & 0x7FFFFFFF, cmd.username, cmd.password, true);
|
||||
s->license_manager->add(l);
|
||||
|
||||
shared_ptr<License> l(new License());
|
||||
l->serial_number = fnv1a32(cmd.username) & 0x7FFFFFFF;
|
||||
l->bb_username = cmd.username;
|
||||
l->bb_password = cmd.password;
|
||||
l->flags |= License::Flag::TEMPORARY;
|
||||
s->license_index->add(l);
|
||||
c->set_license(l);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-7
@@ -2227,8 +2227,9 @@ void send_ep3_media_update(
|
||||
|
||||
void send_ep3_rank_update(shared_ptr<Client> c) {
|
||||
auto s = c->require_server_state();
|
||||
uint32_t meseta = s->ep3_infinite_meseta ? 1000000 : 0;
|
||||
S_RankUpdate_GC_Ep3_B7 cmd = {0, "\0\0\0\0\0\0\0\0\0\0\0", meseta, meseta, 0xFFFFFFFF};
|
||||
uint32_t current_meseta = s->ep3_infinite_meseta ? 1000000 : c->license->ep3_current_meseta;
|
||||
uint32_t total_meseta_earned = s->ep3_infinite_meseta ? 1000000 : c->license->ep3_total_meseta_earned;
|
||||
S_RankUpdate_GC_Ep3_B7 cmd = {0, "\0\0\0\0\0\0\0\0\0\0\0", current_meseta, total_meseta_earned, 0xFFFFFFFF};
|
||||
send_command_t(c, 0xB7, 0x00, cmd);
|
||||
}
|
||||
|
||||
@@ -2564,7 +2565,7 @@ void send_ep3_set_tournament_player_decks(shared_ptr<Client> c) {
|
||||
// TODO: Handle disconnection during the match (the other team should win)
|
||||
}
|
||||
|
||||
void send_ep3_tournament_match_result(shared_ptr<Lobby> l) {
|
||||
void send_ep3_tournament_match_result(shared_ptr<Lobby> l, uint32_t meseta_reward) {
|
||||
auto s = l->require_server_state();
|
||||
auto& match = l->tournament_match;
|
||||
auto tourn = match->tournament.lock();
|
||||
@@ -2603,10 +2604,7 @@ void send_ep3_tournament_match_result(shared_ptr<Lobby> l) {
|
||||
cmd.round_num = (match == tourn->get_final_match()) ? 6 : match->round_num;
|
||||
cmd.num_players_per_team = match->preceding_a->winner_team->max_players;
|
||||
cmd.winner_team_id = (match->preceding_b->winner_team == match->winner_team);
|
||||
// TODO: This amount should vary depending on the match level / round number,
|
||||
// but newserv doesn't currently implement meseta at all - we just always give
|
||||
// the player 1000000 and never charge for anything.
|
||||
cmd.meseta_amount = 100;
|
||||
cmd.meseta_amount = meseta_reward;
|
||||
cmd.meseta_reward_text = "You got %s meseta!";
|
||||
if (!(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
|
||||
uint8_t mask_key = (random_object<uint32_t>() % 0xFF) + 1;
|
||||
|
||||
+1
-1
@@ -331,7 +331,7 @@ void send_ep3_tournament_info(
|
||||
std::shared_ptr<Client> c,
|
||||
std::shared_ptr<const Episode3::Tournament> t);
|
||||
void send_ep3_set_tournament_player_decks(std::shared_ptr<Client> c);
|
||||
void send_ep3_tournament_match_result(std::shared_ptr<Lobby> l);
|
||||
void send_ep3_tournament_match_result(std::shared_ptr<Lobby> l, uint32_t meseta_reward);
|
||||
|
||||
void send_ep3_tournament_details(
|
||||
std::shared_ptr<Client> c,
|
||||
|
||||
+1
-1
@@ -306,7 +306,7 @@ vector<shared_ptr<Client>> Server::get_clients_by_identifier(const string& ident
|
||||
results.emplace_back(std::move(c));
|
||||
continue;
|
||||
}
|
||||
if (c->license && c->license->username == ident) {
|
||||
if (c->license && c->license->bb_username == ident) {
|
||||
results.emplace_back(std::move(c));
|
||||
continue;
|
||||
}
|
||||
|
||||
+65
-55
@@ -134,7 +134,7 @@ 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\
|
||||
privileges=<privilege-mask> (can be normal, mod, admin, root, or numeric)\n\
|
||||
flags=<privilege-mask> (can be normal, mod, admin, root, or numeric)\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\
|
||||
@@ -310,7 +310,7 @@ Proxy session commands:\n\
|
||||
if (token.size() >= 32) {
|
||||
throw invalid_argument("username too long");
|
||||
}
|
||||
l->username = token.substr(12);
|
||||
l->bb_username = token.substr(12);
|
||||
|
||||
} else if (starts_with(token, "bb-password=")) {
|
||||
if (token.size() >= 32) {
|
||||
@@ -333,18 +333,18 @@ Proxy session commands:\n\
|
||||
} else if (starts_with(token, "serial=")) {
|
||||
l->serial_number = stoul(token.substr(7));
|
||||
|
||||
} else if (starts_with(token, "privileges=")) {
|
||||
} else if (starts_with(token, "flags=")) {
|
||||
string mask = token.substr(11);
|
||||
if (mask == "normal") {
|
||||
l->privileges = 0;
|
||||
l->flags = 0;
|
||||
} else if (mask == "mod") {
|
||||
l->privileges = Privilege::MODERATOR;
|
||||
l->flags = License::Flag::MODERATOR;
|
||||
} else if (mask == "admin") {
|
||||
l->privileges = Privilege::ADMINISTRATOR;
|
||||
l->flags = License::Flag::ADMINISTRATOR;
|
||||
} else if (mask == "root") {
|
||||
l->privileges = Privilege::ROOT;
|
||||
l->flags = License::Flag::ROOT;
|
||||
} else {
|
||||
l->privileges = stoul(mask);
|
||||
l->flags = stoul(mask);
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -356,7 +356,8 @@ Proxy session commands:\n\
|
||||
throw invalid_argument("license does not contain serial number");
|
||||
}
|
||||
|
||||
this->state->license_manager->add(l);
|
||||
l->save();
|
||||
this->state->license_index->add(l);
|
||||
fprintf(stderr, "license added\n");
|
||||
|
||||
} else if (command_name == "update-license") {
|
||||
@@ -366,71 +367,80 @@ Proxy session commands:\n\
|
||||
}
|
||||
uint32_t serial_number = stoul(tokens[0]);
|
||||
tokens.erase(tokens.begin());
|
||||
auto orig_l = this->state->license_manager->get(serial_number);
|
||||
auto orig_l = this->state->license_index->get(serial_number);
|
||||
shared_ptr<License> l(new License(*orig_l));
|
||||
|
||||
for (const string& token : tokens) {
|
||||
if (starts_with(token, "bb-username=")) {
|
||||
if (token.size() >= 32) {
|
||||
throw invalid_argument("username too long");
|
||||
}
|
||||
l->username = token.substr(12);
|
||||
this->state->license_index->remove(orig_l->serial_number);
|
||||
try {
|
||||
for (const string& token : tokens) {
|
||||
if (starts_with(token, "bb-username=")) {
|
||||
if (token.size() >= 32) {
|
||||
throw invalid_argument("username too long");
|
||||
}
|
||||
l->bb_username = token.substr(12);
|
||||
|
||||
} else if (starts_with(token, "bb-password=")) {
|
||||
if (token.size() >= 32) {
|
||||
throw invalid_argument("bb-password too long");
|
||||
}
|
||||
l->bb_password = token.substr(12);
|
||||
} else if (starts_with(token, "bb-password=")) {
|
||||
if (token.size() >= 32) {
|
||||
throw invalid_argument("bb-password too long");
|
||||
}
|
||||
l->bb_password = token.substr(12);
|
||||
|
||||
} else if (starts_with(token, "gc-password=")) {
|
||||
if (token.size() > 20) {
|
||||
throw invalid_argument("gc-password too long");
|
||||
}
|
||||
l->gc_password = token.substr(12);
|
||||
} else if (starts_with(token, "gc-password=")) {
|
||||
if (token.size() > 20) {
|
||||
throw invalid_argument("gc-password too long");
|
||||
}
|
||||
l->gc_password = token.substr(12);
|
||||
|
||||
} else if (starts_with(token, "access-key=")) {
|
||||
if (token.size() > 23) {
|
||||
throw invalid_argument("access-key is too long");
|
||||
}
|
||||
l->access_key = token.substr(11);
|
||||
} else if (starts_with(token, "access-key=")) {
|
||||
if (token.size() > 23) {
|
||||
throw invalid_argument("access-key is too long");
|
||||
}
|
||||
l->access_key = token.substr(11);
|
||||
|
||||
} else if (starts_with(token, "serial=")) {
|
||||
l->serial_number = stoul(token.substr(7));
|
||||
} else if (starts_with(token, "serial=")) {
|
||||
l->serial_number = stoul(token.substr(7));
|
||||
|
||||
} else if (starts_with(token, "flags=")) {
|
||||
string mask = token.substr(11);
|
||||
if (mask == "normal") {
|
||||
l->flags = 0;
|
||||
} else if (mask == "mod") {
|
||||
l->flags = License::Flag::MODERATOR;
|
||||
} else if (mask == "admin") {
|
||||
l->flags = License::Flag::ADMINISTRATOR;
|
||||
} else if (mask == "root") {
|
||||
l->flags = License::Flag::ROOT;
|
||||
} else {
|
||||
l->flags = stoul(mask);
|
||||
}
|
||||
|
||||
} else if (starts_with(token, "privileges=")) {
|
||||
string mask = token.substr(11);
|
||||
if (mask == "normal") {
|
||||
l->privileges = 0;
|
||||
} else if (mask == "mod") {
|
||||
l->privileges = Privilege::MODERATOR;
|
||||
} else if (mask == "admin") {
|
||||
l->privileges = Privilege::ADMINISTRATOR;
|
||||
} else if (mask == "root") {
|
||||
l->privileges = Privilege::ROOT;
|
||||
} else {
|
||||
l->privileges = stoul(mask);
|
||||
throw invalid_argument("incorrect field: " + token);
|
||||
}
|
||||
|
||||
} else {
|
||||
throw invalid_argument("incorrect field: " + token);
|
||||
}
|
||||
|
||||
if (!l->serial_number) {
|
||||
throw invalid_argument("license does not contain serial number");
|
||||
}
|
||||
} catch (const exception&) {
|
||||
this->state->license_index->add(orig_l);
|
||||
throw;
|
||||
}
|
||||
|
||||
if (!l->serial_number) {
|
||||
throw invalid_argument("license does not contain serial number");
|
||||
}
|
||||
|
||||
this->state->license_manager->add(l);
|
||||
l->save();
|
||||
this->state->license_index->add(l);
|
||||
fprintf(stderr, "license updated\n");
|
||||
|
||||
} else if (command_name == "delete-license") {
|
||||
uint32_t serial_number = stoul(command_args);
|
||||
this->state->license_manager->remove(serial_number);
|
||||
auto l = this->state->license_index->get(serial_number);
|
||||
l->delete_file();
|
||||
this->state->license_index->remove(l->serial_number);
|
||||
fprintf(stderr, "license deleted\n");
|
||||
|
||||
} else if (command_name == "list-licenses") {
|
||||
for (const auto& l : this->state->license_manager->snapshot()) {
|
||||
string s = l.str();
|
||||
for (const auto& l : this->state->license_index->all()) {
|
||||
string s = l->str();
|
||||
fprintf(stderr, "%s\n", s.c_str());
|
||||
}
|
||||
|
||||
|
||||
+24
-7
@@ -26,9 +26,13 @@ ServerState::ServerState(const char* config_filename, bool is_replay)
|
||||
allow_saving(true),
|
||||
item_tracking_enabled(true),
|
||||
drops_enabled(true),
|
||||
episode_3_send_function_call_enabled(false),
|
||||
ep3_send_function_call_enabled(false),
|
||||
catch_handler_exceptions(true),
|
||||
ep3_infinite_meseta(true),
|
||||
ep3_infinite_meseta(false),
|
||||
ep3_defeat_player_meseta_rewards({400, 500, 600, 700, 800}),
|
||||
ep3_defeat_com_meseta_rewards({100, 200, 300, 400, 500}),
|
||||
ep3_final_round_meseta_bonus(300),
|
||||
ep3_jukebox_is_free(false),
|
||||
ep3_behavior_flags(0),
|
||||
run_shell_behavior(RunShellBehavior::DEFAULT),
|
||||
cheat_mode_behavior(CheatModeBehavior::OFF_BY_DEFAULT),
|
||||
@@ -534,13 +538,26 @@ void ServerState::parse_config(const JSON& json, bool is_reload) {
|
||||
this->allow_unregistered_users = json.get_bool("AllowUnregisteredUsers", this->allow_unregistered_users);
|
||||
this->item_tracking_enabled = json.get_bool("EnableItemTracking", this->item_tracking_enabled);
|
||||
this->drops_enabled = json.get_bool("EnableDrops", this->drops_enabled);
|
||||
this->episode_3_send_function_call_enabled = json.get_bool("EnableEpisode3SendFunctionCall", this->episode_3_send_function_call_enabled);
|
||||
this->ep3_send_function_call_enabled = json.get_bool("EnableEpisode3SendFunctionCall", this->ep3_send_function_call_enabled);
|
||||
this->catch_handler_exceptions = json.get_bool("CatchHandlerExceptions", this->catch_handler_exceptions);
|
||||
|
||||
auto parse_int_list = +[](const JSON& json) -> vector<uint32_t> {
|
||||
vector<uint32_t> ret;
|
||||
for (const auto& item : json.as_list()) {
|
||||
ret.emplace_back(item->as_int());
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
this->ep3_infinite_meseta = json.get_bool("Episode3InfiniteMeseta", this->ep3_infinite_meseta);
|
||||
this->proxy_allow_save_files = json.get_bool("ProxyAllowSaveFiles", this->proxy_allow_save_files);
|
||||
this->proxy_enable_login_options = json.get_bool("ProxyEnableLoginOptions", this->proxy_enable_login_options);
|
||||
this->ep3_defeat_player_meseta_rewards = parse_int_list(json.get("Episode3DefeatPlayerMeseta", JSON::list()));
|
||||
this->ep3_defeat_com_meseta_rewards = parse_int_list(json.get("Episode3DefeatCOMMeseta", JSON::list()));
|
||||
this->ep3_final_round_meseta_bonus = json.get_int("Episode3FinalRoundMesetaBonus", this->ep3_final_round_meseta_bonus);
|
||||
this->ep3_jukebox_is_free = json.get_bool("Episode3JukeboxIsFree", this->ep3_jukebox_is_free);
|
||||
this->ep3_behavior_flags = json.get_int("Episode3BehaviorFlags", this->ep3_behavior_flags);
|
||||
this->ep3_card_auction_points = json.get_int("CardAuctionPoints", this->ep3_card_auction_points);
|
||||
this->proxy_allow_save_files = json.get_bool("ProxyAllowSaveFiles", this->proxy_allow_save_files);
|
||||
this->proxy_enable_login_options = json.get_bool("ProxyEnableLoginOptions", this->proxy_enable_login_options);
|
||||
|
||||
try {
|
||||
const auto& i = json.at("CardAuctionSize");
|
||||
@@ -767,9 +784,9 @@ void ServerState::load_bb_private_keys() {
|
||||
|
||||
void ServerState::load_licenses() {
|
||||
config_log.info("Loading license list");
|
||||
this->license_manager.reset(new LicenseManager("system/licenses.nsi"));
|
||||
this->license_index.reset(new LicenseIndex());
|
||||
if (this->is_replay) {
|
||||
this->license_manager->set_autosave(false);
|
||||
this->license_index->set_autosave(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -59,9 +59,13 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
bool allow_saving;
|
||||
bool item_tracking_enabled;
|
||||
bool drops_enabled;
|
||||
bool episode_3_send_function_call_enabled;
|
||||
bool ep3_send_function_call_enabled;
|
||||
bool catch_handler_exceptions;
|
||||
bool ep3_infinite_meseta;
|
||||
std::vector<uint32_t> ep3_defeat_player_meseta_rewards;
|
||||
std::vector<uint32_t> ep3_defeat_com_meseta_rewards;
|
||||
uint32_t ep3_final_round_meseta_bonus;
|
||||
bool ep3_jukebox_is_free;
|
||||
uint32_t ep3_behavior_flags;
|
||||
RunShellBehavior run_shell_behavior;
|
||||
CheatModeBehavior cheat_mode_behavior;
|
||||
@@ -110,7 +114,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
};
|
||||
std::vector<Ep3LobbyBannerEntry> ep3_lobby_banners;
|
||||
|
||||
std::shared_ptr<LicenseManager> license_manager;
|
||||
std::shared_ptr<LicenseIndex> license_index;
|
||||
|
||||
std::shared_ptr<const Menu> information_menu_v2;
|
||||
std::shared_ptr<const Menu> information_menu_v3;
|
||||
|
||||
Reference in New Issue
Block a user