Fixed cutscene item cutscene trying to recursively load

This commit is contained in:
2026-09-09 21:21:50 -05:00
parent 70f46e89d3
commit cf51ed1988
3 changed files with 70 additions and 20 deletions
+1 -1
View File
@@ -86,7 +86,7 @@ typedef struct cutscene_s {
#define CUTSCENE_CUTSCENE(CUTSCENE) \ #define CUTSCENE_CUTSCENE(CUTSCENE) \
{ \ { \
.type = CUTSCENE_ITEM_TYPE_CUTSCENE, \ .type = CUTSCENE_ITEM_TYPE_CUTSCENE, \
.cutscene = CUTSCENE_REFERENCE(CUTSCENE) \ .cutsceneRef = { .cutscene = CUTSCENE_REFERENCE(CUTSCENE), .name = NULL } \
} }
#define CUTSCENE_CALLBACK(CALLBACK) \ #define CUTSCENE_CALLBACK(CALLBACK) \
+29 -13
View File
@@ -11,6 +11,7 @@
#include "asset/assetfile.h" #include "asset/assetfile.h"
#include "asset/loader/assetloader.h" #include "asset/loader/assetloader.h"
#include "util/string.h" #include "util/string.h"
#include "ui/overlay/uifatalerror.h"
cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = { cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = {
[CUTSCENE_ITEM_TYPE_NULL] = { 0 }, [CUTSCENE_ITEM_TYPE_NULL] = { 0 },
@@ -364,7 +365,14 @@ void cutsceneCutsceneStart(
const cutsceneitem_t *item, const cutsceneitem_t *item,
cutsceneitemdata_t *data cutsceneitemdata_t *data
) { ) {
if(item->cutscene != NULL) cutsceneSystemStartCutscene(item->cutscene); const cutscene_t *cutscene = item->cutsceneRef.cutscene;
if(cutscene == NULL && item->cutsceneRef.name != NULL) {
cutscene = cutsceneCutsceneResolve(item->cutsceneRef.name);
// Cache the resolved pointer back so replaying this same item (e.g.
// a looping intro) never re-resolves/re-locks the asset again.
((cutsceneitem_t *)item)->cutsceneRef.cutscene = cutscene;
}
if(cutscene != NULL) cutsceneSystemStartCutscene(cutscene);
} }
bool_t cutsceneCutsceneUpdate( bool_t cutsceneCutsceneUpdate(
@@ -374,6 +382,21 @@ bool_t cutsceneCutsceneUpdate(
return false; return false;
} }
const cutscene_t * cutsceneCutsceneResolve(const char_t *name) {
char_t path[ASSET_FILE_NAME_MAX];
stringFormat(path, sizeof(path), "cutscenes/%s.jsonc", name);
assetentry_t *entry = assetLock(path, ASSET_LOADER_TYPE_CUTSCENE, NULL);
errorret_t result = assetRequireLoaded(entry);
if(errorIsNotOk(result)) {
errorCatch(errorPrint(result));
uiFatalErrorOpen(NULL);
return NULL;
}
return &entry->data.cutscene.cutscene;
}
errorret_t cutsceneCutsceneLoad( errorret_t cutsceneCutsceneLoad(
yyjson_val *itemObj, yyjson_val *itemObj,
cutsceneitem_t *item, cutsceneitem_t *item,
@@ -385,23 +408,16 @@ errorret_t cutsceneCutsceneLoad(
errorThrow("Cutscene item (CUTSCENE): missing 'name'"); errorThrow("Cutscene item (CUTSCENE): missing 'name'");
} }
// Referenced cutscene is a sibling asset (assets/cutscenes/<name>.jsonc), // Only validate that the eventual "cutscenes/<name>.jsonc" path would
// resolved to a stable pointer right now rather than deferred to // fit - the reference itself is not resolved/locked until this item
// runtime - assetRequireLoaded re-enters assetUpdate() safely from // actually runs (see cutsceneCutsceneStart/cutsceneCutsceneResolve).
// within this sync load (see its ASSET_ENTRY_STATE_LOADING_SYNC case),
// and the entry is locked forever (never unlocked) so the pointer
// stays valid for the process lifetime, same convention as
// ui/screen/mainmenu/uimainmenu.c's MAIN_MENU_CUTSCENE_ENTRY.
char_t path[ASSET_FILE_NAME_MAX];
int32_t pathLen = stringFormat(NULL, 0, "cutscenes/%s.jsonc", name); int32_t pathLen = stringFormat(NULL, 0, "cutscenes/%s.jsonc", name);
if(pathLen < 0 || (size_t)pathLen >= ASSET_FILE_NAME_MAX) { if(pathLen < 0 || (size_t)pathLen >= ASSET_FILE_NAME_MAX) {
errorThrow("Cutscene item (CUTSCENE): 'name' too long"); errorThrow("Cutscene item (CUTSCENE): 'name' too long");
} }
stringFormat(path, sizeof(path), "cutscenes/%s.jsonc", name);
assetentry_t *refEntry = assetLock(path, ASSET_LOADER_TYPE_CUTSCENE, NULL); item->cutsceneRef.cutscene = NULL;
errorChain(assetRequireLoaded(refEntry)); item->cutsceneRef.name = name;
item->cutscene = &refEntry->data.cutscene.cutscene;
errorOk(); errorOk();
} }
+40 -6
View File
@@ -55,6 +55,20 @@
typedef struct cutscene_s cutscene_t; typedef struct cutscene_s cutscene_t;
// A CUTSCENE item's target. C-authored cutscenes (the CUTSCENE_CUTSCENE
// macro in cutscene.h) resolve cutscene at compile time and leave name
// NULL. JSON-authored cutscenes (cutsceneCutsceneLoad) do the opposite -
// only name is set at parse time, since resolving/locking the referenced
// asset there would eagerly load every cutscene a cutscene merely
// references, even ones never played. cutsceneCutsceneStart resolves
// name to cutscene the first time this item actually runs (via
// cutsceneCutsceneResolve) and caches the result back into this field so
// replaying the same item never re-resolves it.
typedef struct {
const cutscene_t *cutscene;
const char_t *name;
} cutscenecutsceneref_t;
typedef enum { typedef enum {
CUTSCENE_ITEM_TYPE_NULL, CUTSCENE_ITEM_TYPE_NULL,
@@ -117,7 +131,7 @@ struct cutsceneitem_s {
cutscenetextminihide_t textMiniHide; cutscenetextminihide_t textMiniHide;
cutscenecallback_t callback; cutscenecallback_t callback;
cutscenewait_t wait; cutscenewait_t wait;
const cutscene_t *cutscene; cutscenecutsceneref_t cutsceneRef;
cutsceneentityteleport_t entityTeleport; cutsceneentityteleport_t entityTeleport;
cutsceneentitywalkto_t entityWalkTo; cutsceneentitywalkto_t entityWalkTo;
cutscenefade_t fade; cutscenefade_t fade;
@@ -235,7 +249,9 @@ bool_t cutsceneItemLookupType(
/** /**
* Starts a nested-cutscene item, handing control over to the * Starts a nested-cutscene item, handing control over to the
* referenced cutscene. * referenced cutscene. If item->cutsceneRef.cutscene hasn't been
* resolved yet (a JSON-authored reference, only known by name so far),
* resolves and caches it first via cutsceneCutsceneResolve.
* *
* @param item The cutscene item. * @param item The cutscene item.
* @param data Runtime data storage. * @param data Runtime data storage.
@@ -245,6 +261,22 @@ void cutsceneCutsceneStart(
cutsceneitemdata_t *data cutsceneitemdata_t *data
); );
/**
* Resolves a JSON-authored CUTSCENE item's referenced cutscene by name,
* locking (forever - never unlocked, matching cutsceneCutsceneLoad's old
* convention) and blocking on assets/cutscenes/<name>.jsonc. Called
* lazily from cutsceneCutsceneStart the first time the item actually
* runs, not at parse time, so a cutscene referencing many others doesn't
* load every one of them up front. On failure, opens the fatal error
* overlay (matching cutsceneSystemLoad's runtime-failure handling) and
* returns NULL.
*
* @param name Bare cutscene name (without "cutscenes/" prefix or
* ".jsonc" suffix).
* @return The resolved cutscene, or NULL on failure.
*/
const cutscene_t * cutsceneCutsceneResolve(const char_t *name);
/** /**
* Updates a nested-cutscene item. By the time this would run, control * Updates a nested-cutscene item. By the time this would run, control
* has already moved on to the referenced cutscene, so this always * has already moved on to the referenced cutscene, so this always
@@ -262,10 +294,12 @@ bool_t cutsceneCutsceneUpdate(
/** /**
* Parses a CUTSCENE cutscene item's JSON fields into item. Kept inline here * Parses a CUTSCENE cutscene item's JSON fields into item. Kept inline here
* (rather than its own file) since it's tightly bound to the asset/loader * (rather than its own file) since it's tightly bound to the asset/loader
* system itself - it resolves its 'name' to a sibling * system itself. Only stores the 'name' string (which points into the
* assets/cutscenes/<name>.jsonc asset at parse time, mirroring how * cutscene asset's still-alive yyjson doc, so no copy is needed) -
* cutsceneCutsceneStart/cutsceneCutsceneUpdate stay inline in * resolving it to the referenced assets/cutscenes/<name>.jsonc asset is
* cutsceneitem.c rather than getting their own file. * deferred to cutsceneCutsceneStart, the first time this item actually
* runs, so a cutscene referencing many others doesn't pay to load every
* one of them up front.
* *
* @param itemObj The item's JSON object. * @param itemObj The item's JSON object.
* @param item Destination item, already zeroed by the caller. * @param item Destination item, already zeroed by the caller.