@hotbunny/hackhub-content-sdk 0.9.4 → 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -9
- package/index.d.ts +45 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,13 +39,18 @@ export default class MyMod extends Bootstrap {
|
|
|
39
39
|
|
|
40
40
|
## Quests
|
|
41
41
|
|
|
42
|
-
Quests use `CreateData()`
|
|
42
|
+
Quests use a generic type parameter for type-safe data. `CreateData()` initializes the data once when the quest is first claimed. After that, access via `this.Data` and update via `this.SetData()`:
|
|
43
43
|
|
|
44
44
|
```typescript
|
|
45
|
-
import { Quest,
|
|
45
|
+
import { Quest, Network, RegisterQuest } from "@hotbunny/hackhub-content-sdk";
|
|
46
|
+
|
|
47
|
+
interface InfiltrationData {
|
|
48
|
+
targetIp: string;
|
|
49
|
+
attempts: number;
|
|
50
|
+
}
|
|
46
51
|
|
|
47
52
|
@RegisterQuest
|
|
48
|
-
class InfiltrationQuest extends Quest {
|
|
53
|
+
class InfiltrationQuest extends Quest<InfiltrationData> {
|
|
49
54
|
Name = "Infiltration";
|
|
50
55
|
Title = "Server Infiltration";
|
|
51
56
|
Description = "Hack into the target server and download the data.";
|
|
@@ -60,26 +65,32 @@ class InfiltrationQuest extends Quest {
|
|
|
60
65
|
},
|
|
61
66
|
];
|
|
62
67
|
|
|
63
|
-
CreateData() {
|
|
68
|
+
CreateData(): InfiltrationData {
|
|
64
69
|
return {
|
|
65
70
|
targetIp: Network.randomIp(),
|
|
71
|
+
attempts: 0,
|
|
66
72
|
};
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
OnStart() {
|
|
70
76
|
Network.createSubnetNetwork({
|
|
71
|
-
ip: this.Data.targetIp,
|
|
77
|
+
ip: this.Data.targetIp, // fully typed
|
|
72
78
|
type: "ROUTER",
|
|
73
79
|
ports: [{ external: 22, internal: 22, active: true, service: "ssh" }],
|
|
74
80
|
users: [Network.createUser({ username: "admin", password: "secret123" })],
|
|
75
81
|
children: [],
|
|
76
82
|
});
|
|
77
83
|
|
|
78
|
-
Events.on(
|
|
79
|
-
|
|
84
|
+
// Use this.Events.on() — listeners are automatically cleaned up
|
|
85
|
+
// when the quest completes or is abandoned. No memory leaks!
|
|
86
|
+
this.Events.on("Terminal.NmapScan", (data) => {
|
|
87
|
+
if (data.ip === this.Data.targetIp) {
|
|
88
|
+
this.SetData("attempts", this.Data.attempts + 1); // type-safe key & value
|
|
89
|
+
this.completeObjective("scan");
|
|
90
|
+
}
|
|
80
91
|
});
|
|
81
92
|
|
|
82
|
-
Events.on("Terminal.SSH.Connected", (data) => {
|
|
93
|
+
this.Events.on("Terminal.SSH.Connected", (data) => {
|
|
83
94
|
if (data.ip === this.Data.targetIp) this.completeObjective("connect");
|
|
84
95
|
});
|
|
85
96
|
}
|
|
@@ -96,11 +107,15 @@ Quests can send in-game emails and start phone call dialogs:
|
|
|
96
107
|
|
|
97
108
|
```typescript
|
|
98
109
|
@RegisterQuest
|
|
99
|
-
class StoryQuest extends Quest {
|
|
110
|
+
class StoryQuest extends Quest<{ contacted: boolean }> {
|
|
100
111
|
Name = "StoryQuest";
|
|
101
112
|
Title = "Story Quest";
|
|
102
113
|
Objectives = [{ name: "start", description: "Begin the mission" }];
|
|
103
114
|
|
|
115
|
+
CreateData() {
|
|
116
|
+
return { contacted: false };
|
|
117
|
+
}
|
|
118
|
+
|
|
104
119
|
Mails = [
|
|
105
120
|
{ title: "Mission Briefing", content: "Your target is ready. Good luck." },
|
|
106
121
|
];
|
package/index.d.ts
CHANGED
|
@@ -1078,31 +1078,54 @@ export declare abstract class Bootstrap {
|
|
|
1078
1078
|
OnModPackageUnloaded(): void;
|
|
1079
1079
|
}
|
|
1080
1080
|
/**
|
|
1081
|
-
*
|
|
1081
|
+
* Scoped event manager for a quest. All listeners registered via
|
|
1082
|
+
* `this.Events.on()` are automatically removed when the quest
|
|
1083
|
+
* completes or is abandoned, preventing memory leaks.
|
|
1084
|
+
*/
|
|
1085
|
+
export declare class QuestEvents {
|
|
1086
|
+
private _listeners;
|
|
1087
|
+
/** @internal */ _globalOn?: (event: string, callback: Function) => () => void;
|
|
1088
|
+
/**
|
|
1089
|
+
* Listen to a game event or custom event. The listener is automatically
|
|
1090
|
+
* removed when the quest completes or is abandoned.
|
|
1091
|
+
*/
|
|
1092
|
+
on<T extends string>(event: T, callback: (data: T extends keyof ModEventMap ? ModEventMap[T] : any) => void): () => void;
|
|
1093
|
+
/** Remove a specific listener. */
|
|
1094
|
+
off(event: string, callback: Function): void;
|
|
1095
|
+
/** Remove all listeners registered through this quest. */
|
|
1096
|
+
offAll(): void;
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Base class for mod quests. Extend with a generic type for type-safe quest data.
|
|
1100
|
+
*
|
|
1101
|
+
* Use `this.Events.on()` instead of the global `Events.on()` so listeners
|
|
1102
|
+
* are automatically cleaned up when the quest completes or is abandoned.
|
|
1082
1103
|
*
|
|
1083
1104
|
* @example
|
|
1084
1105
|
* ```ts
|
|
1085
|
-
*
|
|
1106
|
+
* interface MyData { targetIp: string; attempts: number; }
|
|
1086
1107
|
*
|
|
1087
1108
|
* @RegisterQuest
|
|
1088
|
-
* export class HackThePlanet extends Quest {
|
|
1109
|
+
* export class HackThePlanet extends Quest<MyData> {
|
|
1089
1110
|
* Name = "HackThePlanet";
|
|
1090
1111
|
* Title = "Hack the Planet";
|
|
1091
|
-
* Rewards = { money: 5000 };
|
|
1092
1112
|
* Objectives = [
|
|
1093
1113
|
* { name: "scan", description: "Scan the target with nmap" },
|
|
1094
|
-
* { name: "exploit", description: "Exploit the vulnerability", unlocksAfter: ["scan"] },
|
|
1095
1114
|
* ];
|
|
1096
1115
|
*
|
|
1116
|
+
* CreateData() {
|
|
1117
|
+
* return { targetIp: "192.168.1.50", attempts: 0 };
|
|
1118
|
+
* }
|
|
1119
|
+
*
|
|
1097
1120
|
* OnStart() {
|
|
1098
|
-
* Events.on("Terminal.NmapScan", (data) => {
|
|
1099
|
-
* if (data.ip ===
|
|
1121
|
+
* this.Events.on("Terminal.NmapScan", (data) => {
|
|
1122
|
+
* if (data.ip === this.Data.targetIp) this.completeObjective("scan");
|
|
1100
1123
|
* });
|
|
1101
1124
|
* }
|
|
1102
1125
|
* }
|
|
1103
1126
|
* ```
|
|
1104
1127
|
*/
|
|
1105
|
-
export declare abstract class Quest {
|
|
1128
|
+
export declare abstract class Quest<T extends Record<string, any> = Record<string, any>> {
|
|
1106
1129
|
abstract Name: string;
|
|
1107
1130
|
abstract Title: string;
|
|
1108
1131
|
abstract Objectives: QuestObjectiveDefinition[];
|
|
@@ -1130,10 +1153,15 @@ export declare abstract class Quest {
|
|
|
1130
1153
|
/** Phone-call dialog tree for this quest. Use this.createDialog(branch) to start. */
|
|
1131
1154
|
Dialog?: QuestDialogDefinition;
|
|
1132
1155
|
/** Quest data created by CreateData(). Populated at runtime by the game engine. */
|
|
1133
|
-
Data:
|
|
1156
|
+
Data: T;
|
|
1157
|
+
/**
|
|
1158
|
+
* Scoped event manager. Listeners registered here are automatically
|
|
1159
|
+
* cleaned up when the quest completes or is abandoned.
|
|
1160
|
+
*/
|
|
1161
|
+
Events: QuestEvents;
|
|
1134
1162
|
/**
|
|
1135
1163
|
* Called when the quest is first claimed/started.
|
|
1136
|
-
* Use this to set up event listeners
|
|
1164
|
+
* Use this to set up event listeners via `this.Events.on()`.
|
|
1137
1165
|
*/
|
|
1138
1166
|
OnStart(): void | Promise<void>;
|
|
1139
1167
|
/** Called when all objectives are completed and the quest finishes. */
|
|
@@ -1143,15 +1171,19 @@ export declare abstract class Quest {
|
|
|
1143
1171
|
/** Called when the quest's objectives listener starts (e.g. after game load). */
|
|
1144
1172
|
OnObjectivesStart(): void;
|
|
1145
1173
|
/** Return initial quest data. Override to provide custom data storage. */
|
|
1146
|
-
CreateData():
|
|
1174
|
+
CreateData(): T;
|
|
1175
|
+
/** Update a single key in the quest data and persist it. */
|
|
1176
|
+
SetData<K extends keyof T>(key: K, value: T[K]): void;
|
|
1147
1177
|
/** Complete an objective by its name. */
|
|
1148
1178
|
completeObjective(name: string): void;
|
|
1149
1179
|
/** Send a mail from the Mails array by index. */
|
|
1150
1180
|
sendMail(index: number, from?: string, to?: string): void;
|
|
1151
1181
|
/** Start a phone-call dialog. Defaults to "default" branch. */
|
|
1152
1182
|
createDialog(branch?: string, startIndex?: number): void;
|
|
1183
|
+
/** @internal */ _completeObjectiveInternal?: (index: number) => void;
|
|
1153
1184
|
/** @internal */ _sendMailInternal?: (index: number, from?: string, to?: string) => void;
|
|
1154
1185
|
/** @internal */ _createDialogInternal?: (branch: string, startIndex: number) => void;
|
|
1186
|
+
/** @internal */ _saveDataInternal?: () => void;
|
|
1155
1187
|
}
|
|
1156
1188
|
/**
|
|
1157
1189
|
* Base class for mod websites. Mod authors extend this to create
|
|
@@ -1311,6 +1343,7 @@ export declare abstract class App {
|
|
|
1311
1343
|
*/
|
|
1312
1344
|
Exports?: Record<string, any>;
|
|
1313
1345
|
}
|
|
1346
|
+
type () => void$1 = () => void;
|
|
1314
1347
|
/**
|
|
1315
1348
|
* Game events API. Allows mods to listen to in-game events
|
|
1316
1349
|
* such as terminal commands, file operations, network events, etc.
|
|
@@ -1348,7 +1381,7 @@ export declare namespace Events {
|
|
|
1348
1381
|
* For known `ModEventMap` keys, the callback is fully typed.
|
|
1349
1382
|
* For custom string events, the callback receives `any`.
|
|
1350
1383
|
*/
|
|
1351
|
-
export function on<T extends string>(event: T, callback: (data: T extends keyof ModEventMap ? ModEventMap[T] : any) => void): () => void;
|
|
1384
|
+
export function on<T extends string>(event: T, callback: (data: T extends keyof ModEventMap ? ModEventMap[T] : any) => void): () => void$1;
|
|
1352
1385
|
/** Remove a specific event listener. */
|
|
1353
1386
|
export function off(event: string, callback: Function): void;
|
|
1354
1387
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hotbunny/hackhub-content-sdk",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.6",
|
|
4
4
|
"description": "Official modding SDK for HackHub - Ultimate Hacker Simulator on Steam. Create custom quests, websites, terminal commands, and desktop apps.",
|
|
5
5
|
"types": "index.d.ts",
|
|
6
6
|
"exports": {
|