Merge master into DC/GC XP boost branch

This commit is contained in:
2026-05-24 10:30:09 -04:00
19 changed files with 490 additions and 495 deletions
+1
View File
@@ -19,6 +19,7 @@
#include "QuestScript.hh"
#include "TeamIndex.hh"
#include "Text.hh"
#include <string>
extern const uint64_t CLIENT_CONFIG_MAGIC;
+68 -75
View File
@@ -2,6 +2,7 @@
#include <inttypes.h>
#include <string.h>
#include <unistd.h>
#include <fstream>
#include <memory>
@@ -33,12 +34,9 @@ const char* BATTLE_TABLE_DISCONNECT_HOOK_NAME = "battle_table_state";
const char* QUEST_BARRIER_DISCONNECT_HOOK_NAME = "quest_barrier";
const char* ADD_NEXT_CLIENT_DISCONNECT_HOOK_NAME = "add_next_game_client";
static string bb_test_taint_filename(shared_ptr<Client> c) {
return c->character_filename() + ".test-tainted";
}
static string bb_test_taint_grandfather_filename(shared_ptr<Client> c) {
return c->character_filename() + ".grandfathered-before-test-taint";
static string bb_test_filename(shared_ptr<Client> c) {
return c->character_filename() + ".test";
}
static bool file_exists_for_bb_taint(const string& filename) {
@@ -46,28 +44,67 @@ static bool file_exists_for_bb_taint(const string& filename) {
return f.good();
}
static bool bb_character_is_test_tainted(shared_ptr<Client> c) {
return file_exists_for_bb_taint(bb_test_taint_filename(c));
static bool bb_character_is_test(shared_ptr<Client> c) {
return file_exists_for_bb_taint(bb_test_filename(c));
}
static bool bb_character_is_test_taint_grandfathered(shared_ptr<Client> c) {
return file_exists_for_bb_taint(bb_test_taint_grandfather_filename(c));
}
static bool mark_bb_character_test_tainted(shared_ptr<Client> c) {
string filename = bb_test_taint_filename(c);
static bool mark_bb_character_test(shared_ptr<Client> c) {
string filename = bb_test_filename(c);
ofstream f(filename, ios::out | ios::trunc);
if (!f.good()) {
return false;
}
f << "status=test-tainted\n";
f << "reason=entered-test-ship\n";
f << "account_id=" << c->login->account->account_id << "\n";
uint32_t account_id = 0;
if (c->login && c->login->account) {
account_id = c->login->account->account_id;
}
f << "status=test\n";
f << "reason=created-on-test-ship\n";
f << "account_id=" << account_id << "\n";
f << "character_file=" << c->character_filename() << "\n";
return f.good();
}
static void clear_bb_ship_state_markers_for_recreated_character(shared_ptr<Client> c) {
const string base_filename = c->character_filename();
const vector<string> suffixes = {
".hardcore",
".hardcore-dead",
".hardcore-ineligible",
".test",
".test-tainted",
".grandfathered-before-test-taint",
};
for (const auto& suffix : suffixes) {
const string filename = base_filename + suffix;
if (file_exists_for_bb_taint(filename)) {
if (::unlink(filename.c_str()) == 0) {
c->log.info_f("Removed stale BB ship-state marker for recreated character: {}", filename);
} else {
c->log.warning_f("Failed to remove stale BB ship-state marker for recreated character: {}", filename);
}
}
}
}
static bool enforce_bb_test_ship_lock(shared_ptr<Client> c, bool current_ship_is_test) {
if (current_ship_is_test) {
if (!bb_character_is_test(c)) {
send_message_box(c, "$C6Only Test characters can enter Test.\n\n$C7Create a new character on the Test ship.");
return false;
}
} else {
if (bb_character_is_test(c)) {
send_message_box(c, "$C6This BB character is locked to Test.\n\n$C7Test characters cannot enter public ships.");
return false;
}
}
return true;
}
static string bb_hardcore_filename(shared_ptr<Client> c) {
return c->character_filename() + ".hardcore";
@@ -126,9 +163,7 @@ static bool enforce_bb_hardcore_ship_lock(shared_ptr<Client> c, bool current_shi
}
if (current_ship_is_hardcore) {
if (bb_character_is_hardcore_ineligible(c) ||
bb_character_is_test_tainted(c) ||
bb_character_is_test_taint_grandfathered(c)) {
if (bb_character_is_hardcore_ineligible(c)) {
if (!bb_character_is_hardcore_ineligible(c)) {
mark_bb_character_hardcore_ineligible(c);
}
@@ -235,8 +270,6 @@ static void send_main_menu(shared_ptr<Client> c) {
((c->version() == Version::BB_V4) || (c->version() == Version::BB_PATCH));
bool bb_frontdoor_ship_menu = s->enable_bb_ship_selection_menu && is_bb_ship_menu_client;
bool bb_destination_transport_menu = !s->enable_bb_ship_selection_menu && is_bb_ship_menu_client;
bool show_bb_live_test_menu_items = bb_frontdoor_ship_menu || bb_destination_transport_menu;
bool show_bb_restricted_ship_menu_items = bb_frontdoor_ship_menu;
uint32_t go_to_lobby_menu_item_flags =
(s->proxy_destinations_dc.empty() ? 0 : MenuItem::Flag::INVISIBLE_ON_DC) |
@@ -302,43 +335,8 @@ static void send_main_menu(shared_ptr<Client> c) {
// pre-lobby page and the lobby counter Transport list. Keep the frontdoor
// as the full ship selector, but only expose safe transport choices on
// destination ships.
uint32_t bb_live_test_menu_item_flags =
show_bb_live_test_menu_items
? MenuItem::Flag::BB_ONLY
: (MenuItem::Flag::INVISIBLE_ON_DC |
MenuItem::Flag::INVISIBLE_ON_PC |
MenuItem::Flag::INVISIBLE_ON_GC |
MenuItem::Flag::INVISIBLE_ON_XB |
MenuItem::Flag::INVISIBLE_ON_BB);
uint32_t bb_restricted_ship_menu_item_flags =
show_bb_restricted_ship_menu_items
? MenuItem::Flag::BB_ONLY
: (MenuItem::Flag::INVISIBLE_ON_DC |
MenuItem::Flag::INVISIBLE_ON_PC |
MenuItem::Flag::INVISIBLE_ON_GC |
MenuItem::Flag::INVISIBLE_ON_XB |
MenuItem::Flag::INVISIBLE_ON_BB);
main_menu->items.emplace_back(MainMenuItemID::BB_LIVE_SHIP, "PSO-Peeps Live",
"Join the live\nPSO-Peeps ship", bb_live_test_menu_item_flags);
main_menu->items.emplace_back(MainMenuItemID::BB_TEST_SHIP, "Test Ship",
"Join the test\nconfiguration ship", bb_live_test_menu_item_flags);
main_menu->items.emplace_back(MainMenuItemID::BB_DEV_SHIP, "Dev Ship",
"Join the dev\nexperimental ship", bb_restricted_ship_menu_item_flags);
main_menu->items.emplace_back(MainMenuItemID::BB_VANILLA_SHIP, "Vanilla Ship",
"Join the vanilla\ndefault-settings ship", bb_restricted_ship_menu_item_flags);
main_menu->items.emplace_back(MainMenuItemID::BB_HARDCORE_SHIP, "Hardcore Ship",
"Join the hardcore\npermadeath ship", bb_restricted_ship_menu_item_flags);
uint32_t proxy_destinations_menu_item_flags =
(s->proxy_destinations_dc.empty() ? MenuItem::Flag::INVISIBLE_ON_DC : 0) |
(s->proxy_destinations_pc.empty() ? MenuItem::Flag::INVISIBLE_ON_PC : 0) |
(s->proxy_destinations_gc.empty() ? MenuItem::Flag::INVISIBLE_ON_GC : 0) |
(s->proxy_destinations_xb.empty() ? MenuItem::Flag::INVISIBLE_ON_XB : 0) |
MenuItem::Flag::INVISIBLE_ON_BB;
main_menu->items.emplace_back(MainMenuItemID::PROXY_DESTINATIONS, "Select ship",
"Choose Live,\nVanilla, or Test", proxy_destinations_menu_item_flags);
main_menu->items.emplace_back(MainMenuItemID::DOWNLOAD_QUESTS, "Download quests",
"Download quests", MenuItem::Flag::INVISIBLE_ON_DC_PROTOS | MenuItem::Flag::INVISIBLE_ON_PC_NTE | MenuItem::Flag::INVISIBLE_ON_BB);
@@ -2850,14 +2848,6 @@ static asio::awaitable<void> on_10_main_menu(shared_ptr<Client> c, uint32_t item
break;
}
// PSO Peeps alpha test: entering Test taints only non-grandfathered BB characters.
// Existing characters were grandfathered before this feature was enabled.
if (!bb_character_is_test_taint_grandfathered(c)) {
if (!mark_bb_character_test_tainted(c)) {
send_message_box(c, "$C6Could not mark this character for Test access.\n\n$C7Please report this.");
break;
}
}
send_reconnect(c, s->connect_address_for_client(c), 19345);
break;
@@ -2879,13 +2869,6 @@ static asio::awaitable<void> on_10_main_menu(shared_ptr<Client> c, uint32_t item
break;
}
// PSO Peeps dev/test isolation: entering Dev taints only non-grandfathered BB characters.
if (!bb_character_is_test_taint_grandfathered(c)) {
if (!mark_bb_character_test_tainted(c)) {
send_message_box(c, "$C6Could not mark this character for Dev access.\n\n$C7Please report this.");
break;
}
}
send_reconnect(c, s->connect_address_for_client(c), 19445);
break;
@@ -2907,8 +2890,8 @@ static asio::awaitable<void> on_10_main_menu(shared_ptr<Client> c, uint32_t item
break;
}
if (bb_character_is_test_tainted(c)) {
send_message_box(c, "$C6This character has been used outside of Vanilla and cannot enter Vanilla.\n\n$C7Use Live/Test with this character, or create a fresh Vanilla character.");
if (bb_character_is_test(c)) {
send_message_box(c, "$C6This BB character is locked to Test.\n\n$C7Test characters cannot enter public ships.");
c->channel->disconnect();
break;
}
@@ -2936,8 +2919,7 @@ static asio::awaitable<void> on_10_main_menu(shared_ptr<Client> c, uint32_t item
}
if (bb_character_is_hardcore_ineligible(c) ||
bb_character_is_test_tainted(c) ||
bb_character_is_test_taint_grandfathered(c)) {
bb_character_is_test(c)) {
if (!bb_character_is_hardcore_ineligible(c)) {
mark_bb_character_hardcore_ineligible(c);
}
@@ -4164,6 +4146,10 @@ static asio::awaitable<void> on_E3_BB(shared_ptr<Client> c, Channel::Message& ms
c->bb_bank_character_index = cmd.character_index;
auto s = c->require_server_state();
if (!enforce_bb_test_ship_lock(c, s->enable_test_mode)) {
c->unload_character(false);
co_return;
}
if (!enforce_bb_hardcore_ship_lock(c, s->enable_hardcore_mode)) {
c->unload_character(false);
co_return;
@@ -4415,7 +4401,14 @@ static asio::awaitable<void> on_E5_BB(shared_ptr<Client> c, Channel::Message& ms
} else {
try {
auto s = c->require_server_state();
clear_bb_ship_state_markers_for_recreated_character(c);
c->create_character_file(c->login->account->account_id, c->language(), cmd.preview, s->level_table(c->version()));
if (s->enable_test_mode) {
if (!mark_bb_character_test(c)) {
throw runtime_error("could not mark new character as Test");
}
c->log.info_f("Marked BB character as Test: {}", c->character_filename());
}
} catch (const exception& e) {
send_message_box(c, std::format("$C6New character could not be created:\n{}", e.what()));
should_send_approve = false;
+6
View File
@@ -878,6 +878,11 @@ void ServerState::load_config_early() {
this->client_ping_interval_usecs = this->config_json->get_int("ClientPingInterval", 30000000);
this->client_idle_timeout_usecs = this->config_json->get_int("ClientIdleTimeout", 60000000);
this->patch_client_idle_timeout_usecs = this->config_json->get_int("PatchClientIdleTimeout", 300000000);
this->psopeeps_dcv2_exp_multiplier = this->config_json->get_int("PsoPeepsDCV2EXPMultiplier", 5);
if ((this->psopeeps_dcv2_exp_multiplier != 5) && (this->psopeeps_dcv2_exp_multiplier != 10)) {
throw runtime_error("PsoPeepsDCV2EXPMultiplier must be 5 or 10");
}
this->ip_stack_debug = this->config_json->get_bool("IPStackDebug", false);
this->allow_unregistered_users = this->config_json->get_bool("AllowUnregisteredUsers", false);
@@ -952,6 +957,7 @@ void ServerState::load_config_early() {
this->enable_bb_ship_selection_menu = this->config_json->get_bool("EnableBBShipSelectionMenu", false);
this->enable_blueballz = this->config_json->get_bool("EnableBlueballz", false);
this->enable_hardcore_mode = this->config_json->get_bool("EnableHardcoreMode", false);
this->enable_test_mode = this->config_json->get_bool("EnableTestMode", false);
this->blueballz_max_tier = std::min<int64_t>(10, std::max<int64_t>(0, this->config_json->get_int("BlueballzMaxTier", 10)));
this->blueballz_unlocked_tier_v2 = std::min<int64_t>(
this->blueballz_max_tier,
+2
View File
@@ -123,6 +123,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
uint64_t client_ping_interval_usecs = 30000000;
uint64_t client_idle_timeout_usecs = 60000000;
uint64_t patch_client_idle_timeout_usecs = 300000000;
uint64_t psopeeps_dcv2_exp_multiplier = 5;
bool is_debug = false;
bool ip_stack_debug = false;
bool allow_unregistered_users = false;
@@ -177,6 +178,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
bool enable_blueballz = false;
int64_t blueballz_enemy_hp_scale_tier = -1; // -1 = disabled; 0..10 = scale BB enemy HP in stream files
bool enable_hardcore_mode = false;
bool enable_test_mode = false;
int8_t blueballz_max_tier = 10;
int8_t blueballz_unlocked_tier_v2 = 0;
int8_t blueballz_unlocked_tier_v3 = 0;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,6 +1,5 @@
.meta key="PsoPeepsEP2EXP10x"
.meta name="EP2 10x"
.meta client_flag="0x0000200000000000"
.meta description="Sets EP2 enemy EXP\nto 10x for GC crossplay"
.versions 3OE2 3OJ5
@@ -1,7 +1,5 @@
.meta visibility="menu"
.meta key="PsoPeepsEP2EXP10xJP"
.meta name="EP2 10x"
.meta client_flag="0x0000200000000000"
.meta description="Sets EP2 enemy EXP\nto 10x for GC crossplay"
.versions 3OJ5
@@ -1,7 +1,6 @@
.meta visibility="menu"
.meta key="PsoPeepsEP2EXP10xUS"
.meta name="EP2 10x"
.meta client_flag="0x0000200000000000"
.meta description="Sets EP2 enemy EXP\nto 10x for GC crossplay"
.versions 3OE2
@@ -1,6 +1,5 @@
.meta key="PsoPeepsEP2EXP5x"
.meta name="EP2 5x"
.meta client_flag="0x0000200000000000"
.meta description="Sets EP2 enemy EXP\nto 5x for GC crossplay"
.versions 3OE2 3OJ5
@@ -1,7 +1,5 @@
.meta visibility="menu"
.meta key="PsoPeepsEP2EXP5xJP"
.meta name="EP2 5x"
.meta client_flag="0x0000200000000000"
.meta description="Sets EP2 enemy EXP\nto 5x for GC crossplay"
.versions 3OJ5
@@ -1,7 +1,6 @@
.meta visibility="menu"
.meta key="PsoPeepsEP2EXP5xUS"
.meta name="EP2 5x"
.meta client_flag="0x0000200000000000"
.meta description="Sets EP2 enemy EXP\nto 5x for GC crossplay"
.versions 3OE2
Binary file not shown.
@@ -0,0 +1 @@
This file exists to test the patch download system.
+1
View File
@@ -0,0 +1 @@
{"./Media/PSO/BattleParamEntry.dat":[62976,1777951677030279266,217294441,[333854591,3127350609,1651077234]],"./Media/PSO/newserv-test-pc.txt":[51,1777951677024279220,3510656944,[3510656944]],"./Media/PSO/BattleParamEntry_on.dat":[62976,1777951677030279246,2007581284,[3877699156,780358345,290569120]]}
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
This file exists to test the patch download system.