79 lines
3.1 KiB
JavaScript
79 lines
3.1 KiB
JavaScript
// 发布版专用。全文索引只存在于内存,不读写台词文件。
|
|
class StoryCache {
|
|
constructor(cdp, ops, triggers, { retryMs = 3000, maxAttempts = 4, now = Date.now } = {}) {
|
|
Object.assign(this, { cdp, ops, triggers, retryMs, maxAttempts, now });
|
|
this.revision = 0;
|
|
this.reset();
|
|
}
|
|
key() {
|
|
return this.cdp.ws?.readyState === 1 && this.cdp.sessionId
|
|
? `${this.cdp.port}:${this.cdp.generation}:${this.cdp.sessionId}` : null;
|
|
}
|
|
reset(key = null) {
|
|
this.connection = key;
|
|
this.index = null;
|
|
this.pending = null;
|
|
this.attempts = 0;
|
|
this.nextTryAt = 0;
|
|
this.error = '';
|
|
this.at = 0;
|
|
++this.revision;
|
|
}
|
|
sync() {
|
|
const key = this.key();
|
|
if (key !== this.connection) this.reset(key);
|
|
return key;
|
|
}
|
|
status() {
|
|
const key = this.sync();
|
|
const loaded = !!this.index;
|
|
return {
|
|
state: !key ? 'disconnected' : loaded ? 'ready' : this.pending ? 'loading'
|
|
: this.error ? (this.attempts >= this.maxAttempts ? 'error' : 'retrying') : 'waiting',
|
|
loaded, revision: this.revision, connection: key, error: this.error,
|
|
attempts: this.attempts, at: this.at,
|
|
count: loaded ? Object.keys(this.index).length : 0,
|
|
triggersWithLine: loaded ? this.triggers.filter(t => this.index[String(t.storyId)]).length : 0,
|
|
};
|
|
}
|
|
get(id) { this.sync(); return this.index?.[String(id)] || null; }
|
|
async ensure({ force = false } = {}) {
|
|
let key = this.sync();
|
|
if (!key) {
|
|
if (!(await this.cdp.isAlive())) return null;
|
|
try { await this.cdp.connect(); } catch { this.sync(); return null; }
|
|
key = this.sync();
|
|
}
|
|
if (!key) return null;
|
|
if (this.pending) return this.pending;
|
|
if (force) { this.reset(key); }
|
|
else if (this.index || this.attempts >= this.maxAttempts || this.now() < this.nextTryAt) return this.index;
|
|
const revision = this.revision;
|
|
++this.attempts;
|
|
const task = (async () => {
|
|
try {
|
|
const result = await this.cdp.evalJson(this.ops.readStoryIndex());
|
|
if (this.key() !== key || this.revision !== revision) return null;
|
|
if (!result?.ok || !result.index || !Object.keys(result.index).length) {
|
|
throw new Error(result?.error || '故事表尚未就绪或没有可读台词');
|
|
}
|
|
this.index = result.index;
|
|
this.error = '';
|
|
this.at = this.now();
|
|
++this.revision;
|
|
return this.index;
|
|
} catch (e) {
|
|
if (this.key() !== key || this.revision !== revision) return null;
|
|
this.error = String(e.message || e);
|
|
this.nextTryAt = this.now() + this.retryMs;
|
|
++this.revision;
|
|
return null;
|
|
}
|
|
})();
|
|
this.pending = task;
|
|
try { return await task; }
|
|
finally { if (this.pending === task) this.pending = null; }
|
|
}
|
|
}
|
|
module.exports = { StoryCache };
|