fix externally-generated item IDs in item tracking code

This commit is contained in:
Martin Michelsen
2023-10-25 16:55:27 -07:00
parent 5f0a6f3d8e
commit 6466eec106
4 changed files with 61 additions and 49 deletions
+14
View File
@@ -332,6 +332,20 @@ uint32_t Lobby::generate_item_id(uint8_t client_id) {
return this->next_game_item_id++;
}
void Lobby::on_item_id_generated_externally(uint8_t client_id, uint32_t item_id) {
if (this->base_version == GameVersion::BB) {
throw logic_error("BB games cannot have externally-generated item IDs");
}
if ((item_id > 0x00010000) && (item_id < 0x02010000)) {
uint16_t item_client_id = (item_id >> 21) & 0x7FF;
if (item_client_id != client_id) {
throw runtime_error("externally-generated item ID does not match expected client ID");
}
uint32_t& next_item_id = this->next_item_id.at(client_id);
next_item_id = std::max<uint32_t>(next_item_id, item_id + 1);
}
}
unordered_map<uint32_t, shared_ptr<Client>> Lobby::clients_by_serial_number() const {
unordered_map<uint32_t, shared_ptr<Client>> ret;
for (auto c : this->clients) {
+1
View File
@@ -161,6 +161,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
ItemData remove_item(uint32_t item_id);
size_t find_item(uint32_t item_id);
uint32_t generate_item_id(uint8_t client_id);
void on_item_id_generated_externally(uint8_t client_id, uint32_t item_id);
static uint8_t game_event_for_lobby_event(uint8_t lobby_event);
+1 -1
View File
@@ -485,6 +485,6 @@ void SavedPlayerDataBB::print_inventory(FILE* stream) const {
const auto& item = this->inventory.items[x];
auto name = item.data.name(false);
auto hex = item.data.hex();
fprintf(stream, "[PlayerInventory] %zu: %s (%s)\n", x, hex.c_str(), name.c_str());
fprintf(stream, "[PlayerInventory] %2zu: %s (%s)\n", x, hex.c_str(), name.c_str());
}
}
+45 -48
View File
@@ -222,7 +222,7 @@ static void on_sync_joining_player_item_state(shared_ptr<Client> c, uint8_t comm
// NOTE: If we use this codepath for non-V3 in the future, we'll need to
// change this hardcoded version. This only works because GC's mag
// encoding/decoding is symmetric (encode and decode do the same thing).
floor_items[z].item_data.decode_if_mag(GameVersion::GC);
floor_items[z].item.decode_if_mag(GameVersion::GC);
}
string out_compressed_data = bc0_compress(decompressed);
@@ -689,8 +689,7 @@ static void on_player_drop_item(shared_ptr<Client> c, uint8_t command, uint8_t f
auto name = item.name(false);
l->log.info("Player %hu dropped item %08" PRIX32 " (%s) at %hu:(%g, %g)",
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str(),
cmd.area.load(), cmd.x.load(), cmd.z.load());
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str(), cmd.area.load(), cmd.x.load(), cmd.z.load());
if (c->options.debug) {
string name = item.name(true);
send_text_message_printf(c, "$C5DROP %08" PRIX32 "\n%s",
@@ -740,18 +739,16 @@ static void on_create_inventory_item_t(shared_ptr<Client> c, uint8_t command, ui
auto l = c->require_lobby();
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
auto p = c->game_data.player();
{
ItemData item = cmd.item_data;
item.decode_if_mag(c->version());
p->add_item(item);
}
ItemData item = cmd.item_data;
item.decode_if_mag(c->version());
l->on_item_id_generated_externally(c->lobby_client_id, item.id);
p->add_item(item);
auto name = cmd.item_data.name(false);
l->log.info("Player %hu created inventory item %08" PRIX32 " (%s)",
cmd.header.client_id.load(), cmd.item_data.id.load(), name.c_str());
auto name = item.name(false);
l->log.info("Player %hu created inventory item %08" PRIX32 " (%s)", c->lobby_client_id, item.id.load(), name.c_str());
if (c->options.debug) {
string name = cmd.item_data.name(true);
send_text_message_printf(c, "$C5CREATE %08" PRIX32 "\n%s", cmd.item_data.id.load(), name.c_str());
string name = item.name(true);
send_text_message_printf(c, "$C5CREATE %08" PRIX32 "\n%s", item.id.load(), name.c_str());
}
p->print_inventory(stderr);
}
@@ -785,19 +782,18 @@ static void on_drop_partial_stack_t(shared_ptr<Client> c, uint8_t command, uint8
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
// TODO: Should we delete anything from the inventory here? Does the client
// send an appropriate 6x29 alongside this?
{
ItemData item = cmd.item_data;
item.decode_if_mag(c->version());
l->add_item(item, cmd.area, cmd.x, cmd.z);
}
ItemData item = cmd.item_data;
item.decode_if_mag(c->version());
l->on_item_id_generated_externally(c->lobby_client_id, item.id);
l->add_item(item, cmd.area, cmd.x, cmd.z);
auto name = cmd.item_data.name(false);
l->log.info("Player %hu split stack to create ground item %08" PRIX32 " (%s) at %hu:(%g, %g)",
cmd.header.client_id.load(), cmd.item_data.id.load(), name.c_str(),
auto name = item.name(false);
l->log.info("Player %hu split stack to create floor item %08" PRIX32 " (%s) at %hu:(%g, %g)",
cmd.header.client_id.load(), item.id.load(), name.c_str(),
cmd.area.load(), cmd.x.load(), cmd.z.load());
if (c->options.debug) {
string name = cmd.item_data.name(true);
send_text_message_printf(c, "$C5SPLIT %08" PRIX32 "\n%s", cmd.item_data.id.load(), name.c_str());
string name = item.name(true);
send_text_message_printf(c, "$C5SPLIT %08" PRIX32 "\n%s", item.id.load(), name.c_str());
}
c->game_data.player()->print_inventory(stderr);
}
@@ -831,8 +827,9 @@ static void on_drop_partial_stack_bb(shared_ptr<Client> c, uint8_t command, uint
auto p = c->game_data.player();
auto item = p->remove_item(cmd.item_id, cmd.amount, c->version() != GameVersion::BB);
// if a stack was split, the original item still exists, so the dropped item
// needs a new ID. remove_item signals this by returning an item with id=-1
// If a stack was split, the original item still exists, so the dropped item
// needs a new ID. remove_item signals this by returning an item with an ID
// of 0xFFFFFFFF.
if (item.id == 0xFFFFFFFF) {
item.id = l->generate_item_id(c->lobby_client_id);
}
@@ -876,19 +873,18 @@ static void on_buy_shop_item(shared_ptr<Client> c, uint8_t command, uint8_t flag
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
auto p = c->game_data.player();
{
ItemData item = cmd.item_data;
item.decode_if_mag(c->version());
p->add_item(item);
}
ItemData item = cmd.item_data;
item.decode_if_mag(c->version());
l->on_item_id_generated_externally(c->lobby_client_id, item.id);
p->add_item(item);
size_t price = s->item_parameter_table->price_for_item(cmd.item_data);
auto name = cmd.item_data.name(false);
size_t price = s->item_parameter_table->price_for_item(item);
auto name = item.name(false);
l->log.info("Player %hu bought item %08" PRIX32 " (%s) from shop (%zu Meseta)",
cmd.header.client_id.load(), cmd.item_data.id.load(), name.c_str(), price);
cmd.header.client_id.load(), item.id.load(), name.c_str(), price);
if (c->options.debug) {
string name = cmd.item_data.name(true);
send_text_message_printf(c, "$C5BUY %08" PRIX32 "\n%s", cmd.item_data.id.load(), name.c_str());
string name = item.name(true);
send_text_message_printf(c, "$C5BUY %08" PRIX32 "\n%s", item.id.load(), name.c_str());
}
p->remove_meseta(price, c->version() != GameVersion::BB);
p->print_inventory(stderr);
@@ -918,11 +914,12 @@ static void on_box_or_enemy_item_drop_t(shared_ptr<Client> c, uint8_t command, u
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
ItemData item = cmd.item.item;
item.decode_if_mag(c->version());
l->on_item_id_generated_externally(c->lobby_client_id, item.id);
l->add_item(item, cmd.item.area, cmd.item.x, cmd.item.z);
auto name = item.name(false);
l->log.info("Leader created ground item %08" PRIX32 " (%s) at %hhu:(%g, %g)",
item.id.load(), name.c_str(), cmd.item.area, cmd.item.x.load(), cmd.item.z.load());
l->log.info("Player %hhu (leader) created floor item %08" PRIX32 " (%s) at %hhu:(%g, %g)",
l->leader_id, item.id.load(), name.c_str(), cmd.item.area, cmd.item.x.load(), cmd.item.z.load());
if (c->options.debug) {
string name = item.name(true);
send_text_message_printf(c, "$C5DROP %08" PRIX32 "\n%s", item.id.load(), name.c_str());
@@ -1006,7 +1003,7 @@ static void on_pick_up_item_request(shared_ptr<Client> c, uint8_t command, uint8
p->add_item(item);
auto name = item.name(false);
l->log.info("Player %hu picked up %08" PRIX32 " (%s)",
l->log.info("Player %hu picked up (BB) %08" PRIX32 " (%s)",
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
if (c->options.debug) {
string name = item.name(true);
@@ -1070,8 +1067,8 @@ static void on_use_item(
}
player_use_item(c, index);
l->log.info("Player used item %hu:%08" PRIX32 " (%s)",
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
l->log.info("Player %hhu used item %hu:%08" PRIX32 " (%s)",
c->lobby_client_id, cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
if (c->options.debug) {
send_text_message_printf(c, "$C5USE %08" PRIX32 "\n%s",
cmd.item_id.load(), colored_name.c_str());
@@ -1120,8 +1117,8 @@ static void on_feed_mag(
p->remove_item(cmd.fed_item_id, 1, false);
}
l->log.info("Player fed item %hu:%08" PRIX32 " (%s) to mag %hu:%08" PRIX32 " (%s)",
cmd.header.client_id.load(), cmd.fed_item_id.load(), fed_name.c_str(),
l->log.info("Player %hhu fed item %hu:%08" PRIX32 " (%s) to mag %hu:%08" PRIX32 " (%s)",
c->lobby_client_id, cmd.header.client_id.load(), cmd.fed_item_id.load(), fed_name.c_str(),
cmd.header.client_id.load(), cmd.mag_item_id.load(), mag_name.c_str());
if (c->options.debug) {
send_text_message_printf(c, "$C5FEED %08" PRIX32 "\n%s\n...TO %08" PRIX32 "\n%s",
@@ -1658,8 +1655,8 @@ static void on_destroy_inventory_item(shared_ptr<Client> c, uint8_t command, uin
auto p = c->game_data.player();
auto item = p->remove_item(cmd.item_id, cmd.amount, c->version() != GameVersion::BB);
auto name = item.name(false);
l->log.info("Inventory item %hu:%08" PRIX32 " destroyed (%s)",
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
l->log.info("Player %hhu destroyed inventory item %hu:%08" PRIX32 " (%s)",
c->lobby_client_id, cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
if (c->options.debug) {
string name = item.name(true);
send_text_message_printf(c, "$C5DESTROY %08" PRIX32 "\n%s",
@@ -1681,8 +1678,8 @@ static void on_destroy_ground_item(shared_ptr<Client> c, uint8_t command, uint8_
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
auto item = l->remove_item(cmd.item_id);
auto name = item.name(false);
l->log.info("Ground item %08" PRIX32 " destroyed (%s)", cmd.item_id.load(),
name.c_str());
l->log.info("Player %hhu destroyed floor item %08" PRIX32 " (%s)",
c->lobby_client_id, cmd.item_id.load(), name.c_str());
if (c->options.debug) {
string name = item.name(true);
send_text_message_printf(c, "$C5DESTROY/GND %08" PRIX32 "\n%s",
@@ -1763,7 +1760,7 @@ static void on_sell_item_at_shop_bb(shared_ptr<Client> c, uint8_t command, uint8
p->add_meseta(price);
auto name = item.name(false);
l->log.info("Inventory item %hu:%08" PRIX32 " (%s) destroyed via sale (%zu Meseta)",
l->log.info("Player %hhu sold inventory item %08" PRIX32 " (%s) for %zu Meseta",
c->lobby_client_id, cmd.item_id.load(), name.c_str(), price);
p->print_inventory(stderr);
if (c->options.debug) {
@@ -1802,7 +1799,7 @@ static void on_buy_shop_item_bb(shared_ptr<Client> c, uint8_t, uint8_t, const vo
send_create_inventory_item(c, item);
auto name = item.name(false);
l->log.info("Inventory item %hu:%08" PRIX32 " created via purchase (%s) for %zu meseta",
l->log.info("Player %hhu purchased item %08" PRIX32 " (%s) for %zu meseta",
c->lobby_client_id, cmd.inventory_item_id.load(), name.c_str(), price);
p->print_inventory(stderr);
if (c->options.debug) {