Update Healer Man server

This commit is contained in:
phenom committed 2026-10-04 13:13:35 -04:00
1 parent 39458d5de6
commit 6cc1b9217f
203 files changed
+133929 -31271

No files matched your search

+43 -6
View File
@@ -11,6 +11,8 @@ import {
} from "./combatMediaPack";
const MAX_COMBAT_VOICES = 32;
const MAX_CACHED_AUDIO_BUFFERS = 64;
const MAX_CACHED_AUDIO_BYTES = 32 * 1024 * 1024;
interface ActiveVoice {
readonly group: CombatActorGroup;
@@ -34,6 +36,23 @@ const activeVoices: ActiveVoice[] = [];
const activeCastLoops = new Map<string, ActiveCastLoop>();
const activeMasterVoices = new Set<() => void>();
const decodedBuffers = new Map<string, Promise<AudioBuffer | null>>();
const decodedBufferBytes = new Map<string, number>();
let cachedAudioBytes = 0;
function removeCachedAudio(url: string): void {
decodedBuffers.delete(url);
cachedAudioBytes -= decodedBufferBytes.get(url) ?? 0;
decodedBufferBytes.delete(url);
}
function trimCachedAudio(): void {
while (decodedBuffers.size > MAX_CACHED_AUDIO_BUFFERS || cachedAudioBytes > MAX_CACHED_AUDIO_BYTES) {
const oldest = decodedBuffers.keys().next().value;
if (oldest === undefined) break;
// Active sources retain their own buffer; eviction never stops playback.
removeCachedAudio(oldest);
}
}
export function masterAudioOutputEnabled(masterVolume: number): boolean {
return Number.isFinite(masterVolume) && masterVolume > 0;
@@ -222,13 +241,31 @@ function soundsForEvent(event: CombatPresentationEvent): readonly CombatEffectSo
async function decodeAudio(url: string): Promise<AudioBuffer | null> {
const audioContext = ensureContext();
if (!audioContext) return null;
if (!decodedBuffers.has(url)) {
decodedBuffers.set(url, fetchOptionalMediaAsset(url)
.then((response) => response.ok ? response.arrayBuffer() : Promise.reject(new Error(String(response.status))))
.then((data) => audioContext.decodeAudioData(data))
.catch(() => null));
const cached = decodedBuffers.get(url);
if (cached) {
decodedBuffers.delete(url);
decodedBuffers.set(url, cached);
return cached;
}
return decodedBuffers.get(url)!;
const request = fetchOptionalMediaAsset(url)
.then((response) => response.ok ? response.arrayBuffer() : Promise.reject(new Error(String(response.status))))
.then((data) => audioContext.decodeAudioData(data))
.then((buffer) => {
if (decodedBuffers.get(url) === request) {
const bytes = buffer.length * buffer.numberOfChannels * Float32Array.BYTES_PER_ELEMENT;
decodedBufferBytes.set(url, Number.isFinite(bytes) ? Math.max(0, bytes) : 0);
cachedAudioBytes += decodedBufferBytes.get(url)!;
trimCachedAudio();
}
return buffer;
})
.catch(() => {
if (decodedBuffers.get(url) === request) removeCachedAudio(url);
return null;
});
decodedBuffers.set(url, request);
trimCachedAudio();
return request;
}
export async function playMasterAudioCue(