From d104559cc4571b4c5657ee14cec5093d13e2e825 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Mon, 17 Mar 2025 11:16:56 -0700 Subject: [PATCH 1/4] fix: avery npc not having animations anymore (#1751) * for avery * remove label --- dScripts/02_server/DLU/DLUVanityTeleportingObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dScripts/02_server/DLU/DLUVanityTeleportingObject.cpp b/dScripts/02_server/DLU/DLUVanityTeleportingObject.cpp index 60d2d715..0908bbdc 100644 --- a/dScripts/02_server/DLU/DLUVanityTeleportingObject.cpp +++ b/dScripts/02_server/DLU/DLUVanityTeleportingObject.cpp @@ -16,6 +16,7 @@ void DLUVanityTeleportingObject::OnStartup(Entity* self) { void DLUVanityTeleportingObject::OnTimerDone(Entity* self, std::string timerName) { if (timerName == "setupTeleport") { + RenderComponent::PlayAnimation(self, u"interact"); GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportBeam", "teleportBeam"); GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportRings", "teleportRings"); @@ -27,7 +28,6 @@ void DLUVanityTeleportingObject::OnTimerDone(Entity* self, std::string timerName } else if (timerName == "teleport") { std::vector& locations = m_Object->m_Locations[Game::server->GetZoneID()]; - selectLocation: VanityObjectLocation& newLocation = locations[GeneralUtils::GenerateRandomNumber(0, locations.size() - 1)]; // try to get not the same position, but if we get the same one twice, it's fine From 347fc46f0188ca0af2aa5525421842bc02a42bb3 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Fri, 28 Mar 2025 15:03:04 -0700 Subject: [PATCH 2/4] check pending names too (#1748) --- dDatabase/GameDatabase/ITables/ICharInfo.h | 2 ++ dDatabase/GameDatabase/MySQL/MySQLDatabase.h | 3 ++- dDatabase/GameDatabase/MySQL/Tables/CharInfo.cpp | 6 ++++++ dDatabase/GameDatabase/SQLite/SQLiteDatabase.h | 1 + dDatabase/GameDatabase/SQLite/Tables/CharInfo.cpp | 6 ++++++ dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h | 2 ++ dGame/UserManager.cpp | 13 +++++++------ 7 files changed, 26 insertions(+), 7 deletions(-) diff --git a/dDatabase/GameDatabase/ITables/ICharInfo.h b/dDatabase/GameDatabase/ITables/ICharInfo.h index 416dad2c..d908d993 100644 --- a/dDatabase/GameDatabase/ITables/ICharInfo.h +++ b/dDatabase/GameDatabase/ITables/ICharInfo.h @@ -44,6 +44,8 @@ public: // Updates the given character ids last login to be right now. virtual void UpdateLastLoggedInCharacter(const uint32_t characterId) = 0; + + virtual bool IsNameInUse(const std::string_view name) = 0; }; #endif //!__ICHARINFO__H__ diff --git a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h index f56f229a..0f50f174 100644 --- a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h +++ b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h @@ -124,8 +124,9 @@ public: void IncrementTimesPlayed(const uint32_t playerId, const uint32_t gameId) override; void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional characterId) override; void DeleteUgcBuild(const LWOOBJID bigId) override; - sql::PreparedStatement* CreatePreppedStmt(const std::string& query); uint32_t GetAccountCount() override; + bool IsNameInUse(const std::string_view name) override; + sql::PreparedStatement* CreatePreppedStmt(const std::string& query); private: // Generic query functions that can be used for any query. diff --git a/dDatabase/GameDatabase/MySQL/Tables/CharInfo.cpp b/dDatabase/GameDatabase/MySQL/Tables/CharInfo.cpp index 7406e69b..ca8103d6 100644 --- a/dDatabase/GameDatabase/MySQL/Tables/CharInfo.cpp +++ b/dDatabase/GameDatabase/MySQL/Tables/CharInfo.cpp @@ -76,3 +76,9 @@ void MySQLDatabase::SetPendingCharacterName(const uint32_t characterId, const st void MySQLDatabase::UpdateLastLoggedInCharacter(const uint32_t characterId) { ExecuteUpdate("UPDATE charinfo SET last_login = ? WHERE id = ? LIMIT 1", static_cast(time(NULL)), characterId); } + +bool MySQLDatabase::IsNameInUse(const std::string_view name) { + auto result = ExecuteSelect("SELECT name FROM charinfo WHERE name = ? or pending_name = ? LIMIT 1;", name, name); + + return result->next(); +} diff --git a/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h b/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h index 14aad75d..f456c459 100644 --- a/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h +++ b/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h @@ -123,6 +123,7 @@ public: void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional characterId) override; void DeleteUgcBuild(const LWOOBJID bigId) override; uint32_t GetAccountCount() override; + bool IsNameInUse(const std::string_view name) override; private: CppSQLite3Statement CreatePreppedStmt(const std::string& query); diff --git a/dDatabase/GameDatabase/SQLite/Tables/CharInfo.cpp b/dDatabase/GameDatabase/SQLite/Tables/CharInfo.cpp index 27ae3611..7853442c 100644 --- a/dDatabase/GameDatabase/SQLite/Tables/CharInfo.cpp +++ b/dDatabase/GameDatabase/SQLite/Tables/CharInfo.cpp @@ -77,3 +77,9 @@ void SQLiteDatabase::SetPendingCharacterName(const uint32_t characterId, const s void SQLiteDatabase::UpdateLastLoggedInCharacter(const uint32_t characterId) { ExecuteUpdate("UPDATE charinfo SET last_login = ? WHERE id = ?;", static_cast(time(NULL)), characterId); } + +bool SQLiteDatabase::IsNameInUse(const std::string_view name) { + auto [_, result] = ExecuteSelect("SELECT name FROM charinfo WHERE name = ? or pending_name = ? LIMIT 1;", name, name); + + return result.eof(); +} diff --git a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h index 5a56610c..1cbb3c7b 100644 --- a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h +++ b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h @@ -102,6 +102,8 @@ class TestSQLDatabase : public GameDatabase { void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional characterId) override {}; void DeleteUgcBuild(const LWOOBJID bigId) override {}; uint32_t GetAccountCount() override { return 0; }; + + bool IsNameInUse(const std::string_view name) override { return false; }; }; #endif //!TESTSQLDATABASE_H diff --git a/dGame/UserManager.cpp b/dGame/UserManager.cpp index e0e37e7c..1cd9bbe5 100644 --- a/dGame/UserManager.cpp +++ b/dGame/UserManager.cpp @@ -305,13 +305,13 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) LOT shirtLOT = FindCharShirtID(shirtColor, shirtStyle); LOT pantsLOT = FindCharPantsID(pantsColor); - if (!name.empty() && Database::Get()->GetCharacterInfo(name)) { + if (!name.empty() && Database::Get()->IsNameInUse(name)) { LOG("AccountID: %i chose unavailable name: %s", u->GetAccountID(), name.c_str()); WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::CUSTOM_NAME_IN_USE); return; } - if (Database::Get()->GetCharacterInfo(predefinedName)) { + if (Database::Get()->IsNameInUse(predefinedName)) { LOG("AccountID: %i chose unavailable predefined name: %s", u->GetAccountID(), predefinedName.c_str()); WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::PREDEFINED_NAME_IN_USE); return; @@ -324,7 +324,7 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) } //Now that the name is ok, we can get an objectID from Master: - ObjectIDManager::RequestPersistentID([=, this](uint32_t objectID) mutable { + ObjectIDManager::RequestPersistentID([=, this](uint32_t objectID) { if (Database::Get()->GetCharacterInfo(objectID)) { LOG("Character object id unavailable, check object_id_tracker!"); WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE); @@ -369,13 +369,14 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) // If predefined name is invalid, change it to be their object id // that way more than one player can create characters if the predefined name files are not provided - if (predefinedName == "INVALID") { + auto assignedPredefinedName = predefinedName; + if (assignedPredefinedName == "INVALID") { std::stringstream nameObjID; nameObjID << "minifig" << objectID; - predefinedName = nameObjID.str(); + assignedPredefinedName = nameObjID.str(); } - std::string_view nameToAssign = !name.empty() && nameOk ? name : predefinedName; + std::string_view nameToAssign = !name.empty() && nameOk ? name : assignedPredefinedName; std::string pendingName = !name.empty() && !nameOk ? name : ""; ICharInfo::Info info; From f78baee534ed0913dc45e114ae51c9120d2b38c5 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Fri, 28 Mar 2025 15:04:35 -0700 Subject: [PATCH 3/4] fix the wu man (#1743) --- dCommon/dEnums/eInventoryType.h | 3 +- dGame/dComponents/InventoryComponent.cpp | 46 +++++++++++++------- dGame/dComponents/InventoryComponent.h | 4 +- dScripts/02_server/Map/AM/AmTeapotServer.cpp | 11 ++--- 4 files changed, 37 insertions(+), 27 deletions(-) diff --git a/dCommon/dEnums/eInventoryType.h b/dCommon/dEnums/eInventoryType.h index 1c6688b2..05896ffb 100644 --- a/dCommon/dEnums/eInventoryType.h +++ b/dCommon/dEnums/eInventoryType.h @@ -28,7 +28,8 @@ enum eInventoryType : uint32_t { DONATION, VAULT_MODELS, ITEM_SETS, //internal, technically this is BankBehaviors. - INVALID // made up, for internal use!!!, Technically this called the ALL inventory. + INVALID, // made up, for internal use!!!, Technically this called the ALL inventory. + ALL, // Use this to search all inventories instead of a specific one. }; class InventoryType { diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index 4e49abff..4cc0a3de 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -150,11 +150,11 @@ uint32_t InventoryComponent::GetLotCount(const LOT lot) const { return count; } -uint32_t InventoryComponent::GetLotCountNonTransfer(LOT lot) const { +uint32_t InventoryComponent::GetLotCountNonTransfer(LOT lot, bool includeVault) const { uint32_t count = 0; for (const auto& inventory : m_Inventories) { - if (IsTransferInventory(inventory.second->GetType())) continue; + if (IsTransferInventory(inventory.second->GetType(), includeVault)) continue; count += inventory.second->GetLotCount(lot); } @@ -305,21 +305,35 @@ bool InventoryComponent::RemoveItem(const LOT lot, const uint32_t count, eInvent LOG("Attempted to remove 0 of item (%i) from the inventory!", lot); return false; } - if (inventoryType == INVALID) inventoryType = Inventory::FindInventoryTypeForLot(lot); - auto* inventory = GetInventory(inventoryType); - if (!inventory) return false; + if (inventoryType != eInventoryType::ALL) { + if (inventoryType == INVALID) inventoryType = Inventory::FindInventoryTypeForLot(lot); + auto* inventory = GetInventory(inventoryType); + if (!inventory) return false; - auto left = std::min(count, inventory->GetLotCount(lot)); - if (left != count) return false; + auto left = std::min(count, inventory->GetLotCount(lot)); + if (left != count) return false; - while (left > 0) { - auto* item = FindItemByLot(lot, inventoryType, false, ignoreBound); - if (!item) break; - const auto delta = std::min(left, item->GetCount()); - item->SetCount(item->GetCount() - delta, silent); - left -= delta; + while (left > 0) { + auto* item = FindItemByLot(lot, inventoryType, false, ignoreBound); + if (!item) break; + const auto delta = std::min(left, item->GetCount()); + item->SetCount(item->GetCount() - delta, silent); + left -= delta; + } + return true; + } else { + auto left = count; + for (const auto& inventory : m_Inventories | std::views::values) { + while (left > 0 && inventory->GetLotCount(lot) > 0) { + auto* item = inventory->FindItemByLot(lot, false, ignoreBound); + if (!item) break; + const auto delta = std::min(item->GetCount(), left); + item->SetCount(item->GetCount() - delta, silent); + left -= delta; + } + } + return left == 0; } - return true; } void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType inventory, const uint32_t count, const bool showFlyingLot, bool isModMoveAndEquip, const bool ignoreEquipped, const int32_t preferredSlot) { @@ -1318,8 +1332,8 @@ BehaviorSlot InventoryComponent::FindBehaviorSlot(const eItemType type) { } } -bool InventoryComponent::IsTransferInventory(eInventoryType type) { - return type == VENDOR_BUYBACK || type == VAULT_ITEMS || type == VAULT_MODELS || type == TEMP_ITEMS || type == TEMP_MODELS || type == MODELS_IN_BBB; +bool InventoryComponent::IsTransferInventory(eInventoryType type, bool includeVault) { + return type == VENDOR_BUYBACK || (includeVault && (type == VAULT_ITEMS || type == VAULT_MODELS)) || type == TEMP_ITEMS || type == TEMP_MODELS || type == MODELS_IN_BBB; } uint32_t InventoryComponent::FindSkill(const LOT lot) { diff --git a/dGame/dComponents/InventoryComponent.h b/dGame/dComponents/InventoryComponent.h index 0055fcea..395dd5d6 100644 --- a/dGame/dComponents/InventoryComponent.h +++ b/dGame/dComponents/InventoryComponent.h @@ -100,7 +100,7 @@ public: * @param lot the lot to search for * @return the amount of items this entity possesses of the specified lot */ - uint32_t GetLotCountNonTransfer(LOT lot) const; + uint32_t GetLotCountNonTransfer(LOT lot, bool includeVault = true) const; /** * Returns the items that are currently equipped by this entity @@ -373,7 +373,7 @@ public: * @param type the inventory type to check * @return if the inventory type is a temp inventory */ - static bool IsTransferInventory(eInventoryType type); + static bool IsTransferInventory(eInventoryType type, bool includeVault = true); /** * Finds the skill related to the passed LOT from the ObjectSkills table diff --git a/dScripts/02_server/Map/AM/AmTeapotServer.cpp b/dScripts/02_server/Map/AM/AmTeapotServer.cpp index 93f05326..66c935ce 100644 --- a/dScripts/02_server/Map/AM/AmTeapotServer.cpp +++ b/dScripts/02_server/Map/AM/AmTeapotServer.cpp @@ -8,16 +8,11 @@ void AmTeapotServer::OnUse(Entity* self, Entity* user) { auto* inventoryComponent = user->GetComponent(); if (!inventoryComponent) return; - auto* blueFlowerItem = inventoryComponent->FindItemByLot(BLUE_FLOWER_LEAVES, eInventoryType::ITEMS); - if (!blueFlowerItem) { - blueFlowerItem = inventoryComponent->FindItemByLot(BLUE_FLOWER_LEAVES, eInventoryType::VAULT_ITEMS); - if (!blueFlowerItem) return; - } - // The client allows you to use the teapot only if you have a stack of 10 leaves in some inventory somewhere. - if (blueFlowerItem->GetCount() >= 10) { - blueFlowerItem->SetCount(blueFlowerItem->GetCount() - 10); + if (inventoryComponent->GetLotCountNonTransfer(BLUE_FLOWER_LEAVES, false) >= 10) { + inventoryComponent->RemoveItem(BLUE_FLOWER_LEAVES, 10, eInventoryType::ALL); inventoryComponent->AddItem(WU_S_IMAGINATION_TEA, 1); } + GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); } From aa49aaae7646f7f826d85e2ebca5b10111b1ca4f Mon Sep 17 00:00:00 2001 From: jadebenn Date: Fri, 28 Mar 2025 20:12:28 -0500 Subject: [PATCH 4/4] invert sqlite lookup result to fix name in use lookup errors (#1755) Co-authored-by: jadebenn <9892985+jadebenn@users.noreply.github.com> --- dDatabase/GameDatabase/SQLite/Tables/CharInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dDatabase/GameDatabase/SQLite/Tables/CharInfo.cpp b/dDatabase/GameDatabase/SQLite/Tables/CharInfo.cpp index 7853442c..efe5e1ab 100644 --- a/dDatabase/GameDatabase/SQLite/Tables/CharInfo.cpp +++ b/dDatabase/GameDatabase/SQLite/Tables/CharInfo.cpp @@ -81,5 +81,5 @@ void SQLiteDatabase::UpdateLastLoggedInCharacter(const uint32_t characterId) { bool SQLiteDatabase::IsNameInUse(const std::string_view name) { auto [_, result] = ExecuteSelect("SELECT name FROM charinfo WHERE name = ? or pending_name = ? LIMIT 1;", name, name); - return result.eof(); + return !result.eof(); }