diff --git a/sql/updates/characters/4.3.4/custom_2019_08_20_00_characters.sql b/sql/updates/characters/4.3.4/custom_2019_08_20_00_characters.sql new file mode 100644 index 0000000000..b5b51dcf28 --- /dev/null +++ b/sql/updates/characters/4.3.4/custom_2019_08_20_00_characters.sql @@ -0,0 +1,6 @@ +ALTER TABLE `item_instance` +MODIFY COLUMN `flags` int(10) UNSIGNED NOT NULL DEFAULT 0 AFTER `charges`; + +ALTER TABLE `item_instance` +CHANGE COLUMN `playedTime` `creationTime` int(10) UNSIGNED NOT NULL DEFAULT 0 AFTER `durability`; +UPDATE `item_instance` SET `creationTime`= 0; diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index cb8f6ebf28..bce5f62782 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -100,7 +100,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_RESET_CHARACTER_REWARDSTATUS_LFG_DAILY, "DELETE FROM character_rewardstatus_lfg WHERE dailyReset = 1", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_REPUTATION, "SELECT faction, standing, flags FROM character_reputation WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_CHARACTER_INVENTORY, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, bag, slot, " + PrepareStatement(CHAR_SEL_CHARACTER_INVENTORY, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, creationTime, text, bag, slot, " "item, itemEntry FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid WHERE ci.guid = ? ORDER BY bag, slot", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS, "SELECT a.button, a.action, a.type FROM character_action as a, characters as c WHERE a.guid = c.guid AND a.spec = c.activeTalentGroup AND a.guid = ? ORDER BY button", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_MAILCOUNT, "SELECT COUNT(id) FROM mail WHERE receiver = ? AND (checked & 1) = 0 AND deliver_time <= ?", CONNECTION_ASYNC); @@ -128,8 +128,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_ACCOUNT_INSTANCELOCKTIMES, "SELECT instanceId, releaseTime FROM account_instance_times WHERE accountId = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS_SPEC, "SELECT button, action, type FROM character_action WHERE guid = ? AND spec = ? ORDER BY button", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_MAILITEMS, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, item_guid, itemEntry, owner_guid FROM mail_items mi JOIN item_instance ii ON mi.item_guid = ii.guid WHERE mail_id = ?", CONNECTION_SYNCH); - PrepareStatement(CHAR_SEL_AUCTION_ITEMS, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, itemguid, itemEntry FROM auctionhouse ah JOIN item_instance ii ON ah.itemguid = ii.guid", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_MAILITEMS, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, creationTime, text, item_guid, itemEntry, owner_guid FROM mail_items mi JOIN item_instance ii ON mi.item_guid = ii.guid WHERE mail_id = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_AUCTION_ITEMS, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, creationTime, text, itemguid, itemEntry FROM auctionhouse ah JOIN item_instance ii ON ah.itemguid = ii.guid", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_AUCTIONS, "SELECT id, houseid, itemguid, itemEntry, count, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit FROM auctionhouse ah INNER JOIN item_instance ii ON ii.guid = ah.itemguid", CONNECTION_SYNCH); PrepareStatement(CHAR_INS_AUCTION, "INSERT INTO auctionhouse (id, houseid, itemguid, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_AUCTION, "DELETE FROM auctionhouse WHERE id = ?", CONNECTION_ASYNC); @@ -154,8 +154,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_ITEM_BOP_TRADE, "DELETE FROM item_soulbound_trade_data WHERE itemGuid = ? LIMIT 1", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_ITEM_BOP_TRADE, "INSERT INTO item_soulbound_trade_data VALUES (?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_REP_INVENTORY_ITEM, "REPLACE INTO character_inventory (guid, bag, slot, item) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_REP_ITEM_INSTANCE, "REPLACE INTO item_instance (itemEntry, owner_guid, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, guid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_ITEM_INSTANCE, "UPDATE item_instance SET itemEntry = ?, owner_guid = ?, creatorGuid = ?, giftCreatorGuid = ?, count = ?, duration = ?, charges = ?, flags = ?, enchantments = ?, randomPropertyId = ?, durability = ?, playedTime = ?, text = ? WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_REP_ITEM_INSTANCE, "REPLACE INTO item_instance (itemEntry, owner_guid, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, creationTime, text, guid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_ITEM_INSTANCE, "UPDATE item_instance SET itemEntry = ?, owner_guid = ?, creatorGuid = ?, giftCreatorGuid = ?, count = ?, duration = ?, charges = ?, flags = ?, enchantments = ?, randomPropertyId = ?, durability = ?, creationTime = ?, text = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ITEM_INSTANCE_ON_LOAD, "UPDATE item_instance SET duration = ?, flags = ?, durability = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ITEM_INSTANCE, "DELETE FROM item_instance WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ITEM_INSTANCE_BY_OWNER, "DELETE FROM item_instance WHERE owner_guid = ?", CONNECTION_ASYNC); diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index 35bd3e2b0f..9437209eb2 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -261,7 +261,6 @@ Item::Item() m_container = nullptr; m_lootGenerated = false; mb_in_trade = false; - m_lastPlayedTimeUpdate = time(nullptr); m_refundRecipient = 0; m_paidMoney = 0; @@ -293,7 +292,7 @@ bool Item::Create(ObjectGuid::LowType guidlow, uint32 itemId, Player const* owne SetSpellCharges(i, itemProto->Spells[i].SpellCharges); SetUInt32Value(ITEM_FIELD_DURATION, itemProto->Duration); - SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, 0); + SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, owner ? owner->GetTotalPlayedTime() : 0); return true; } @@ -413,8 +412,8 @@ void Item::SaveToDB(SQLTransaction& trans) bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fields, uint32 entry) { - // 0 1 2 3 4 5 6 7 8 9 10 - //result = CharacterDatabase.PQuery("SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text FROM item_instance WHERE guid = '%u'", guid); + // 0 1 2 3 4 5 6 7 8 9 10 + //result = CharacterDatabase.PQuery("SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, creationTime, text FROM item_instance WHERE guid = '%u'", guid); // create item before any checks for store correct guid // and allow use "FSetState(ITEM_REMOVED); SaveToDB();" for deleting item from DB @@ -1093,42 +1092,23 @@ void Item::SetNotRefundable(Player* owner, bool changestate /*= true*/, SQLTrans void Item::UpdatePlayedTime(Player* owner) { - /* Here we update our played time - We simply add a number to the current played time, - based on the time elapsed since the last update hereof. - */ - // Get current played time - uint32 current_playtime = GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME); - // Calculate time elapsed since last played time update - time_t curtime = time(nullptr); - uint32 elapsed = uint32(curtime - m_lastPlayedTimeUpdate); - uint32 new_playtime = current_playtime + elapsed; // Check if the refund timer has expired yet - if (new_playtime <= 2*HOUR) - { - // No? Proceed. - // Update the data field - SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, new_playtime); - // Flag as changed to get saved to DB - SetState(ITEM_CHANGED, owner); - // Speaks for itself - m_lastPlayedTimeUpdate = curtime; + if (GetPlayedTime(owner) <= 2 * HOUR) return; - } - // Yes + + // Flag as changed to get saved to DB + SetState(ITEM_CHANGED, owner); SetNotRefundable(owner); } -uint32 Item::GetPlayedTime() +uint32 Item::GetPlayedTime(Player const* owner) const { - time_t curtime = time(nullptr); - uint32 elapsed = uint32(curtime - m_lastPlayedTimeUpdate); - return GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME) + elapsed; + return owner->GetTotalPlayedTime() - GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME); } -bool Item::IsRefundExpired() +bool Item::IsRefundExpired(Player const* owner) const { - return (GetPlayedTime() > 2*HOUR); + return (GetPlayedTime(owner) > 2 * HOUR); } void Item::SetSoulboundTradeable(GuidSet const& allowedLooters) diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index 796f35163e..4007f0b72a 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -189,8 +189,8 @@ class TC_GAME_API Item : public Object uint32 GetPaidExtendedCost() const { return m_paidExtendedCost; } void UpdatePlayedTime(Player* owner); - uint32 GetPlayedTime(); - bool IsRefundExpired(); + uint32 GetPlayedTime(Player const* owner) const; + bool IsRefundExpired(Player const* owner) const; // Soulbound trade system void SetSoulboundTradeable(GuidSet const& allowedLooters); @@ -228,7 +228,6 @@ class TC_GAME_API Item : public Object ItemUpdateState uState; int16 uQueuePos; bool mb_in_trade; // true if item is currently in trade-window - time_t m_lastPlayedTimeUpdate; ObjectGuid::LowType m_refundRecipient; uint32 m_paidMoney; uint32 m_paidExtendedCost; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index a64b344ac2..40328d0dbd 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -18664,10 +18664,10 @@ Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, F } else if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE)) { - if (item->GetPlayedTime() > (2 * HOUR)) + if (item->GetPlayedTime(this) > (2 * HOUR)) { TC_LOG_DEBUG("entities.player.loading", "Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) with expired refund time (%u). Deleting refund data and removing refundable flag.", - GetGUID().GetCounter(), GetName().c_str(), item->GetGUID().GetCounter(), item->GetEntry(), item->GetPlayedTime()); + GetGUID().GetCounter(), GetName().c_str(), item->GetGUID().GetCounter(), item->GetEntry(), item->GetPlayedTime(this)); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE); stmt->setUInt32(0, item->GetGUID().GetCounter()); @@ -27508,8 +27508,8 @@ void Player::SendRefundInfo(Item* item) data.FlushBits(); data.WriteByteSeq(guid[7]); - data << uint32(GetTotalPlayedTime() - item->GetPlayedTime()); - for (uint8 i = 0; i < MAX_ITEM_EXT_COST_ITEMS; ++i) // item cost data + data << uint32(2 * HOUR * IN_MILLISECONDS - item->GetPlayedTime(this) * IN_MILLISECONDS); // Refund Time Left + for (uint8 i = 0; i < MAX_ITEM_EXT_COST_ITEMS; ++i) // item cost data { data << uint32(iece->RequiredItemCount[i]); data << uint32(iece->RequiredItem[i]); @@ -27629,7 +27629,7 @@ void Player::RefundItem(Item* item) return; } - if (item->IsRefundExpired()) // item refund has expired + if (item->IsRefundExpired(this)) // item refund has expired { item->SetNotRefundable(this); SendItemRefundResult(item, nullptr, 10); diff --git a/src/server/game/Guilds/GuildMgr.cpp b/src/server/game/Guilds/GuildMgr.cpp index faa72db5d0..48296a3081 100644 --- a/src/server/game/Guilds/GuildMgr.cpp +++ b/src/server/game/Guilds/GuildMgr.cpp @@ -418,8 +418,8 @@ void GuildMgr::LoadGuilds() // Delete orphan guild bank items CharacterDatabase.DirectExecute("DELETE gbi FROM guild_bank_item gbi LEFT JOIN guild g ON gbi.guildId = g.guildId WHERE g.guildId IS NULL"); - // 0 1 2 3 4 5 6 7 8 9 10 - QueryResult result = CharacterDatabase.Query("SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, " + // 0 1 2 3 4 5 6 7 8 9 10 + QueryResult result = CharacterDatabase.Query("SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, creationTime, text, " // 11 12 13 14 15 "guildid, TabId, SlotId, item_guid, itemEntry FROM guild_bank_item gbi INNER JOIN item_instance ii ON gbi.item_guid = ii.guid");