ModEventMap
The ModEventMap interface defines all typed game events available through the Events.on() and Events.emit() APIs. These events correspond to in-game actions and systems.
ModEventMap is the authoritative source for event payloads: it is generated from the game's own event bus, so your editor infers the exact payload for an event name. The Events guide lists all 99 events with their payload types.
Declaration Merging
You can extend ModEventMap to add type-safe custom events:
declare module "@hotbunny/hackhub-content-sdk" {
interface ModEventMap {
"MyMod.BossDefeated": { bossName: string; reward: number };
"MyMod.LevelUp": { level: number; xp: number };
}
}After declaration merging, Events.on() and Events.emit() provide full type inference:
Events.on("MyMod.BossDefeated", (data) => {
// data is typed as { bossName: string; reward: number }
console.log(data.bossName);
});Payloads are not all objects
A payload is whatever the engine raised, which is not always a wrapper object. ModEventMap maps each name to one of:
| Shape | Example events |
|---|---|
A named *Event interface | Terminal.NmapScan, Network.PortChanges, Bank.Transfer |
A bare string | Terminal.SSH.Connected, Terminal.Geoip, WeeChat.Connected, Subfinder.Try |
A bare boolean | Bettercap.NetProbe |
null | Network.WifiDisconnected, Bettercap.Open |
undefined | Game.SessionStarted, Metasploit.Msfconsole |
FileInfo | Terminal.Cat, Terminal.Ls, Files.Deleted, Hashcat |
| An engine object with PascalCase fields | Quest.Claimed, Browser.WebsiteOpened |
Because the map is typed, destructuring a primitive payload is a compile error rather than a silent undefined:
// Right: the payload IS the IP.
Events.on("Terminal.SSH.Connected", (ip) => ip === "192.168.1.50");
// Wrong: Property 'ip' does not exist on type 'string'.
Events.on("Terminal.SSH.Connected", ({ ip }) => ip === "192.168.1.50");Deprecated payload interfaces
A number of *Event interfaces described payloads that the game never actually sent. They are still exported so older packs keep compiling, but each is marked @deprecated with the shape to use instead. Hover one in your editor to see the replacement.
Among them: SSHConnectedEvent, FTPConnectEvent, GeoipEvent, TerminalSSHShutdownEvent, TerminalLynxSearchEvent, WeeChatConnectedEvent, WeeChatDisconnectedEvent, SubfinderTryEvent, BCCNewsOpenedEvent, BettercapNetProbeEvent, NetworkWifiDisconnectedEvent, MetasploitMsfconsoleEvent, CatEvent, TerminalLsEvent, HashcatEvent, SSHFileDownloadEvent.
If a condition written against a payload field never matches, this is usually why.
Using Events in Quests
Inside a Quest class, use this.Events.on() for automatic cleanup. Register listeners in OnObjectivesStart() (runs on claim and on every game start), not OnStart() (runs once at claim). Listeners are lost on reload and only OnObjectivesStart() re-attaches them:
@RegisterQuest
class HackTheServer extends Quest {
// Runs on claim and on every game start, so listeners survive reloads.
OnObjectivesStart() {
this.Events.on("Terminal.NmapScan", (data) => {
if (data.ip === "192.168.1.50") {
this.completeObjective("scan");
}
});
this.Events.on("Terminal.SSH.Connected", (ip) => {
if (ip === "192.168.1.50") {
this.completeObjective("connect");
}
});
}
}Custom Events for Cross-Mod Communication
// Register (optional, for discoverability)
Events.register("MyMod.DataReady");
// Emit
Events.emit("MyMod.DataReady", { items: ["sword", "shield"] });
// Listen (from any mod)
Events.on("MyMod.DataReady", (data) => {
console.log(data.items);
});Custom events are delivered through both the game event bus and the custom event bus, allowing both game-engine systems and other mods to react.
