@hotbunny/hackhub-content-sdk 0.9.3 → 0.9.5
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 +71 -34
- package/index.d.ts +17 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,13 +23,12 @@ npm install @hotbunny/hackhub-content-sdk --save-dev
|
|
|
23
23
|
Every mod needs a `Bootstrap` class decorated with `@RegisterModPackage`:
|
|
24
24
|
|
|
25
25
|
```typescript
|
|
26
|
-
import { Bootstrap, RegisterModPackage
|
|
26
|
+
import { Bootstrap, RegisterModPackage } from "@hotbunny/hackhub-content-sdk";
|
|
27
27
|
|
|
28
28
|
@RegisterModPackage
|
|
29
29
|
export default class MyMod extends Bootstrap {
|
|
30
30
|
OnModPackageLoaded() {
|
|
31
|
-
|
|
32
|
-
console.log(`Loaded with difficulty: ${difficulty}`);
|
|
31
|
+
console.log("Mod loaded!");
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
OnModPackageUnloaded() {
|
|
@@ -40,43 +39,62 @@ export default class MyMod extends Bootstrap {
|
|
|
40
39
|
|
|
41
40
|
## Quests
|
|
42
41
|
|
|
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
|
```typescript
|
|
44
|
-
import { Quest, Network, RegisterQuest } from "@hotbunny/hackhub-content-sdk";
|
|
45
|
+
import { Quest, Events, Network, RegisterQuest } from "@hotbunny/hackhub-content-sdk";
|
|
46
|
+
|
|
47
|
+
interface InfiltrationData {
|
|
48
|
+
targetIp: string;
|
|
49
|
+
attempts: number;
|
|
50
|
+
}
|
|
45
51
|
|
|
46
52
|
@RegisterQuest
|
|
47
|
-
class InfiltrationQuest extends Quest {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
name: "connect",
|
|
62
|
-
description: "Connect via SSH",
|
|
63
|
-
trigger: { event: "Terminal.SSH.Connected", condition: (data) => data.ip === this.Data.targetIp },
|
|
64
|
-
},
|
|
65
|
-
],
|
|
66
|
-
};
|
|
53
|
+
class InfiltrationQuest extends Quest<InfiltrationData> {
|
|
54
|
+
Name = "Infiltration";
|
|
55
|
+
Title = "Server Infiltration";
|
|
56
|
+
Description = "Hack into the target server and download the data.";
|
|
57
|
+
Employer = { name: "Mr. X", avatar: "mrx" };
|
|
58
|
+
Rewards = { money: 5000, xp: 200 };
|
|
59
|
+
Objectives = [
|
|
60
|
+
{ name: "scan", description: "Scan the target server" },
|
|
61
|
+
{
|
|
62
|
+
name: "connect",
|
|
63
|
+
description: "Connect via SSH",
|
|
64
|
+
unlocksAfter: ["scan"],
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
CreateData(): InfiltrationData {
|
|
69
|
+
return {
|
|
70
|
+
targetIp: Network.randomIp(),
|
|
71
|
+
attempts: 0,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
71
74
|
|
|
72
75
|
OnStart() {
|
|
73
76
|
Network.createSubnetNetwork({
|
|
74
|
-
ip: this.Data.targetIp,
|
|
77
|
+
ip: this.Data.targetIp, // fully typed
|
|
75
78
|
type: "ROUTER",
|
|
76
79
|
ports: [{ external: 22, internal: 22, active: true, service: "ssh" }],
|
|
77
80
|
users: [Network.createUser({ username: "admin", password: "secret123" })],
|
|
78
81
|
children: [],
|
|
79
82
|
});
|
|
83
|
+
|
|
84
|
+
Events.on("Terminal.NmapScan", (data) => {
|
|
85
|
+
if (data.ip === this.Data.targetIp) {
|
|
86
|
+
this.SetData("attempts", this.Data.attempts + 1); // type-safe key & value
|
|
87
|
+
this.completeObjective("scan");
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
Events.on("Terminal.SSH.Connected", (data) => {
|
|
92
|
+
if (data.ip === this.Data.targetIp) this.completeObjective("connect");
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
OnComplete() {
|
|
97
|
+
Network.destroyNetwork(this.Data.targetIp);
|
|
80
98
|
}
|
|
81
99
|
}
|
|
82
100
|
```
|
|
@@ -87,8 +105,14 @@ Quests can send in-game emails and start phone call dialogs:
|
|
|
87
105
|
|
|
88
106
|
```typescript
|
|
89
107
|
@RegisterQuest
|
|
90
|
-
class StoryQuest extends Quest {
|
|
91
|
-
|
|
108
|
+
class StoryQuest extends Quest<{ contacted: boolean }> {
|
|
109
|
+
Name = "StoryQuest";
|
|
110
|
+
Title = "Story Quest";
|
|
111
|
+
Objectives = [{ name: "start", description: "Begin the mission" }];
|
|
112
|
+
|
|
113
|
+
CreateData() {
|
|
114
|
+
return { contacted: false };
|
|
115
|
+
}
|
|
92
116
|
|
|
93
117
|
Mails = [
|
|
94
118
|
{ title: "Mission Briefing", content: "Your target is ready. Good luck." },
|
|
@@ -160,10 +184,10 @@ class MyApp extends App {
|
|
|
160
184
|
|
|
161
185
|
## Mod Settings
|
|
162
186
|
|
|
163
|
-
Players can configure your mod from the in-game Mods menu.
|
|
187
|
+
Players can configure your mod from the in-game Mods menu. Use the `ModSettings` API to read and write setting values:
|
|
164
188
|
|
|
165
189
|
```typescript
|
|
166
|
-
import { Bootstrap, ModSettingDefinition,
|
|
190
|
+
import { Bootstrap, ModSettingDefinition, ModSettings, RegisterModPackage } from "@hotbunny/hackhub-content-sdk";
|
|
167
191
|
|
|
168
192
|
@RegisterModPackage
|
|
169
193
|
export default class MyMod extends Bootstrap {
|
|
@@ -184,8 +208,8 @@ export default class MyMod extends Bootstrap {
|
|
|
184
208
|
];
|
|
185
209
|
|
|
186
210
|
OnModPackageLoaded() {
|
|
187
|
-
const difficulty =
|
|
188
|
-
const hints =
|
|
211
|
+
const difficulty = ModSettings.get<string>("difficulty");
|
|
212
|
+
const hints = ModSettings.get<boolean>("showHints");
|
|
189
213
|
console.log(`Difficulty: ${difficulty}, Hints: ${hints}`);
|
|
190
214
|
}
|
|
191
215
|
}
|
|
@@ -214,6 +238,7 @@ export default class MyMod extends Bootstrap {
|
|
|
214
238
|
| `Mail` | Send in-game emails |
|
|
215
239
|
| `Bank` | Bank accounts and transactions |
|
|
216
240
|
| `Random` | Random generation (id, password, username, pick, etc.) |
|
|
241
|
+
| `ModSettings` | Read/write mod settings configured by the player |
|
|
217
242
|
| `Storage` | Persistent key-value storage per mod |
|
|
218
243
|
| `Variables` | Session-only variables (reset on game close) |
|
|
219
244
|
| `SharedStorage` | Persistent storage shared between mods |
|
|
@@ -239,6 +264,18 @@ Random.pickMultiple(arr, 3); // 3 unique random elements
|
|
|
239
264
|
await Random.sleep(2000); // Wait 2 seconds
|
|
240
265
|
```
|
|
241
266
|
|
|
267
|
+
### ModSettings
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
import { ModSettings } from "@hotbunny/hackhub-content-sdk";
|
|
271
|
+
|
|
272
|
+
ModSettings.get<string>("difficulty"); // Read a setting value
|
|
273
|
+
ModSettings.getAll(); // All settings as key-value map
|
|
274
|
+
ModSettings.set("difficulty", "hard"); // Write a setting value
|
|
275
|
+
ModSettings.reset("difficulty"); // Reset to default
|
|
276
|
+
ModSettings.resetAll(); // Reset all to defaults
|
|
277
|
+
```
|
|
278
|
+
|
|
242
279
|
### Events
|
|
243
280
|
|
|
244
281
|
```typescript
|
package/index.d.ts
CHANGED
|
@@ -1078,31 +1078,32 @@ export declare abstract class Bootstrap {
|
|
|
1078
1078
|
OnModPackageUnloaded(): void;
|
|
1079
1079
|
}
|
|
1080
1080
|
/**
|
|
1081
|
-
* Base class for mod quests.
|
|
1081
|
+
* Base class for mod quests. Extend with a generic type for type-safe quest data.
|
|
1082
1082
|
*
|
|
1083
1083
|
* @example
|
|
1084
1084
|
* ```ts
|
|
1085
|
-
*
|
|
1085
|
+
* interface MyData { targetIp: string; attempts: number; }
|
|
1086
1086
|
*
|
|
1087
1087
|
* @RegisterQuest
|
|
1088
|
-
* export class HackThePlanet extends Quest {
|
|
1088
|
+
* export class HackThePlanet extends Quest<MyData> {
|
|
1089
1089
|
* Name = "HackThePlanet";
|
|
1090
1090
|
* Title = "Hack the Planet";
|
|
1091
|
-
* Rewards = { money: 5000 };
|
|
1092
1091
|
* Objectives = [
|
|
1093
1092
|
* { name: "scan", description: "Scan the target with nmap" },
|
|
1094
|
-
* { name: "exploit", description: "Exploit the vulnerability", unlocksAfter: ["scan"] },
|
|
1095
1093
|
* ];
|
|
1096
1094
|
*
|
|
1095
|
+
* CreateData() {
|
|
1096
|
+
* return { targetIp: "192.168.1.50", attempts: 0 };
|
|
1097
|
+
* }
|
|
1098
|
+
*
|
|
1097
1099
|
* OnStart() {
|
|
1098
|
-
*
|
|
1099
|
-
*
|
|
1100
|
-
* });
|
|
1100
|
+
* console.log(this.Data.targetIp); // fully typed
|
|
1101
|
+
* this.SetData("attempts", 1); // type-safe key & value
|
|
1101
1102
|
* }
|
|
1102
1103
|
* }
|
|
1103
1104
|
* ```
|
|
1104
1105
|
*/
|
|
1105
|
-
export declare abstract class Quest {
|
|
1106
|
+
export declare abstract class Quest<T extends Record<string, any> = Record<string, any>> {
|
|
1106
1107
|
abstract Name: string;
|
|
1107
1108
|
abstract Title: string;
|
|
1108
1109
|
abstract Objectives: QuestObjectiveDefinition[];
|
|
@@ -1129,6 +1130,8 @@ export declare abstract class Quest {
|
|
|
1129
1130
|
Mails?: QuestMailDefinition[];
|
|
1130
1131
|
/** Phone-call dialog tree for this quest. Use this.createDialog(branch) to start. */
|
|
1131
1132
|
Dialog?: QuestDialogDefinition;
|
|
1133
|
+
/** Quest data created by CreateData(). Populated at runtime by the game engine. */
|
|
1134
|
+
Data: T;
|
|
1132
1135
|
/**
|
|
1133
1136
|
* Called when the quest is first claimed/started.
|
|
1134
1137
|
* Use this to set up event listeners for objective completion.
|
|
@@ -1141,15 +1144,19 @@ export declare abstract class Quest {
|
|
|
1141
1144
|
/** Called when the quest's objectives listener starts (e.g. after game load). */
|
|
1142
1145
|
OnObjectivesStart(): void;
|
|
1143
1146
|
/** Return initial quest data. Override to provide custom data storage. */
|
|
1144
|
-
CreateData():
|
|
1147
|
+
CreateData(): T;
|
|
1148
|
+
/** Update a single key in the quest data and persist it. */
|
|
1149
|
+
SetData<K extends keyof T>(key: K, value: T[K]): void;
|
|
1145
1150
|
/** Complete an objective by its name. */
|
|
1146
1151
|
completeObjective(name: string): void;
|
|
1147
1152
|
/** Send a mail from the Mails array by index. */
|
|
1148
1153
|
sendMail(index: number, from?: string, to?: string): void;
|
|
1149
1154
|
/** Start a phone-call dialog. Defaults to "default" branch. */
|
|
1150
1155
|
createDialog(branch?: string, startIndex?: number): void;
|
|
1156
|
+
/** @internal */ _completeObjectiveInternal?: (index: number) => void;
|
|
1151
1157
|
/** @internal */ _sendMailInternal?: (index: number, from?: string, to?: string) => void;
|
|
1152
1158
|
/** @internal */ _createDialogInternal?: (branch: string, startIndex: number) => void;
|
|
1159
|
+
/** @internal */ _saveDataInternal?: () => void;
|
|
1153
1160
|
}
|
|
1154
1161
|
/**
|
|
1155
1162
|
* Base class for mod websites. Mod authors extend this to create
|
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.5",
|
|
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": {
|