Cache certain item generation calls from interfaces
Helps a little with the lag from recipes when having crafting interfaces open, but it's still noticeable. Also micro-optimized Root maintenance by unlocking the Root mutexes for their respective shared_ptrs earlier once we have our own shared_ptr.
This commit is contained in:
parent
121d27446b
commit
0aee45a117
@ -142,7 +142,7 @@ ContainerPane::ContainerPane(WorldClientPtr worldClient, PlayerPtr player, Conta
|
||||
|
||||
if (container->iconItem()) {
|
||||
auto itemDatabase = Root::singleton().itemDatabase();
|
||||
auto iconItem = itemDatabase->item(container->iconItem());
|
||||
auto iconItem = itemDatabase->itemShared(container->iconItem());
|
||||
auto icon = make_shared<ItemSlotWidget>(iconItem, "/interface/inventory/portrait.png");
|
||||
icon->showDurability(false);
|
||||
icon->showRarity(false);
|
||||
|
@ -127,7 +127,7 @@ CraftingPane::CraftingPane(WorldClientPtr worldClient, PlayerPtr player, Json co
|
||||
if (auto container = as<ContainerEntity>(entity)) {
|
||||
if (container->iconItem()) {
|
||||
auto itemDatabase = Root::singleton().itemDatabase();
|
||||
auto iconItem = itemDatabase->item(container->iconItem());
|
||||
auto iconItem = itemDatabase->itemShared(container->iconItem());
|
||||
auto icon = make_shared<ItemSlotWidget>(iconItem, "/interface/inventory/portrait.png");
|
||||
String title = this->title();
|
||||
if (title.empty())
|
||||
@ -259,7 +259,7 @@ void CraftingPane::update(float dt) {
|
||||
auto description = fetchChild<Widget>("description");
|
||||
description->removeAllChildren();
|
||||
|
||||
auto item = Root::singleton().itemDatabase()->item(recipe.output);
|
||||
auto item = Root::singleton().itemDatabase()->itemShared(recipe.output);
|
||||
ItemTooltipBuilder::buildItemDescription(description, item);
|
||||
}
|
||||
}
|
||||
@ -383,7 +383,7 @@ void CraftingPane::setupWidget(WidgetPtr const& widget, ItemRecipe const& recipe
|
||||
auto single = recipe.output.singular();
|
||||
ItemPtr item = m_itemCache[single];
|
||||
if (!item) {
|
||||
item = root.itemDatabase()->item(single);
|
||||
item = root.itemDatabase()->itemShared(single);
|
||||
m_itemCache[single] = item;
|
||||
}
|
||||
|
||||
@ -475,13 +475,13 @@ PanePtr CraftingPane::setupTooltip(ItemRecipe const& recipe) {
|
||||
auto currenciesConfig = root.assets()->json("/currencies.config");
|
||||
for (auto const& p : recipe.currencyInputs) {
|
||||
if (p.second > 0) {
|
||||
auto currencyItem = root.itemDatabase()->item(ItemDescriptor(currenciesConfig.get(p.first).getString("representativeItem")));
|
||||
auto currencyItem = root.itemDatabase()->itemShared(ItemDescriptor(currenciesConfig.get(p.first).getString("representativeItem")));
|
||||
addIngredient(currencyItem, m_player->currency(p.first), p.second);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& input : recipe.inputs) {
|
||||
auto item = root.itemDatabase()->item(input.singular());
|
||||
auto item = root.itemDatabase()->itemShared(input.singular());
|
||||
size_t itemCount = itemDb->getCountOfItem(normalizedBag, input, recipe.matchInputParameters);
|
||||
addIngredient(item, itemCount, input.count());
|
||||
}
|
||||
@ -576,7 +576,7 @@ void CraftingPane::craft(int count) {
|
||||
remainingItemCount -= craftedItem->count();
|
||||
m_player->giveItem(craftedItem);
|
||||
|
||||
for (auto collectable : recipe.collectables)
|
||||
for (auto& collectable : recipe.collectables)
|
||||
m_player->addCollectable(collectable.first, collectable.second);
|
||||
}
|
||||
|
||||
@ -666,10 +666,10 @@ List<ItemRecipe> CraftingPane::determineRecipes() {
|
||||
|
||||
float printTime = m_settings.getFloat("printTime", 0);
|
||||
float printFactor = m_settings.getFloat("printCostFactor", 1.0);
|
||||
for (auto itemName : itemList) {
|
||||
for (auto& itemName : itemList) {
|
||||
ItemRecipe recipe;
|
||||
recipe.output = ItemDescriptor(itemName, 1);
|
||||
auto recipeItem = itemDb->item(recipe.output);
|
||||
auto recipeItem = itemDb->itemShared(recipe.output);
|
||||
int itemPrice = int(recipeItem->price() * printFactor);
|
||||
recipe.currencyInputs["money"] = itemPrice;
|
||||
recipe.outputRarity = recipeItem->rarity();
|
||||
@ -679,7 +679,7 @@ List<ItemRecipe> CraftingPane::determineRecipes() {
|
||||
recipes.add(recipe);
|
||||
}
|
||||
} else if (m_settings.contains("recipes")) {
|
||||
for (auto entry : m_settings.getArray("recipes")) {
|
||||
for (auto& entry : m_settings.getArray("recipes")) {
|
||||
if (entry.type() == Json::Type::String)
|
||||
recipes.addAll(itemDb->recipesForOutputItem(entry.toString()));
|
||||
else
|
||||
|
@ -127,7 +127,7 @@ PanePtr MerchantPane::createTooltip(Vec2I const& screenPosition) {
|
||||
auto entry = m_itemGuiList->itemAt(i);
|
||||
if (entry->getChildAt(screenPosition)) {
|
||||
auto itemConfig = m_itemList.get(i);
|
||||
ItemPtr item = Root::singleton().itemDatabase()->item(ItemDescriptor(itemConfig.get("item")));
|
||||
ItemPtr item = Root::singleton().itemDatabase()->itemShared(ItemDescriptor(itemConfig.get("item")));
|
||||
return ItemTooltipBuilder::buildItemTooltip(item, m_player);
|
||||
}
|
||||
}
|
||||
@ -232,7 +232,7 @@ void MerchantPane::buildItemList() {
|
||||
void MerchantPane::setupWidget(WidgetPtr const& widget, Json const& itemConfig) {
|
||||
auto& root = Root::singleton();
|
||||
auto assets = root.assets();
|
||||
ItemPtr item = root.itemDatabase()->item(ItemDescriptor(itemConfig.get("item")));
|
||||
ItemPtr item = root.itemDatabase()->itemShared(ItemDescriptor(itemConfig.get("item")));
|
||||
|
||||
String name = item->friendlyName();
|
||||
if (item->count() > 1)
|
||||
@ -265,7 +265,7 @@ void MerchantPane::updateSelection() {
|
||||
|
||||
if (m_selectedIndex != NPos) {
|
||||
auto itemConfig = m_itemList.get(m_selectedIndex);
|
||||
m_selectedItem = Root::singleton().itemDatabase()->item(ItemDescriptor(itemConfig.get("item")));
|
||||
m_selectedItem = Root::singleton().itemDatabase()->itemShared(ItemDescriptor(itemConfig.get("item")));
|
||||
findChild<ButtonWidget>("spinCount.up")->enable();
|
||||
findChild<ButtonWidget>("spinCount.down")->enable();
|
||||
m_countTextBox->setColor(Color::White);
|
||||
|
@ -109,7 +109,7 @@ Collectable CollectionDatabase::parseMonsterCollectable(String const& name, Json
|
||||
Collectable CollectionDatabase::parseItemCollectable(String const& name, Json const& config) const {
|
||||
Collectable collectable = parseGenericCollectable(name, config);
|
||||
auto itemDatabase = Root::singleton().itemDatabase();
|
||||
auto item = itemDatabase->item(ItemDescriptor(config.getString("item")));
|
||||
auto item = itemDatabase->itemShared(ItemDescriptor(config.getString("item")));
|
||||
|
||||
collectable.title = item->friendlyName();
|
||||
collectable.description = item->description();
|
||||
|
@ -140,6 +140,15 @@ ItemDatabase::ItemDatabase()
|
||||
addBlueprints();
|
||||
}
|
||||
|
||||
void ItemDatabase::cleanup() {
|
||||
{
|
||||
MutexLocker locker(m_cacheMutex);
|
||||
m_itemCache.cleanup([](ItemCacheEntry const&, ItemPtr const& item) {
|
||||
return !item.unique();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ItemPtr ItemDatabase::diskLoad(Json const& diskStore) const {
|
||||
if (diskStore) {
|
||||
return item(ItemDescriptor::loadStore(diskStore));
|
||||
@ -204,20 +213,30 @@ ItemDatabase::ItemConfig ItemDatabase::itemConfig(String const& itemName, Json p
|
||||
return itemConfig;
|
||||
}
|
||||
|
||||
ItemPtr ItemDatabase::item(ItemDescriptor descriptor, Maybe<float> level, Maybe<uint64_t> seed) const {
|
||||
ItemPtr ItemDatabase::itemShared(ItemDescriptor descriptor, Maybe<float> level, Maybe<uint64_t> seed) const {
|
||||
if (!descriptor)
|
||||
return {};
|
||||
|
||||
ItemPtr item;
|
||||
try {
|
||||
item = createItem(m_items.get(descriptor.name()).type, itemConfig(descriptor.name(), descriptor.parameters(), level, seed));
|
||||
} catch (std::exception const& e) {
|
||||
Logger::error("Could not instantiate item '{}'. {}", descriptor, outputException(e, false));
|
||||
item = createItem(m_items.get("perfectlygenericitem").type, itemConfig("perfectlygenericitem", {}, {}));
|
||||
}
|
||||
item->setCount(descriptor.count());
|
||||
ItemCacheEntry entry{ descriptor, level, seed };
|
||||
MutexLocker locker(m_cacheMutex);
|
||||
if (ItemPtr* cached = m_itemCache.ptr(entry))
|
||||
return *cached;
|
||||
else {
|
||||
locker.unlock();
|
||||
|
||||
return item;
|
||||
ItemPtr item = tryCreateItem(descriptor, level, seed);
|
||||
get<2>(entry) = item->parameters().optUInt("seed"); // Seed could've been changed by the buildscript
|
||||
|
||||
locker.lock();
|
||||
return m_itemCache.get(entry, [&](ItemCacheEntry const&) -> ItemPtr { return move(item); });
|
||||
}
|
||||
}
|
||||
|
||||
ItemPtr ItemDatabase::item(ItemDescriptor descriptor, Maybe<float> level, Maybe<uint64_t> seed) const {
|
||||
if (!descriptor)
|
||||
return {};
|
||||
else
|
||||
return tryCreateItem(descriptor, level, seed);
|
||||
}
|
||||
|
||||
bool ItemDatabase::hasRecipeToMake(ItemDescriptor const& item) const {
|
||||
@ -332,7 +351,7 @@ ItemRecipe ItemDatabase::parseRecipe(Json const& config) const {
|
||||
for (auto input : config.getArray("input")) {
|
||||
auto id = ItemDescriptor(input);
|
||||
if (itemType(id.name()) == ItemType::CurrencyItem) {
|
||||
auto currencyItem = as<CurrencyItem>(item(id));
|
||||
auto currencyItem = as<CurrencyItem>(itemShared(id));
|
||||
res.currencyInputs[currencyItem->currencyType()] += currencyItem->totalValue();
|
||||
} else {
|
||||
res.inputs.push_back(id);
|
||||
@ -342,7 +361,7 @@ ItemRecipe ItemDatabase::parseRecipe(Json const& config) const {
|
||||
res.output = ItemDescriptor(config.get("output"));
|
||||
res.duration = config.getFloat("duration", Root::singleton().assets()->json("/items/defaultParameters.config:defaultCraftDuration").toFloat());
|
||||
res.groups = StringSet::from(jsonToStringList(config.get("groups", JsonArray())));
|
||||
if (auto item = ItemDatabase::item(res.output)) {
|
||||
if (auto item = ItemDatabase::itemShared(res.output)) {
|
||||
res.outputRarity = item->rarity();
|
||||
res.guiFilterString = guiFilterString(item);
|
||||
}
|
||||
@ -480,6 +499,20 @@ ItemPtr ItemDatabase::createItem(ItemType type, ItemConfig const& config) {
|
||||
}
|
||||
}
|
||||
|
||||
ItemPtr ItemDatabase::tryCreateItem(ItemDescriptor const& descriptor, Maybe<float> level, Maybe<uint64_t> seed) const {
|
||||
ItemPtr result;
|
||||
try {
|
||||
result = createItem(m_items.get(descriptor.name()).type, itemConfig(descriptor.name(), descriptor.parameters(), level, seed));
|
||||
}
|
||||
catch (std::exception const& e) {
|
||||
Logger::error("Could not instantiate item '{}'. {}", descriptor, outputException(e, false));
|
||||
result = createItem(m_items.get("perfectlygenericitem").type, itemConfig("perfectlygenericitem", {}, {}));
|
||||
}
|
||||
result->setCount(descriptor.count());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ItemDatabase::ItemData const& ItemDatabase::itemData(String const& name) const {
|
||||
if (auto p = m_items.ptr(name))
|
||||
return *p;
|
||||
@ -492,7 +525,7 @@ ItemRecipe ItemDatabase::makeRecipe(List<ItemDescriptor> inputs, ItemDescriptor
|
||||
res.output = move(output);
|
||||
res.duration = duration;
|
||||
res.groups = move(groups);
|
||||
if (auto item = ItemDatabase::item(res.output)) {
|
||||
if (auto item = ItemDatabase::itemShared(res.output)) {
|
||||
res.outputRarity = item->rarity();
|
||||
res.guiFilterString = guiFilterString(item);
|
||||
}
|
||||
@ -627,7 +660,7 @@ void ItemDatabase::addBlueprints() {
|
||||
|
||||
for (auto const& recipe : m_recipes) {
|
||||
auto baseDesc = recipe.output;
|
||||
auto baseItem = item(baseDesc);
|
||||
auto baseItem = itemShared(baseDesc);
|
||||
|
||||
String blueprintName = strf("{}-recipe", baseItem->name());
|
||||
if (m_items.contains(blueprintName))
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "StarItem.hpp"
|
||||
#include "StarCasting.hpp"
|
||||
#include "StarLuaRoot.hpp"
|
||||
#include "StarTtlCache.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
@ -75,6 +76,8 @@ public:
|
||||
|
||||
ItemDatabase();
|
||||
|
||||
void cleanup();
|
||||
|
||||
// Load an item based on item descriptor. If loadItem is called with a
|
||||
// live ptr, and the ptr matches the descriptor read, then no new item is
|
||||
// constructed. If ItemT is some other type than Item, then loadItem will
|
||||
@ -112,8 +115,12 @@ public:
|
||||
// from the appropriate factory. If there is a problem instantiating the
|
||||
// item, will return a default item instead. If item is passed a null
|
||||
// ItemDescriptor, it will return a null pointer.
|
||||
// The returned item pointer will be shared. Either call ->clone() or use item() instead for a copy.
|
||||
ItemPtr itemShared(ItemDescriptor descriptor, Maybe<float> level = {}, Maybe<uint64_t> seed = {}) const;
|
||||
// Same as itemShared, but makes a copy instead. Does not cache.
|
||||
ItemPtr item(ItemDescriptor descriptor, Maybe<float> level = {}, Maybe<uint64_t> seed = {}) const;
|
||||
|
||||
|
||||
bool hasRecipeToMake(ItemDescriptor const& item) const;
|
||||
bool hasRecipeToMake(ItemDescriptor const& item, StringSet const& allowedTypes) const;
|
||||
|
||||
@ -153,6 +160,7 @@ private:
|
||||
};
|
||||
|
||||
static ItemPtr createItem(ItemType type, ItemConfig const& config);
|
||||
ItemPtr tryCreateItem(ItemDescriptor const& descriptor, Maybe<float> level = {}, Maybe<uint64_t> seed = {}) const;
|
||||
|
||||
ItemData const& itemData(String const& name) const;
|
||||
ItemRecipe makeRecipe(List<ItemDescriptor> inputs, ItemDescriptor output, float duration, StringSet groups) const;
|
||||
@ -171,6 +179,11 @@ private:
|
||||
|
||||
mutable RecursiveMutex m_luaMutex;
|
||||
LuaRootPtr m_luaRoot;
|
||||
|
||||
typedef tuple<ItemDescriptor, Maybe<float>, Maybe<uint64_t>> ItemCacheEntry;
|
||||
|
||||
mutable Mutex m_cacheMutex;
|
||||
mutable HashTtlCache<ItemCacheEntry, ItemPtr> m_itemCache;
|
||||
};
|
||||
|
||||
template <typename ItemT>
|
||||
|
@ -1151,7 +1151,7 @@ ItemPtr Player::pickupItems(ItemPtr const& items) {
|
||||
m_effectsAnimator->playSound("pickup");
|
||||
}
|
||||
auto itemDb = Root::singleton().itemDatabase();
|
||||
queueItemPickupMessage(itemDb->item(items->descriptor()));
|
||||
queueItemPickupMessage(itemDb->itemShared(items->descriptor()));
|
||||
return m_inventory->addItems(items);
|
||||
}
|
||||
|
||||
|
@ -279,12 +279,12 @@ String questParamText(QuestParam const& parameter) {
|
||||
|
||||
if (parameter.detail.is<QuestItem>()) {
|
||||
QuestItem item = parameter.detail.get<QuestItem>();
|
||||
return itemDatabase->item(item.descriptor())->friendlyName();
|
||||
return itemDatabase->itemShared(item.descriptor())->friendlyName();
|
||||
|
||||
} else if (parameter.detail.is<QuestItemList>()) {
|
||||
QuestItemList itemList = parameter.detail.get<QuestItemList>();
|
||||
StringList itemStrings = itemList.transformed([&itemDatabase](ItemDescriptor const& itemDesc) -> String {
|
||||
return strf("{} {}", itemDesc.count(), itemDatabase->item(itemDesc)->friendlyName());
|
||||
return strf("{} {}", itemDesc.count(), itemDatabase->itemShared(itemDesc)->friendlyName());
|
||||
});
|
||||
return itemStrings.join(", ");
|
||||
|
||||
|
@ -106,23 +106,38 @@ Root::Root(Settings settings) {
|
||||
|
||||
{
|
||||
MutexLocker locker(m_objectDatabaseMutex);
|
||||
if (m_objectDatabase)
|
||||
m_objectDatabase->cleanup();
|
||||
if (ObjectDatabasePtr objectDb = m_objectDatabase) {
|
||||
locker.unlock();
|
||||
objectDb->cleanup();
|
||||
}
|
||||
}
|
||||
{
|
||||
MutexLocker locker(m_itemDatabaseMutex);
|
||||
if (ItemDatabasePtr itemDb = m_itemDatabase) {
|
||||
locker.unlock();
|
||||
itemDb->cleanup();
|
||||
}
|
||||
}
|
||||
{
|
||||
MutexLocker locker(m_monsterDatabaseMutex);
|
||||
if (m_monsterDatabase)
|
||||
m_monsterDatabase->cleanup();
|
||||
if (MonsterDatabasePtr monsterDb = m_monsterDatabase) {
|
||||
locker.unlock();
|
||||
monsterDb->cleanup();
|
||||
}
|
||||
}
|
||||
{
|
||||
MutexLocker locker(m_assetsMutex);
|
||||
if (m_assets)
|
||||
m_assets->cleanup();
|
||||
if (AssetsPtr assets = m_assets) {
|
||||
locker.unlock();
|
||||
assets->cleanup();
|
||||
}
|
||||
}
|
||||
{
|
||||
MutexLocker locker(m_tenantDatabaseMutex);
|
||||
if (m_tenantDatabase)
|
||||
m_tenantDatabase->cleanup();
|
||||
if (TenantDatabasePtr tenantDb = m_tenantDatabase) {
|
||||
locker.unlock();
|
||||
tenantDb->cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
Random::addEntropy();
|
||||
|
Loading…
Reference in New Issue
Block a user