import { IPlugin } from "@/src/types/pluginTypes"; import { logger } from "@/src/utils/logger"; import staticWorldState from "@/static/fixed_responses/worldState/worldState.json"; import { config } from "@/src/services/configService"; /* Add this to your config.json worldState section to enable world state sync: "sync":{ "enabled": true, "url": "https://content.warframe.com/dynamic/worldState.php", "fields": { // adjust these fields to your needs "Events": "merge", "InGameMarket": "replace", "SyndicateMissions": "replace", "ActiveMissions": "replace", "VoidTraders": "replace", "PrimeVaultTraders": "replace", "DailyDeals": "replace", "Goals": "replace" }, "interval": 3000 } */ type AnyObj = { [key: string]: object | Array | string | number | boolean }; const staticWorldStateBackup: AnyObj = structuredClone(staticWorldState); let syncWorldStateTimer: NodeJS.Timeout | null = null; interface myConfig { worldState?: { sync?: { enabled: boolean; url: string; fields: { [key: string]: string }; interval?: number; }; }; } export const syncWorldState = async (): Promise => { if (syncWorldStateTimer) { clearTimeout(syncWorldStateTimer); syncWorldStateTimer = null; } const config_ = config as myConfig; if (!config_.worldState?.sync) { logger.info("World state sync is disabled, skipping"); return; } const { enabled, url, fields, interval } = config_.worldState.sync; if (!enabled || !url || !fields) { logger.info("World state sync is not enabled or misconfigured, skipping"); return; } const res = await fetch(url, { method: "GET" }); if (!res.ok) { logger.error("Failed to fetch remote world state, will retry in 5 min", { status: res.status, statusText: res.statusText }); syncWorldStateTimer = setTimeout( () => { void syncWorldState(); }, 5 * 60 * 1000 ); return; } const data = await res.json(); if (!data || typeof data !== "object") { logger.error("Invalid world state sync response", { data }); return; } const staticWorldState_ = staticWorldState as AnyObj; const data_ = data as AnyObj; for (const [name, action_] of Object.entries(fields)) { if (!(name in data)) { logger.warn(`Field ${name} not found in world state sync response`, { data }); continue; } const action = action_ as string; if (action === "replace") { staticWorldState_[name] = data_[name]; continue; } if (action === "merge") { switch (name) { case "Events": { const remoteValue = data_[name] as Array; const localValue = staticWorldStateBackup[name] as Array; if (Array.isArray(remoteValue) && Array.isArray(localValue)) { staticWorldState_[name] = localValue.concat(remoteValue); } } break; default: logger.warn(`Not supported merge action for field ${name} in world state sync`); } continue; } logger.warn(`Unknown action ${action} for field ${name} in world state sync`); continue; } if (interval && interval > 0) { logger.info(`Next world state sync in ${interval} seconds`); syncWorldStateTimer = setTimeout(() => { void syncWorldState(); }, interval * 1000); } else { logger.info("No next world state sync scheduled"); } }; export default class WorldStateSync implements IPlugin { public name = "WorldStateSync"; public version = "1.0.0"; public description = "WorldStateSync plugin for Warframe Emulator"; async initialize(): Promise { logger.info(`[${this.name}] Plugin initialized successfully!`); await syncWorldState(); } async cleanup(): Promise { logger.info(`[${this.name}] Plugin cleanup completed`); if (syncWorldStateTimer) { clearTimeout(syncWorldStateTimer); syncWorldStateTimer = null; } } }