Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Asset query api #551

Merged
merged 33 commits into from
Jul 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
b28828a
core: Fix out of date comment
BastianBlokland Jul 4, 2023
5fab1a0
asset: asset_repo_query api
BastianBlokland Jul 5, 2023
29af39e
asset: Implement repo query for mem repo
BastianBlokland Jul 5, 2023
6d93111
core: Minor cleanup in path impl
BastianBlokland Jul 5, 2023
1c15cad
asset: Add result code to repository query
BastianBlokland Jul 6, 2023
0ae08c3
core: Fix typo
BastianBlokland Jul 7, 2023
8135c1e
asset: Implement filesystem repo query
BastianBlokland Jul 7, 2023
d7a4b49
asset: Log filesystem repository query failure
BastianBlokland Jul 7, 2023
90c9d44
asset: Add query api
BastianBlokland Jul 7, 2023
b752efa
asset: Clean up variable name
BastianBlokland Jul 7, 2023
2c7a972
asset: Add query test
BastianBlokland Jul 7, 2023
957ab7b
asset: Additional asset_query test coverage
BastianBlokland Jul 7, 2023
787b344
scene: Update level loading to work with asset handles
BastianBlokland Jul 8, 2023
77a8ad9
scene: Track currently loaded level asset
BastianBlokland Jul 8, 2023
dddf8ff
scene: Add scene_level_reload api
BastianBlokland Jul 8, 2023
efa06d4
game: Update to new level load api
BastianBlokland Jul 8, 2023
04a6a21
debug: Temporary disable level loading / saving
BastianBlokland Jul 8, 2023
fadfe5a
debug: Query level entities
BastianBlokland Jul 8, 2023
70917af
debug: List all levels
BastianBlokland Jul 8, 2023
7ee236c
debug: Highlight loaded level
BastianBlokland Jul 8, 2023
254d2fc
ui: Add refresh shape
BastianBlokland Jul 8, 2023
848315d
debug: Add level refresh button
BastianBlokland Jul 8, 2023
4391543
debug: Add level load buttons
BastianBlokland Jul 8, 2023
ab7fb54
ui: Add save shape
BastianBlokland Jul 8, 2023
cd4854d
debug: Add level save button
BastianBlokland Jul 8, 2023
37108e4
scene: Support level unload
BastianBlokland Jul 8, 2023
8f9bfaa
scene: Change reload when no level has loaded to warning
BastianBlokland Jul 8, 2023
079bc43
debug: Add level unload button
BastianBlokland Jul 8, 2023
56e8548
debug: Minor refactor in level debug panel
BastianBlokland Jul 8, 2023
eae6c48
debug: Disable level button when no level is loaded
BastianBlokland Jul 8, 2023
cecb689
debug: Cleanup save-current level
BastianBlokland Jul 8, 2023
3de7cb1
debug: Add level save hotkey
BastianBlokland Jul 8, 2023
377e54e
Merge branch 'master' into feature/asset-iteration
BastianBlokland Jul 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/game/src/app.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ static void app_action_restart_draw(UiCanvasComp* canvas, const AppActionContext
app_action_notify(ctx, string_lit("Restart"));
log_i("Restart");

scene_level_load(ctx->world, g_appLevel);
scene_level_reload(ctx->world);
}
}

Expand Down Expand Up @@ -610,7 +610,7 @@ void app_ecs_init(EcsWorld* world, const CliInvocation* invoc) {
input_resource_load_map(inputResource, string_lit("global/game-input.imp"));
input_resource_load_map(inputResource, string_lit("global/debug-input.imp"));

scene_level_load(world, g_appLevel);
scene_level_load(world, asset_lookup(world, assets, g_appLevel));
scene_prefab_init(world, string_lit("global/game-prefabs.pfb"));
scene_weapon_init(world, string_lit("global/game-weapons.wea"));
scene_terrain_init(
Expand Down
2 changes: 1 addition & 1 deletion assets/fonts/ui.ftx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
{
"id": "fonts/materialicons_regular.ttf",
"yOffset": -0.15,
"characters": "\uE9E4\uEF5B\uE3AE\uE1DB\uEA5F\uE069\uE338\uE4FC\uE80E\uE5CA\uE9BA\uE5D0\uE89E\uE312\uE5CD\uF016\uE412\uE25E\uE5CF\uE5CE\uE5D7\uE5D6\uE9FE\uE87B\uE71C\uE425\uF230\uEA4A\uEA3C\uE92B\uE162\uE145\uE518\uE8B8\uE322\uE429\uE8F4\uE80B\uE405\uE574\uE868\uE050\uE04F\uE3F4\uE034\uF053\uE7EF"
"characters": "\uE9E4\uEF5B\uE3AE\uE1DB\uEA5F\uE069\uE338\uE4FC\uE80E\uE5CA\uE9BA\uE5D0\uE89E\uE312\uE5CD\uF016\uE412\uE25E\uE5CF\uE5CE\uE5D7\uE5D6\uE9FE\uE87B\uE71C\uE425\uF230\uEA4A\uEA3C\uE92B\uE162\uE145\uE518\uE8B8\uE322\uE429\uE8F4\uE80B\uE405\uE574\uE868\uE050\uE04F\uE3F4\uE034\uF053\uE7EF\uE5D5\uE161"
},
{
"id": "fonts/shapes.ttf",
Expand Down
15 changes: 11 additions & 4 deletions assets/global/debug-input.imp
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,31 @@
"blockers": [ "TextInput" ],
"bindings": [
{ "type": "Down", "key": "ArrowUp" },
{ "type": "Down", "key": "W" }
{ "type": "Down", "key": "W", "illegalModifiers": [ "Control" ] }
]
},
{
"name": "CameraPanBackward",
"blockers": [ "TextInput" ],
"bindings": [
{ "type": "Down", "key": "ArrowDown" },
{ "type": "Down", "key": "S" }
{ "type": "Down", "key": "S", "illegalModifiers": [ "Control" ] }
]
},
{
"name": "CameraPanRight",
"blockers": [ "TextInput" ],
"bindings": [
{ "type": "Down", "key": "ArrowRight" },
{ "type": "Down", "key": "D" }
{ "type": "Down", "key": "D", "illegalModifiers": [ "Control" ] }
]
},
{
"name": "CameraPanLeft",
"blockers": [ "TextInput" ],
"bindings": [
{ "type": "Down", "key": "ArrowLeft" },
{ "type": "Down", "key": "A" }
{ "type": "Down", "key": "A", "illegalModifiers": [ "Control" ] }
]
},
{
Expand All @@ -48,6 +48,13 @@
{ "type": "Down", "key": "MouseExtra1" }
]
},
{
"name": "SaveLevel",
"blockers": [ "TextInput" ],
"bindings": [
{ "type": "Down", "key": "S", "requiredModifiers": [ "Control" ] }
]
},
{
"name": "DebugTimePauseToggle",
"blockers": [ "TextInput" ],
Expand Down
17 changes: 17 additions & 0 deletions libs/asset/include/asset_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "ecs_entity.h"
#include "ecs_module.h"

#define asset_query_max_results 512

typedef struct {
String id, data;
} AssetMemRecord;
Expand Down Expand Up @@ -83,6 +85,21 @@ void asset_reload_request(EcsWorld*, EcsEntityId assetEntity);
*/
bool asset_save(AssetManagerComp*, String id, String data);

/**
* Query for assets that match the given id pattern.
*
* Supported pattern syntax:
* '?' matches any single character.
* '*' matches any number of any characters including none.
*
* NOTE: Returns the number of found assets.
*/
u32 asset_query(
EcsWorld*,
AssetManagerComp*,
String pattern,
EcsEntityId out[PARAM_ARRAY_SIZE(asset_query_max_results)]);

/**
* Debug apis.
*/
Expand Down
24 changes: 24 additions & 0 deletions libs/asset/src/manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,30 @@ bool asset_save(AssetManagerComp* manager, const String id, const String data) {
return asset_repo_save(manager->repo, id, data);
}

typedef struct {
EcsWorld* world;
AssetManagerComp* manager;
u32 count;
EcsEntityId* out;
} AssetQueryContext;

static void asset_query_output(void* ctxRaw, const String id) {
AssetQueryContext* ctx = ctxRaw;
if (LIKELY(ctx->count != asset_query_max_results)) {
ctx->out[ctx->count++] = asset_lookup(ctx->world, ctx->manager, id);
}
}

u32 asset_query(
EcsWorld* world,
AssetManagerComp* manager,
const String pattern,
EcsEntityId out[PARAM_ARRAY_SIZE(asset_query_max_results)]) {
AssetQueryContext ctx = {.world = world, .manager = manager, .out = out};
asset_repo_query(manager->repo, pattern, &ctx, asset_query_output);
return ctx.count;
}

void asset_register_dep(EcsWorld* world, EcsEntityId asset, const EcsEntityId dependency) {
diag_assert(asset);
diag_assert(dependency);
Expand Down
29 changes: 29 additions & 0 deletions libs/asset/src/repo.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
#include "core_alloc.h"
#include "core_array.h"
#include "core_diag.h"

#include "repo_internal.h"

static const String g_assetRepoQueryResultStrs[] = {
string_static("RepoQuerySuccess"),
string_static("RepoQueryErrorNotSupported"),
string_static("RepoQueryErrorPatternNotSupported"),
string_static("RepoQueryErrorWhileQuerying"),
};

ASSERT(
array_elems(g_assetRepoQueryResultStrs) == AssetRepoQueryResult_Count,
"Incorrect number of AssetRepoQueryResult strings");

String asset_repo_query_result_str(const AssetRepoQueryResult result) {
diag_assert(result < AssetRepoQueryResult_Count);
return g_assetRepoQueryResultStrs[result];
}

void asset_repo_destroy(AssetRepo* repo) { repo->destroy(repo); }

AssetSource* asset_repo_source_open(AssetRepo* repo, String id) { return repo->open(repo, id); }
Expand Down Expand Up @@ -30,3 +48,14 @@ bool asset_repo_changes_poll(AssetRepo* repo, u64* outUserData) {
}
return false;
}

AssetRepoQueryResult asset_repo_query(
AssetRepo* repo,
const String filterPattern,
void* context,
const AssetRepoQueryHandler handler) {
if (repo->query) {
return repo->query(repo, filterPattern, context, handler);
}
return AssetRepoQueryResult_ErrorNotSupported;
}
100 changes: 100 additions & 0 deletions libs/asset/src/repo_fs.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "core_alloc.h"
#include "core_file.h"
#include "core_file_iterator.h"
#include "core_file_monitor.h"
#include "core_path.h"
#include "log_logger.h"
Expand Down Expand Up @@ -94,6 +95,104 @@ static bool asset_repo_fs_changes_poll(AssetRepo* repo, u64* outUserData) {
return false;
}

typedef enum {
AssetRepoFsQuery_Recursive = 1 << 0,
} AssetRepoFsQueryFlags;

static AssetRepoQueryResult asset_repo_fs_query_iteration(
AssetRepoFs* repoFs,
const String directory,
const String pattern,
const AssetRepoFsQueryFlags flags,
void* context,
const AssetRepoQueryHandler handler) {

if (UNLIKELY(directory.size) > 256) {
// Sanity check the maximum directory length (relative to the repo root-path).
log_w("AssetRepository: Directory path length exceeds maximum");
return AssetRepoQueryResult_ErrorWhileQuerying;
}

Allocator* alloc = alloc_bump_create_stack(768);
DynString dirBuffer = dynstring_create(alloc, 512);

// Open a file iterator for the absolute path starting from the repo root-path.
path_append(&dirBuffer, repoFs->rootPath);
path_append(&dirBuffer, directory);
FileIterator* itr = file_iterator_create(alloc, dynstring_view(&dirBuffer));

FileIteratorEntry entry;
FileIteratorResult itrResult;
while ((itrResult = file_iterator_next(itr, &entry)) == FileIteratorResult_Found) {
// Construct a file path relative to the repo root-path.
dynstring_clear(&dirBuffer);
path_append(&dirBuffer, directory);
path_append(&dirBuffer, entry.name);
const String path = dynstring_view(&dirBuffer);

switch (entry.type) {
case FileType_Regular:
if (string_match_glob(path, pattern, StringMatchFlags_None)) {
handler(context, path);
}
break;
case FileType_Directory:
if (flags & AssetRepoFsQuery_Recursive) {
// TODO: Handle errors for sub-directory iteration failure.
asset_repo_fs_query_iteration(repoFs, path, pattern, flags, context, handler);
}
break;
case FileType_None:
case FileType_Unknown:
break;
}
}
file_iterator_destroy(itr);

if (UNLIKELY(itrResult != FileIteratorResult_End)) {
log_w(
"AssetRepository: Error while performing file query",
log_param("result", fmt_text(file_iterator_result_str(itrResult))));
return AssetRepoQueryResult_ErrorWhileQuerying;
}
return AssetRepoQueryResult_Success;
}

static AssetRepoQueryResult asset_repo_fs_query(
AssetRepo* repo, const String pattern, void* context, const AssetRepoQueryHandler handler) {
AssetRepoFs* repoFs = (AssetRepoFs*)repo;

// Find a root directory for the query.
const String directory = path_parent(pattern);

static const String g_globChars = string_static("*?");
if (UNLIKELY(!sentinel_check(string_find_first_any(directory, g_globChars)))) {
/**
* Filtering in the directory part part is not supported at the moment.
* Supporting this would require recursing from the first non-filtered directory.
*/
log_w("AssetRepository: Unsupported file query pattern");
return AssetRepoQueryResult_ErrorPatternNotSupported;
}

AssetRepoFsQueryFlags flags = 0;

/**
* Recursive queries are defined by a file-name starting with a wildcard.
*
* For example a query of `dir/ *.txt` will match both 'dir/hello.txt' and 'dir/sub/hello.txt',
* '*.txt' will match any 'txt' files regardless how deeply its nested. This means there's no
* way to search for direct children starting with a wildcard at the moment, in the future we
* can consider supporting more exotic syntax like 'dir/ ** / *.txt' for recursive queries.
*/
const String fileFilter = path_filename(pattern);
if (string_starts_with(fileFilter, string_lit("*"))) {
flags |= AssetRepoFsQuery_Recursive;
}

return asset_repo_fs_query_iteration(repoFs, directory, pattern, flags, context, handler);
}

static void asset_repo_fs_destroy(AssetRepo* repo) {
AssetRepoFs* repoFs = (AssetRepoFs*)repo;

Expand All @@ -113,6 +212,7 @@ AssetRepo* asset_repo_create_fs(String rootPath) {
.destroy = asset_repo_fs_destroy,
.changesWatch = asset_repo_fs_changes_watch,
.changesPoll = asset_repo_fs_changes_poll,
.query = asset_repo_fs_query,
},
.rootPath = string_dup(g_alloc_heap, rootPath),
.monitor = file_monitor_create(g_alloc_heap, rootPath),
Expand Down
19 changes: 17 additions & 2 deletions libs/asset/src/repo_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@
typedef struct sAssetRepo AssetRepo;
typedef struct sAssetSource AssetSource;

typedef void (*AssetRepoQueryHandler)(void* ctx, String assetId);

typedef enum {
AssetRepoQueryResult_Success,
AssetRepoQueryResult_ErrorNotSupported,
AssetRepoQueryResult_ErrorPatternNotSupported,
AssetRepoQueryResult_ErrorWhileQuerying,

AssetRepoQueryResult_Count,
} AssetRepoQueryResult;

/**
* Asset repository.
* NOTE: Api is thread-safe.
Expand All @@ -16,6 +27,7 @@ struct sAssetRepo {
void (*destroy)(AssetRepo*);
void (*changesWatch)(AssetRepo*, String id, u64 userData);
bool (*changesPoll)(AssetRepo*, u64* outUserData);
AssetRepoQueryResult (*query)(AssetRepo*, String pattern, void* ctx, AssetRepoQueryHandler);
};

struct sAssetSource {
Expand All @@ -24,6 +36,8 @@ struct sAssetSource {
void (*close)(AssetSource*);
};

String asset_repo_query_result_str(AssetRepoQueryResult);

AssetRepo* asset_repo_create_fs(String rootPath);
AssetRepo* asset_repo_create_mem(const AssetMemRecord* records, usize recordCount);
void asset_repo_destroy(AssetRepo*);
Expand All @@ -32,5 +46,6 @@ AssetSource* asset_repo_source_open(AssetRepo*, String id);
bool asset_repo_save(AssetRepo*, String id, String data);
void asset_repo_source_close(AssetSource*);

void asset_repo_changes_watch(AssetRepo*, String id, u64 userData);
bool asset_repo_changes_poll(AssetRepo*, u64* outUserData);
void asset_repo_changes_watch(AssetRepo*, String id, u64 userData);
bool asset_repo_changes_poll(AssetRepo*, u64* outUserData);
AssetRepoQueryResult asset_repo_query(AssetRepo*, String pattern, void* ctx, AssetRepoQueryHandler);
21 changes: 20 additions & 1 deletion libs/asset/src/repo_mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

typedef struct {
StringHash idHash;
String id;
String data;
} RepoEntry;

Expand Down Expand Up @@ -37,10 +38,26 @@ static AssetSource* asset_source_mem_open(AssetRepo* repo, const String id) {
return src;
}

static AssetRepoQueryResult asset_repo_mem_query(
AssetRepo* repo, const String pattern, void* ctx, const AssetRepoQueryHandler handler) {
AssetRepoMem* repoMem = (AssetRepoMem*)repo;

dynarray_for_t(&repoMem->entries, RepoEntry, entry) {
if (string_match_glob(entry->id, pattern, StringMatchFlags_None)) {
handler(ctx, entry->id);
}
}

return AssetRepoQueryResult_Success;
}

static void asset_repo_mem_destroy(AssetRepo* repo) {
AssetRepoMem* repoMem = (AssetRepoMem*)repo;

dynarray_for_t(&repoMem->entries, RepoEntry, entry) { string_free(g_alloc_heap, entry->data); };
dynarray_for_t(&repoMem->entries, RepoEntry, entry) {
string_free(g_alloc_heap, entry->id);
string_free(g_alloc_heap, entry->data);
};
dynarray_destroy(&repoMem->entries);

alloc_free_t(g_alloc_heap, repoMem);
Expand All @@ -56,13 +73,15 @@ AssetRepo* asset_repo_create_mem(const AssetMemRecord* records, const usize reco
.destroy = asset_repo_mem_destroy,
.changesWatch = null,
.changesPoll = null,
.query = asset_repo_mem_query,
},
.entries = dynarray_create_t(g_alloc_heap, RepoEntry, recordCount),
};

for (usize i = 0; i != recordCount; ++i) {
RepoEntry entry = {
.idHash = string_hash(records[i].id),
.id = string_dup(g_alloc_heap, records[i].id),
.data = string_dup(g_alloc_heap, records[i].data),
};
*dynarray_insert_sorted_t(&repo->entries, RepoEntry, asset_compare_entry, &entry) = entry;
Expand Down
Loading