Fixed cutscene item cutscene trying to recursively load
This commit is contained in:
@@ -86,7 +86,7 @@ typedef struct cutscene_s {
|
||||
#define CUTSCENE_CUTSCENE(CUTSCENE) \
|
||||
{ \
|
||||
.type = CUTSCENE_ITEM_TYPE_CUTSCENE, \
|
||||
.cutscene = CUTSCENE_REFERENCE(CUTSCENE) \
|
||||
.cutsceneRef = { .cutscene = CUTSCENE_REFERENCE(CUTSCENE), .name = NULL } \
|
||||
}
|
||||
|
||||
#define CUTSCENE_CALLBACK(CALLBACK) \
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "asset/assetfile.h"
|
||||
#include "asset/loader/assetloader.h"
|
||||
#include "util/string.h"
|
||||
#include "ui/overlay/uifatalerror.h"
|
||||
|
||||
cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = {
|
||||
[CUTSCENE_ITEM_TYPE_NULL] = { 0 },
|
||||
@@ -364,7 +365,14 @@ void cutsceneCutsceneStart(
|
||||
const cutsceneitem_t *item,
|
||||
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(
|
||||
@@ -374,6 +382,21 @@ bool_t cutsceneCutsceneUpdate(
|
||||
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(
|
||||
yyjson_val *itemObj,
|
||||
cutsceneitem_t *item,
|
||||
@@ -385,23 +408,16 @@ errorret_t cutsceneCutsceneLoad(
|
||||
errorThrow("Cutscene item (CUTSCENE): missing 'name'");
|
||||
}
|
||||
|
||||
// Referenced cutscene is a sibling asset (assets/cutscenes/<name>.jsonc),
|
||||
// resolved to a stable pointer right now rather than deferred to
|
||||
// runtime - assetRequireLoaded re-enters assetUpdate() safely from
|
||||
// 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];
|
||||
// Only validate that the eventual "cutscenes/<name>.jsonc" path would
|
||||
// fit - the reference itself is not resolved/locked until this item
|
||||
// actually runs (see cutsceneCutsceneStart/cutsceneCutsceneResolve).
|
||||
int32_t pathLen = stringFormat(NULL, 0, "cutscenes/%s.jsonc", name);
|
||||
if(pathLen < 0 || (size_t)pathLen >= ASSET_FILE_NAME_MAX) {
|
||||
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);
|
||||
errorChain(assetRequireLoaded(refEntry));
|
||||
item->cutscene = &refEntry->data.cutscene.cutscene;
|
||||
item->cutsceneRef.cutscene = NULL;
|
||||
item->cutsceneRef.name = name;
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -55,6 +55,20 @@
|
||||
|
||||
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 {
|
||||
CUTSCENE_ITEM_TYPE_NULL,
|
||||
|
||||
@@ -117,7 +131,7 @@ struct cutsceneitem_s {
|
||||
cutscenetextminihide_t textMiniHide;
|
||||
cutscenecallback_t callback;
|
||||
cutscenewait_t wait;
|
||||
const cutscene_t *cutscene;
|
||||
cutscenecutsceneref_t cutsceneRef;
|
||||
cutsceneentityteleport_t entityTeleport;
|
||||
cutsceneentitywalkto_t entityWalkTo;
|
||||
cutscenefade_t fade;
|
||||
@@ -235,7 +249,9 @@ bool_t cutsceneItemLookupType(
|
||||
|
||||
/**
|
||||
* 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 data Runtime data storage.
|
||||
@@ -245,6 +261,22 @@ void cutsceneCutsceneStart(
|
||||
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
|
||||
* 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
|
||||
* (rather than its own file) since it's tightly bound to the asset/loader
|
||||
* system itself - it resolves its 'name' to a sibling
|
||||
* assets/cutscenes/<name>.jsonc asset at parse time, mirroring how
|
||||
* cutsceneCutsceneStart/cutsceneCutsceneUpdate stay inline in
|
||||
* cutsceneitem.c rather than getting their own file.
|
||||
* system itself. Only stores the 'name' string (which points into the
|
||||
* cutscene asset's still-alive yyjson doc, so no copy is needed) -
|
||||
* resolving it to the referenced assets/cutscenes/<name>.jsonc asset is
|
||||
* 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 item Destination item, already zeroed by the caller.
|
||||
|
||||
Reference in New Issue
Block a user