Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/frontend/StarContainerInterface.cpp2
-rw-r--r--source/frontend/StarCraftingInterface.cpp18
-rw-r--r--source/frontend/StarMerchantInterface.cpp6
-rw-r--r--source/game/StarCollectionDatabase.cpp2
-rw-r--r--source/game/StarItemDatabase.cpp59
-rw-r--r--source/game/StarItemDatabase.hpp13
-rw-r--r--source/game/StarPlayer.cpp2
-rw-r--r--source/game/StarQuestDescriptor.cpp4
-rw-r--r--source/game/StarRoot.cpp31
9 files changed, 99 insertions, 38 deletions
diff --git a/source/frontend/StarContainerInterface.cpp b/source/frontend/StarContainerInterface.cpp
index 6540dfb..d69138a 100644
--- a/source/frontend/StarContainerInterface.cpp
+++ b/source/frontend/StarContainerInterface.cpp
@@ -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);
diff --git a/source/frontend/StarCraftingInterface.cpp b/source/frontend/StarCraftingInterface.cpp
index 5ad25ce..3156a78 100644
--- a/source/frontend/StarCraftingInterface.cpp
+++ b/source/frontend/StarCraftingInterface.cpp
@@ -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
diff --git a/source/frontend/StarMerchantInterface.cpp b/source/frontend/StarMerchantInterface.cpp
index 8e3ad3a..17f4995 100644
--- a/source/frontend/StarMerchantInterface.cpp
+++ b/source/frontend/StarMerchantInterface.cpp
@@ -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);
diff --git a/source/game/StarCollectionDatabase.cpp b/source/game/StarCollectionDatabase.cpp
index d8d573d..40e7595 100644
--- a/source/game/StarCollectionDatabase.cpp
+++ b/source/game/StarCollectionDatabase.cpp
@@ -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();
diff --git a/source/game/StarItemDatabase.cpp b/source/game/StarItemDatabase.cpp
index dfeddfd..200ebc1 100644
--- a/source/game/StarItemDatabase.cpp
+++ b/source/game/StarItemDatabase.cpp
@@ -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", {}, {}));
+ ItemCacheEntry entry{ descriptor, level, seed };
+ MutexLocker locker(m_cacheMutex);
+ if (ItemPtr* cached = m_itemCache.ptr(entry))
+ return *cached;
+ else {
+ locker.unlock();
+
+ 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); });
}
- item->setCount(descriptor.count());
+}
- return 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))
diff --git a/source/game/StarItemDatabase.hpp b/source/game/StarItemDatabase.hpp
index 2c1fb1c..15dd7ee 100644
--- a/source/game/StarItemDatabase.hpp
+++ b/source/game/StarItemDatabase.hpp
@@ -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>
diff --git a/source/game/StarPlayer.cpp b/source/game/StarPlayer.cpp
index 7bfa224..7036a7b 100644
--- a/source/game/StarPlayer.cpp
+++ b/source/game/StarPlayer.cpp
@@ -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);
}
diff --git a/source/game/StarQuestDescriptor.cpp b/source/game/StarQuestDescriptor.cpp
index 3709ef3..cb791e1 100644
--- a/source/game/StarQuestDescriptor.cpp
+++ b/source/game/StarQuestDescriptor.cpp
@@ -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(", ");
diff --git a/source/game/StarRoot.cpp b/source/game/StarRoot.cpp
index df2fc98..5adbd0c 100644
--- a/source/game/StarRoot.cpp
+++ b/source/game/StarRoot.cpp
@@ -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();