fix externally-generated item IDs in item tracking code
This commit is contained in:
@@ -332,6 +332,20 @@ uint32_t Lobby::generate_item_id(uint8_t client_id) {
|
|||||||
return this->next_game_item_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>> Lobby::clients_by_serial_number() const {
|
||||||
unordered_map<uint32_t, shared_ptr<Client>> ret;
|
unordered_map<uint32_t, shared_ptr<Client>> ret;
|
||||||
for (auto c : this->clients) {
|
for (auto c : this->clients) {
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
|||||||
ItemData remove_item(uint32_t item_id);
|
ItemData remove_item(uint32_t item_id);
|
||||||
size_t find_item(uint32_t item_id);
|
size_t find_item(uint32_t item_id);
|
||||||
uint32_t generate_item_id(uint8_t client_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);
|
static uint8_t game_event_for_lobby_event(uint8_t lobby_event);
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -485,6 +485,6 @@ void SavedPlayerDataBB::print_inventory(FILE* stream) const {
|
|||||||
const auto& item = this->inventory.items[x];
|
const auto& item = this->inventory.items[x];
|
||||||
auto name = item.data.name(false);
|
auto name = item.data.name(false);
|
||||||
auto hex = item.data.hex();
|
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
@@ -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
|
// 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
|
// change this hardcoded version. This only works because GC's mag
|
||||||
// encoding/decoding is symmetric (encode and decode do the same thing).
|
// 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);
|
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);
|
auto name = item.name(false);
|
||||||
l->log.info("Player %hu dropped item %08" PRIX32 " (%s) at %hu:(%g, %g)",
|
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.header.client_id.load(), cmd.item_id.load(), name.c_str(), cmd.area.load(), cmd.x.load(), cmd.z.load());
|
||||||
cmd.area.load(), cmd.x.load(), cmd.z.load());
|
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = item.name(true);
|
string name = item.name(true);
|
||||||
send_text_message_printf(c, "$C5DROP %08" PRIX32 "\n%s",
|
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();
|
auto l = c->require_lobby();
|
||||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||||
auto p = c->game_data.player();
|
auto p = c->game_data.player();
|
||||||
{
|
ItemData item = cmd.item_data;
|
||||||
ItemData item = cmd.item_data;
|
item.decode_if_mag(c->version());
|
||||||
item.decode_if_mag(c->version());
|
l->on_item_id_generated_externally(c->lobby_client_id, item.id);
|
||||||
p->add_item(item);
|
p->add_item(item);
|
||||||
}
|
|
||||||
|
|
||||||
auto name = cmd.item_data.name(false);
|
auto name = item.name(false);
|
||||||
l->log.info("Player %hu created inventory item %08" PRIX32 " (%s)",
|
l->log.info("Player %hu created inventory item %08" PRIX32 " (%s)", c->lobby_client_id, item.id.load(), name.c_str());
|
||||||
cmd.header.client_id.load(), cmd.item_data.id.load(), name.c_str());
|
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = cmd.item_data.name(true);
|
string name = item.name(true);
|
||||||
send_text_message_printf(c, "$C5CREATE %08" PRIX32 "\n%s", cmd.item_data.id.load(), name.c_str());
|
send_text_message_printf(c, "$C5CREATE %08" PRIX32 "\n%s", item.id.load(), name.c_str());
|
||||||
}
|
}
|
||||||
p->print_inventory(stderr);
|
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) {
|
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||||
// TODO: Should we delete anything from the inventory here? Does the client
|
// TODO: Should we delete anything from the inventory here? Does the client
|
||||||
// send an appropriate 6x29 alongside this?
|
// send an appropriate 6x29 alongside this?
|
||||||
{
|
ItemData item = cmd.item_data;
|
||||||
ItemData item = cmd.item_data;
|
item.decode_if_mag(c->version());
|
||||||
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);
|
l->add_item(item, cmd.area, cmd.x, cmd.z);
|
||||||
}
|
|
||||||
|
|
||||||
auto name = cmd.item_data.name(false);
|
auto name = item.name(false);
|
||||||
l->log.info("Player %hu split stack to create ground item %08" PRIX32 " (%s) at %hu:(%g, %g)",
|
l->log.info("Player %hu split stack to create floor item %08" PRIX32 " (%s) at %hu:(%g, %g)",
|
||||||
cmd.header.client_id.load(), cmd.item_data.id.load(), name.c_str(),
|
cmd.header.client_id.load(), item.id.load(), name.c_str(),
|
||||||
cmd.area.load(), cmd.x.load(), cmd.z.load());
|
cmd.area.load(), cmd.x.load(), cmd.z.load());
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = cmd.item_data.name(true);
|
string name = item.name(true);
|
||||||
send_text_message_printf(c, "$C5SPLIT %08" PRIX32 "\n%s", cmd.item_data.id.load(), name.c_str());
|
send_text_message_printf(c, "$C5SPLIT %08" PRIX32 "\n%s", item.id.load(), name.c_str());
|
||||||
}
|
}
|
||||||
c->game_data.player()->print_inventory(stderr);
|
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 p = c->game_data.player();
|
||||||
auto item = p->remove_item(cmd.item_id, cmd.amount, c->version() != GameVersion::BB);
|
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
|
// 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
|
// needs a new ID. remove_item signals this by returning an item with an ID
|
||||||
|
// of 0xFFFFFFFF.
|
||||||
if (item.id == 0xFFFFFFFF) {
|
if (item.id == 0xFFFFFFFF) {
|
||||||
item.id = l->generate_item_id(c->lobby_client_id);
|
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) {
|
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||||
auto p = c->game_data.player();
|
auto p = c->game_data.player();
|
||||||
{
|
ItemData item = cmd.item_data;
|
||||||
ItemData item = cmd.item_data;
|
item.decode_if_mag(c->version());
|
||||||
item.decode_if_mag(c->version());
|
l->on_item_id_generated_externally(c->lobby_client_id, item.id);
|
||||||
p->add_item(item);
|
p->add_item(item);
|
||||||
}
|
|
||||||
|
|
||||||
size_t price = s->item_parameter_table->price_for_item(cmd.item_data);
|
size_t price = s->item_parameter_table->price_for_item(item);
|
||||||
auto name = cmd.item_data.name(false);
|
auto name = item.name(false);
|
||||||
l->log.info("Player %hu bought item %08" PRIX32 " (%s) from shop (%zu Meseta)",
|
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) {
|
if (c->options.debug) {
|
||||||
string name = cmd.item_data.name(true);
|
string name = item.name(true);
|
||||||
send_text_message_printf(c, "$C5BUY %08" PRIX32 "\n%s", cmd.item_data.id.load(), name.c_str());
|
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->remove_meseta(price, c->version() != GameVersion::BB);
|
||||||
p->print_inventory(stderr);
|
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) {
|
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||||
ItemData item = cmd.item.item;
|
ItemData item = cmd.item.item;
|
||||||
item.decode_if_mag(c->version());
|
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);
|
l->add_item(item, cmd.item.area, cmd.item.x, cmd.item.z);
|
||||||
|
|
||||||
auto name = item.name(false);
|
auto name = item.name(false);
|
||||||
l->log.info("Leader created ground item %08" PRIX32 " (%s) at %hhu:(%g, %g)",
|
l->log.info("Player %hhu (leader) created floor 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->leader_id, item.id.load(), name.c_str(), cmd.item.area, cmd.item.x.load(), cmd.item.z.load());
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = item.name(true);
|
string name = item.name(true);
|
||||||
send_text_message_printf(c, "$C5DROP %08" PRIX32 "\n%s", item.id.load(), name.c_str());
|
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);
|
p->add_item(item);
|
||||||
|
|
||||||
auto name = item.name(false);
|
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());
|
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = item.name(true);
|
string name = item.name(true);
|
||||||
@@ -1070,8 +1067,8 @@ static void on_use_item(
|
|||||||
}
|
}
|
||||||
player_use_item(c, index);
|
player_use_item(c, index);
|
||||||
|
|
||||||
l->log.info("Player used item %hu:%08" PRIX32 " (%s)",
|
l->log.info("Player %hhu used item %hu:%08" PRIX32 " (%s)",
|
||||||
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
|
c->lobby_client_id, cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
send_text_message_printf(c, "$C5USE %08" PRIX32 "\n%s",
|
send_text_message_printf(c, "$C5USE %08" PRIX32 "\n%s",
|
||||||
cmd.item_id.load(), colored_name.c_str());
|
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);
|
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)",
|
l->log.info("Player %hhu 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(),
|
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());
|
cmd.header.client_id.load(), cmd.mag_item_id.load(), mag_name.c_str());
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
send_text_message_printf(c, "$C5FEED %08" PRIX32 "\n%s\n...TO %08" PRIX32 "\n%s",
|
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 p = c->game_data.player();
|
||||||
auto item = p->remove_item(cmd.item_id, cmd.amount, c->version() != GameVersion::BB);
|
auto item = p->remove_item(cmd.item_id, cmd.amount, c->version() != GameVersion::BB);
|
||||||
auto name = item.name(false);
|
auto name = item.name(false);
|
||||||
l->log.info("Inventory item %hu:%08" PRIX32 " destroyed (%s)",
|
l->log.info("Player %hhu destroyed inventory item %hu:%08" PRIX32 " (%s)",
|
||||||
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
|
c->lobby_client_id, cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = item.name(true);
|
string name = item.name(true);
|
||||||
send_text_message_printf(c, "$C5DESTROY %08" PRIX32 "\n%s",
|
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) {
|
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||||
auto item = l->remove_item(cmd.item_id);
|
auto item = l->remove_item(cmd.item_id);
|
||||||
auto name = item.name(false);
|
auto name = item.name(false);
|
||||||
l->log.info("Ground item %08" PRIX32 " destroyed (%s)", cmd.item_id.load(),
|
l->log.info("Player %hhu destroyed floor item %08" PRIX32 " (%s)",
|
||||||
name.c_str());
|
c->lobby_client_id, cmd.item_id.load(), name.c_str());
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = item.name(true);
|
string name = item.name(true);
|
||||||
send_text_message_printf(c, "$C5DESTROY/GND %08" PRIX32 "\n%s",
|
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);
|
p->add_meseta(price);
|
||||||
|
|
||||||
auto name = item.name(false);
|
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);
|
c->lobby_client_id, cmd.item_id.load(), name.c_str(), price);
|
||||||
p->print_inventory(stderr);
|
p->print_inventory(stderr);
|
||||||
if (c->options.debug) {
|
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);
|
send_create_inventory_item(c, item);
|
||||||
|
|
||||||
auto name = item.name(false);
|
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);
|
c->lobby_client_id, cmd.inventory_item_id.load(), name.c_str(), price);
|
||||||
p->print_inventory(stderr);
|
p->print_inventory(stderr);
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
|
|||||||
Reference in New Issue
Block a user