The creature-objective count offset was applied to the log slot instead of
the counter index, so looting quest items wrote the running item count into
whatever quest sat N slots below the collecting quest in the log (N = the
collecting quest's creature objective count). Item counters are packed after
the creature counters within the same quest slot, so the offset belongs on
the counter index.
The corruption is display only - the real counters and character_queststatus
are unaffected, and a relog clears it.
Observed live: players showed "Captain Vachon slain: 8/1" on quest 371
without ever fighting him. The 8 was their Embalming Ichor count from The
Graverobbers (quest 358: 2 creature objectives + 8x item 2834) sitting two
slots above in the quest log.
Also bound the counter index: a quest with 4 combined objectives would
shift the 6-bit counter into the state byte.
Introduced in 4f067300b (#2459).
ReadableBuffer stores its payload in a union of shared_ptrs with manual
lifetime management. The destructor correctly calls Destruct(), but none of
the three assignment operators released the pointer they were already
holding before overwriting the union:
- operator=(std::nullptr_t) only set m_type to Unset, so the held
shared_ptr was never destroyed and its refcount never reached zero.
- The copy and move assignment operators placement-new over the live
shared_ptr.
AsyncSocket assigns m_writeSrc = nullptr when a write completes, so every
write that went through m_writeSrc stranded one shared_ptr<ByteBuffer>
holding that connection send buffer.
Also adds the missing self-assignment check to move assignment, which
becomes necessary once Destruct() is called: a self-move would otherwise
free the payload and then placement-new from the freed object.
Windows is affected far more severely than Linux, because
AsyncSocket_posix::Write() sends inline and returns early without ever
touching m_writeSrc when the whole buffer fits into the kernel socket
buffer, while the IOCP implementation always stores it.
SMSG_LIST_INVENTORY numbers only the rows that survive class, race,
reputation, and condition filtering, but BuyItemFromVendor still resolves
and reports raw backing-list indices. A hidden earlier row therefore
makes SMSG_BUY_ITEM name the wrong compact slot, so the client
decrements a different item's limited-stock display; hidden rows also
remain directly purchasable. Introduce one visibility helper used by
both the list and buy paths, reject purchases of filtered rows, and
report the compact visible slot in the purchase response.
Fixes#2591.
The 1.12 client's auction list handlers add the received time-left dword to
the current 32-bit tick and Script_GetAuctionItemTimeLeft later subtracts
with signed 32-bit arithmetic. Auction durations extended past that half
range (e.g. via Rate.Auction.Time multipliers) wrap a valid future auction
into the client's expired path. Clamp the interval to the signed range and
floor it at zero so an auction expiring while its list packet is built
cannot underflow.
Script_SetLootMethod resolves the requested master to the active player
or a party/raid roster member before sending the packet, so an arbitrary
GUID from a modified client must not become durable group loot state.
Require roster membership for master loot and canonicalize the stored
looter GUID to zero for every other method, matching what the stock
client sends.
Script_RandomRoll in client 1.12.1.5875 accepts every range through
1,000,000 and sends both uint32 bounds in MSG_RANDOM_ROLL. The handler's
older 10,000 ceiling silently drops otherwise-valid stock requests.
Multiple raiders could hold Nefarius's Corruption during the same timed
run in vanilla, while Nefarian still yielded only one Red Scepter Shard.
The instance already designates the first accepter as its Scepter
Champion and conditions the sole shard drop on that GUID, so later
acceptances while Vaelastrasz still offers the quest in the pre-start
SPECIAL state no longer insta-fail; the shard owner stays stable.
Relates #2998.