make games and quests work on gc
This commit is contained in:
+134
-72
@@ -17,9 +17,9 @@ using namespace std;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
vector<u16string> section_id_to_name({
|
||||
u"Viridia", u"Greennill", u"Skyly", u"Bluefull", u"Purplenum", u"Pinkal",
|
||||
u"Redria", u"Oran", u"Yellowboze", u"Whitill"});
|
||||
vector<string> section_id_to_name({
|
||||
"Viridia", "Greennill", "Skyly", "Bluefull", "Purplenum", "Pinkal", "Redria",
|
||||
"Oran", "Yellowboze", "Whitill"});
|
||||
|
||||
unordered_map<u16string, uint8_t> name_to_section_id({
|
||||
{u"viridia", 0},
|
||||
@@ -35,53 +35,53 @@ unordered_map<u16string, uint8_t> name_to_section_id({
|
||||
|
||||
vector<u16string> lobby_event_to_name({
|
||||
u"none", u"xmas", u"none", u"val", u"easter", u"hallo", u"sonic",
|
||||
u"newyear", u"spring", u"white", u"wedding", u"fall", u"s-summer",
|
||||
u"s-spring", u"summer"});
|
||||
u"newyear", u"summer", u"white", u"wedding", u"fall", u"s-spring",
|
||||
u"s-summer", u"spring"});
|
||||
|
||||
unordered_map<u16string, uint8_t> name_to_lobby_event({
|
||||
{u"none", 0},
|
||||
{u"xmas", 1},
|
||||
{u"val", 3},
|
||||
{u"easter", 4},
|
||||
{u"hallo", 5},
|
||||
{u"sonic", 6},
|
||||
{u"newyear", 7},
|
||||
{u"spring", 8},
|
||||
{u"white", 9},
|
||||
{u"wedding", 10},
|
||||
{u"fall", 11},
|
||||
{u"s-summer", 12},
|
||||
{u"s-spring", 13},
|
||||
{u"summer", 14},
|
||||
{u"none", 0},
|
||||
{u"xmas", 1},
|
||||
{u"val", 3},
|
||||
{u"easter", 4},
|
||||
{u"hallo", 5},
|
||||
{u"sonic", 6},
|
||||
{u"newyear", 7},
|
||||
{u"summer", 8},
|
||||
{u"white", 9},
|
||||
{u"wedding", 10},
|
||||
{u"fall", 11},
|
||||
{u"s-spring", 12},
|
||||
{u"s-summer", 13},
|
||||
{u"spring", 14},
|
||||
});
|
||||
|
||||
unordered_map<uint8_t, u16string> lobby_type_to_name({
|
||||
{0x00, u"normal"},
|
||||
{0x0F, u"inormal"},
|
||||
{0x10, u"ipc"},
|
||||
{0x11, u"iball"},
|
||||
{0x67, u"cave2u"},
|
||||
{0xD4, u"cave1"},
|
||||
{0xE9, u"planet"},
|
||||
{0xEA, u"clouds"},
|
||||
{0xED, u"cave"},
|
||||
{0xEE, u"jungle"},
|
||||
{0xEF, u"forest2-2"},
|
||||
{0xF0, u"forest2-1"},
|
||||
{0xF1, u"windpower"},
|
||||
{0xF2, u"overview"},
|
||||
{0xF3, u"seaside"},
|
||||
{0xF4, u"some?"},
|
||||
{0xF5, u"dmorgue"},
|
||||
{0xF6, u"caelum"},
|
||||
{0xF8, u"digital"},
|
||||
{0xF9, u"boss1"},
|
||||
{0xFA, u"boss2"},
|
||||
{0xFB, u"boss3"},
|
||||
{0xFC, u"dragon"},
|
||||
{0xFD, u"derolle"},
|
||||
{0xFE, u"volopt"},
|
||||
{0xFF, u"darkfalz"},
|
||||
{0x00, u"normal"},
|
||||
{0x0F, u"inormal"},
|
||||
{0x10, u"ipc"},
|
||||
{0x11, u"iball"},
|
||||
{0x67, u"cave2u"},
|
||||
{0xD4, u"cave1"},
|
||||
{0xE9, u"planet"},
|
||||
{0xEA, u"clouds"},
|
||||
{0xED, u"cave"},
|
||||
{0xEE, u"jungle"},
|
||||
{0xEF, u"forest2-2"},
|
||||
{0xF0, u"forest2-1"},
|
||||
{0xF1, u"windpower"},
|
||||
{0xF2, u"overview"},
|
||||
{0xF3, u"seaside"},
|
||||
{0xF4, u"some?"},
|
||||
{0xF5, u"dmorgue"},
|
||||
{0xF6, u"caelum"},
|
||||
{0xF8, u"digital"},
|
||||
{0xF9, u"boss1"},
|
||||
{0xFA, u"boss2"},
|
||||
{0xFB, u"boss3"},
|
||||
{0xFC, u"dragon"},
|
||||
{0xFD, u"derolle"},
|
||||
{0xFE, u"volopt"},
|
||||
{0xFF, u"darkfalz"},
|
||||
});
|
||||
|
||||
unordered_map<u16string, uint8_t> name_to_lobby_type({
|
||||
@@ -164,42 +164,57 @@ unordered_map<u16string, uint8_t> name_to_npc_id({
|
||||
|
||||
|
||||
|
||||
class precondition_failed {
|
||||
public:
|
||||
precondition_failed(const char16_t* user_msg) : user_msg(user_msg) { }
|
||||
~precondition_failed() = default;
|
||||
|
||||
const char16_t* what() const {
|
||||
return this->user_msg;
|
||||
}
|
||||
|
||||
private:
|
||||
const char16_t* user_msg;
|
||||
};
|
||||
|
||||
static void check_privileges(shared_ptr<Client> c, uint64_t mask) {
|
||||
if (!c->license) {
|
||||
throw runtime_error("not logged in");
|
||||
throw precondition_failed(u"$C6You are not\nlogged in.");
|
||||
}
|
||||
if ((c->license->privileges & mask) != mask) {
|
||||
throw runtime_error("insufficient permissions");
|
||||
throw precondition_failed(u"$C6You do not have\npermission to\nrun this command.");
|
||||
}
|
||||
}
|
||||
|
||||
static void check_version(shared_ptr<Client> c, GameVersion version) {
|
||||
if (c->version != version) {
|
||||
throw runtime_error("incorrect version");
|
||||
throw precondition_failed(u"$C6This command cannot\nbe used for your\nversion of PSO.");
|
||||
}
|
||||
}
|
||||
|
||||
static void check_not_version(shared_ptr<Client> c, GameVersion version) {
|
||||
if (c->version == version) {
|
||||
throw runtime_error("incorrect version");
|
||||
throw precondition_failed(u"$C6This command cannot\nbe used for your\nversion of PSO.");
|
||||
}
|
||||
}
|
||||
|
||||
static void check_is_game(shared_ptr<Lobby> l, bool is_game) {
|
||||
if (l->is_game() != is_game) {
|
||||
throw runtime_error(is_game ? "can only be used in games" : "can only be used in lobbies");
|
||||
throw precondition_failed(is_game ?
|
||||
u"$C6This command cannot\nbe used in lobbies." :
|
||||
u"$C6This command cannot\nbe used in games.");
|
||||
}
|
||||
}
|
||||
|
||||
static void check_cheats_enabled(shared_ptr<Lobby> l) {
|
||||
if (!(l->flags & LobbyFlag::CheatsEnabled)) {
|
||||
throw runtime_error("can only be used in cheat mode");
|
||||
throw precondition_failed(u"$C6This command can\nonly be used in\ncheat mode.");
|
||||
}
|
||||
}
|
||||
|
||||
static void check_is_leader(shared_ptr<Lobby> l, shared_ptr<Client> c) {
|
||||
if (l->leader_id != c->lobby_client_id) {
|
||||
throw runtime_error("you are not the game leader");
|
||||
throw precondition_failed(u"$C6This command can\nonly be used by\nthe game leader.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,8 +227,10 @@ static void command_lobby_info(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
|
||||
shared_ptr<Client> c, const char16_t* args) {
|
||||
// no preconditions - everyone can use this command
|
||||
|
||||
string buffer;
|
||||
if (l->is_game()) {
|
||||
if (!l) {
|
||||
send_text_message(c, u"$C6No lobby information");
|
||||
|
||||
} else if (l->is_game()) {
|
||||
string level_string;
|
||||
if (l->max_level == 0xFFFFFFFF) {
|
||||
level_string = string_printf("Levels: %d+", l->min_level + 1);
|
||||
@@ -221,15 +238,17 @@ static void command_lobby_info(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
|
||||
level_string = string_printf("Levels: %d-%d", l->min_level + 1, l->max_level + 1);
|
||||
}
|
||||
|
||||
send_text_message_printf(c, "$C6Lobby ID: %08X\n%s\nSection ID: %s\nCheat mode: %s",
|
||||
send_text_message_printf(c, "$C6Game ID: %08X\n%s\nSection ID: %s\nCheat mode: %s",
|
||||
l->lobby_id, level_string.c_str(),
|
||||
section_id_to_name.at(l->section_id).c_str(),
|
||||
(l->flags & LobbyFlag::CheatsEnabled) ? u"on" : u"off");
|
||||
(l->flags & LobbyFlag::CheatsEnabled) ? "on" : "off");
|
||||
|
||||
} else {
|
||||
send_text_message_printf(c, "$C6Lobby ID: %08X", l->lobby_id);
|
||||
size_t num_clients = l->count_clients();
|
||||
size_t max_clients = l->max_clients;
|
||||
send_text_message_printf(c, "$C6Lobby ID: %08X\nPlayers: %zu/%zu",
|
||||
l->lobby_id, num_clients, max_clients);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void command_ax(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
|
||||
@@ -264,7 +283,7 @@ static void command_cheat(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
|
||||
|
||||
l->flags ^= LobbyFlag::CheatsEnabled;
|
||||
send_text_message_printf(l, "Cheat mode %s",
|
||||
(l->flags & LobbyFlag::CheatsEnabled) ? u"enabled" : u"disabled");
|
||||
(l->flags & LobbyFlag::CheatsEnabled) ? "enabled" : "disabled");
|
||||
|
||||
// if cheat mode was disabled, turn off all the cheat features that were on
|
||||
if (!(l->flags & LobbyFlag::CheatsEnabled)) {
|
||||
@@ -286,9 +305,17 @@ static void command_lobby_event(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
|
||||
check_is_game(l, false);
|
||||
check_privileges(c, Privilege::ChangeEvent);
|
||||
|
||||
uint8_t new_event;
|
||||
try {
|
||||
new_event = name_to_lobby_event.at(args);
|
||||
} catch (const out_of_range& e) {
|
||||
send_text_message(c, u"$C6No such lobby event.");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
rw_guard g(l->lock, true);
|
||||
l->event = name_to_lobby_event.at(args);
|
||||
l->event = new_event;
|
||||
}
|
||||
send_command(l, 0xDA, l->event, NULL, 0);
|
||||
}
|
||||
@@ -297,7 +324,13 @@ static void command_lobby_event_all(shared_ptr<ServerState> s, shared_ptr<Lobby>
|
||||
shared_ptr<Client> c, const char16_t* args) {
|
||||
check_privileges(c, Privilege::ChangeEvent);
|
||||
|
||||
uint8_t event = name_to_lobby_event.at(args);
|
||||
uint8_t new_event;
|
||||
try {
|
||||
new_event = name_to_lobby_event.at(args);
|
||||
} catch (const out_of_range& e) {
|
||||
send_text_message(c, u"$C6No such lobby event.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto l : s->all_lobbies()) {
|
||||
if (l->is_game() || !(l->flags & LobbyFlag::Default)) {
|
||||
@@ -306,9 +339,9 @@ static void command_lobby_event_all(shared_ptr<ServerState> s, shared_ptr<Lobby>
|
||||
|
||||
{
|
||||
rw_guard g(l->lock, true);
|
||||
l->event = event;
|
||||
l->event = new_event;
|
||||
}
|
||||
send_command(l, 0xDA, event, NULL, 0);
|
||||
send_command(l, 0xDA, new_event, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,9 +350,17 @@ static void command_lobby_type(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
|
||||
check_is_game(l, false);
|
||||
check_privileges(c, Privilege::ChangeEvent);
|
||||
|
||||
uint8_t new_type;
|
||||
try {
|
||||
new_type = name_to_lobby_type.at(args);
|
||||
} catch (const out_of_range& e) {
|
||||
send_text_message(c, u"$C6No such lobby type.");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
rw_guard g(l->lock, true);
|
||||
l->type = name_to_lobby_type.at(args);
|
||||
l->type = new_type;
|
||||
if (l->type < ((l->flags & LobbyFlag::Episode3) ? 20 : 15)) {
|
||||
l->type = l->block - 1;
|
||||
}
|
||||
@@ -348,7 +389,7 @@ static void command_password(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
|
||||
} else {
|
||||
char16cpy(l->password, args, 0x10);
|
||||
auto encoded = encode_sjis(l->password);
|
||||
send_text_message_printf(l, "$C6Game locked with password:\n%s",
|
||||
send_text_message_printf(l, "$C6Game password:\n%s",
|
||||
encoded.c_str());
|
||||
}
|
||||
}
|
||||
@@ -426,7 +467,12 @@ static void command_edit(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
|
||||
} else if (tokens[0] == "namecolor") {
|
||||
sscanf(tokens[1].c_str(), "%8X", &c->player.disp.name_color);
|
||||
} else if (tokens[0] == "secid") {
|
||||
c->player.disp.section_id = name_to_section_id.at(decode_sjis(tokens[1]));
|
||||
try {
|
||||
c->player.disp.section_id = name_to_section_id.at(decode_sjis(tokens[1]));
|
||||
} catch (const out_of_range&) {
|
||||
send_text_message(c, u"$C6No such section ID.");
|
||||
return;
|
||||
}
|
||||
} else if (tokens[0] == "name") {
|
||||
decode_sjis(c->player.disp.name, tokens[1].c_str(), 0x10);
|
||||
add_language_marker_inplace(c->player.disp.name, u'J', 0x10);
|
||||
@@ -435,7 +481,12 @@ static void command_edit(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
|
||||
c->player.disp.extra_model = 0;
|
||||
c->player.disp.v2_flags &= 0xFD;
|
||||
} else {
|
||||
c->player.disp.extra_model = name_to_npc_id.at(decode_sjis(tokens[1]));
|
||||
try {
|
||||
c->player.disp.extra_model = name_to_npc_id.at(decode_sjis(tokens[1]));
|
||||
} catch (const out_of_range&) {
|
||||
send_text_message(c, u"$C6No such NPC.");
|
||||
return;
|
||||
}
|
||||
c->player.disp.v2_flags |= 0x02;
|
||||
}
|
||||
} else if ((tokens[0] == "tech") && (tokens.size() > 2)) {
|
||||
@@ -445,11 +496,16 @@ static void command_edit(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
|
||||
c->player.disp.technique_levels[x] = level;
|
||||
}
|
||||
} else {
|
||||
uint8_t tech_id = name_to_tech_id.at(decode_sjis(tokens[1]));
|
||||
c->player.disp.technique_levels[tech_id] = level;
|
||||
try {
|
||||
uint8_t tech_id = name_to_tech_id.at(decode_sjis(tokens[1]));
|
||||
c->player.disp.technique_levels[tech_id] = level;
|
||||
} catch (const out_of_range&) {
|
||||
send_text_message(c, u"$C6No such technique.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
send_text_message(c, u"$C6Unknown field");
|
||||
send_text_message(c, u"$C6Unknown field.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -711,9 +767,14 @@ static const unordered_map<u16string, ChatCommandDefinition> chat_commands({
|
||||
void process_chat_command(std::shared_ptr<ServerState> s, std::shared_ptr<Lobby> l,
|
||||
std::shared_ptr<Client> c, const char16_t* text) {
|
||||
|
||||
// remove the chat command marker
|
||||
if (text[0] == u'$') {
|
||||
text++;
|
||||
}
|
||||
|
||||
u16string command_name;
|
||||
u16string text_str(text);
|
||||
size_t space_pos = text_str.find(L' ');
|
||||
size_t space_pos = text_str.find(u' ');
|
||||
if (space_pos != string::npos) {
|
||||
command_name = text_str.substr(0, space_pos);
|
||||
text_str = text_str.substr(space_pos + 1);
|
||||
@@ -732,8 +793,9 @@ void process_chat_command(std::shared_ptr<ServerState> s, std::shared_ptr<Lobby>
|
||||
|
||||
try {
|
||||
def->handler(s, l, c, text_str.c_str());
|
||||
} catch (const precondition_failed& e) {
|
||||
send_text_message(c, e.what());
|
||||
} catch (const exception& e) {
|
||||
send_text_message_printf(c, "$C6Failed:\n%s", e.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ Lobby::Lobby() : lobby_id(0), min_level(0), max_level(0xFFFFFFFF),
|
||||
}
|
||||
|
||||
bool Lobby::is_game() const {
|
||||
return (this->lobby_id < 0);
|
||||
return this->flags & LobbyFlag::IsGame;
|
||||
}
|
||||
|
||||
void Lobby::reassign_leader_on_client_departure_locked(size_t leaving_client_index) {
|
||||
@@ -106,7 +106,13 @@ void Lobby::remove_client_locked(shared_ptr<Client> c) {
|
||||
}
|
||||
|
||||
this->clients[c->lobby_client_id] = NULL;
|
||||
c->lobby_id = 0;
|
||||
|
||||
// unassign the client's lobby if it matches the current lobby's id (it may
|
||||
// not match if the client was already added to another lobby - this can
|
||||
// happen during the lobby change procedure)
|
||||
if (c->lobby_id == this->lobby_id) {
|
||||
c->lobby_id = 0;
|
||||
}
|
||||
|
||||
this->reassign_leader_on_client_departure_locked(c->lobby_client_id);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ enum LobbyFlag {
|
||||
struct Lobby {
|
||||
mutable rw_lock lock;
|
||||
|
||||
uint64_t lobby_id;
|
||||
uint32_t lobby_id;
|
||||
|
||||
uint32_t min_level;
|
||||
uint32_t max_level;
|
||||
|
||||
@@ -34,8 +34,12 @@ const char* name_for_category(QuestCategory category) {
|
||||
return "VR";
|
||||
case QuestCategory::Tower:
|
||||
return "Tower";
|
||||
case QuestCategory::Government:
|
||||
return "Government";
|
||||
case QuestCategory::GovernmentEpisode1:
|
||||
return "GovernmentEpisode1";
|
||||
case QuestCategory::GovernmentEpisode2:
|
||||
return "GovernmentEpisode2";
|
||||
case QuestCategory::GovernmentEpisode4:
|
||||
return "GovernmentEpisode4";
|
||||
case QuestCategory::Download:
|
||||
return "Download";
|
||||
case QuestCategory::Battle:
|
||||
@@ -170,18 +174,29 @@ Quest::Quest(const string& bin_filename) : quest_id(-1),
|
||||
|
||||
// get the category from the second token if needed
|
||||
if (this->category == QuestCategory::Unknown) {
|
||||
static const unordered_map<std::string, QuestCategory> name_to_category({
|
||||
{"ret", QuestCategory::Retrieval},
|
||||
{"ext", QuestCategory::Extermination},
|
||||
{"evt", QuestCategory::Event},
|
||||
{"shp", QuestCategory::Shop},
|
||||
{"vr", QuestCategory::VR},
|
||||
{"twr", QuestCategory::Tower},
|
||||
{"gov", QuestCategory::Government},
|
||||
{"dl", QuestCategory::Download},
|
||||
{"1p", QuestCategory::Solo},
|
||||
});
|
||||
this->category = name_to_category.at(tokens[1]);
|
||||
if (tokens[1] == "gov") {
|
||||
if (this->episode == 0) {
|
||||
this->category = QuestCategory::GovernmentEpisode1;
|
||||
} else if (this->episode == 1) {
|
||||
this->category = QuestCategory::GovernmentEpisode2;
|
||||
} else if (this->episode == 2) {
|
||||
this->category = QuestCategory::GovernmentEpisode4;
|
||||
} else {
|
||||
throw invalid_argument("government quest has incorrect episode");
|
||||
}
|
||||
} else {
|
||||
static const unordered_map<std::string, QuestCategory> name_to_category({
|
||||
{"ret", QuestCategory::Retrieval},
|
||||
{"ext", QuestCategory::Extermination},
|
||||
{"evt", QuestCategory::Event},
|
||||
{"shp", QuestCategory::Shop},
|
||||
{"vr", QuestCategory::VR},
|
||||
{"twr", QuestCategory::Tower},
|
||||
{"dl", QuestCategory::Download},
|
||||
{"1p", QuestCategory::Solo},
|
||||
});
|
||||
this->category = name_to_category.at(tokens[1]);
|
||||
}
|
||||
tokens.erase(tokens.begin() + 1);
|
||||
}
|
||||
|
||||
@@ -299,10 +314,10 @@ shared_ptr<const string> Quest::bin_contents() const {
|
||||
}
|
||||
|
||||
shared_ptr<const string> Quest::dat_contents() const {
|
||||
if (!this->bin_contents_ptr) {
|
||||
this->bin_contents_ptr.reset(new string(load_file(this->file_basename + ".dat")));
|
||||
if (!this->dat_contents_ptr) {
|
||||
this->dat_contents_ptr.reset(new string(load_file(this->file_basename + ".dat")));
|
||||
}
|
||||
return this->bin_contents_ptr;
|
||||
return this->dat_contents_ptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ enum class QuestCategory {
|
||||
Shop,
|
||||
VR,
|
||||
Tower,
|
||||
Government,
|
||||
GovernmentEpisode1,
|
||||
GovernmentEpisode2,
|
||||
GovernmentEpisode4,
|
||||
Download,
|
||||
Battle,
|
||||
Challenge,
|
||||
|
||||
+43
-37
@@ -530,6 +530,20 @@ void process_menu_item_info_request(shared_ptr<ServerState> s, shared_ptr<Client
|
||||
}
|
||||
break;
|
||||
|
||||
case QUEST_MENU_ID: {
|
||||
if (!s->quest_index) {
|
||||
send_quest_info(c, u"$C6Quests are not available.");
|
||||
break;
|
||||
}
|
||||
auto q = s->quest_index->get(c->version, cmd->item_id);
|
||||
if (!q) {
|
||||
send_quest_info(c, u"$C6Quest does not exist.");
|
||||
break;
|
||||
}
|
||||
send_quest_info(c, q->long_description.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
send_ship_info(c, u"Incorrect menu ID.");
|
||||
break;
|
||||
@@ -678,7 +692,7 @@ void process_menu_selection(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
auto l = s->find_lobby(c->lobby_id);
|
||||
auto quests = s->quest_index->filter(c->version,
|
||||
c->flags & ClientFlag::IsDCv1,
|
||||
static_cast<QuestCategory>(cmd->item_id), l->episode);
|
||||
static_cast<QuestCategory>(cmd->item_id & 0xFF), l->episode - 1);
|
||||
if (quests.empty()) {
|
||||
send_lobby_message_box(c, u"$C6There are no quests\navailable in that\ncategory.");
|
||||
break;
|
||||
@@ -794,40 +808,40 @@ void process_change_block(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
// Quest commands
|
||||
|
||||
vector<MenuItem> quest_categories_menu({
|
||||
MenuItem(0x01000000, u"Retrieval", u"$E$C6Quests that involve\nretrieving an object", 0),
|
||||
MenuItem(0x01000001, u"Extermination", u"$E$C6Quests that involve\ndestroying all\nmonsters", 0),
|
||||
MenuItem(0x01000002, u"Events", u"$E$C6Quests that are part\nof an event", 0),
|
||||
MenuItem(0x01000003, u"Shops", u"$E$C6Quests that contain\nshops", 0),
|
||||
MenuItem(0x01000004, u"Virtual Reality", u"$E$C6Quests that are\ndone in a simulator", MenuItemFlag::InvisibleOnDC | MenuItemFlag::InvisibleOnPC),
|
||||
MenuItem(0x01000005, u"Control Tower", u"$E$C6Quests that take\nplace at the Control\nTower", MenuItemFlag::InvisibleOnDC | MenuItemFlag::InvisibleOnPC),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::Retrieval), u"Retrieval", u"$E$C6Quests that involve\nretrieving an object", 0),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::Extermination), u"Extermination", u"$E$C6Quests that involve\ndestroying all\nmonsters", 0),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::Event), u"Events", u"$E$C6Quests that are part\nof an event", 0),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::Shop), u"Shops", u"$E$C6Quests that contain\nshops", 0),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::VR), u"Virtual Reality", u"$E$C6Quests that are\ndone in a simulator", MenuItemFlag::InvisibleOnDC | MenuItemFlag::InvisibleOnPC),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::Tower), u"Control Tower", u"$E$C6Quests that take\nplace at the Control\nTower", MenuItemFlag::InvisibleOnDC | MenuItemFlag::InvisibleOnPC),
|
||||
});
|
||||
|
||||
vector<MenuItem> quest_battle_menu({
|
||||
MenuItem(0x01000100, u"Battle", u"$E$C6Battle mode rule\nsets", 0),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::Battle), u"Battle", u"$E$C6Battle mode rule\nsets", 0),
|
||||
});
|
||||
|
||||
vector<MenuItem> quest_challenge_menu({
|
||||
MenuItem(0x01000200, u"Challenge", u"$E$C6Challenge mode\nquests", 0),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::Challenge), u"Challenge", u"$E$C6Challenge mode\nquests", 0),
|
||||
});
|
||||
|
||||
vector<MenuItem> quest_solo_menu({
|
||||
MenuItem(0x01000200, u"Solo Quests", u"$E$C6Quests that require\na single player", 0),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::Solo), u"Solo Quests", u"$E$C6Quests that require\na single player", 0),
|
||||
});
|
||||
|
||||
vector<MenuItem> quest_government_menu({
|
||||
MenuItem(0x01000300, u"Hero in Red",u"$E$CG-Red Ring Rico-\n$C6Quests that follow\nthe Episode 1\nstoryline", 0),
|
||||
MenuItem(0x01000301, u"The Military's Hero",u"$E$CG-Heathcliff Flowen-\n$C6Quests that follow\nthe Episode 2\nstoryline", 0),
|
||||
MenuItem(0x01000302, u"The Meteor Impact Incident", u"$E$C6Quests that follow\nthe Episode 4\nstoryline", 0),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::GovernmentEpisode1), u"Hero in Red",u"$E$CG-Red Ring Rico-\n$C6Quests that follow\nthe Episode 1\nstoryline", 0),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::GovernmentEpisode2), u"The Military's Hero",u"$E$CG-Heathcliff Flowen-\n$C6Quests that follow\nthe Episode 2\nstoryline", 0),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::GovernmentEpisode4), u"The Meteor Impact Incident", u"$E$C6Quests that follow\nthe Episode 4\nstoryline", 0),
|
||||
});
|
||||
|
||||
vector<MenuItem> quest_download_menu({
|
||||
MenuItem(0x01000400, u"Retrieval", u"$E$C6Quests that involve\nretrieving an object", 0),
|
||||
MenuItem(0x01000401, u"Extermination", u"$E$C6Quests that involve\ndestroying all\nmonsters", 0),
|
||||
MenuItem(0x01000402, u"Events", u"$E$C6Quests that are part\nof an event", 0),
|
||||
MenuItem(0x01000403, u"Shops", u"$E$C6Quests that contain\nshops", 0),
|
||||
MenuItem(0x01000404, u"Virtual Reality", u"$E$C6Quests that are\ndone in a simulator", MenuItemFlag::InvisibleOnDC | MenuItemFlag::InvisibleOnPC),
|
||||
MenuItem(0x01000405, u"Control Tower", u"$E$C6Quests that take\nplace at the Control\nTower", MenuItemFlag::InvisibleOnDC | MenuItemFlag::InvisibleOnPC),
|
||||
MenuItem(0x01000406, u"Download", u"$E$C6Quests to download\nto your Memory Card", 0),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::Retrieval), u"Retrieval", u"$E$C6Quests that involve\nretrieving an object", 0),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::Extermination), u"Extermination", u"$E$C6Quests that involve\ndestroying all\nmonsters", 0),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::Event), u"Events", u"$E$C6Quests that are part\nof an event", 0),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::Shop), u"Shops", u"$E$C6Quests that contain\nshops", 0),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::VR), u"Virtual Reality", u"$E$C6Quests that are\ndone in a simulator", MenuItemFlag::InvisibleOnDC | MenuItemFlag::InvisibleOnPC),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::Tower), u"Control Tower", u"$E$C6Quests that take\nplace at the Control\nTower", MenuItemFlag::InvisibleOnDC | MenuItemFlag::InvisibleOnPC),
|
||||
MenuItem(static_cast<uint32_t>(QuestCategory::Download), u"Download", u"$E$C6Quests to download\nto your Memory Card", 0),
|
||||
});
|
||||
|
||||
void process_quest_list_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
@@ -874,6 +888,11 @@ void process_quest_ready(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
rw_guard g(c->lock, true);
|
||||
c->flags &= ~ClientFlag::Loading;
|
||||
}
|
||||
|
||||
// check if any client is still loading
|
||||
// TODO: we need to handle clients disconnecting while loading. probably
|
||||
// process_client_disconnect needs to check for this case or something
|
||||
@@ -956,15 +975,10 @@ void process_player_data(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
c->pending_bb_save_username.clear();
|
||||
}
|
||||
|
||||
// if it's 61 and the client isn't in a lobby, add them to an available lobby
|
||||
if ((command == 0x61) && !c->lobby_id &&
|
||||
(c->server_behavior == ServerBehavior::LobbyServer)) {
|
||||
// if the client isn't in a lobby, add them to an available lobby
|
||||
if (!c->lobby_id && (c->server_behavior == ServerBehavior::LobbyServer)) {
|
||||
s->add_client_to_available_lobby(c);
|
||||
}
|
||||
|
||||
if (command == 0x98) {
|
||||
s->change_client_lobby(c, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -1002,7 +1016,7 @@ void process_chat_generic(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
if (processed_text[0] == L'$') {
|
||||
auto l = s->find_lobby(c->lobby_id);
|
||||
if (l) {
|
||||
process_chat_command(s, l, c, &text[1]);
|
||||
process_chat_command(s, l, c, &processed_text[1]);
|
||||
}
|
||||
} else {
|
||||
if (!c->can_chat) {
|
||||
@@ -1533,16 +1547,8 @@ void process_create_game_bb(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
check_size(size, sizeof(Cmd));
|
||||
const auto* cmd = reinterpret_cast<const Cmd*>(data);
|
||||
|
||||
uint8_t episode = cmd->episode;
|
||||
if (c->version == GameVersion::DC) {
|
||||
episode = 1;
|
||||
}
|
||||
if (c->flags & ClientFlag::Episode3Games) {
|
||||
episode = 0xFF;
|
||||
}
|
||||
|
||||
auto game = create_game_generic(s, c, cmd->name, cmd->password,
|
||||
episode, cmd->difficulty, cmd->battle_mode, cmd->challenge_mode,
|
||||
cmd->episode, cmd->difficulty, cmd->battle_mode, cmd->challenge_mode,
|
||||
cmd->solo_mode);
|
||||
|
||||
s->add_lobby(game);
|
||||
|
||||
+29
-28
@@ -184,9 +184,9 @@ static void process_subcommand_use_technique(shared_ptr<ServerState> s,
|
||||
static void process_subcommand_drop_item(shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const PSOSubcommand* p, size_t count) {
|
||||
check_size(count, 6);
|
||||
|
||||
if (l->version == GameVersion::BB) {
|
||||
check_size(count, 6);
|
||||
|
||||
struct Cmd {
|
||||
uint8_t command;
|
||||
uint8_t size;
|
||||
@@ -221,9 +221,9 @@ static void process_subcommand_drop_item(shared_ptr<ServerState> s,
|
||||
static void process_subcommand_drop_stacked_item(shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const PSOSubcommand* p, size_t count) {
|
||||
check_size(count, 6);
|
||||
|
||||
if (l->version == GameVersion::BB) {
|
||||
check_size(count, 6);
|
||||
|
||||
struct Cmd {
|
||||
uint8_t command;
|
||||
uint8_t size;
|
||||
@@ -268,9 +268,9 @@ static void process_subcommand_drop_stacked_item(shared_ptr<ServerState> s,
|
||||
static void process_subcommand_pick_up_item(shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const PSOSubcommand* p, size_t count) {
|
||||
check_size(count, 3);
|
||||
|
||||
if (l->version == GameVersion::BB) {
|
||||
check_size(count, 3);
|
||||
|
||||
struct Cmd {
|
||||
uint8_t command;
|
||||
uint8_t size;
|
||||
@@ -305,9 +305,9 @@ static void process_subcommand_pick_up_item(shared_ptr<ServerState> s,
|
||||
static void process_subcommand_equip_unequip_item(shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const PSOSubcommand* p, size_t count) {
|
||||
check_size(count, 3);
|
||||
|
||||
if (l->version == GameVersion::BB) {
|
||||
check_size(count, 3);
|
||||
|
||||
auto* cmd = reinterpret_cast<const ItemSubcommand*>(p);
|
||||
if ((cmd->size != 3) || (cmd->client_id != c->lobby_client_id)) {
|
||||
return;
|
||||
@@ -326,13 +326,12 @@ static void process_subcommand_equip_unequip_item(shared_ptr<ServerState> s,
|
||||
}
|
||||
}
|
||||
|
||||
// player uses an item (see ClientUseItem for specific item handlers)
|
||||
static void process_subcommand_use_item(shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const PSOSubcommand* p, size_t count) {
|
||||
check_size(count, 2);
|
||||
|
||||
if (l->version == GameVersion::BB) {
|
||||
check_size(count, 2);
|
||||
|
||||
auto* cmd = reinterpret_cast<const ItemSubcommand*>(p);
|
||||
if ((cmd->size != 2) || (cmd->client_id != c->lobby_client_id)) {
|
||||
return;
|
||||
@@ -352,7 +351,6 @@ static void process_subcommand_use_item(shared_ptr<ServerState> s,
|
||||
forward_subcommand(l, c, command, flag, p, count);
|
||||
}
|
||||
|
||||
// player opens the bank window
|
||||
static void process_subcommand_open_bank(shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const PSOSubcommand* p, size_t count) {
|
||||
@@ -365,9 +363,9 @@ static void process_subcommand_open_bank(shared_ptr<ServerState> s,
|
||||
static void process_subcommand_bank_action(shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const PSOSubcommand* p, size_t count) {
|
||||
check_size(count, 4);
|
||||
|
||||
if (l->version == GameVersion::BB) {
|
||||
check_size(count, 4);
|
||||
|
||||
struct Cmd {
|
||||
uint8_t subcommand;
|
||||
uint8_t size;
|
||||
@@ -427,9 +425,9 @@ static void process_subcommand_bank_action(shared_ptr<ServerState> s,
|
||||
static void process_subcommand_sort_inventory(shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const PSOSubcommand* p, size_t count) {
|
||||
check_size(count, 31);
|
||||
|
||||
if (l->version == GameVersion::BB) {
|
||||
check_size(count, 31);
|
||||
|
||||
struct Cmd {
|
||||
uint8_t command;
|
||||
uint8_t size;
|
||||
@@ -470,9 +468,9 @@ static void process_subcommand_sort_inventory(shared_ptr<ServerState> s,
|
||||
static void process_subcommand_enemy_drop_item(shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const PSOSubcommand* p, size_t count) {
|
||||
check_size(count, 6);
|
||||
|
||||
if (l->version == GameVersion::BB) {
|
||||
check_size(count, 6);
|
||||
|
||||
struct Cmd {
|
||||
uint8_t command;
|
||||
uint8_t size;
|
||||
@@ -532,9 +530,9 @@ static void process_subcommand_enemy_drop_item(shared_ptr<ServerState> s,
|
||||
static void process_subcommand_box_drop_item(shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const PSOSubcommand* p, size_t count) {
|
||||
check_size(count, 10);
|
||||
|
||||
if (l->version == GameVersion::BB) {
|
||||
check_size(count, 10);
|
||||
|
||||
struct Cmd {
|
||||
uint8_t command;
|
||||
uint8_t size;
|
||||
@@ -601,9 +599,9 @@ static void process_subcommand_box_drop_item(shared_ptr<ServerState> s,
|
||||
static void process_subcommand_monster_hit(shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const PSOSubcommand* p, size_t count) {
|
||||
check_size(count, 10);
|
||||
|
||||
if (l->version == GameVersion::BB) {
|
||||
check_size(count, 10);
|
||||
|
||||
struct Cmd {
|
||||
uint8_t command;
|
||||
uint8_t size;
|
||||
@@ -637,7 +635,9 @@ static void process_subcommand_monster_hit(shared_ptr<ServerState> s,
|
||||
static void process_subcommand_monster_killed(shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const PSOSubcommand* p, size_t count) {
|
||||
check_size(count, 3);
|
||||
if (l->version == GameVersion::BB) {
|
||||
check_size(count, 3);
|
||||
}
|
||||
|
||||
forward_subcommand(l, c, command, flag, p, count);
|
||||
|
||||
@@ -713,9 +713,9 @@ static void process_subcommand_monster_killed(shared_ptr<ServerState> s,
|
||||
static void process_subcommand_destroy_item(shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const PSOSubcommand* p, size_t count) {
|
||||
check_size(count, 3);
|
||||
|
||||
if (l->version == GameVersion::BB) {
|
||||
check_size(count, 3);
|
||||
|
||||
auto* cmd = reinterpret_cast<const ItemSubcommand*>(p);
|
||||
if ((cmd->size != 3) || !l->is_game()) {
|
||||
return;
|
||||
@@ -730,9 +730,9 @@ static void process_subcommand_destroy_item(shared_ptr<ServerState> s,
|
||||
static void process_subcommand_identify_item(shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const PSOSubcommand* p, size_t count) {
|
||||
check_size(count, 3);
|
||||
|
||||
if (l->version == GameVersion::BB) {
|
||||
check_size(count, 3);
|
||||
|
||||
auto* cmd = reinterpret_cast<const ItemSubcommand*>(p);
|
||||
if (!l->is_game() || (cmd->size != 3) || (cmd->client_id != c->lobby_client_id)) {
|
||||
return;
|
||||
@@ -767,9 +767,10 @@ static void process_subcommand_identify_item(shared_ptr<ServerState> s,
|
||||
// static void process_subcommand_accept_identified_item(shared_ptr<ServerState> s,
|
||||
// shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
// const PSOSubcommand* p, size_t count) {
|
||||
// check_size(count, 3);
|
||||
//
|
||||
// if (l->version == GameVersion::BB) {
|
||||
// check_size(count, 3);
|
||||
//
|
||||
// auto* cmd = reinterpret_cast<const ItemSubcommand*>(p);
|
||||
// if ((cmd->size != 3) || (cmd->client_id != c->lobby_client_id)) {
|
||||
// return;
|
||||
|
||||
+18
-2
@@ -523,7 +523,7 @@ void send_chat_message(shared_ptr<Client> c, uint32_t from_serial_number,
|
||||
if (c->version == GameVersion::BB) {
|
||||
data.append(u"\x09J");
|
||||
}
|
||||
data.append(from_name);
|
||||
data.append(remove_language_marker(from_name));
|
||||
data.append(u"\x09\x09J");
|
||||
data.append(text);
|
||||
send_large_message(c, 0x06, data.c_str(), from_serial_number, true);
|
||||
@@ -1090,6 +1090,7 @@ static void send_quest_menu_pc(shared_ptr<Client> c, uint32_t menu_id,
|
||||
e.quest_id = quest->quest_id;
|
||||
char16cpy(e.name, quest->name.c_str(), 0x20);
|
||||
char16cpy(e.short_desc, quest->short_description.c_str(), 0x70);
|
||||
add_color_inplace(e.short_desc);
|
||||
}
|
||||
|
||||
send_command(c, is_download_menu ? 0xA4 : 0xA2, entries.size(), entries);
|
||||
@@ -1112,6 +1113,7 @@ static void send_quest_menu_pc(std::shared_ptr<Client> c, uint32_t menu_id,
|
||||
e.item_id = item.item_id;
|
||||
char16cpy(e.name, item.name.c_str(), 0x20);
|
||||
char16cpy(e.short_desc, item.description.c_str(), 0x70);
|
||||
add_color_inplace(e.short_desc);
|
||||
}
|
||||
|
||||
send_command(c, is_download_menu ? 0xA4 : 0xA2, entries.size(), entries);
|
||||
@@ -1134,6 +1136,7 @@ static void send_quest_menu_gc(shared_ptr<Client> c, uint32_t menu_id,
|
||||
e.quest_id = quest->quest_id;
|
||||
encode_sjis(e.name, quest->name.c_str(), 0x20);
|
||||
encode_sjis(e.short_desc, quest->short_description.c_str(), 0x70);
|
||||
add_color_inplace(e.short_desc);
|
||||
}
|
||||
|
||||
send_command(c, is_download_menu ? 0xA4 : 0xA2, entries.size(), entries);
|
||||
@@ -1156,6 +1159,7 @@ static void send_quest_menu_gc(shared_ptr<Client> c, uint32_t menu_id,
|
||||
e.item_id = item.item_id;
|
||||
encode_sjis(e.name, item.name.c_str(), 0x20);
|
||||
encode_sjis(e.short_desc, item.description.c_str(), 0x70);
|
||||
add_color_inplace(e.short_desc);
|
||||
}
|
||||
|
||||
send_command(c, is_download_menu ? 0xA4 : 0xA2, entries.size(), entries);
|
||||
@@ -1180,6 +1184,7 @@ static void send_quest_menu_bb(shared_ptr<Client> c, uint32_t menu_id,
|
||||
e.quest_id = quest->quest_id;
|
||||
char16cpy(e.name, quest->name.c_str(), 0x20);
|
||||
char16cpy(e.short_desc, quest->short_description.c_str(), 0x7A);
|
||||
add_color_inplace(e.short_desc);
|
||||
}
|
||||
|
||||
send_command(c, is_download_menu ? 0xA4 : 0xA2, entries.size(), entries);
|
||||
@@ -1204,6 +1209,7 @@ static void send_quest_menu_bb(shared_ptr<Client> c, uint32_t menu_id,
|
||||
e.item_id = item.item_id;
|
||||
char16cpy(e.name, item.name.c_str(), 0x20);
|
||||
char16cpy(e.short_desc, item.description.c_str(), 0x7A);
|
||||
add_color_inplace(e.short_desc);
|
||||
}
|
||||
|
||||
send_command(c, is_download_menu ? 0xA4 : 0xA2, entries.size(), entries);
|
||||
@@ -1488,7 +1494,17 @@ static void send_join_lobby_pc(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
static void send_join_lobby_gc(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
rw_guard g(l->lock, false);
|
||||
|
||||
uint8_t lobby_type = (l->type > 14) ? (l->block - 1) : l->type;
|
||||
uint8_t lobby_type = l->type;
|
||||
if (c->flags & ClientFlag::Episode3Games) {
|
||||
if ((l->type > 0x14) && (l->type < 0xE9)) {
|
||||
lobby_type = l->block - 1;
|
||||
}
|
||||
} else {
|
||||
if ((l->type > 0x11) && (l->type != 0x67) && (l->type != 0xD4) && (l->type < 0xFC)) {
|
||||
lobby_type = l->block - 1;
|
||||
}
|
||||
}
|
||||
|
||||
struct {
|
||||
uint8_t client_id;
|
||||
uint8_t leader_id;
|
||||
|
||||
+1
-1
@@ -23,10 +23,10 @@
|
||||
#define QUEST_MENU_ID 0x7F02CA94
|
||||
#define QUEST_FILTER_MENU_ID 0xC38CA039
|
||||
|
||||
#define INFORMATION_MENU_GO_BACK 0xFFFFFFFF
|
||||
#define MAIN_MENU_GO_TO_LOBBY 0x00000001
|
||||
#define MAIN_MENU_INFORMATION 0x00000002
|
||||
#define MAIN_MENU_DISCONNECT 0x00000003
|
||||
#define INFORMATION_MENU_GO_BACK 0xFFFFFFFF
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -212,14 +212,14 @@ void Server::receive_and_process_commands(shared_ptr<Client> c, struct buffereve
|
||||
// to call string functions on the buffer in command handlers
|
||||
string data = c->recv_buffer.substr(offset + header_size, size - header_size);
|
||||
data.append(4, '\0');
|
||||
try {
|
||||
// try {
|
||||
process_command(this->state, c, header->command(c->version),
|
||||
header->flag(c->version), size - header_size, data.data());
|
||||
} catch (const exception& e) {
|
||||
log(INFO, "[Server] error in client stream: %s", e.what());
|
||||
c->should_disconnect = true;
|
||||
return;
|
||||
}
|
||||
// } catch (const exception& e) {
|
||||
// log(INFO, "[Server] error in client stream: %s", e.what());
|
||||
// c->should_disconnect = true;
|
||||
// return;
|
||||
// }
|
||||
|
||||
// BB pads commands to 8-byte boundaries, so if we see a shorter command,
|
||||
// skip over the padding
|
||||
|
||||
+13
-30
@@ -10,8 +10,7 @@ using namespace std;
|
||||
|
||||
|
||||
|
||||
ServerState::ServerState() : run_dns_server(true), next_lobby_id(1),
|
||||
next_game_id(-1) {
|
||||
ServerState::ServerState() : run_dns_server(true), next_lobby_id(1) {
|
||||
this->main_menu.emplace_back(MAIN_MENU_GO_TO_LOBBY, u"Go to lobby",
|
||||
u"Join the lobby.", 0);
|
||||
this->main_menu.emplace_back(MAIN_MENU_INFORMATION, u"Information",
|
||||
@@ -19,14 +18,14 @@ ServerState::ServerState() : run_dns_server(true), next_lobby_id(1),
|
||||
this->main_menu.emplace_back(MAIN_MENU_DISCONNECT, u"Disconnect",
|
||||
u"Disconnect.", 0);
|
||||
|
||||
for (size_t x = 0; x < 15; x++) {
|
||||
for (size_t x = 0; x < 20; x++) {
|
||||
auto lobby_name = decode_sjis(string_printf("LOBBY%zu", x + 1));
|
||||
shared_ptr<Lobby> l(new Lobby());
|
||||
l->flags |= LobbyFlag::Public | LobbyFlag::Default;
|
||||
this->add_lobby(l);
|
||||
}
|
||||
for (size_t x = 0; x < 5; x++) {
|
||||
shared_ptr<Lobby> l(new Lobby());
|
||||
l->flags |= LobbyFlag::Public | LobbyFlag::Default | LobbyFlag::Episode3;
|
||||
l->flags |= LobbyFlag::Public | LobbyFlag::Default | ((x > 14) ? LobbyFlag::Episode3 : 0);
|
||||
l->block = x + 1;
|
||||
l->type = x;
|
||||
char16cpy(l->name, lobby_name.c_str(), 0x24);
|
||||
l->max_clients = 12;
|
||||
this->add_lobby(l);
|
||||
}
|
||||
}
|
||||
@@ -97,16 +96,11 @@ void ServerState::send_lobby_join_notifications(shared_ptr<Lobby> l,
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<Lobby> ServerState::find_lobby(int64_t lobby_id) {
|
||||
shared_ptr<Lobby> ServerState::find_lobby(uint32_t lobby_id) {
|
||||
rw_guard g(this->lobbies_lock, false);
|
||||
return this->id_to_lobby.at(lobby_id);
|
||||
}
|
||||
|
||||
shared_ptr<Lobby> ServerState::find_lobby(const u16string& name) {
|
||||
rw_guard g(this->lobbies_lock, false);
|
||||
return this->name_to_lobby.at(name);
|
||||
}
|
||||
|
||||
vector<shared_ptr<Lobby>> ServerState::all_lobbies() {
|
||||
rw_guard g(this->lobbies_lock, false);
|
||||
vector<shared_ptr<Lobby>> ret;
|
||||
@@ -117,34 +111,23 @@ vector<shared_ptr<Lobby>> ServerState::all_lobbies() {
|
||||
}
|
||||
|
||||
void ServerState::add_lobby(shared_ptr<Lobby> l) {
|
||||
if (l->is_game()) {
|
||||
l->lobby_id = this->next_game_id--;
|
||||
} else {
|
||||
l->lobby_id = this->next_lobby_id++;
|
||||
}
|
||||
l->lobby_id = this->next_lobby_id++;
|
||||
|
||||
rw_guard g(this->lobbies_lock, true);
|
||||
if (this->id_to_lobby.count(l->lobby_id)) {
|
||||
throw logic_error("lobby already exists with the given id");
|
||||
}
|
||||
if (this->name_to_lobby.count(l->name)) {
|
||||
throw invalid_argument("lobby already exists with the given name");
|
||||
}
|
||||
|
||||
log(INFO, "creating lobby %" PRId64, l->lobby_id);
|
||||
this->id_to_lobby.emplace(l->lobby_id, l);
|
||||
if (l->name[0]) {
|
||||
this->name_to_lobby.emplace(l->name, l);
|
||||
}
|
||||
}
|
||||
|
||||
void ServerState::remove_lobby(int64_t lobby_id) {
|
||||
void ServerState::remove_lobby(uint32_t lobby_id) {
|
||||
rw_guard g(this->lobbies_lock, true);
|
||||
auto it = this->id_to_lobby.find(lobby_id);
|
||||
if (it == this->id_to_lobby.end()) {
|
||||
return;
|
||||
}
|
||||
if (it->second->name[0]) {
|
||||
this->name_to_lobby.erase(it->second->name);
|
||||
}
|
||||
this->id_to_lobby.erase(it);
|
||||
}
|
||||
|
||||
|
||||
+4
-7
@@ -28,6 +28,7 @@ struct PortConfiguration {
|
||||
struct ServerState {
|
||||
std::u16string name;
|
||||
std::unordered_map<std::string, PortConfiguration> port_configuration;
|
||||
bool run_dns_server;
|
||||
std::shared_ptr<const QuestIndex> quest_index;
|
||||
std::shared_ptr<const LevelTable> level_table;
|
||||
std::shared_ptr<const BattleParamTable> battle_params;
|
||||
@@ -43,10 +44,7 @@ struct ServerState {
|
||||
|
||||
rw_lock lobbies_lock;
|
||||
std::map<int64_t, std::shared_ptr<Lobby>> id_to_lobby;
|
||||
std::unordered_map<std::u16string, std::shared_ptr<Lobby>> name_to_lobby;
|
||||
|
||||
std::atomic<int64_t> next_lobby_id;
|
||||
std::atomic<int64_t> next_game_id;
|
||||
std::atomic<int32_t> next_lobby_id;
|
||||
|
||||
std::set<uint32_t> all_addresses;
|
||||
uint32_t local_address;
|
||||
@@ -62,12 +60,11 @@ struct ServerState {
|
||||
void send_lobby_join_notifications(std::shared_ptr<Lobby> l,
|
||||
std::shared_ptr<Client> joining_client);
|
||||
|
||||
std::shared_ptr<Lobby> find_lobby(int64_t lobby_id);
|
||||
std::shared_ptr<Lobby> find_lobby(const std::u16string& name);
|
||||
std::shared_ptr<Lobby> find_lobby(uint32_t lobby_id);
|
||||
std::vector<std::shared_ptr<Lobby>> all_lobbies();
|
||||
|
||||
void add_lobby(std::shared_ptr<Lobby> l);
|
||||
void remove_lobby(int64_t lobby_id);
|
||||
void remove_lobby(uint32_t lobby_id);
|
||||
|
||||
std::shared_ptr<Client> find_client(const char16_t* identifier = NULL,
|
||||
uint64_t serial_number = 0, std::shared_ptr<Lobby> l = NULL);
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@
|
||||
// Number of worker threads to run
|
||||
"Threads": 1,
|
||||
// Set to false to disable the DNS server
|
||||
"RunDNSServer": false,
|
||||
"RunDNSServer": true,
|
||||
|
||||
// ****************
|
||||
// INFORMATION MENU
|
||||
@@ -40,7 +40,7 @@
|
||||
["Event values", "$C7Display lobby event\nlist", "These values can be used with the $C6%sevent$C7 command.\n\nnone - no event\nxmas - Christmas event\nval - Valentine's Day\neaster - Easter Sunday event\nhallo - Halloween event\nsonic - Sonic Adventure DX event\nnewyear - New Year's event\nbval - White Day\nwedding - Wedding Day event\nspring - spring event\ns-spring - spring event with striped background\nsummer - summer event\ns-summer - summer event with striped background\nfall - fall event"],
|
||||
["GC lobby types", "$C7Display lobby type\nlist for Episodes\nI & II", "These values can be used with the %sln command.\n$C6*$C7 indicates lobbies where players can't move.\n$C2Green$C7 indicates Episode 1 & 2 (GC) only lobbies.\n\nnormal - standard lobby\n$C2inormal$C7 - under standard lobby $C6*$C7\n$C2ipc$C7 - under PC lobby $C6*$C7\n$C2iball$C7 - under soccer lobby $C6*$C7\n$C2cave1$C7 - Cave 1 $C6*$C7\n$C2cave2u$C7 - Cave 2 Ultimate $C6*$C7\n$C2dragon$C7 - Dragon stage (floor is black)\n$C2derolle$C7 - De Rol Le stage (water/walls are gone)\n$C2volopt$C7 - Vol Opt stage\n$C2darkfalz$C7 - Dark Falz stage"],
|
||||
["Ep3 lobby types", "$C7Display lobby type\nlist for Episode\nIII", "These values can be used with the %sln command.\n$C6*$C7 indicates lobbies where players can't move.\n$C8Pink$C7 indicates Episode 3 only lobbies.\n\nnormal - standard lobby\n$C8planet$C7 - Blank Ragol Lobby\n$C8clouds$C7 - Blank Sky Lobby\n$C8cave$C7 - Unguis Lapis\n$C8jungle$C7 - Episode 2 Jungle\n$C8forest2-1$C7 - Episode 1 Forest 2 (ground)\n$C8forest2-2$C7 - Episode 1 Forest 2 (near Dome)\n$C8windpower$C7\n$C8overview$C7\n$C8seaside$C7 - Episode 2 Seaside\n$C8some?$C7\n$C8dmorgue$C7 - Destroyed Morgue\n$C8caelum$C7 - Caelum\n$C8digital$C7\n$C8boss1$C7\n$C8boss2$C7\n$C8boss3$C7\n$C8knight$C7 - Leukon Knight stage\n$C8sky$C7 - Via Tubus\n$C8morgue$C7 - Morgue"],
|
||||
["Area list", "$C7Display stage code\nlist", "These values can be used with the $C6%swarp$C7 and\n$C6%smove$C7 commands.\n\n$C2Green$C7 areas will be empty unless you are in a quest.\n$C6Yellow$C7 areas will not allow you to move.\n\n $C8Episode 1 / Episode 2 / Episode 4$C7\n0: Pioneer 2 / Pioneer 2 / Pioneer 2\n1: Forest 1 / Temple Alpha / Crater East\n2: Forest 2 / Temple Beta / Crater West\n3: Caves 1 / Spaceship Alpha / Crater South\n4: Caves 2 / Spaceship Beta / Crater North\n5: Caves 3 / CCA / Crater Interior\n6: Mines 1 / Jungle North / Desert 1\n7: Mines 2 / Jungle South / Desert 2\n8: Ruins 1 / Mountain / Desert 3\n9: Ruins 2 / Seaside / Saint Million\n10: Ruins 3 / Seabed Upper / $C6Purgatory$C7\n11: Dragon / Seabed Lower\n12: De Rol Le / Gal Gryphon\n13: Vol Opt / Olga Flow\n14: Dark Falz / Barba Ray\n15: $C2Lobby$C7 / Gol Dragon\n16: $C6Battle 1$C7 / $C6Seaside Night$C7\n17: $C6Battle 2$C7 / $C2Tower$C7"],
|
||||
["Area list", "$C7Display stage code\nlist", "These values can be used with the $C6%swarp$C7 command.\n\n$C2Green$C7 areas will be empty unless you are in a quest.\n$C6Yellow$C7 areas will not allow you to move.\n\n $C8Episode 1 / Episode 2 / Episode 4$C7\n0: Pioneer 2 / Pioneer 2 / Pioneer 2\n1: Forest 1 / Temple Alpha / Crater East\n2: Forest 2 / Temple Beta / Crater West\n3: Caves 1 / Spaceship Alpha / Crater South\n4: Caves 2 / Spaceship Beta / Crater North\n5: Caves 3 / CCA / Crater Interior\n6: Mines 1 / Jungle North / Desert 1\n7: Mines 2 / Jungle South / Desert 2\n8: Ruins 1 / Mountain / Desert 3\n9: Ruins 2 / Seaside / Saint Million\n10: Ruins 3 / Seabed Upper / $C6Purgatory$C7\n11: Dragon / Seabed Lower\n12: De Rol Le / Gal Gryphon\n13: Vol Opt / Olga Flow\n14: Dark Falz / Barba Ray\n15: $C2Lobby$C7 / Gol Dragon\n16: $C6Battle 1$C7 / $C6Seaside Night$C7\n17: $C6Battle 2$C7 / $C2Tower$C7"],
|
||||
],
|
||||
|
||||
// ***************
|
||||
|
||||
Reference in New Issue
Block a user