@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.
Files changed (3) hide show
  1. package/README.md +71 -34
  2. package/index.d.ts +17 -10
  3. 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, Storage } from "@hotbunny/hackhub-content-sdk";
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
- const difficulty = Storage.get<string>("difficulty");
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
- definition = {
49
- name: "Infiltration",
50
- title: "Server Infiltration",
51
- description: "Hack into the target server and download the data.",
52
- employer: { name: "Mr. X", avatar: "mrx" },
53
- rewards: { money: 5000, xp: 200 },
54
- objectives: [
55
- {
56
- name: "scan",
57
- description: "Scan the target server",
58
- trigger: { event: "Terminal.NmapScan", condition: (data) => data.ip === this.Data.targetIp },
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
- Data = {
69
- targetIp: "10.0.0.50",
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
- // ... definition, Data ...
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. Settings values are accessible via `Storage.get(key)`.
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, RegisterModPackage, Storage } from "@hotbunny/hackhub-content-sdk";
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 = Storage.get<string>("difficulty");
188
- const hints = Storage.get<boolean>("showHints");
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. Mod authors extend this to create custom 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
- * import { Quest, RegisterQuest } from "@hotbunny/hackhub-content-sdk";
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
- * Events.on("Terminal.NmapScan", (data) => {
1099
- * if (data.ip === "192.168.1.50") this.completeObjective("scan");
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(): any;
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",
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": {