implement rare item pickup notifications
This commit is contained in:
@@ -1417,7 +1417,7 @@ struct S_LeaveLobby_66_69_Ep3_E9 {
|
||||
// 7E: Invalid command
|
||||
// 7F: Invalid command
|
||||
|
||||
// 80: Valid but ignored (all versions)
|
||||
// 80: Valid but ignored (all versions except BB)
|
||||
// Internal names: RcvGenerateID and SndGenerateID
|
||||
// This command appears to be used to set the next item ID for the given player
|
||||
// slot. PSO V3 and later accept this command, but ignore it entirely. Notably,
|
||||
|
||||
+1
-1
@@ -585,7 +585,7 @@ JSON HTTPServer::generate_lobby_json_st(shared_ptr<const Lobby> l, shared_ptr<co
|
||||
{"LocationX", item->x},
|
||||
{"LocationZ", item->z},
|
||||
{"DropNumber", item->drop_number},
|
||||
{"VisibilityFlags", item->visibility_flags},
|
||||
{"Flags", item->flags},
|
||||
{"Data", item->data.hex()},
|
||||
{"ItemID", item->data.id.load()},
|
||||
});
|
||||
|
||||
+12
-12
@@ -12,7 +12,7 @@
|
||||
using namespace std;
|
||||
|
||||
bool Lobby::FloorItem::visible_to_client(uint8_t client_id) const {
|
||||
return this->visibility_flags & (1 << client_id);
|
||||
return this->flags & (1 << client_id);
|
||||
}
|
||||
|
||||
Lobby::FloorItemManager::FloorItemManager(uint32_t lobby_id, uint8_t floor)
|
||||
@@ -27,18 +27,18 @@ shared_ptr<Lobby::FloorItem> Lobby::FloorItemManager::find(uint32_t item_id) con
|
||||
return this->items.at(item_id);
|
||||
}
|
||||
|
||||
void Lobby::FloorItemManager::add(const ItemData& item, float x, float z, uint16_t visibility_flags) {
|
||||
void Lobby::FloorItemManager::add(const ItemData& item, float x, float z, uint16_t 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;
|
||||
fi->flags = flags;
|
||||
this->add(fi);
|
||||
}
|
||||
|
||||
void Lobby::FloorItemManager::add(shared_ptr<Lobby::FloorItem> fi) {
|
||||
if (fi->visibility_flags == 0) {
|
||||
if (fi->flags == 0) {
|
||||
throw logic_error("floor item is not visible to any player");
|
||||
}
|
||||
|
||||
@@ -51,8 +51,8 @@ void Lobby::FloorItemManager::add(shared_ptr<Lobby::FloorItem> fi) {
|
||||
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);
|
||||
this->log.info("Added floor item %08" PRIX32 " at %g, %g with drop number %" PRIu64 " with flags %03hX",
|
||||
fi->data.id.load(), fi->x, fi->z, fi->drop_number, fi->flags);
|
||||
}
|
||||
|
||||
std::shared_ptr<Lobby::FloorItem> Lobby::FloorItemManager::remove(uint32_t item_id, uint8_t client_id) {
|
||||
@@ -70,8 +70,8 @@ std::shared_ptr<Lobby::FloorItem> Lobby::FloorItemManager::remove(uint32_t item_
|
||||
}
|
||||
}
|
||||
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);
|
||||
this->log.info("Removed floor item %08" PRIX32 " at %g, %g with drop number %" PRIu64 " with flags %03hX",
|
||||
fi->data.id.load(), fi->x, fi->z, fi->drop_number, fi->flags);
|
||||
return fi;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ std::unordered_set<std::shared_ptr<Lobby::FloorItem>> Lobby::FloorItemManager::e
|
||||
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) {
|
||||
if ((it.second->flags & remaining_clients_mask) == 0) {
|
||||
item_ids_to_delete.emplace(it.first);
|
||||
}
|
||||
}
|
||||
@@ -102,7 +102,7 @@ void Lobby::FloorItemManager::clear_inaccessible(uint16_t remaining_clients_mask
|
||||
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) {
|
||||
if ((it.second->flags & 0x00F) != 0x00F) {
|
||||
item_ids_to_delete.emplace(it.first);
|
||||
}
|
||||
}
|
||||
@@ -843,9 +843,9 @@ shared_ptr<Lobby::FloorItem> Lobby::find_item(uint8_t floor, uint32_t item_id) c
|
||||
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) {
|
||||
void Lobby::add_item(uint8_t floor, const ItemData& data, float x, float z, uint16_t flags) {
|
||||
auto& m = this->floor_item_managers.at(floor);
|
||||
m.add(data, x, z, visibility_flags);
|
||||
m.add(data, x, z, flags);
|
||||
this->evict_items_from_floor(floor);
|
||||
}
|
||||
|
||||
|
||||
+9
-3
@@ -29,7 +29,13 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
float x;
|
||||
float z;
|
||||
uint64_t drop_number;
|
||||
uint16_t visibility_flags;
|
||||
// The low 12 bits of flags are visibility flags, specifying which clients
|
||||
// can see the item. (In practice, only the lowest 4 of these bits are used,
|
||||
// but the game has fields for 12 players so we do too.)
|
||||
// The 13th bit (0x1000) specifies whether a rare item notification should
|
||||
// be sent to all players when the item is picked up. This has no effect for
|
||||
// non-rare items.
|
||||
uint16_t flags;
|
||||
|
||||
bool visible_to_client(uint8_t client_id) const;
|
||||
};
|
||||
@@ -46,7 +52,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
|
||||
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(const ItemData& item, float x, float z, uint16_t 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();
|
||||
@@ -290,7 +296,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
|
||||
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, const ItemData& item, float x, float z, uint16_t 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);
|
||||
|
||||
@@ -1896,7 +1896,7 @@ static void on_box_or_enemy_item_drop_t(shared_ptr<Client> c, uint8_t command, u
|
||||
ItemData item = cmd.item.item;
|
||||
item.decode_for_version(c->version());
|
||||
l->on_item_id_generated_externally(item.id);
|
||||
l->add_item(cmd.item.floor, item, cmd.item.x, cmd.item.z, 0x00F);
|
||||
l->add_item(cmd.item.floor, item, cmd.item.x, cmd.item.z, 0x100F);
|
||||
|
||||
auto name = s->describe_item(c->version(), item, false);
|
||||
l->log.info("Player %hhu (leader) created floor item %08" PRIX32 " (%s) at %hhu:(%g, %g)",
|
||||
@@ -1993,6 +1993,47 @@ static void on_pick_up_item_generic(
|
||||
send_create_inventory_item_to_client(lc, client_id, fi->data);
|
||||
}
|
||||
}
|
||||
|
||||
if ((fi->flags & 0x1000) && (fi->data.data1[0] < 0x04)) {
|
||||
auto pmt = s->item_parameter_table(c->version());
|
||||
bool should_send_game_notif = false;
|
||||
bool should_send_global_notif = false;
|
||||
switch (fi->data.data1[0]) {
|
||||
case 0x00:
|
||||
case 0x01: {
|
||||
uint8_t stars = pmt->get_item_adjusted_stars(fi->data);
|
||||
should_send_game_notif = (stars >= s->game_rare_notif_min_stars);
|
||||
should_send_global_notif = (stars >= s->global_rare_notif_min_stars);
|
||||
break;
|
||||
}
|
||||
case 0x02: {
|
||||
should_send_game_notif = s->game_rare_mag_notifs_enabled && pmt->is_item_rare(fi->data);
|
||||
break;
|
||||
}
|
||||
case 0x03: {
|
||||
should_send_game_notif = s->game_rare_tool_notifs_enabled && pmt->is_item_rare(fi->data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (should_send_game_notif || should_send_global_notif) {
|
||||
string p_name = p->disp.name.decode();
|
||||
string desc = s->describe_item(c->version(), fi->data, true);
|
||||
string message = string_printf("$C6%s$C7 has found\n%s", p_name.c_str(), desc.c_str());
|
||||
if (should_send_global_notif) {
|
||||
for (auto& it : s->channel_to_client) {
|
||||
if (it.second->license &&
|
||||
!is_patch(it.second->version()) &&
|
||||
!is_ep3(it.second->version()) &&
|
||||
it.second->lobby.lock()) {
|
||||
send_text_message(it.second, message);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
send_text_message(l, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2605,7 +2646,7 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
|
||||
res.item.id = l->generate_item_id(0xFF);
|
||||
l->log.info("Creating item %08" PRIX32 " at %02hhX:%g,%g for %s",
|
||||
res.item.id.load(), cmd.floor, cmd.x.load(), cmd.z.load(), lc->channel.name.c_str());
|
||||
l->add_item(cmd.floor, res.item, cmd.x, cmd.z, (1 << lc->lobby_client_id));
|
||||
l->add_item(cmd.floor, res.item, cmd.x, cmd.z, 0x1000 | (1 << lc->lobby_client_id));
|
||||
send_drop_item_to_channel(s, lc->channel, res.item, !rec.is_box, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
|
||||
send_item_notification_if_needed(s, lc->channel, lc->config, res.item, res.is_from_rare_table);
|
||||
}
|
||||
@@ -2615,7 +2656,7 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
|
||||
res.item.id = l->generate_item_id(0xFF);
|
||||
l->log.info("Creating item %08" PRIX32 " at %02hhX:%g,%g for all clients",
|
||||
res.item.id.load(), cmd.floor, cmd.x.load(), cmd.z.load());
|
||||
l->add_item(cmd.floor, res.item, cmd.x, cmd.z, 0x00F);
|
||||
l->add_item(cmd.floor, res.item, cmd.x, cmd.z, 0x100F);
|
||||
send_drop_item_to_lobby(l, res.item, !rec.is_box, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
|
||||
for (auto lc : l->clients) {
|
||||
if (lc) {
|
||||
@@ -2638,7 +2679,7 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
|
||||
res.item.id = l->generate_item_id(0xFF);
|
||||
l->log.info("Creating item %08" PRIX32 " at %02hhX:%g,%g for %s",
|
||||
res.item.id.load(), cmd.floor, cmd.x.load(), cmd.z.load(), lc->channel.name.c_str());
|
||||
l->add_item(cmd.floor, res.item, cmd.x, cmd.z, (1 << lc->lobby_client_id));
|
||||
l->add_item(cmd.floor, res.item, cmd.x, cmd.z, 0x1000 | (1 << lc->lobby_client_id));
|
||||
send_drop_item_to_channel(s, lc->channel, res.item, !rec.is_box, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
|
||||
send_item_notification_if_needed(s, lc->channel, lc->config, res.item, res.is_from_rare_table);
|
||||
}
|
||||
@@ -3703,7 +3744,7 @@ static void on_request_challenge_grave_recovery_item_bb(shared_ptr<Client> c, ui
|
||||
};
|
||||
ItemData item = items.at(cmd.item_type);
|
||||
item.id = l->generate_item_id(cmd.header.client_id);
|
||||
l->add_item(cmd.floor, item, cmd.x, cmd.z, 0x00F);
|
||||
l->add_item(cmd.floor, item, cmd.x, cmd.z, 0x100F);
|
||||
send_drop_stacked_item_to_lobby(l, item, cmd.floor, cmd.x, cmd.z);
|
||||
}
|
||||
}
|
||||
@@ -4072,7 +4113,7 @@ static void on_quest_F95E_result_bb(shared_ptr<Client> c, uint8_t, uint8_t, void
|
||||
}
|
||||
|
||||
item.id = l->generate_item_id(0xFF);
|
||||
l->add_item(cmd.floor, item, cmd.x, cmd.z, 0x00F);
|
||||
l->add_item(cmd.floor, item, cmd.x, cmd.z, 0x100F);
|
||||
|
||||
send_drop_stacked_item_to_lobby(l, item, cmd.floor, cmd.x, cmd.z);
|
||||
}
|
||||
|
||||
@@ -733,6 +733,10 @@ void ServerState::load_config_early() {
|
||||
this->default_rare_notifs_enabled_v3_v4 = this->default_rare_notifs_enabled_v1_v2;
|
||||
this->default_rare_notifs_enabled_v1_v2 = this->config_json->get_bool("RareNotificationsEnabledByDefaultV1V2", this->default_rare_notifs_enabled_v1_v2);
|
||||
this->default_rare_notifs_enabled_v3_v4 = this->config_json->get_bool("RareNotificationsEnabledByDefaultV3V4", this->default_rare_notifs_enabled_v3_v4);
|
||||
this->game_rare_notif_min_stars = this->config_json->get_int("GameRareNotifMinStars", this->game_rare_notif_min_stars);
|
||||
this->global_rare_notif_min_stars = this->config_json->get_int("GlobalRareNotifMinStars", this->global_rare_notif_min_stars);
|
||||
this->game_rare_mag_notifs_enabled = this->config_json->get_bool("GameRareMagNotifsEnabled", this->game_rare_mag_notifs_enabled);
|
||||
this->game_rare_tool_notifs_enabled = this->config_json->get_bool("GameRareToolNotifsEnabled", this->game_rare_tool_notifs_enabled);
|
||||
this->ep3_send_function_call_enabled = this->config_json->get_bool("EnableEpisode3SendFunctionCall", false);
|
||||
this->enable_v3_v4_protected_subcommands = this->config_json->get_bool("EnableV3V4ProtectedSubcommands", false);
|
||||
this->catch_handler_exceptions = this->config_json->get_bool("CatchHandlerExceptions", true);
|
||||
|
||||
@@ -128,6 +128,10 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
bool use_game_creator_section_id = false;
|
||||
bool default_rare_notifs_enabled_v1_v2 = false;
|
||||
bool default_rare_notifs_enabled_v3_v4 = false;
|
||||
uint8_t game_rare_notif_min_stars = 0xFF;
|
||||
uint8_t global_rare_notif_min_stars = 0xFF;
|
||||
bool game_rare_mag_notifs_enabled = false;
|
||||
bool game_rare_tool_notifs_enabled = false;
|
||||
std::vector<std::shared_ptr<const PSOBBEncryption::KeyFile>> bb_private_keys;
|
||||
std::shared_ptr<const FunctionCodeIndex> function_code_index;
|
||||
std::shared_ptr<const PatchFileIndex> pc_patch_file_index;
|
||||
|
||||
@@ -969,6 +969,26 @@
|
||||
"RareNotificationsEnabledByDefaultV1V2": false,
|
||||
"RareNotificationsEnabledByDefaultV3V4": false,
|
||||
|
||||
// Conditions for when to broadcast rare notifications. These only apply to
|
||||
// items dropped by boxes and enemies; items dropped by players or created
|
||||
// with the $item command do not cause notifications when picked up.
|
||||
|
||||
// If a weapon, armor, shield, or unit with at least this many stars is
|
||||
// picked up, everyone in the game is notified. To disable these
|
||||
// notifications, set this to 255.
|
||||
"GameRareNotifMinStars": 10,
|
||||
// If a weapon, armor, shield, or unit with at least this many stars is
|
||||
// picked up, everyone on the server (except Episode 3 players) is notified.
|
||||
// To disable these notifications, set this to 255.
|
||||
"GlobalRareNotifMinStars": 12,
|
||||
// If this option is on and a rare mag is picked up, everyone in the game is
|
||||
// notified. Note that there are no rare mags in any of the default drop
|
||||
// tables, so this has no effect unless you add rare mags to the drop tables.
|
||||
"GameRareMagNotifsEnabled": true,
|
||||
// If this option is on and a rare tool item (for example, a Photon Drop) is
|
||||
// picked up, everyone in the game is notified.
|
||||
"GameRareToolNotifsEnabled": true,
|
||||
|
||||
// Whether to enable patches on Episode 3 USA. This functionality depends on
|
||||
// exploiting a bug in Episode 3, and while it seems to work reliably on
|
||||
// Dolphin, it hasn't been tested on a real GameCube. So, newserv doesn't
|
||||
|
||||
@@ -32,6 +32,10 @@
|
||||
"CheatModeBehavior": "OnByDefault",
|
||||
"UnlockAllAreas": false,
|
||||
"RareNotificationsEnabledByDefault": false,
|
||||
"GameRareNotifMinStars": 10,
|
||||
"GlobalRareNotifMinStars": 12,
|
||||
"GameRareMagNotifsEnabled": true,
|
||||
"GameRareToolNotifsEnabled": true,
|
||||
|
||||
"LocalAddress": "en0",
|
||||
"ExternalAddress": "en0",
|
||||
|
||||
Reference in New Issue
Block a user