[Linux-only] basically bloxstap for sober
at dev 166 lines 4.2 kB view raw
1import type { 2 CurrentStateAPI, 3 fflagList, 4 fflagValue, 5 SoberConfig 6} from "./types"; 7import { currentStateManager } from "./CurrentState"; 8import { 9 eventCollector, 10 type EventType, 11 type EventCallback, 12 type EventSubscription 13} from "./EventCollector"; 14 15type PluginMeta = { 16 name: string; 17 id: string; 18 forceEnable?: boolean; 19 /** the lower, the earlier configs are applied. higher means more priority over end value */ 20 configPrio?: number; 21 description?: string; 22}; 23 24export class Plugin { 25 public name: string; 26 public id: string; 27 public forceEnable: boolean; 28 public configPrio: number; 29 public description: string; 30 31 private runtimeSetFFlags: fflagList = {}; 32 private runtimeSetSoberConfig: SoberConfig = {}; 33 34 constructor({ name, id, forceEnable, configPrio, description }: PluginMeta) { 35 this.description = description || `The "${name}" plugin`; 36 this.name = name; 37 this.id = id; 38 this.forceEnable = forceEnable ?? false; 39 this.configPrio = configPrio ?? 0; 40 this.runtimeSetFFlags = {}; 41 } 42 43 get fflags(): fflagList { 44 return this.runtimeSetFFlags; 45 } 46 47 get soberConfig(): SoberConfig { 48 return this.runtimeSetSoberConfig; 49 } 50 51 public setFFlag(flag: string, value: fflagValue) { 52 this.runtimeSetFFlags[flag] = value; 53 } 54 55 public setSoberConfigOption<K extends keyof SoberConfig>( 56 opt: K, 57 value: NonNullable<SoberConfig[K]> 58 ) { 59 this.runtimeSetSoberConfig[opt] = value; 60 } 61 62 /** 63 * Subscribe to events using the new event system 64 */ 65 public on<T extends EventType>( 66 eventType: T, 67 callback: EventCallback<T> 68 ): EventSubscription { 69 return eventCollector.on(eventType, callback); 70 } 71 72 /** 73 * Legacy method for backward compatibility - maps to new event system 74 */ 75 public hookLog = { 76 GAME_JOIN: (callback: EventCallback<"GAME_JOIN">) => 77 this.on("GAME_JOIN", callback), 78 GAME_LEAVE: (callback: EventCallback<"GAME_LEAVE">) => 79 this.on("GAME_LEAVE", callback), 80 JOIN_LEAVE: ( 81 callback: EventCallback<"PLAYER_JOIN" | "PLAYER_LEAVE"> 82 ) => { 83 // Subscribe to both player join and leave events 84 const joinSub = this.on( 85 "PLAYER_JOIN", 86 callback as EventCallback<"PLAYER_JOIN"> 87 ); 88 const leaveSub = this.on( 89 "PLAYER_LEAVE", 90 callback as EventCallback<"PLAYER_LEAVE"> 91 ); 92 93 return { 94 unsubscribe: () => { 95 joinSub.unsubscribe(); 96 leaveSub.unsubscribe(); 97 } 98 }; 99 }, 100 BLOXSTRAP: (callback: EventCallback<"BLOXSTRAP_RPC">) => 101 this.on("BLOXSTRAP_RPC", callback) 102 }; 103 104 get currentState(): CurrentStateAPI { 105 return { 106 getCurrentState: () => currentStateManager.getCurrentState(), 107 onStateChange: (callback) => 108 currentStateManager.onStateChange(callback), 109 isInGame: () => currentStateManager.isInGame(), 110 getPlaceId: () => currentStateManager.getPlaceId(), 111 getJobId: () => currentStateManager.getJobId() 112 }; 113 } 114} 115 116let pluginsRegistered: Plugin[] = []; 117let pluginMetadatas: PluginMeta[] = []; 118let pluginsRegisterFuncs: ((forceEnable: string[], forceDisable: string[]) => void)[] = []; 119 120export function registerPlugin( 121 details: PluginMeta, 122 initFunc: (plugin: Plugin) => void 123) { 124 pluginMetadatas.push(details); 125 pluginsRegisterFuncs.push(async (forceEnable, forceDisable) => { 126 if (!details.forceEnable && !forceEnable.includes(details.id)) return; 127 if (forceDisable.includes(details.id)) return; 128 if (!details.configPrio) details.configPrio = 0; 129 while (true) { 130 if ( 131 !pluginsRegistered.find( 132 (a) => a.configPrio === details.configPrio 133 ) 134 ) 135 break; 136 details.configPrio++; 137 } 138 const plugin = new Plugin(details); 139 try { 140 await initFunc(plugin); 141 pluginsRegistered.push(plugin); 142 console.log( 143 `[api/Plugin] PluginInit(${plugin.id}): "${plugin.name}" successfully initalized` 144 ); 145 } catch (error) { 146 console.error( 147 `[api/Plugin] PluginInitError(${plugin.id}): "${plugin.name}" errored:`, 148 error 149 ); 150 } 151 }); 152} 153 154export async function registerPluginsAllFinal(forceEnable: string[], forceDisable: string[]) { 155 for (const f of pluginsRegisterFuncs) { 156 await f(forceEnable, forceDisable); 157 } 158} 159 160export function getPlugins(): Plugin[] { 161 return pluginsRegistered; 162} 163 164export function getPluginInfos(): PluginMeta[] { 165 return pluginMetadatas; 166}