gistfile1.txt
· 2.9 KiB · Text
Brut
/**
* CharacterRegistry — central lookup for Character objects.
*
* Usage:
* import { CharacterRegistry } from "@registry/CharacterRegistry";
*
* const char = CharacterRegistry.find("«Flash»");
* const chars = CharacterRegistry.forUser("flash");
* const active = CharacterRegistry.getActive("flash");
*/
import fs from "fs";
import path from "path";
import { Character, UserKey, CharName } from "@types";
import { Paths } from "@helpers/paths";
const CHARS_PATH = Paths.data("characters.json");
let _cache: Record<string, { characters: Character[] }> | null = null;
function loadChars(): Record<string, { characters: Character[] }> {
if (!_cache) {
try {
_cache = JSON.parse(fs.readFileSync(CHARS_PATH, "utf8"));
console.log("[CharacterRegistry] cache miss — loading from disk");
}
catch { _cache = {}; }
}
return _cache!;
}
export const CharacterRegistry = {
/**
* Find a character by name across all users.
* Character names are unique across users (enforced by conflict system).
*/
find(charName: CharName): Character | null {
const chars = loadChars();
for (const data of Object.values(chars)) {
const found = data.characters?.find(
(c) => c.name.toLowerCase() === charName.toLowerCase()
);
if (found) return found;
}
return null;
},
all(): Character[] {
const chars = loadChars();
return Object.values(chars).flatMap((data: any) => data.characters ?? []);
},
/**
* Find a character by name for a specific user.
*/
findForUser(userKey: UserKey, charName: CharName): Character | null {
const chars = loadChars();
return chars[userKey]?.characters?.find(
(c) => c.name.toLowerCase() === charName.toLowerCase()
) ?? null;
},
/**
* Get all characters for a user.
*/
forUser(userKey: UserKey): Character[] {
const chars = loadChars();
return chars[userKey]?.characters ?? [];
},
/**
* Get the active character for a user (from characters.json only, no borrow).
* For effective char (including borrows), use getEffectiveCharacter from borrow.ts.
*/
getActive(userKey: UserKey): Character | null {
return CharacterRegistry.forUser(userKey).find((c) => c.active) ?? null;
},
/**
* Get all characters shared with a given user across all owners.
*/
sharedWith(userKey: UserKey): { char: Character; ownerKey: UserKey }[] {
const chars = loadChars();
const result: { char: Character; ownerKey: UserKey }[] = [];
for (const [ownerKey, data] of Object.entries(chars)) {
if (ownerKey === userKey) continue;
for (const char of data.characters ?? []) {
if (char.sharedWith?.includes(userKey)) {
result.push({ char, ownerKey });
}
}
}
return result;
},
invalidateCache(): void {
_cache = null;
},
};
| 1 | /** |
| 2 | * CharacterRegistry — central lookup for Character objects. |
| 3 | * |
| 4 | * Usage: |
| 5 | * import { CharacterRegistry } from "@registry/CharacterRegistry"; |
| 6 | * |
| 7 | * const char = CharacterRegistry.find("«Flash»"); |
| 8 | * const chars = CharacterRegistry.forUser("flash"); |
| 9 | * const active = CharacterRegistry.getActive("flash"); |
| 10 | */ |
| 11 | |
| 12 | import fs from "fs"; |
| 13 | import path from "path"; |
| 14 | import { Character, UserKey, CharName } from "@types"; |
| 15 | import { Paths } from "@helpers/paths"; |
| 16 | |
| 17 | const CHARS_PATH = Paths.data("characters.json"); |
| 18 | |
| 19 | let _cache: Record<string, { characters: Character[] }> | null = null; |
| 20 | |
| 21 | function loadChars(): Record<string, { characters: Character[] }> { |
| 22 | if (!_cache) { |
| 23 | try { |
| 24 | _cache = JSON.parse(fs.readFileSync(CHARS_PATH, "utf8")); |
| 25 | console.log("[CharacterRegistry] cache miss — loading from disk"); |
| 26 | } |
| 27 | catch { _cache = {}; } |
| 28 | } |
| 29 | |
| 30 | return _cache!; |
| 31 | } |
| 32 | |
| 33 | export const CharacterRegistry = { |
| 34 | /** |
| 35 | * Find a character by name across all users. |
| 36 | * Character names are unique across users (enforced by conflict system). |
| 37 | */ |
| 38 | find(charName: CharName): Character | null { |
| 39 | const chars = loadChars(); |
| 40 | for (const data of Object.values(chars)) { |
| 41 | const found = data.characters?.find( |
| 42 | (c) => c.name.toLowerCase() === charName.toLowerCase() |
| 43 | ); |
| 44 | if (found) return found; |
| 45 | } |
| 46 | return null; |
| 47 | }, |
| 48 | |
| 49 | all(): Character[] { |
| 50 | const chars = loadChars(); |
| 51 | return Object.values(chars).flatMap((data: any) => data.characters ?? []); |
| 52 | }, |
| 53 | |
| 54 | /** |
| 55 | * Find a character by name for a specific user. |
| 56 | */ |
| 57 | findForUser(userKey: UserKey, charName: CharName): Character | null { |
| 58 | const chars = loadChars(); |
| 59 | return chars[userKey]?.characters?.find( |
| 60 | (c) => c.name.toLowerCase() === charName.toLowerCase() |
| 61 | ) ?? null; |
| 62 | }, |
| 63 | |
| 64 | /** |
| 65 | * Get all characters for a user. |
| 66 | */ |
| 67 | forUser(userKey: UserKey): Character[] { |
| 68 | const chars = loadChars(); |
| 69 | return chars[userKey]?.characters ?? []; |
| 70 | }, |
| 71 | |
| 72 | /** |
| 73 | * Get the active character for a user (from characters.json only, no borrow). |
| 74 | * For effective char (including borrows), use getEffectiveCharacter from borrow.ts. |
| 75 | */ |
| 76 | getActive(userKey: UserKey): Character | null { |
| 77 | return CharacterRegistry.forUser(userKey).find((c) => c.active) ?? null; |
| 78 | }, |
| 79 | |
| 80 | /** |
| 81 | * Get all characters shared with a given user across all owners. |
| 82 | */ |
| 83 | sharedWith(userKey: UserKey): { char: Character; ownerKey: UserKey }[] { |
| 84 | const chars = loadChars(); |
| 85 | const result: { char: Character; ownerKey: UserKey }[] = []; |
| 86 | for (const [ownerKey, data] of Object.entries(chars)) { |
| 87 | if (ownerKey === userKey) continue; |
| 88 | for (const char of data.characters ?? []) { |
| 89 | if (char.sharedWith?.includes(userKey)) { |
| 90 | result.push({ char, ownerKey }); |
| 91 | } |
| 92 | } |
| 93 | } |
| 94 | return result; |
| 95 | }, |
| 96 | |
| 97 | invalidateCache(): void { |
| 98 | _cache = null; |
| 99 | }, |
| 100 | }; |