mirror of
https://git.srhtnin.garden/sarah/srhtnin.github.io.git
synced 2026-10-13 18:38:11 +02:00
362 lines
6.4 KiB
JavaScript
362 lines
6.4 KiB
JavaScript
const GameContentCacheName =
|
|
"SarahtoninGardenContent";
|
|
|
|
const GameContentCacheSchemaVersion = 3;
|
|
|
|
/*
|
|
* Once content has been checked, page loads for the next
|
|
* five minutes can use the local cache without any API call.
|
|
* After that, only the tiny ContentVersion endpoint is needed
|
|
* unless the catalogue has actually changed.
|
|
*/
|
|
const GameContentVersionCheckInterval =
|
|
5 * 60 * 1000;
|
|
|
|
|
|
let GameContentLoadPromise = null;
|
|
|
|
|
|
async function LoadGameContent() {
|
|
if (GameContentLoadPromise === null) {
|
|
GameContentLoadPromise =
|
|
LoadGameContentInternal();
|
|
}
|
|
|
|
|
|
try {
|
|
return await GameContentLoadPromise;
|
|
} catch (Error) {
|
|
GameContentLoadPromise = null;
|
|
|
|
throw Error;
|
|
}
|
|
}
|
|
|
|
|
|
async function LoadGameContentInternal() {
|
|
const Cache =
|
|
ReadGameContentCache();
|
|
|
|
const Now = Date.now();
|
|
|
|
|
|
if (
|
|
Cache !== null &&
|
|
Now - Cache.CheckedAt <
|
|
GameContentVersionCheckInterval
|
|
) {
|
|
ApplyGameContent(
|
|
Cache.Plants,
|
|
Cache.Mutations
|
|
);
|
|
|
|
return GetLoadedGameContent();
|
|
}
|
|
|
|
|
|
try {
|
|
const ContentVersion =
|
|
await FetchGameContentVersion();
|
|
|
|
|
|
if (
|
|
Cache !== null &&
|
|
Cache.ContentVersion ===
|
|
ContentVersion
|
|
) {
|
|
Cache.CheckedAt = Now;
|
|
|
|
WriteGameContentCache(
|
|
Cache
|
|
);
|
|
|
|
ApplyGameContent(
|
|
Cache.Plants,
|
|
Cache.Mutations
|
|
);
|
|
|
|
return GetLoadedGameContent();
|
|
}
|
|
|
|
|
|
return await RefreshGameContent(
|
|
ContentVersion
|
|
);
|
|
} catch (VersionError) {
|
|
console.warn(
|
|
"Couldn't check Garden content version. " +
|
|
"Refreshing the catalogues directly.",
|
|
VersionError
|
|
);
|
|
|
|
|
|
try {
|
|
return await RefreshGameContent(
|
|
null
|
|
);
|
|
} catch (RefreshError) {
|
|
if (Cache !== null) {
|
|
console.warn(
|
|
"Couldn't refresh Garden content. " +
|
|
"Using the last cached catalogue.",
|
|
RefreshError
|
|
);
|
|
|
|
ApplyGameContent(
|
|
Cache.Plants,
|
|
Cache.Mutations
|
|
);
|
|
|
|
return GetLoadedGameContent();
|
|
}
|
|
|
|
throw RefreshError;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
async function FetchGameContentVersion() {
|
|
const Response =
|
|
await fetch(
|
|
ApiUrl +
|
|
"/ContentVersion.php",
|
|
{
|
|
method: "GET",
|
|
|
|
headers: {
|
|
"Accept":
|
|
"application/json"
|
|
},
|
|
|
|
cache: "no-store"
|
|
}
|
|
);
|
|
|
|
|
|
if (!Response.ok) {
|
|
throw new Error(
|
|
"Content version API returned HTTP " +
|
|
Response.status
|
|
);
|
|
}
|
|
|
|
|
|
const Result =
|
|
await Response.json();
|
|
|
|
|
|
if (
|
|
!Result.Success ||
|
|
typeof Result.Version !==
|
|
"string" ||
|
|
Result.Version.length === 0
|
|
) {
|
|
throw new Error(
|
|
Result.Error ??
|
|
"Content version API returned invalid data."
|
|
);
|
|
}
|
|
|
|
|
|
return Result.Version;
|
|
}
|
|
|
|
|
|
async function RefreshGameContent(
|
|
ContentVersion
|
|
) {
|
|
const [
|
|
FreshPlants,
|
|
FreshMutations
|
|
] =
|
|
await Promise.all([
|
|
FetchPlantsFromApi(),
|
|
FetchMutationsFromApi()
|
|
]);
|
|
|
|
|
|
const Cache = {
|
|
SchemaVersion:
|
|
GameContentCacheSchemaVersion,
|
|
|
|
ContentVersion:
|
|
ContentVersion,
|
|
|
|
CheckedAt:
|
|
Date.now(),
|
|
|
|
Plants:
|
|
FreshPlants,
|
|
|
|
Mutations:
|
|
FreshMutations
|
|
};
|
|
|
|
|
|
WriteGameContentCache(
|
|
Cache
|
|
);
|
|
|
|
ApplyGameContent(
|
|
FreshPlants,
|
|
FreshMutations
|
|
);
|
|
|
|
|
|
return GetLoadedGameContent();
|
|
}
|
|
|
|
|
|
function ApplyGameContent(
|
|
LoadedPlants,
|
|
LoadedMutations
|
|
) {
|
|
Plants = LoadedPlants;
|
|
MutationSets = LoadedMutations;
|
|
}
|
|
|
|
|
|
function GetLoadedGameContent() {
|
|
return {
|
|
Plants: Plants,
|
|
Mutations: MutationSets
|
|
};
|
|
}
|
|
|
|
|
|
function ReadGameContentCache() {
|
|
let RawCache;
|
|
|
|
|
|
try {
|
|
RawCache =
|
|
localStorage.getItem(
|
|
GameContentCacheName
|
|
);
|
|
} catch (Error) {
|
|
console.warn(
|
|
"Couldn't read the Garden content cache.",
|
|
Error
|
|
);
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
if (RawCache === null) {
|
|
return null;
|
|
}
|
|
|
|
|
|
try {
|
|
const Cache =
|
|
JSON.parse(
|
|
RawCache
|
|
);
|
|
|
|
|
|
if (
|
|
!IsValidGameContentCache(
|
|
Cache
|
|
)
|
|
) {
|
|
RemoveGameContentCache();
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
return Cache;
|
|
} catch (Error) {
|
|
RemoveGameContentCache();
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
function IsValidGameContentCache(
|
|
Cache
|
|
) {
|
|
return (
|
|
Cache !== null &&
|
|
typeof Cache === "object" &&
|
|
!Array.isArray(Cache) &&
|
|
|
|
Cache.SchemaVersion ===
|
|
GameContentCacheSchemaVersion &&
|
|
|
|
Number.isFinite(
|
|
Cache.CheckedAt
|
|
) &&
|
|
|
|
Cache.CheckedAt >= 0 &&
|
|
|
|
(
|
|
Cache.ContentVersion ===
|
|
null ||
|
|
typeof Cache.ContentVersion ===
|
|
"string"
|
|
) &&
|
|
|
|
IsGameContentObject(
|
|
Cache.Plants
|
|
) &&
|
|
|
|
IsGameContentObject(
|
|
Cache.Mutations
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
function IsGameContentObject(
|
|
Value
|
|
) {
|
|
return (
|
|
Value !== null &&
|
|
typeof Value === "object" &&
|
|
!Array.isArray(Value)
|
|
);
|
|
}
|
|
|
|
|
|
function WriteGameContentCache(
|
|
Cache
|
|
) {
|
|
try {
|
|
localStorage.setItem(
|
|
GameContentCacheName,
|
|
JSON.stringify(
|
|
Cache
|
|
)
|
|
);
|
|
} catch (Error) {
|
|
console.warn(
|
|
"Couldn't write the Garden content cache.",
|
|
Error
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
function RemoveGameContentCache() {
|
|
try {
|
|
localStorage.removeItem(
|
|
GameContentCacheName
|
|
);
|
|
} catch (Error) {
|
|
console.warn(
|
|
"Couldn't clear the Garden content cache.",
|
|
Error
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
function ClearGameContentCache() {
|
|
RemoveGameContentCache();
|
|
GameContentLoadPromise = null;
|
|
}
|