implement nonstandard drop modes
This commit is contained in:
+106
-62
@@ -108,14 +108,24 @@ static void server_command_lobby_info(shared_ptr<Client> c, const std::string&)
|
||||
}
|
||||
lines.emplace_back(string_printf("$C7Section ID: $C6%s$C7", name_for_section_id(l->section_id)));
|
||||
|
||||
if (l->check_flag(Lobby::Flag::DROPS_ENABLED)) {
|
||||
if (l->item_creator) {
|
||||
lines.emplace_back("Server item table");
|
||||
} else {
|
||||
switch (l->drop_mode) {
|
||||
case Lobby::DropMode::DISABLED:
|
||||
lines.emplace_back("Drops disabled");
|
||||
break;
|
||||
case Lobby::DropMode::CLIENT:
|
||||
lines.emplace_back("Client item table");
|
||||
}
|
||||
} else {
|
||||
lines.emplace_back("No item drops");
|
||||
break;
|
||||
case Lobby::DropMode::SERVER_SHARED:
|
||||
lines.emplace_back("Server item table");
|
||||
break;
|
||||
case Lobby::DropMode::SERVER_PRIVATE:
|
||||
lines.emplace_back("Server indiv items");
|
||||
break;
|
||||
case Lobby::DropMode::SERVER_DUPLICATE:
|
||||
lines.emplace_back("Server dup items");
|
||||
break;
|
||||
default:
|
||||
lines.emplace_back("$C4Unknown drop mode$C7");
|
||||
}
|
||||
if (l->check_flag(Lobby::Flag::CHEATS_ENABLED)) {
|
||||
lines.emplace_back("Cheats enabled");
|
||||
@@ -1320,32 +1330,28 @@ static void server_command_what(shared_ptr<Client> c, const std::string&) {
|
||||
if (!episode_has_arpg_semantics(l->episode)) {
|
||||
return;
|
||||
}
|
||||
if (!l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
send_text_message(c, "$C4Item tracking is\nnot available");
|
||||
} else {
|
||||
float min_dist2 = 0.0f;
|
||||
uint32_t nearest_item_id = 0xFFFFFFFF;
|
||||
for (const auto& it : l->item_id_to_floor_item) {
|
||||
if (it.second.floor != c->floor) {
|
||||
continue;
|
||||
}
|
||||
float dx = it.second.x - c->x;
|
||||
float dz = it.second.z - c->z;
|
||||
float dist2 = (dx * dx) + (dz * dz);
|
||||
if ((nearest_item_id == 0xFFFFFFFF) || (dist2 < min_dist2)) {
|
||||
nearest_item_id = it.first;
|
||||
min_dist2 = dist2;
|
||||
}
|
||||
}
|
||||
|
||||
if (nearest_item_id == 0xFFFFFFFF) {
|
||||
send_text_message(c, "$C4No items are near you");
|
||||
} else {
|
||||
auto s = c->require_server_state();
|
||||
const auto& item = l->item_id_to_floor_item.at(nearest_item_id);
|
||||
string name = s->describe_item(c->version(), item.data, true);
|
||||
send_text_message(c, name);
|
||||
float min_dist2 = 0.0f;
|
||||
shared_ptr<const Lobby::FloorItem> nearest_fi;
|
||||
for (const auto& it : l->floor_item_managers.at(c->floor).items) {
|
||||
if (!it.second->visible_to_client(c->lobby_client_id)) {
|
||||
continue;
|
||||
}
|
||||
float dx = it.second->x - c->x;
|
||||
float dz = it.second->z - c->z;
|
||||
float dist2 = (dx * dx) + (dz * dz);
|
||||
if (!nearest_fi || (dist2 < min_dist2)) {
|
||||
nearest_fi = it.second;
|
||||
min_dist2 = dist2;
|
||||
}
|
||||
}
|
||||
|
||||
if (!nearest_fi) {
|
||||
send_text_message(c, "$C4No items are near you");
|
||||
} else {
|
||||
auto s = c->require_server_state();
|
||||
string name = s->describe_item(c->version(), nearest_fi->data, true);
|
||||
send_text_message(c, name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1420,35 +1426,69 @@ static void proxy_command_switch_assist(shared_ptr<ProxyServer::LinkedSession> s
|
||||
ses->config.check_flag(Client::Flag::SWITCH_ASSIST_ENABLED) ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
static void server_command_drop(shared_ptr<Client> c, const std::string&) {
|
||||
static void server_command_dropmode(shared_ptr<Client> c, const std::string& args) {
|
||||
auto l = c->require_lobby();
|
||||
check_is_game(l, true);
|
||||
check_is_leader(l, c);
|
||||
if (l->check_flag(Lobby::Flag::CANNOT_CHANGE_DROPS_ENABLED)) {
|
||||
send_text_message(c, "Drop mode cannot\nbe changed on this\nserver");
|
||||
} else {
|
||||
l->toggle_flag(Lobby::Flag::DROPS_ENABLED);
|
||||
send_text_message_printf(l, "Drops %s", l->check_flag(Lobby::Flag::DROPS_ENABLED) ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
if (args.empty()) {
|
||||
switch (l->drop_mode) {
|
||||
case Lobby::DropMode::DISABLED:
|
||||
send_text_message(c, "Drop mode: disabled");
|
||||
break;
|
||||
case Lobby::DropMode::CLIENT:
|
||||
send_text_message(c, "Drop mode: client");
|
||||
break;
|
||||
case Lobby::DropMode::SERVER_SHARED:
|
||||
send_text_message(c, "Drop mode: server\nshared");
|
||||
break;
|
||||
case Lobby::DropMode::SERVER_PRIVATE:
|
||||
send_text_message(c, "Drop mode: server\nprivate");
|
||||
break;
|
||||
case Lobby::DropMode::SERVER_DUPLICATE:
|
||||
send_text_message(c, "Drop mode: server\nduplicate");
|
||||
break;
|
||||
}
|
||||
|
||||
static void server_command_itemtable(shared_ptr<Client> c, const std::string&) {
|
||||
auto s = c->require_server_state();
|
||||
auto l = c->require_lobby();
|
||||
check_is_game(l, true);
|
||||
check_is_leader(l, c);
|
||||
if (l->check_flag(Lobby::Flag::CANNOT_CHANGE_ITEM_TABLE)) {
|
||||
send_text_message(c, "Cannot switch item\ntables on this\nserver");
|
||||
} else if (l->base_version == Version::BB_V4) {
|
||||
send_text_message(c, "Cannot use client\nitem table on BB");
|
||||
} else if (!l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
send_text_message(c, "Cannot use server\nitem tables if item\ntracking is off");
|
||||
} else if (l->item_creator) {
|
||||
l->item_creator.reset();
|
||||
send_text_message(l, "Game switched to\nclient item tables");
|
||||
} else {
|
||||
l->create_item_creator();
|
||||
send_text_message(l, "Game switched to\nserver item tables");
|
||||
check_is_leader(l, c);
|
||||
Lobby::DropMode new_mode;
|
||||
if ((args == "none") || (args == "disabled")) {
|
||||
new_mode = Lobby::DropMode::DISABLED;
|
||||
} else if (args == "client") {
|
||||
new_mode = Lobby::DropMode::CLIENT;
|
||||
} else if ((args == "shared") || (args == "server")) {
|
||||
new_mode = Lobby::DropMode::SERVER_SHARED;
|
||||
} else if ((args == "private") || (args == "priv")) {
|
||||
new_mode = Lobby::DropMode::SERVER_PRIVATE;
|
||||
} else if ((args == "duplicate") || (args == "dup")) {
|
||||
new_mode = Lobby::DropMode::SERVER_DUPLICATE;
|
||||
} else {
|
||||
send_text_message(c, "Invalid drop mode");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(l->allowed_drop_modes & (1 << static_cast<size_t>(new_mode)))) {
|
||||
send_text_message(c, "Drop mode not\nallowed");
|
||||
return;
|
||||
}
|
||||
|
||||
l->set_drop_mode(new_mode);
|
||||
switch (l->drop_mode) {
|
||||
case Lobby::DropMode::DISABLED:
|
||||
send_text_message(l, "Item drops disabled");
|
||||
break;
|
||||
case Lobby::DropMode::CLIENT:
|
||||
send_text_message(l, "Item drops changed\nto client mode");
|
||||
break;
|
||||
case Lobby::DropMode::SERVER_SHARED:
|
||||
send_text_message(l, "Item drops changed\nto server shared\nmode");
|
||||
break;
|
||||
case Lobby::DropMode::SERVER_PRIVATE:
|
||||
send_text_message(l, "Item drops changed\nto server private\nmode");
|
||||
break;
|
||||
case Lobby::DropMode::SERVER_DUPLICATE:
|
||||
send_text_message(l, "Item drops changed\nto server duplicate\nmode");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1461,8 +1501,13 @@ static void server_command_item(shared_ptr<Client> c, const std::string& args) {
|
||||
ItemData item = s->item_name_index->parse_item_description(c->version(), args);
|
||||
item.id = l->generate_item_id(c->lobby_client_id);
|
||||
|
||||
l->add_item(item, c->floor, c->x, c->z);
|
||||
send_drop_stacked_item(l, item, c->floor, c->x, c->z);
|
||||
if ((l->drop_mode == Lobby::DropMode::SERVER_PRIVATE) || (l->drop_mode == Lobby::DropMode::SERVER_DUPLICATE)) {
|
||||
l->add_item(c->floor, item, c->x, c->z, (1 << c->lobby_client_id));
|
||||
send_drop_stacked_item_to_channel(s, c->channel, item, c->floor, c->x, c->z);
|
||||
} else {
|
||||
l->add_item(c->floor, item, c->x, c->z, 0x00F);
|
||||
send_drop_stacked_item_to_lobby(l, item, c->floor, c->x, c->z);
|
||||
}
|
||||
|
||||
string name = s->describe_item(c->version(), item, true);
|
||||
send_text_message(c, "$C7Item created:\n" + name);
|
||||
@@ -1496,8 +1541,8 @@ static void proxy_command_item(shared_ptr<ProxyServer::LinkedSession> ses, const
|
||||
send_text_message(ses->client_channel, "$C7Next drop:\n" + name);
|
||||
|
||||
} else {
|
||||
send_drop_stacked_item(s, ses->client_channel, item, ses->floor, ses->x, ses->z);
|
||||
send_drop_stacked_item(s, ses->server_channel, item, ses->floor, ses->x, ses->z);
|
||||
send_drop_stacked_item_to_channel(s, ses->client_channel, item, ses->floor, ses->x, ses->z);
|
||||
send_drop_stacked_item_to_channel(s, ses->server_channel, item, ses->floor, ses->x, ses->z);
|
||||
|
||||
string name = s->describe_item(ses->version(), item, true);
|
||||
send_text_message(ses->client_channel, "$C7Item created:\n" + name);
|
||||
@@ -1754,7 +1799,7 @@ static const unordered_map<string, ChatCommandDefinition> chat_commands({
|
||||
{"$cheat", {server_command_cheat, nullptr}},
|
||||
{"$debug", {server_command_debug, nullptr}},
|
||||
{"$defrange", {server_command_ep3_set_def_dice_range, nullptr}},
|
||||
{"$drop", {server_command_drop, nullptr}},
|
||||
{"$dropmode", {server_command_dropmode, nullptr}},
|
||||
{"$edit", {server_command_edit, nullptr}},
|
||||
{"$ep3battledebug", {server_command_enable_ep3_battle_debug_menu, nullptr}},
|
||||
{"$event", {server_command_lobby_event, proxy_command_lobby_event}},
|
||||
@@ -1764,7 +1809,6 @@ static const unordered_map<string, ChatCommandDefinition> chat_commands({
|
||||
{"$inftime", {server_command_ep3_infinite_time, nullptr}},
|
||||
{"$inftp", {server_command_infinite_tp, proxy_command_infinite_tp}},
|
||||
{"$item", {server_command_item, proxy_command_item}},
|
||||
{"$itemtable", {server_command_itemtable, nullptr}},
|
||||
{"$i", {server_command_item, proxy_command_item}},
|
||||
{"$kick", {server_command_kick, nullptr}},
|
||||
{"$li", {server_command_lobby_info, proxy_command_lobby_info}},
|
||||
|
||||
+15
-15
@@ -4449,17 +4449,18 @@ struct G_BuyShopItem_6x5E {
|
||||
// 6x5F: Drop item from box/enemy
|
||||
|
||||
struct FloorItem {
|
||||
uint8_t floor = 0;
|
||||
uint8_t from_enemy = 0;
|
||||
le_uint16_t entity_id = 0; // < 0x0B50 if from_enemy != 0; otherwise < 0x0BA0
|
||||
le_float x = 0.0f;
|
||||
le_float z = 0.0f;
|
||||
le_uint16_t unknown_a2 = 0;
|
||||
/* 00 */ uint8_t floor = 0;
|
||||
/* 01 */ uint8_t from_enemy = 0;
|
||||
/* 02 */ le_uint16_t entity_id = 0; // < 0x0B50 if from_enemy != 0; otherwise < 0x0BA0
|
||||
/* 04 */ le_float x = 0.0f;
|
||||
/* 08 */ le_float z = 0.0f;
|
||||
/* 0C */ le_uint16_t unknown_a2 = 0;
|
||||
// The drop number is scoped to the floor and increments by 1 each time an
|
||||
// item is dropped. The last item dropped in each floor has drop_number equal
|
||||
// to total_items_dropped_per_floor[floor - 1] - 1.
|
||||
le_uint16_t drop_number = 0;
|
||||
ItemData item;
|
||||
/* 0E */ le_uint16_t drop_number = 0;
|
||||
/* 10 */ ItemData item;
|
||||
/* 24 */
|
||||
} __packed__;
|
||||
|
||||
struct G_DropItem_DC_6x5F {
|
||||
@@ -4500,9 +4501,9 @@ struct G_ActivateMagEffect_6x61 {
|
||||
// 6x62: Unknown
|
||||
// This subcommand is completely ignored (at least, by PSO GC).
|
||||
|
||||
// 6x63: Destroy ground item (used when too many items have been dropped)
|
||||
// 6x63: Destroy floor item (used when too many items have been dropped)
|
||||
|
||||
struct G_DestroyGroundItem_6x63 {
|
||||
struct G_DestroyFloorItem_6x63 {
|
||||
G_UnusedHeader header;
|
||||
le_uint32_t item_id = 0;
|
||||
le_uint32_t floor = 0;
|
||||
@@ -4622,13 +4623,12 @@ struct G_SyncObjectState_6x6C_Entry_Decompressed {
|
||||
// commands cannot be processed on the same frame.
|
||||
|
||||
struct G_SyncItemState_6x6D_Decompressed {
|
||||
// TODO: Verify this format on DC and PC. It appears correct for GC and BB.
|
||||
// Note: 16 vs. 15 is not a bug here - there really is an extra field in the
|
||||
// total drop count vs. the floor item count. Despite this, Pioneer 2 or Lab
|
||||
// (floor 0) isn't included in total_items_dropped_per_floor (so Forest 1 is
|
||||
// [0] in that array) but it is included in floor_item_count_per_floor (so
|
||||
// Forest 1 is [1] there).
|
||||
parray<le_uint16_t, 16> total_items_dropped_per_floor;
|
||||
// (floor 0) isn't included in next_drop_number_per_floor (so Forest 1 is [0]
|
||||
// in that array) but it is included in floor_item_count_per_floor (so Forest
|
||||
// 1 is [1] there).
|
||||
parray<le_uint16_t, 16> next_drop_number_per_floor;
|
||||
// Only [0]-[3] in this array are ever actually used in normal gameplay, but
|
||||
// the client fills in all 12 of these with reasonable values.
|
||||
parray<le_uint32_t, 12> next_item_id_per_player;
|
||||
|
||||
+15
-7
@@ -52,6 +52,14 @@ void ItemCreator::set_random_state(uint32_t seed, uint32_t absolute_offset) {
|
||||
}
|
||||
}
|
||||
|
||||
void ItemCreator::set_box_destroyed(uint16_t entity_id) {
|
||||
this->destroyed_boxes.emplace(entity_id);
|
||||
}
|
||||
|
||||
void ItemCreator::set_monster_destroyed(uint16_t entity_id) {
|
||||
this->destroyed_monsters.emplace(entity_id);
|
||||
}
|
||||
|
||||
void ItemCreator::clear_destroyed_entities() {
|
||||
this->destroyed_monsters.clear();
|
||||
this->destroyed_boxes.clear();
|
||||
@@ -127,15 +135,15 @@ uint8_t ItemCreator::normalize_area_number(uint8_t area) const {
|
||||
}
|
||||
|
||||
ItemData ItemCreator::on_box_item_drop(uint16_t entity_id, uint8_t area) {
|
||||
return this->destroyed_boxes.emplace(entity_id).second
|
||||
? this->on_box_item_drop_with_area_norm(this->normalize_area_number(area))
|
||||
: ItemData();
|
||||
return this->destroyed_boxes.count(entity_id)
|
||||
? ItemData()
|
||||
: this->on_box_item_drop_with_area_norm(this->normalize_area_number(area));
|
||||
}
|
||||
|
||||
ItemData ItemCreator::on_monster_item_drop(uint16_t entity_id, uint32_t enemy_type, uint8_t area) {
|
||||
return this->destroyed_monsters.emplace(entity_id).second
|
||||
? this->on_monster_item_drop_with_area_norm(enemy_type, this->normalize_area_number(area))
|
||||
: ItemData();
|
||||
return this->destroyed_monsters.count(entity_id)
|
||||
? ItemData()
|
||||
: this->on_monster_item_drop_with_area_norm(enemy_type, this->normalize_area_number(area));
|
||||
}
|
||||
|
||||
ItemData ItemCreator::on_box_item_drop_with_area_norm(uint8_t area_norm) {
|
||||
@@ -1656,7 +1664,7 @@ void ItemCreator::generate_weapon_shop_item_bonus2(ItemData& item, size_t player
|
||||
|
||||
ItemData ItemCreator::on_specialized_box_item_drop(
|
||||
uint16_t entity_id, uint8_t area, float def_z, uint32_t def0, uint32_t def1, uint32_t def2) {
|
||||
if (!this->destroyed_boxes.emplace(entity_id).second) {
|
||||
if (this->destroyed_boxes.count(entity_id)) {
|
||||
return ItemData();
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,9 @@ public:
|
||||
ItemData on_box_item_drop(uint16_t entity_id, uint8_t area);
|
||||
ItemData on_specialized_box_item_drop(uint16_t entity_id, uint8_t area, float def_z, uint32_t def0, uint32_t def1, uint32_t def2);
|
||||
|
||||
void set_monster_destroyed(uint16_t entity_id);
|
||||
void set_box_destroyed(uint16_t entity_id);
|
||||
|
||||
static ItemData base_item_for_specialized_box(uint32_t def0, uint32_t def1, uint32_t def2);
|
||||
|
||||
std::vector<ItemData> generate_armor_shop_contents(size_t player_level);
|
||||
|
||||
+3
-3
@@ -192,9 +192,9 @@ void player_use_item(shared_ptr<Client> c, size_t item_index) {
|
||||
item.data.data1.clear_after(3);
|
||||
should_delete_item = false;
|
||||
|
||||
auto l = c->lobby.lock();
|
||||
if (l) {
|
||||
send_create_inventory_item(c, item.data);
|
||||
auto l = c->require_lobby();
|
||||
if (l->base_version == Version::BB_V4) {
|
||||
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item.data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
+254
-47
@@ -11,13 +11,137 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
bool Lobby::FloorItem::visible_to_client(uint8_t client_id) const {
|
||||
return this->visibility_flags & (1 << client_id);
|
||||
}
|
||||
|
||||
Lobby::FloorItemManager::FloorItemManager(uint32_t lobby_id, uint8_t floor)
|
||||
: log(string_printf("[Lobby:%08" PRIX32 ":FloorItems:%02hhX] ", lobby_id, floor), lobby_log.min_level),
|
||||
next_drop_number(0) {}
|
||||
|
||||
bool Lobby::FloorItemManager::exists(uint32_t item_id) const {
|
||||
return this->items.count(item_id);
|
||||
}
|
||||
|
||||
shared_ptr<Lobby::FloorItem> Lobby::FloorItemManager::find(uint32_t item_id) const {
|
||||
return this->items.at(item_id);
|
||||
}
|
||||
|
||||
void Lobby::FloorItemManager::add(const ItemData& item, float x, float z, uint16_t visibility_flags) {
|
||||
auto fi = make_shared<FloorItem>();
|
||||
fi->data = item;
|
||||
fi->x = x;
|
||||
fi->z = z;
|
||||
fi->drop_number = this->next_drop_number++;
|
||||
fi->visibility_flags = visibility_flags & 0x0FFF;
|
||||
this->add(fi);
|
||||
}
|
||||
|
||||
void Lobby::FloorItemManager::add(shared_ptr<Lobby::FloorItem> fi) {
|
||||
if (fi->visibility_flags == 0) {
|
||||
throw logic_error("floor item is not visible to any player");
|
||||
}
|
||||
|
||||
auto emplace_ret = this->items.emplace(fi->data.id, fi);
|
||||
if (!emplace_ret.second) {
|
||||
throw runtime_error("floor item already exists with the same ID");
|
||||
}
|
||||
for (size_t z = 0; z < 12; z++) {
|
||||
if (fi->visible_to_client(z)) {
|
||||
this->queue_for_client[z].emplace(fi->drop_number, fi);
|
||||
}
|
||||
}
|
||||
this->log.info("Added floor item %08" PRIX32 " at %g, %g with drop number %" PRIu64 " visible to %03hX",
|
||||
fi->data.id.load(), fi->x, fi->z, fi->drop_number, fi->visibility_flags);
|
||||
}
|
||||
|
||||
std::shared_ptr<Lobby::FloorItem> Lobby::FloorItemManager::remove(uint32_t item_id, uint8_t client_id) {
|
||||
auto item_it = this->items.find(item_id);
|
||||
if (item_it == this->items.end()) {
|
||||
throw out_of_range("item not present");
|
||||
}
|
||||
auto fi = item_it->second;
|
||||
if ((client_id != 0xFF) && !fi->visible_to_client(client_id)) {
|
||||
throw runtime_error("client does not have access to item");
|
||||
}
|
||||
for (size_t z = 0; z < 12; z++) {
|
||||
if (fi->visible_to_client(z) && !this->queue_for_client[z].erase(fi->drop_number)) {
|
||||
throw logic_error("item queue for client is inconsistent");
|
||||
}
|
||||
}
|
||||
this->items.erase(item_it);
|
||||
this->log.info("Removed floor item %08" PRIX32 " at %g, %g with drop number %" PRIu64 " visible to %03hX",
|
||||
fi->data.id.load(), fi->x, fi->z, fi->drop_number, fi->visibility_flags);
|
||||
return fi;
|
||||
}
|
||||
|
||||
std::unordered_set<std::shared_ptr<Lobby::FloorItem>> Lobby::FloorItemManager::evict() {
|
||||
unordered_set<shared_ptr<FloorItem>> ret;
|
||||
for (size_t z = 0; z < 12; z++) {
|
||||
while (this->queue_for_client[z].size() > 48) {
|
||||
ret.emplace(this->remove(this->queue_for_client[z].begin()->second->data.id, 0xFF));
|
||||
}
|
||||
}
|
||||
this->log.info("Evicted %zu items", ret.size());
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Lobby::FloorItemManager::clear_inaccessible(uint16_t remaining_clients_mask) {
|
||||
unordered_set<uint32_t> item_ids_to_delete;
|
||||
for (const auto& it : this->items) {
|
||||
if ((it.second->visibility_flags & remaining_clients_mask) == 0) {
|
||||
item_ids_to_delete.emplace(it.first);
|
||||
}
|
||||
}
|
||||
for (uint32_t item_id : item_ids_to_delete) {
|
||||
this->remove(item_id, 0xFF);
|
||||
}
|
||||
this->log.info("Deleted %zu inaccessible items", item_ids_to_delete.size());
|
||||
}
|
||||
|
||||
void Lobby::FloorItemManager::clear_private() {
|
||||
unordered_set<uint32_t> item_ids_to_delete;
|
||||
for (const auto& it : this->items) {
|
||||
if ((it.second->visibility_flags & 0x00F) != 0x00F) {
|
||||
item_ids_to_delete.emplace(it.first);
|
||||
}
|
||||
}
|
||||
for (uint32_t item_id : item_ids_to_delete) {
|
||||
this->remove(item_id, 0xFF);
|
||||
}
|
||||
this->log.info("Deleted %zu private items", item_ids_to_delete.size());
|
||||
}
|
||||
|
||||
void Lobby::FloorItemManager::clear() {
|
||||
size_t num_items = this->items.size();
|
||||
this->items.clear();
|
||||
for (auto& queue : this->queue_for_client) {
|
||||
queue.clear();
|
||||
}
|
||||
this->next_drop_number = 0;
|
||||
this->log.info("Deleted %zu items", num_items);
|
||||
}
|
||||
|
||||
uint32_t Lobby::FloorItemManager::reassign_all_item_ids(uint32_t next_item_id) {
|
||||
unordered_map<uint32_t, shared_ptr<FloorItem>> old_items;
|
||||
old_items.swap(this->items);
|
||||
for (auto& queue : this->queue_for_client) {
|
||||
queue.clear();
|
||||
}
|
||||
for (auto& it : old_items) {
|
||||
it.second->data.id = next_item_id++;
|
||||
this->add(it.second);
|
||||
}
|
||||
return next_item_id;
|
||||
}
|
||||
|
||||
Lobby::Lobby(shared_ptr<ServerState> s, uint32_t id)
|
||||
: server_state(s),
|
||||
log(string_printf("[Lobby:%" PRIX32 "] ", id), lobby_log.min_level),
|
||||
lobby_id(id),
|
||||
min_level(0),
|
||||
max_level(0xFFFFFFFF),
|
||||
next_game_item_id(0x00810000),
|
||||
next_game_item_id(0xCC000000),
|
||||
base_version(Version::GC_V3),
|
||||
allowed_versions(0x0000),
|
||||
section_id(0),
|
||||
@@ -27,6 +151,7 @@ Lobby::Lobby(shared_ptr<ServerState> s, uint32_t id)
|
||||
base_exp_multiplier(1),
|
||||
challenge_exp_multiplier(1.0f),
|
||||
random_seed(random_object<uint32_t>()),
|
||||
drop_mode(DropMode::CLIENT),
|
||||
event(0),
|
||||
block(0),
|
||||
leader_id(0),
|
||||
@@ -38,7 +163,7 @@ Lobby::Lobby(shared_ptr<ServerState> s, uint32_t id)
|
||||
event_free) {
|
||||
this->log.info("Created");
|
||||
for (size_t x = 0; x < 12; x++) {
|
||||
this->next_item_id[x] = 0x00010000 + 0x00200000 * x;
|
||||
this->next_item_id_for_client[x] = 0x00010000 + 0x00200000 * x;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +186,18 @@ shared_ptr<Lobby::ChallengeParameters> Lobby::require_challenge_params() const {
|
||||
return this->challenge_params;
|
||||
}
|
||||
|
||||
void Lobby::set_drop_mode(DropMode new_mode) {
|
||||
this->drop_mode = new_mode;
|
||||
|
||||
bool should_have_item_creator = (this->base_version == Version::BB_V4) ||
|
||||
((new_mode != DropMode::DISABLED) && (new_mode != DropMode::CLIENT));
|
||||
if (should_have_item_creator && !this->item_creator) {
|
||||
this->create_item_creator();
|
||||
} else if (!should_have_item_creator && this->item_creator) {
|
||||
this->item_creator.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Lobby::create_item_creator() {
|
||||
auto s = this->require_server_state();
|
||||
|
||||
@@ -135,9 +272,6 @@ void Lobby::load_maps() {
|
||||
dat_contents.size(),
|
||||
this->random_seed,
|
||||
this->rare_enemy_rates ? this->rare_enemy_rates : Map::NO_RARE_ENEMIES);
|
||||
if (this->item_creator) {
|
||||
this->item_creator->clear_destroyed_entities();
|
||||
}
|
||||
|
||||
} else { // No quest loaded
|
||||
for (size_t floor = 0; floor < 0x10; floor++) {
|
||||
@@ -330,27 +464,25 @@ void Lobby::add_client(shared_ptr<Client> c, ssize_t required_client_id) {
|
||||
this->leader_id = c->lobby_client_id;
|
||||
}
|
||||
|
||||
// If the lobby is a game and item tracking is enabled, assign the inventory's
|
||||
// item IDs. If there was no one else in the lobby, reset all the next item
|
||||
// IDs also
|
||||
if (this->is_game() && this->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
// If the lobby is a game and there was no one in it, reassign all the floor
|
||||
// item IDs and reset the next item IDs
|
||||
if (this->is_game()) {
|
||||
if (leader_index >= this->max_clients) {
|
||||
for (size_t x = 0; x < 12; x++) {
|
||||
this->next_item_id[x] = 0x00010000 + 0x00200000 * x;
|
||||
this->next_item_id_for_client[x] = 0x00010000 + 0x00200000 * x;
|
||||
}
|
||||
this->next_game_item_id = 0x00810000;
|
||||
this->next_game_item_id = 0xCC000000;
|
||||
|
||||
// Reassign all floor item IDs so they won't conflict with any players'
|
||||
// item IDs
|
||||
unordered_map<uint32_t, FloorItem> new_item_id_to_floor_item;
|
||||
for (const auto& it : this->item_id_to_floor_item) {
|
||||
uint32_t new_item_id = this->generate_item_id(0xFF);
|
||||
auto& new_fi = new_item_id_to_floor_item.emplace(new_item_id, it.second).first->second;
|
||||
new_fi.data.id = new_item_id;
|
||||
for (auto& m : this->floor_item_managers) {
|
||||
this->next_game_item_id = m.reassign_all_item_ids(this->next_game_item_id);
|
||||
}
|
||||
this->item_id_to_floor_item = std::move(new_item_id_to_floor_item);
|
||||
}
|
||||
this->assign_inventory_and_bank_item_ids(c);
|
||||
// We don't consume item IDs here because the 6F handler will do it for
|
||||
// real; we just want to see what they would be when the join command is
|
||||
// sent
|
||||
this->assign_inventory_and_bank_item_ids(c, false);
|
||||
}
|
||||
|
||||
// If the lobby is recording a battle record, add the player join event
|
||||
@@ -425,12 +557,31 @@ void Lobby::remove_client(shared_ptr<Client> c) {
|
||||
}
|
||||
}
|
||||
|
||||
// If the lobby is persistent but has an idle timeout, make it expire after
|
||||
// the specified time
|
||||
if ((this->count_clients() == 0) &&
|
||||
// If there are still players left in the lobby, delete all items that only
|
||||
// the leaving player could see. Don't do this if no one is left in the lobby,
|
||||
// since that would mean items could not persist in empty lobbies.
|
||||
uint16_t remaining_clients_mask = 0;
|
||||
for (size_t z = 0; z < 12; z++) {
|
||||
if (this->clients[z]) {
|
||||
remaining_clients_mask |= (1 << z);
|
||||
}
|
||||
}
|
||||
if (remaining_clients_mask) {
|
||||
for (auto& m : this->floor_item_managers) {
|
||||
m.clear_inaccessible(remaining_clients_mask);
|
||||
}
|
||||
} else {
|
||||
for (auto& m : this->floor_item_managers) {
|
||||
m.clear_private();
|
||||
}
|
||||
}
|
||||
|
||||
if (!remaining_clients_mask &&
|
||||
this->check_flag(Flag::PERSISTENT) &&
|
||||
!this->check_flag(Flag::DEFAULT) &&
|
||||
(this->idle_timeout_usecs > 0)) {
|
||||
// If the lobby is persistent but has an idle timeout, make it expire after
|
||||
// the specified time
|
||||
auto tv = usecs_to_timeval(this->idle_timeout_usecs);
|
||||
event_add(this->idle_timeout_event.get(), &tv);
|
||||
this->log.info("Idle timeout scheduled");
|
||||
@@ -492,35 +643,52 @@ uint8_t Lobby::game_event_for_lobby_event(uint8_t lobby_event) {
|
||||
return lobby_event;
|
||||
}
|
||||
|
||||
bool Lobby::item_exists(uint32_t item_id) const {
|
||||
return this->item_id_to_floor_item.count(item_id);
|
||||
}
|
||||
|
||||
const Lobby::FloorItem& Lobby::find_item(uint32_t item_id) const {
|
||||
return this->item_id_to_floor_item.at(item_id);
|
||||
}
|
||||
|
||||
void Lobby::add_item(const ItemData& data, uint8_t floor, float x, float z) {
|
||||
auto& fi = this->item_id_to_floor_item[data.id];
|
||||
fi.data = data;
|
||||
fi.floor = floor;
|
||||
fi.x = x;
|
||||
fi.z = z;
|
||||
}
|
||||
|
||||
ItemData Lobby::remove_item(uint32_t item_id) {
|
||||
auto item_it = this->item_id_to_floor_item.find(item_id);
|
||||
if (item_it == this->item_id_to_floor_item.end()) {
|
||||
throw out_of_range("item not present");
|
||||
bool Lobby::item_exists(uint8_t floor, uint32_t item_id) const {
|
||||
if (floor >= this->floor_item_managers.size()) {
|
||||
return false;
|
||||
}
|
||||
ItemData ret = item_it->second.data;
|
||||
this->item_id_to_floor_item.erase(item_it);
|
||||
return ret;
|
||||
return this->floor_item_managers.at(floor).exists(item_id);
|
||||
}
|
||||
|
||||
shared_ptr<Lobby::FloorItem> Lobby::find_item(uint8_t floor, uint32_t item_id) const {
|
||||
return this->floor_item_managers.at(floor).find(item_id);
|
||||
}
|
||||
|
||||
void Lobby::add_item(uint8_t floor, const ItemData& data, float x, float z, uint16_t visibility_flags) {
|
||||
auto& m = this->floor_item_managers.at(floor);
|
||||
m.add(data, x, z, visibility_flags);
|
||||
this->evict_items_from_floor(floor);
|
||||
}
|
||||
|
||||
void Lobby::add_item(uint8_t floor, shared_ptr<FloorItem> fi) {
|
||||
auto& m = this->floor_item_managers.at(floor);
|
||||
m.add(fi);
|
||||
this->evict_items_from_floor(floor);
|
||||
}
|
||||
|
||||
void Lobby::evict_items_from_floor(uint8_t floor) {
|
||||
auto& m = this->floor_item_managers.at(floor);
|
||||
auto evicted = m.evict();
|
||||
if (!evicted.empty()) {
|
||||
auto l = this->shared_from_this();
|
||||
for (const auto& fi : evicted) {
|
||||
for (size_t z = 0; z < 12; z++) {
|
||||
auto lc = this->clients[z];
|
||||
if (lc && fi->visible_to_client(z)) {
|
||||
send_destroy_floor_item_to_client(lc, fi->data.id, floor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<Lobby::FloorItem> Lobby::remove_item(uint8_t floor, uint32_t item_id, uint8_t requesting_client_id) {
|
||||
return this->floor_item_managers.at(floor).remove(item_id, requesting_client_id);
|
||||
}
|
||||
|
||||
uint32_t Lobby::generate_item_id(uint8_t client_id) {
|
||||
if (client_id < this->max_clients) {
|
||||
return this->next_item_id[client_id]++;
|
||||
return this->next_item_id_for_client[client_id]++;
|
||||
}
|
||||
return this->next_game_item_id++;
|
||||
}
|
||||
@@ -531,16 +699,20 @@ void Lobby::on_item_id_generated_externally(uint32_t item_id) {
|
||||
// the range further here.
|
||||
if ((item_id > 0x00010000) && (item_id < 0x00810000)) {
|
||||
uint16_t item_client_id = (item_id >> 21) & 0x7FF;
|
||||
uint32_t& next_item_id = this->next_item_id.at(item_client_id);
|
||||
uint32_t& next_item_id = this->next_item_id_for_client.at(item_client_id);
|
||||
next_item_id = std::max<uint32_t>(next_item_id, item_id + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Lobby::assign_inventory_and_bank_item_ids(shared_ptr<Client> c) {
|
||||
void Lobby::assign_inventory_and_bank_item_ids(shared_ptr<Client> c, bool consume_ids) {
|
||||
auto p = c->character();
|
||||
uint32_t start_item_id = this->next_item_id_for_client[c->lobby_client_id];
|
||||
for (size_t z = 0; z < p->inventory.num_items; z++) {
|
||||
p->inventory.items[z].data.id = this->generate_item_id(c->lobby_client_id);
|
||||
}
|
||||
if (!consume_ids) {
|
||||
this->next_item_id_for_client[c->lobby_client_id] = start_item_id;
|
||||
}
|
||||
if (c->log.info("Assigned inventory item IDs")) {
|
||||
p->print_inventory(stderr, c->version(), c->require_server_state()->item_name_index);
|
||||
if (p->bank.num_items) {
|
||||
@@ -649,3 +821,38 @@ bool Lobby::compare_shared(const shared_ptr<const Lobby>& a, const shared_ptr<co
|
||||
|
||||
return a->name < b->name;
|
||||
}
|
||||
|
||||
template <>
|
||||
Lobby::DropMode enum_for_name<Lobby::DropMode>(const char* name) {
|
||||
if (!strcmp(name, "DISABLED")) {
|
||||
return Lobby::DropMode::DISABLED;
|
||||
} else if (!strcmp(name, "CLIENT")) {
|
||||
return Lobby::DropMode::CLIENT;
|
||||
} else if (!strcmp(name, "SERVER_SHARED")) {
|
||||
return Lobby::DropMode::SERVER_SHARED;
|
||||
} else if (!strcmp(name, "SERVER_PRIVATE")) {
|
||||
return Lobby::DropMode::SERVER_PRIVATE;
|
||||
} else if (!strcmp(name, "SERVER_DUPLICATE")) {
|
||||
return Lobby::DropMode::SERVER_DUPLICATE;
|
||||
} else {
|
||||
throw runtime_error("invalid drop mode");
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
const char* name_for_enum<Lobby::DropMode>(Lobby::DropMode value) {
|
||||
switch (value) {
|
||||
case Lobby::DropMode::DISABLED:
|
||||
return "DISABLED";
|
||||
case Lobby::DropMode::CLIENT:
|
||||
return "CLIENT";
|
||||
case Lobby::DropMode::SERVER_SHARED:
|
||||
return "SERVER_SHARED";
|
||||
case Lobby::DropMode::SERVER_PRIVATE:
|
||||
return "SERVER_PRIVATE";
|
||||
case Lobby::DropMode::SERVER_DUPLICATE:
|
||||
return "SERVER_DUPLICATE";
|
||||
default:
|
||||
throw runtime_error("invalid drop mode");
|
||||
}
|
||||
}
|
||||
|
||||
+63
-23
@@ -24,6 +24,35 @@
|
||||
struct ServerState;
|
||||
|
||||
struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
struct FloorItem {
|
||||
ItemData data;
|
||||
float x;
|
||||
float z;
|
||||
uint64_t drop_number;
|
||||
uint16_t visibility_flags;
|
||||
|
||||
bool visible_to_client(uint8_t client_id) const;
|
||||
};
|
||||
struct FloorItemManager {
|
||||
PrefixedLogger log;
|
||||
uint64_t next_drop_number;
|
||||
std::unordered_map<uint32_t, std::shared_ptr<FloorItem>> items; // Keyed on item_id
|
||||
std::array<std::map<uint64_t, std::shared_ptr<FloorItem>>, 12> queue_for_client;
|
||||
|
||||
FloorItemManager(uint32_t lobby_id, uint8_t floor);
|
||||
~FloorItemManager() = default;
|
||||
|
||||
bool exists(uint32_t item_id) const;
|
||||
std::shared_ptr<FloorItem> find(uint32_t item_id) const;
|
||||
void add(const ItemData& item, float x, float z, uint16_t visibility_flags);
|
||||
void add(std::shared_ptr<FloorItem> fi);
|
||||
std::shared_ptr<FloorItem> remove(uint32_t item_id, uint8_t client_id);
|
||||
std::unordered_set<std::shared_ptr<FloorItem>> evict();
|
||||
void clear_inaccessible(uint16_t remaining_clients_mask);
|
||||
void clear_private();
|
||||
void clear();
|
||||
uint32_t reassign_all_item_ids(uint32_t next_item_id);
|
||||
};
|
||||
enum class Flag {
|
||||
GAME = 0x00000001,
|
||||
PERSISTENT = 0x00000002,
|
||||
@@ -33,22 +62,26 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
QUEST_IN_PROGRESS = 0x00000200,
|
||||
BATTLE_IN_PROGRESS = 0x00000400,
|
||||
JOINABLE_QUEST_IN_PROGRESS = 0x00000800,
|
||||
ITEM_TRACKING_ENABLED = 0x00001000,
|
||||
IS_SPECTATOR_TEAM = 0x00002000, // episode must be EP3 also
|
||||
SPECTATORS_FORBIDDEN = 0x00004000,
|
||||
START_BATTLE_PLAYER_IMMEDIATELY = 0x00008000,
|
||||
DROPS_ENABLED = 0x00020000,
|
||||
CANNOT_CHANGE_DROPS_ENABLED = 0x00040000,
|
||||
CANNOT_CHANGE_ITEM_TABLE = 0x00080000,
|
||||
CANNOT_CHANGE_CHEAT_MODE = 0x00100000,
|
||||
CANNOT_CHANGE_CHEAT_MODE = 0x00010000,
|
||||
|
||||
// Flags used only for lobbies
|
||||
PUBLIC = 0x01000000,
|
||||
DEFAULT = 0x02000000,
|
||||
IS_OVERFLOW = 0x08000000,
|
||||
};
|
||||
enum class DropMode {
|
||||
DISABLED = 0,
|
||||
CLIENT = 1, // Not allowed for BB games
|
||||
SERVER_SHARED = 2,
|
||||
SERVER_PRIVATE = 3,
|
||||
SERVER_DUPLICATE = 4,
|
||||
};
|
||||
|
||||
std::weak_ptr<ServerState> server_state;
|
||||
std::weak_ptr<ServerState>
|
||||
server_state;
|
||||
PrefixedLogger log;
|
||||
|
||||
uint32_t lobby_id;
|
||||
@@ -56,18 +89,14 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
uint32_t min_level;
|
||||
uint32_t max_level;
|
||||
|
||||
// Item info
|
||||
struct FloorItem {
|
||||
ItemData data;
|
||||
float x;
|
||||
float z;
|
||||
uint8_t floor;
|
||||
};
|
||||
// Item state
|
||||
std::array<uint32_t, 12> next_item_id_for_client;
|
||||
uint32_t next_game_item_id;
|
||||
std::vector<FloorItemManager> floor_item_managers;
|
||||
|
||||
// Map state
|
||||
std::shared_ptr<const Map::RareEnemyRates> rare_enemy_rates;
|
||||
std::shared_ptr<Map> map;
|
||||
std::array<uint32_t, 12> next_item_id;
|
||||
uint32_t next_game_item_id;
|
||||
std::unordered_map<uint32_t, FloorItem> item_id_to_floor_item;
|
||||
parray<le_uint32_t, 0x20> variations;
|
||||
|
||||
// Game config
|
||||
@@ -87,6 +116,8 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
// This seed is also sent to the client for rare enemy generation
|
||||
uint32_t random_seed;
|
||||
std::shared_ptr<PSOLFGEncryption> random_crypt;
|
||||
uint8_t allowed_drop_modes;
|
||||
DropMode drop_mode;
|
||||
std::shared_ptr<ItemCreator> item_creator;
|
||||
|
||||
struct ChallengeParameters {
|
||||
@@ -102,8 +133,8 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
std::shared_ptr<ChallengeParameters> challenge_params;
|
||||
|
||||
// Ep3 stuff
|
||||
// There are three kinds of Episode 3 games. All of these types have the flag
|
||||
// EPISODE_3_ONLY; types 2 and 3 additionally have the IS_SPECTATOR_TEAM flag.
|
||||
// There are three kinds of Episode 3 games. All of these types have episode
|
||||
// set to EP3; types 2 and 3 additionally have the IS_SPECTATOR_TEAM flag.
|
||||
// 1. Primary games. These are the lobbies where battles may take place.
|
||||
// 2. Watcher games. These lobbies receive all the battle and chat commands
|
||||
// from a primary game. (This the implementation of spectator teams.)
|
||||
@@ -158,6 +189,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
|
||||
std::shared_ptr<ServerState> require_server_state() const;
|
||||
std::shared_ptr<ChallengeParameters> require_challenge_params() const;
|
||||
void set_drop_mode(DropMode new_mode);
|
||||
void create_item_creator();
|
||||
void load_maps();
|
||||
void create_ep3_server();
|
||||
@@ -192,13 +224,16 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
const std::string* identifier = nullptr,
|
||||
uint64_t serial_number = 0);
|
||||
|
||||
bool item_exists(uint32_t item_id) const;
|
||||
const FloorItem& find_item(uint32_t item_id) const;
|
||||
void add_item(const ItemData& item, uint8_t floor, float x, float z);
|
||||
ItemData remove_item(uint32_t item_id);
|
||||
bool item_exists(uint8_t floor, uint32_t item_id) const;
|
||||
std::shared_ptr<FloorItem> find_item(uint8_t floor, uint32_t item_id) const;
|
||||
void add_item(uint8_t floor, const ItemData& item, float x, float z, uint16_t visibility_flags);
|
||||
void add_item(uint8_t floor, std::shared_ptr<FloorItem>);
|
||||
void evict_items_from_floor(uint8_t floor);
|
||||
std::shared_ptr<FloorItem> remove_item(uint8_t floor, uint32_t item_id, uint8_t requesting_client_id);
|
||||
|
||||
uint32_t generate_item_id(uint8_t client_id);
|
||||
void on_item_id_generated_externally(uint32_t item_id);
|
||||
void assign_inventory_and_bank_item_ids(std::shared_ptr<Client> c);
|
||||
void assign_inventory_and_bank_item_ids(std::shared_ptr<Client> c, bool consume_ids);
|
||||
|
||||
QuestIndex::IncludeCondition quest_include_condition() const;
|
||||
|
||||
@@ -210,3 +245,8 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
|
||||
static bool compare_shared(const std::shared_ptr<const Lobby>& a, const std::shared_ptr<const Lobby>& b);
|
||||
};
|
||||
|
||||
template <>
|
||||
Lobby::DropMode enum_for_name<Lobby::DropMode>(const char* name);
|
||||
template <>
|
||||
const char* name_for_enum<Lobby::DropMode>(Lobby::DropMode value);
|
||||
|
||||
@@ -997,8 +997,8 @@ static HandlerResult S_6x(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
const auto& cmd = check_size_t<G_StandardDropItemRequest_DC_6x60>(
|
||||
data, sizeof(G_StandardDropItemRequest_PC_V3_BB_6x60));
|
||||
ses->next_drop_item.id = ses->next_item_id++;
|
||||
send_drop_item(s, ses->server_channel, ses->next_drop_item, true, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
|
||||
send_drop_item(s, ses->client_channel, ses->next_drop_item, true, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
|
||||
send_drop_item_to_channel(s, ses->server_channel, ses->next_drop_item, true, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
|
||||
send_drop_item_to_channel(s, ses->client_channel, ses->next_drop_item, true, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
|
||||
ses->next_drop_item.clear();
|
||||
return HandlerResult::Type::SUPPRESS;
|
||||
|
||||
@@ -1009,8 +1009,8 @@ static HandlerResult S_6x(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
} else if ((static_cast<uint8_t>(data[0]) == 0xA2) && ses->next_drop_item.data1d[0] && !is_v4(ses->version())) {
|
||||
const auto& cmd = check_size_t<G_SpecializableItemDropRequest_6xA2>(data);
|
||||
ses->next_drop_item.id = ses->next_item_id++;
|
||||
send_drop_item(s, ses->server_channel, ses->next_drop_item, false, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
|
||||
send_drop_item(s, ses->client_channel, ses->next_drop_item, false, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
|
||||
send_drop_item_to_channel(s, ses->server_channel, ses->next_drop_item, false, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
|
||||
send_drop_item_to_channel(s, ses->client_channel, ses->next_drop_item, false, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
|
||||
ses->next_drop_item.clear();
|
||||
return HandlerResult::Type::SUPPRESS;
|
||||
|
||||
|
||||
+52
-24
@@ -346,8 +346,8 @@ static void on_1D(shared_ptr<Client> c, uint16_t, uint32_t, string&) {
|
||||
if (c->config.check_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_ITEM_STATE)) {
|
||||
c->config.clear_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_ITEM_STATE);
|
||||
auto l = c->require_lobby();
|
||||
if (!is_ep3(c->version()) && l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
send_artificial_item_state(c);
|
||||
if (!is_ep3(c->version())) {
|
||||
send_game_item_state(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1861,6 +1861,9 @@ static void on_quest_loaded(shared_ptr<Lobby> l) {
|
||||
if ((l->base_version == Version::BB_V4) && l->map && (l->quest->challenge_template_index < 0)) {
|
||||
l->load_maps();
|
||||
}
|
||||
for (auto& m : l->floor_item_managers) {
|
||||
m.clear();
|
||||
}
|
||||
|
||||
for (auto& lc : l->clients) {
|
||||
if (!lc) {
|
||||
@@ -1885,7 +1888,7 @@ static void on_quest_loaded(shared_ptr<Lobby> l) {
|
||||
lc->use_default_bank();
|
||||
lc->create_challenge_overlay(lc->version(), l->quest->challenge_template_index, s->level_table);
|
||||
lc->log.info("Created challenge overlay");
|
||||
l->assign_inventory_and_bank_item_ids(lc);
|
||||
l->assign_inventory_and_bank_item_ids(lc, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3461,7 +3464,7 @@ static void on_DF_BB(shared_ptr<Client> c, uint16_t command, uint32_t, string& d
|
||||
lc->use_default_bank();
|
||||
lc->create_challenge_overlay(lc->version(), l->quest->challenge_template_index, s->level_table);
|
||||
lc->log.info("Created challenge overlay");
|
||||
l->assign_inventory_and_bank_item_ids(lc);
|
||||
l->assign_inventory_and_bank_item_ids(lc, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3905,23 +3908,13 @@ shared_ptr<Lobby> create_game_generic(
|
||||
throw logic_error("invalid quest script version");
|
||||
}
|
||||
|
||||
if ((game->base_version == Version::BB_V4) || s->item_tracking_enabled) {
|
||||
game->set_flag(Lobby::Flag::ITEM_TRACKING_ENABLED);
|
||||
}
|
||||
// Only disable drops if the config flag is set and for regular multi-mode;
|
||||
// drops are still enabled for battle and challenge modes
|
||||
if (s->behavior_enabled(s->enable_drops_behavior) || (mode != GameMode::NORMAL)) {
|
||||
game->set_flag(Lobby::Flag::DROPS_ENABLED);
|
||||
while (game->floor_item_managers.size() < 0x12) {
|
||||
game->floor_item_managers.emplace_back(game->lobby_id, game->floor_item_managers.size());
|
||||
}
|
||||
|
||||
if (s->behavior_enabled(s->cheat_mode_behavior)) {
|
||||
game->set_flag(Lobby::Flag::CHEATS_ENABLED);
|
||||
}
|
||||
if (!s->behavior_can_be_overridden(s->enable_drops_behavior)) {
|
||||
game->set_flag(Lobby::Flag::CANNOT_CHANGE_DROPS_ENABLED);
|
||||
}
|
||||
if (!game->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED) || !s->behavior_can_be_overridden(s->use_server_item_tables_behavior)) {
|
||||
game->set_flag(Lobby::Flag::CANNOT_CHANGE_ITEM_TABLE);
|
||||
}
|
||||
if (!s->behavior_can_be_overridden(s->cheat_mode_behavior)) {
|
||||
game->set_flag(Lobby::Flag::CANNOT_CHANGE_CHEAT_MODE);
|
||||
}
|
||||
@@ -3951,13 +3944,44 @@ shared_ptr<Lobby> create_game_generic(
|
||||
game->base_exp_multiplier = s->bb_global_exp_multiplier;
|
||||
}
|
||||
|
||||
for (size_t x = 0; x < 4; x++) {
|
||||
game->next_item_id[x] = (0x00200000 * x) + 0x00010000;
|
||||
switch (game->base_version) {
|
||||
case Version::DC_NTE:
|
||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||
case Version::DC_V1:
|
||||
case Version::DC_V2:
|
||||
case Version::PC_V2:
|
||||
game->set_drop_mode(s->default_drop_mode_v1_v2);
|
||||
game->allowed_drop_modes = s->allowed_drop_modes_v1_v2;
|
||||
break;
|
||||
case Version::GC_NTE:
|
||||
case Version::GC_V3:
|
||||
case Version::XB_V3:
|
||||
game->set_drop_mode(s->default_drop_mode_v3);
|
||||
game->allowed_drop_modes = s->allowed_drop_modes_v3;
|
||||
break;
|
||||
case Version::GC_EP3_TRIAL_EDITION:
|
||||
case Version::GC_EP3:
|
||||
game->set_drop_mode(Lobby::DropMode::DISABLED);
|
||||
game->allowed_drop_modes = (1 << static_cast<size_t>(game->drop_mode));
|
||||
break;
|
||||
case Version::BB_V4:
|
||||
// Disallow CLIENT mode on BB
|
||||
if (s->default_drop_mode_v4 == Lobby::DropMode::CLIENT) {
|
||||
// If the default is CLIENT (somehow), force it to be SERVER_SHARED
|
||||
game->set_drop_mode(Lobby::DropMode::SERVER_SHARED);
|
||||
game->allowed_drop_modes |= (1 << static_cast<size_t>(game->drop_mode));
|
||||
} else {
|
||||
game->set_drop_mode(s->default_drop_mode_v4);
|
||||
game->allowed_drop_modes = s->allowed_drop_modes_v4 & ~(1 << static_cast<size_t>(Lobby::DropMode::CLIENT));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw logic_error("invalid quest script version");
|
||||
}
|
||||
game->next_game_item_id = 0x00810000;
|
||||
if ((game->base_version == Version::BB_V4) ||
|
||||
(game->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED) && s->behavior_enabled(s->use_server_item_tables_behavior))) {
|
||||
game->create_item_creator();
|
||||
// Only allow DISABLED in Normal; use the default in Battle/Challenge/Solo
|
||||
if ((game->drop_mode == Lobby::DropMode::DISABLED) && (mode != GameMode::NORMAL)) {
|
||||
game->set_drop_mode((game->base_version == Version::BB_V4) ? Lobby::DropMode::SERVER_SHARED : Lobby::DropMode::CLIENT);
|
||||
game->allowed_drop_modes |= (1 << static_cast<size_t>(game->drop_mode));
|
||||
}
|
||||
|
||||
game->event = Lobby::game_event_for_lobby_event(current_lobby->event);
|
||||
@@ -4149,6 +4173,10 @@ static void on_6F(shared_ptr<Client> c, uint16_t command, uint32_t, string& data
|
||||
}
|
||||
c->config.clear_flag(Client::Flag::LOADING);
|
||||
|
||||
if (command == 0x006F) {
|
||||
l->assign_inventory_and_bank_item_ids(c, true);
|
||||
}
|
||||
|
||||
send_server_time(c);
|
||||
if (l->base_version == Version::BB_V4) {
|
||||
send_set_exp_multiplier(l);
|
||||
@@ -4333,7 +4361,7 @@ static void on_D2_V3_BB(shared_ptr<Client> c, uint16_t, uint32_t, string& data)
|
||||
}
|
||||
|
||||
to_p->add_item(trade_item);
|
||||
send_create_inventory_item(to_c, item);
|
||||
send_create_inventory_item_to_lobby(to_c, to_c->lobby_client_id, item);
|
||||
}
|
||||
send_command(to_c, 0xD3, 0x00);
|
||||
|
||||
|
||||
+340
-445
File diff suppressed because it is too large
Load Diff
+63
-45
@@ -2304,43 +2304,39 @@ void send_set_player_visibility(shared_ptr<Lobby> l, shared_ptr<Client> c, bool
|
||||
send_command_t(l, 0x60, 0x00, cmd);
|
||||
}
|
||||
|
||||
void send_artificial_item_state(shared_ptr<Client> c) {
|
||||
void send_game_item_state(shared_ptr<Client> c) {
|
||||
auto l = c->require_lobby();
|
||||
if (c->lobby_client_id != l->leader_id) {
|
||||
throw runtime_error("artificial item state can only be sent to the leader");
|
||||
}
|
||||
if (l->count_clients() != 1) {
|
||||
throw runtime_error("artificial item state can only be sent with no one else in the game");
|
||||
}
|
||||
|
||||
array<StringWriter, 15> floor_ws;
|
||||
auto s = c->require_server_state();
|
||||
StringWriter floor_items_w;
|
||||
|
||||
G_SyncItemState_6x6D_Decompressed decompressed_header;
|
||||
for (size_t z = 0; z < 12; z++) {
|
||||
decompressed_header.next_item_id_per_player[z] = l->next_item_id[z];
|
||||
decompressed_header.next_item_id_per_player[z] = l->next_item_id_for_client[z];
|
||||
}
|
||||
for (const auto& it : l->item_id_to_floor_item) {
|
||||
const auto& item = it.second;
|
||||
for (size_t floor = 0; floor < 0x10; floor++) {
|
||||
const auto& m = l->floor_item_managers.at(floor);
|
||||
for (const auto& it : m.queue_for_client.at(c->lobby_client_id)) {
|
||||
const auto& item = it.second;
|
||||
|
||||
FloorItem fi;
|
||||
fi.floor = item.floor;
|
||||
fi.from_enemy = 0;
|
||||
fi.entity_id = 0xFFFF;
|
||||
fi.x = item.x;
|
||||
fi.z = item.z;
|
||||
fi.unknown_a2 = 0;
|
||||
fi.drop_number = decompressed_header.total_items_dropped_per_floor.at(item.floor)++;
|
||||
fi.item = item.data;
|
||||
floor_ws.at(item.floor).put(fi);
|
||||
FloorItem fi;
|
||||
fi.floor = floor;
|
||||
fi.from_enemy = 0;
|
||||
fi.entity_id = 0xFFFF;
|
||||
fi.x = item->x;
|
||||
fi.z = item->z;
|
||||
fi.unknown_a2 = 0;
|
||||
fi.drop_number = (floor == 0) ? 0xFFFF : (decompressed_header.next_drop_number_per_floor.at(floor - 1)++);
|
||||
fi.item = item->data;
|
||||
fi.item.encode_for_version(c->version(), s->item_parameter_table_for_version(c->version()));
|
||||
floor_items_w.put(fi);
|
||||
|
||||
decompressed_header.floor_item_count_per_floor.at(item.floor)++;
|
||||
decompressed_header.floor_item_count_per_floor.at(floor)++;
|
||||
}
|
||||
}
|
||||
|
||||
StringWriter decompressed_w;
|
||||
decompressed_w.put(decompressed_header);
|
||||
for (const auto& floor_w : floor_ws) {
|
||||
decompressed_w.write(floor_w.str());
|
||||
}
|
||||
decompressed_w.write(floor_items_w.str());
|
||||
|
||||
string compressed_data = bc0_compress(decompressed_w.str());
|
||||
|
||||
@@ -2358,10 +2354,19 @@ void send_artificial_item_state(shared_ptr<Client> c) {
|
||||
while (w.size() & 3) {
|
||||
w.put_u8(0x00);
|
||||
}
|
||||
send_command(c, 0x6D, c->lobby_client_id, w.str());
|
||||
|
||||
if (c->game_join_command_queue) {
|
||||
c->log.info("Client not ready to receive join commands; adding to queue");
|
||||
auto& cmd = c->game_join_command_queue->emplace_back();
|
||||
cmd.command = 0x6D;
|
||||
cmd.flag = c->lobby_client_id;
|
||||
cmd.data = std::move(w.str());
|
||||
} else {
|
||||
send_command(c, 0x6D, c->lobby_client_id, w.str());
|
||||
}
|
||||
}
|
||||
|
||||
void send_drop_item(shared_ptr<ServerState> s, Channel& ch, const ItemData& item,
|
||||
void send_drop_item_to_channel(shared_ptr<ServerState> s, Channel& ch, const ItemData& item,
|
||||
bool from_enemy, uint8_t floor, float x, float z, uint16_t entity_id) {
|
||||
G_DropItem_PC_V3_BB_6x5F cmd = {
|
||||
{{0x5F, 0x0B, 0x0000}, {floor, from_enemy, entity_id, x, z, 0, 0, item}}, 0};
|
||||
@@ -2369,55 +2374,63 @@ void send_drop_item(shared_ptr<ServerState> s, Channel& ch, const ItemData& item
|
||||
ch.send(0x60, 0x00, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
void send_drop_item(shared_ptr<Lobby> l, const ItemData& item,
|
||||
void send_drop_item_to_lobby(shared_ptr<Lobby> l, const ItemData& item,
|
||||
bool from_enemy, uint8_t floor, float x, float z, uint16_t entity_id) {
|
||||
auto s = l->require_server_state();
|
||||
for (auto& c : l->clients) {
|
||||
if (!c) {
|
||||
continue;
|
||||
}
|
||||
send_drop_item(s, c->channel, item, from_enemy, floor, x, z, entity_id);
|
||||
send_drop_item_to_channel(s, c->channel, item, from_enemy, floor, x, z, entity_id);
|
||||
}
|
||||
}
|
||||
|
||||
void send_drop_stacked_item(shared_ptr<ServerState> s, Channel& ch, const ItemData& item, uint8_t floor, float x, float z) {
|
||||
void send_drop_stacked_item_to_channel(
|
||||
shared_ptr<ServerState> s, Channel& ch, const ItemData& item, uint8_t floor, float x, float z) {
|
||||
G_DropStackedItem_PC_V3_BB_6x5D cmd = {{{0x5D, 0x0A, 0x0000}, floor, 0, x, z, item}, 0};
|
||||
cmd.item_data.encode_for_version(ch.version, s->item_parameter_table_for_version(ch.version));
|
||||
ch.send(0x60, 0x00, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
void send_drop_stacked_item(shared_ptr<Lobby> l, const ItemData& item, uint8_t floor, float x, float z) {
|
||||
void send_drop_stacked_item_to_lobby(shared_ptr<Lobby> l, const ItemData& item, uint8_t floor, float x, float z) {
|
||||
auto s = l->require_server_state();
|
||||
for (auto& c : l->clients) {
|
||||
if (!c) {
|
||||
continue;
|
||||
}
|
||||
send_drop_stacked_item(s, c->channel, item, floor, x, z);
|
||||
send_drop_stacked_item_to_channel(s, c->channel, item, floor, x, z);
|
||||
}
|
||||
}
|
||||
|
||||
void send_pick_up_item(shared_ptr<Client> c, uint32_t item_id, uint8_t floor) {
|
||||
auto l = c->require_lobby();
|
||||
uint16_t client_id = c->lobby_client_id;
|
||||
void send_pick_up_item_to_client(shared_ptr<Client> c, uint8_t client_id, uint32_t item_id, uint8_t floor) {
|
||||
G_PickUpItem_6x59 cmd = {{0x59, 0x03, client_id}, client_id, floor, item_id};
|
||||
send_command_t(l, 0x60, 0x00, cmd);
|
||||
send_command_t(c, 0x60, 0x00, cmd);
|
||||
}
|
||||
|
||||
void send_create_inventory_item(shared_ptr<Client> c, const ItemData& item, bool exclude_c) {
|
||||
auto l = c->require_lobby();
|
||||
void send_create_inventory_item_to_client(shared_ptr<Client> c, uint8_t client_id, const ItemData& item) {
|
||||
if (c->version() != Version::BB_V4) {
|
||||
throw logic_error("6xBE can only be sent to BB clients");
|
||||
}
|
||||
uint16_t client_id = c->lobby_client_id;
|
||||
G_CreateInventoryItem_BB_6xBE cmd = {{0xBE, 0x07, client_id}, item, 0};
|
||||
if (exclude_c) {
|
||||
send_command_excluding_client(l, c, 0x60, 0x00, &cmd, sizeof(cmd));
|
||||
} else {
|
||||
send_command_t(l, 0x60, 0x00, cmd);
|
||||
send_command_t(c, 0x60, 0x00, cmd);
|
||||
}
|
||||
|
||||
void send_create_inventory_item_to_lobby(shared_ptr<Client> c, uint8_t client_id, const ItemData& item, bool exclude_c) {
|
||||
auto l = c->require_lobby();
|
||||
for (const auto& lc : l->clients) {
|
||||
if (!lc) {
|
||||
continue;
|
||||
}
|
||||
if (lc->version() != Version::BB_V4) {
|
||||
throw logic_error("6xBE can only be sent to BB clients");
|
||||
}
|
||||
if ((lc != c) || !exclude_c) {
|
||||
send_create_inventory_item_to_client(lc, client_id, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void send_destroy_item(shared_ptr<Client> c, uint32_t item_id, uint32_t amount, bool exclude_c) {
|
||||
void send_destroy_item_to_lobby(shared_ptr<Client> c, uint32_t item_id, uint32_t amount, bool exclude_c) {
|
||||
auto l = c->require_lobby();
|
||||
uint16_t client_id = c->lobby_client_id;
|
||||
G_DeleteInventoryItem_6x29 cmd = {{0x29, 0x03, client_id}, item_id, amount};
|
||||
@@ -2428,6 +2441,11 @@ void send_destroy_item(shared_ptr<Client> c, uint32_t item_id, uint32_t amount,
|
||||
}
|
||||
}
|
||||
|
||||
void send_destroy_floor_item_to_client(shared_ptr<Client> c, uint32_t item_id, uint32_t floor) {
|
||||
G_DestroyFloorItem_6x63 cmd = {{0x63, 0x03, 0x0000}, item_id, floor};
|
||||
send_command_t(c, 0x60, 0x00, cmd);
|
||||
}
|
||||
|
||||
void send_item_identify_result(shared_ptr<Client> c) {
|
||||
auto l = c->require_lobby();
|
||||
if (c->version() != Version::BB_V4) {
|
||||
|
||||
+12
-8
@@ -298,16 +298,20 @@ void send_ep3_change_music(Channel& ch, uint32_t song);
|
||||
void send_set_player_visibility(std::shared_ptr<Client> c, bool visible);
|
||||
void send_revive_player(std::shared_ptr<Client> c);
|
||||
|
||||
void send_artificial_item_state(std::shared_ptr<Client> c);
|
||||
void send_drop_item(std::shared_ptr<ServerState> s, Channel& ch, const ItemData& item,
|
||||
void send_game_item_state(std::shared_ptr<Client> c);
|
||||
void send_drop_item_to_channel(std::shared_ptr<ServerState> s, Channel& ch, const ItemData& item,
|
||||
bool from_enemy, uint8_t floor, float x, float z, uint16_t request_id);
|
||||
void send_drop_item(std::shared_ptr<Lobby> l, const ItemData& item,
|
||||
void send_drop_item_to_lobby(std::shared_ptr<Lobby> l, const ItemData& item,
|
||||
bool from_enemy, uint8_t floor, float x, float z, uint16_t request_id);
|
||||
void send_drop_stacked_item(std::shared_ptr<ServerState> s, Channel& ch, const ItemData& item, uint8_t floor, float x, float z);
|
||||
void send_drop_stacked_item(std::shared_ptr<Lobby> l, const ItemData& item, uint8_t floor, float x, float z);
|
||||
void send_pick_up_item(std::shared_ptr<Client> c, uint32_t id, uint8_t floor);
|
||||
void send_create_inventory_item(std::shared_ptr<Client> c, const ItemData& item, bool exclude_c = false);
|
||||
void send_destroy_item(std::shared_ptr<Client> c, uint32_t item_id, uint32_t amount, bool exclude_c = false);
|
||||
void send_drop_stacked_item_to_channel(
|
||||
std::shared_ptr<ServerState> s, Channel& ch, const ItemData& item, uint8_t floor, float x, float z);
|
||||
void send_drop_stacked_item_to_lobby(
|
||||
std::shared_ptr<Lobby> l, const ItemData& item, uint8_t floor, float x, float z);
|
||||
void send_pick_up_item_to_client(std::shared_ptr<Client> c, uint8_t client_id, uint32_t id, uint8_t floor);
|
||||
void send_create_inventory_item_to_client(std::shared_ptr<Client> c, uint8_t client_id, const ItemData& item);
|
||||
void send_create_inventory_item_to_lobby(std::shared_ptr<Client> c, uint8_t client_id, const ItemData& item, bool exclude_c = false);
|
||||
void send_destroy_item_to_lobby(std::shared_ptr<Client> c, uint32_t item_id, uint32_t amount, bool exclude_c = false);
|
||||
void send_destroy_floor_item_to_client(std::shared_ptr<Client> c, uint32_t item_id, uint32_t floor);
|
||||
void send_item_identify_result(std::shared_ptr<Client> c);
|
||||
void send_bank(std::shared_ptr<Client> c);
|
||||
void send_shop(std::shared_ptr<Client> c, uint8_t shop_type);
|
||||
|
||||
+2
-2
@@ -816,8 +816,8 @@ Proxy session commands:\n\
|
||||
send_text_message(ses->client_channel, "$C7Next drop:\n" + name);
|
||||
|
||||
} else {
|
||||
send_drop_stacked_item(s, ses->client_channel, item, ses->floor, ses->x, ses->z);
|
||||
send_drop_stacked_item(s, ses->server_channel, item, ses->floor, ses->x, ses->z);
|
||||
send_drop_stacked_item_to_channel(s, ses->client_channel, item, ses->floor, ses->x, ses->z);
|
||||
send_drop_stacked_item_to_channel(s, ses->server_channel, item, ses->floor, ses->x, ses->z);
|
||||
|
||||
string name = s->describe_item(ses->version(), ses->next_drop_item, true);
|
||||
send_text_message(ses->client_channel, "$C7Item created:\n" + name);
|
||||
|
||||
+18
-6
@@ -26,9 +26,12 @@ ServerState::ServerState(shared_ptr<struct event_base> base, const string& confi
|
||||
allow_unregistered_users(false),
|
||||
allow_dc_pc_games(false),
|
||||
allow_gc_xb_games(true),
|
||||
item_tracking_enabled(true),
|
||||
enable_drops_behavior(BehaviorSwitch::ON_BY_DEFAULT),
|
||||
use_server_item_tables_behavior(BehaviorSwitch::OFF_BY_DEFAULT),
|
||||
allowed_drop_modes_v1_v2(0xFF),
|
||||
allowed_drop_modes_v3(0xFF),
|
||||
allowed_drop_modes_v4(0xFD), // CLIENT not allowed
|
||||
default_drop_mode_v1_v2(Lobby::DropMode::CLIENT),
|
||||
default_drop_mode_v3(Lobby::DropMode::CLIENT),
|
||||
default_drop_mode_v4(Lobby::DropMode::SERVER_SHARED),
|
||||
persistent_game_idle_timeout_usecs(0),
|
||||
ep3_send_function_call_enabled(false),
|
||||
catch_handler_exceptions(true),
|
||||
@@ -628,9 +631,18 @@ void ServerState::parse_config(const JSON& json, bool is_reload) {
|
||||
|
||||
this->ip_stack_debug = json.get_bool("IPStackDebug", this->ip_stack_debug);
|
||||
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->enable_drops_behavior = parse_behavior_switch("ItemDropMode", this->enable_drops_behavior);
|
||||
this->use_server_item_tables_behavior = parse_behavior_switch("UseServerItemTables", this->use_server_item_tables_behavior);
|
||||
this->allowed_drop_modes_v1_v2 = json.get_int("AllowedDropModesV1V2", this->allowed_drop_modes_v1_v2);
|
||||
this->allowed_drop_modes_v3 = json.get_int("AllowedDropModesV3", this->allowed_drop_modes_v3);
|
||||
this->allowed_drop_modes_v4 = json.get_int("AllowedDropModesV4", this->allowed_drop_modes_v4);
|
||||
this->default_drop_mode_v1_v2 = json.get_enum("DefaultDropModeV1V2", this->default_drop_mode_v1_v2);
|
||||
this->default_drop_mode_v3 = json.get_enum("DefaultDropModeV3", this->default_drop_mode_v3);
|
||||
this->default_drop_mode_v4 = json.get_enum("DefaultDropModeV4", this->default_drop_mode_v4);
|
||||
if (this->default_drop_mode_v4 == Lobby::DropMode::CLIENT) {
|
||||
throw runtime_error("default V4 drop mode cannot be CLIENT");
|
||||
}
|
||||
if (this->allowed_drop_modes_v4 & (1 << static_cast<size_t>(Lobby::DropMode::CLIENT))) {
|
||||
throw runtime_error("CLIENT drop mode cannot be allowed in V4");
|
||||
}
|
||||
this->persistent_game_idle_timeout_usecs = json.get_int("PersistentGameIdleTimeout", this->persistent_game_idle_timeout_usecs);
|
||||
this->cheat_mode_behavior = parse_behavior_switch("CheatModeBehavior", this->cheat_mode_behavior);
|
||||
this->ep3_send_function_call_enabled = json.get_bool("EnableEpisode3SendFunctionCall", this->ep3_send_function_call_enabled);
|
||||
|
||||
+6
-3
@@ -75,9 +75,12 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
bool allow_unregistered_users;
|
||||
bool allow_dc_pc_games;
|
||||
bool allow_gc_xb_games;
|
||||
bool item_tracking_enabled;
|
||||
BehaviorSwitch enable_drops_behavior;
|
||||
BehaviorSwitch use_server_item_tables_behavior;
|
||||
uint8_t allowed_drop_modes_v1_v2;
|
||||
uint8_t allowed_drop_modes_v3;
|
||||
uint8_t allowed_drop_modes_v4;
|
||||
Lobby::DropMode default_drop_mode_v1_v2;
|
||||
Lobby::DropMode default_drop_mode_v3;
|
||||
Lobby::DropMode default_drop_mode_v4;
|
||||
uint64_t persistent_game_idle_timeout_usecs;
|
||||
bool ep3_send_function_call_enabled;
|
||||
bool catch_handler_exceptions;
|
||||
|
||||
Reference in New Issue
Block a user