Merge branch 'main' into websockets

This commit is contained in:
Aaron Kimbre 2025-03-28 22:45:48 -05:00
commit 8b54c551cf
12 changed files with 64 additions and 35 deletions

View File

@ -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 {

View File

@ -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__

View File

@ -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<uint32_t> 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.

View File

@ -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<uint32_t>(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();
}

View File

@ -123,6 +123,7 @@ public:
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> 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);

View File

@ -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<uint32_t>(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();
}

View File

@ -102,6 +102,8 @@ class TestSQLDatabase : public GameDatabase {
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> 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

View File

@ -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;

View File

@ -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<uint32_t>(count, inventory->GetLotCount(lot));
if (left != count) return false;
auto left = std::min<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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) {

View File

@ -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

View File

@ -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<VanityObjectLocation>& locations = m_Object->m_Locations[Game::server->GetZoneID()];
selectLocation:
VanityObjectLocation& newLocation = locations[GeneralUtils::GenerateRandomNumber<size_t>(0, locations.size() - 1)];
// try to get not the same position, but if we get the same one twice, it's fine

View File

@ -8,16 +8,11 @@ void AmTeapotServer::OnUse(Entity* self, Entity* user) {
auto* inventoryComponent = user->GetComponent<InventoryComponent>();
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());
}