Skip to content

Commit

Permalink
feat(assets): add use paths instead of UUIDs to identifying assets
Browse files Browse the repository at this point in the history
  • Loading branch information
GalaxyCrush committed Oct 17, 2024
1 parent 4f2187d commit 1ebeeb5
Show file tree
Hide file tree
Showing 13 changed files with 173 additions and 61 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Allow identifying assets in code from their path (#1177. **@GalaxyCrush**).

## [v0.4.0] - 2024-10-13

### Added
Expand Down
12 changes: 8 additions & 4 deletions engine/include/cubos/engine/assets/asset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ namespace cubos::engine

/// @brief Avoid using this field, use @ref getId() instead.
/// @todo This was added as a dirty fix for #692, should be removed once the issue is fixed.
uuids::uuid reflectedId;
std::string pathOrId;

~AnyAsset();

Expand Down Expand Up @@ -71,9 +71,13 @@ namespace cubos::engine
/// @return Asset version.
int getVersion() const;

/// @brief Gets the UUID of the asset.
/// @brief Gets the Path or UUID of the asset.
/// @return Asset Path or UUID.
std::string getIdString() const;

/// @brief Gets the UUID of the asset if it is the case of having one.
/// @return Asset UUID.
uuids::uuid getId() const;
std::optional<uuids::uuid> getId() const;

/// @brief Checks if the handle is null.
/// @return Whether the handle is null.
Expand Down Expand Up @@ -160,7 +164,7 @@ namespace cubos::engine
inline AnyAsset::operator Asset<T>() const
{
Asset<T> asset;
asset.reflectedId = reflectedId;
asset.pathOrId = pathOrId;
asset.mId = mId;
asset.mRefCount = mRefCount;
asset.mVersion = mVersion;
Expand Down
5 changes: 5 additions & 0 deletions engine/include/cubos/engine/assets/assets.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,11 @@ namespace cubos::engine
/// @return Lock guard.
std::unique_lock<std::shared_mutex> lockWrite(const AnyAsset& handle) const;

/// @brief Checks if the given path is a valid path to an asset.
/// @param path Path to check.
/// @return A string that says if the given string is a path, uuid or not a valid format string
static std::string isPath(const std::string& path);

/// @brief Gets a pointer to the entry associated with the given handle.
/// @param handle Handle to get the entry for.
/// @return Entry for the given handle, or nullptr if there is no such entry.
Expand Down
10 changes: 10 additions & 0 deletions engine/include/cubos/engine/assets/meta.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include <unordered_map>
#include <vector>

#include <uuid.h>

#include <cubos/engine/api.hpp>

namespace cubos::engine
Expand Down Expand Up @@ -44,6 +46,14 @@ namespace cubos::engine
/// @return The value of the parameter, if the parameter exists.
std::optional<std::string> get(std::string_view key) const;

/// @brief Gets the ID of the asset.
/// @return ID of the asset.
uuids::uuid getId() const;

/// @brief Gets the path of the asset.
/// @return Path of the asset.
std::string getPath() const;

/// @brief Sets a parameter on the asset's metadata.
/// @param key Key of the parameter.
/// @param value Value of the parameter.
Expand Down
6 changes: 3 additions & 3 deletions engine/samples/games/cubosurfers/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

using namespace cubos::engine;

static const Asset<Scene> SceneAsset = AnyAsset("ee5bb451-05b7-430f-a641-a746f7009eef");
static const Asset<VoxelPalette> PaletteAsset = AnyAsset("101da567-3d23-46ae-a391-c10ec00e8718");
static const Asset<InputBindings> InputBindingsAsset = AnyAsset("b20900a4-20ee-4caa-8830-14585050bead");
static const Asset<Scene> SceneAsset = AnyAsset("/assets/scenes/main.cubos");
static const Asset<VoxelPalette> PaletteAsset = AnyAsset("/assets/main.pal");
static const Asset<InputBindings> InputBindingsAsset = AnyAsset("/assets/input.bind");

int main(int argc, char** argv)
{
Expand Down
39 changes: 24 additions & 15 deletions engine/src/assets/asset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ AnyAsset::AnyAsset(std::nullptr_t)
}

AnyAsset::AnyAsset(uuids::uuid id)
: reflectedId(id)
: pathOrId(uuids::to_string(id))
, mId(id)
, mRefCount(nullptr)
, mVersion(-1)
Expand All @@ -40,17 +40,21 @@ AnyAsset::AnyAsset(std::string_view str)
{
if (auto id = uuids::uuid::from_string(str))
{
reflectedId = id.value();
pathOrId = str;
mId = id.value();
}
else if (str.find('/') != std::string_view::npos || str.find('\\') != std::string_view::npos)
{
pathOrId = str;
}
else
{
CUBOS_ERROR("Could not create asset handle, invalid UUID: \"{}\"", str);
}
}

AnyAsset::AnyAsset(const AnyAsset& other)
: reflectedId(other.reflectedId)
: pathOrId(other.pathOrId)
, mId(other.mId)
, mRefCount(other.mRefCount)
, mVersion(other.mVersion)
Expand All @@ -59,7 +63,7 @@ AnyAsset::AnyAsset(const AnyAsset& other)
}

AnyAsset::AnyAsset(AnyAsset&& other) noexcept
: reflectedId(other.reflectedId)
: pathOrId(other.pathOrId)
, mId(other.mId)
, mRefCount(other.mRefCount)
, mVersion(other.mVersion)
Expand All @@ -75,7 +79,7 @@ AnyAsset& AnyAsset::operator=(const AnyAsset& other)
}

this->decRef();
reflectedId = other.reflectedId;
pathOrId = other.pathOrId;
mId = other.mId;
mRefCount = other.mRefCount;
mVersion = other.mVersion;
Expand All @@ -91,7 +95,7 @@ AnyAsset& AnyAsset::operator=(AnyAsset&& other) noexcept
}

this->decRef();
reflectedId = other.reflectedId;
pathOrId = other.pathOrId;
mId = other.mId;
mRefCount = other.mRefCount;
mVersion = other.mVersion;
Expand All @@ -101,27 +105,32 @@ AnyAsset& AnyAsset::operator=(AnyAsset&& other) noexcept

bool AnyAsset::operator==(const AnyAsset& other) const
{
return this->getId() == other.getId();
return this->getIdString() == other.getIdString();
}

int AnyAsset::getVersion() const
{
return reflectedId == mId ? mVersion : 0;
return getId().has_value() && getId().value() == mId ? mVersion : 0;
}

std::string AnyAsset::getIdString() const
{
return pathOrId;
}

uuids::uuid AnyAsset::getId() const
std::optional<uuids::uuid> AnyAsset::getId() const
{
return reflectedId;
return uuids::uuid::from_string(pathOrId);
}

bool AnyAsset::isNull() const
{
return reflectedId.is_nil();
return pathOrId.empty();
}

bool AnyAsset::isStrong() const
{
return reflectedId == mId && mRefCount != nullptr;
return getId().has_value() && getId().value() == mId && mRefCount != nullptr;
}

void AnyAsset::makeWeak()
Expand All @@ -136,20 +145,20 @@ cubos::core::reflection::Type& AnyAsset::makeType(std::string name)

return Type::create(std::move(name))
.with(ConstructibleTrait::typed<AnyAsset>().withBasicConstructors().build())
.with(FieldsTrait().withField("id", &AnyAsset::reflectedId));
.with(FieldsTrait().withField("id", &AnyAsset::pathOrId));
}

void AnyAsset::incRef() const
{
if (reflectedId == mId && mRefCount != nullptr)
if (getId().has_value() && getId().value() == mId && mRefCount != nullptr)
{
static_cast<std::atomic<int>*>(mRefCount)->fetch_add(1);
}
}

void AnyAsset::decRef() const
{
if (reflectedId == mId && mRefCount != nullptr)
if (getId().has_value() && getId().value() == mId && mRefCount != nullptr)
{
static_cast<std::atomic<int>*>(mRefCount)->fetch_sub(1);
}
Expand Down
103 changes: 82 additions & 21 deletions engine/src/assets/assets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ void Assets::loadMeta(std::string_view path)
// Get the UUID from the metadata, if it exists.
if (meta.get("id").has_value())
{
id = uuids::uuid::from_string(meta.get("id").value()).value_or(uuids::uuid());
id = meta.getId();
}

// If the UUID is invalid, generate a new random one.
Expand Down Expand Up @@ -311,6 +311,7 @@ AnyAsset Assets::load(AnyAsset handle) const
// Return a strong handle to the asset.
assetEntry->refCount += 1;
handle.mRefCount = &assetEntry->refCount;
handle.mId = assetEntry->meta.getId();
return handle;
}

Expand Down Expand Up @@ -378,7 +379,7 @@ Assets::Status Assets::status(const AnyAsset& handle) const
std::shared_lock lock(mMutex);

// Do not use .entry() here because we don't want to log errors if the asset is unknown.
auto it = mEntries.find(handle.getId());
auto it = mEntries.find(handle.getId().value());
if (it == mEntries.end())
{
return Status::Unknown;
Expand All @@ -396,9 +397,9 @@ bool Assets::update(AnyAsset& handle) const
return false;
}

if (handle.mId != handle.reflectedId)
if (handle.mId != assetEntry->meta.getId())
{
handle = AnyAsset{handle.reflectedId};
handle = AnyAsset{assetEntry->meta.getId()};
handle.mVersion = assetEntry->version;
return true;
}
Expand Down Expand Up @@ -620,6 +621,18 @@ std::unique_lock<std::shared_mutex> Assets::lockWrite(const AnyAsset& handle) co
abort();
}

std::string Assets::isPath(const std::string& path)
{
if (path.find('/') != std::string::npos || path.find('\\') != std::string::npos)
{
return "Path";
}
else if (auto id = uuids::uuid::from_string(path))
{
return "UUID";
}
return "Invalid format";
}
std::shared_ptr<Assets::Entry> Assets::entry(const AnyAsset& handle) const
{
// If the handle is null, we can't access the asset.
Expand All @@ -632,15 +645,33 @@ std::shared_ptr<Assets::Entry> Assets::entry(const AnyAsset& handle) const
// Lock the entries map for reading.
auto sharedLock = std::shared_lock(mMutex);

// Search for the entry in the map.
auto it = mEntries.find(handle.getId());
if (it == mEntries.end())
auto sid = handle.getIdString();

if (isPath(sid) == "Path")
{
for (const auto& [eid, entry] : mEntries)
{
if (entry->meta.getPath() == sid)
{
return entry;
}
}
CUBOS_ERROR("No such asset {}", handle);
return nullptr;
}

return it->second;
if (isPath(sid) == "UUID")
{
// Search for the entry in the map.
auto it = mEntries.find(handle.getId().value());
if (it == mEntries.end())
{
CUBOS_ERROR("No such asset {}", handle);
return nullptr;
}
return it->second;
}
CUBOS_ERROR("Invalid asset handle");
return nullptr;
}

std::shared_ptr<Assets::Entry> Assets::entry(const AnyAsset& handle, bool create)
Expand All @@ -665,27 +696,57 @@ std::shared_ptr<Assets::Entry> Assets::entry(const AnyAsset& handle, bool create
sharedLock.lock();
}

// Search for an existing entry for the asset.
auto it = mEntries.find(handle.getId());
if (it == mEntries.end())
auto sid = handle.getIdString();

if (isPath(sid) == "Path")
{
// If we're creating the asset, create a new entry for it.
// Otherwise, return nullptr.
for (const auto& [eid, entry] : mEntries)
{
if (entry->meta.getPath() == sid)
{
return entry;
}
}
if (create)
{
auto nUuid = uuids::uuid_random_generator(mRandom.value())();
auto entry = std::make_shared<Entry>();
entry->meta.set("id", uuids::to_string(handle.getId()));
it = mEntries.emplace(handle.getId(), std::move(entry)).first;
entry->meta.set("path", sid);
entry->meta.set("id", uuids::to_string(nUuid));
auto it = mEntries.emplace(nUuid, std::move(entry)).first;
CUBOS_TRACE("Created new asset entry for {}", handle);
return it->second;
}
else

CUBOS_ERROR("No such asset {}", handle);
return nullptr;
}
if (isPath(sid) == "UUID")
{

// Search for an existing entry for the asset.
auto it = mEntries.find(handle.getId().value());
if (it == mEntries.end())
{
CUBOS_ERROR("No such asset {}", handle);
return nullptr;
// If we're creating the asset, create a new entry for it.
// Otherwise, return nullptr.
if (create)
{
auto entry = std::make_shared<Entry>();
entry->meta.set("id", uuids::to_string(handle.getId().value()));
it = mEntries.emplace(handle.getId().value(), std::move(entry)).first;
CUBOS_TRACE("Created new asset entry for {}", handle);
}
else
{
CUBOS_ERROR("No such asset {}", handle);
return nullptr;
}
}
return it->second;
}

return it->second;
CUBOS_ERROR("Invalid asset handle");
return nullptr;
}

std::shared_ptr<AssetBridge> Assets::bridge(const AnyAsset& handle, bool logError) const
Expand Down
Loading

0 comments on commit 1ebeeb5

Please sign in to comment.