Skip to content

Commit b4b572b

Browse files
authored
docs + [自定义的标签和检测.md, 5.条件[Conditions].md Action.md]
1 parent b18008a commit b4b572b

3 files changed

Lines changed: 393 additions & 0 deletions

File tree

5.条件[Conditions].md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# 自定义条件
2+
### 新条件
3+
```csharp
4+
public class CustomCondition : Pathfinder.Action.PathfinderCondition
5+
{
6+
[XMLStorage]
7+
public string Attribute;
8+
9+
public override bool Check(object os_obj)
10+
{
11+
OS os = (OS)os_obj;
12+
13+
// return true if actions inside condition should be triggered
14+
// return false otherwise
15+
}
16+
}
17+
```
18+
### 注册
19+
条件可以手动注册或使用条件属性注册。
20+
```csharp
21+
Pathfinder.Action.ConditionManager.RegisterCondition<CustomCondition>("CustomConditionXMLTag");
22+
```
23+
```csharp
24+
[Pathfinder.Meta.Load.Condition]
25+
public class CustomCondition : PathfinderCondition
26+
```
27+
### 将自定义条件添加到操作文件中
28+
```xml
29+
<CustomConditionXMLTag Attribue="value">
30+
<!--Actions go here!-->
31+
</CustomConditionXMLTag>
32+
```
33+
Conditions的大部分情况都是搭配Mission使用或者直接被打包成了Mission的一个Goel
34+
35+
这里我们将通过一个简单例子来展示如何自定义条件。
36+
这将检查当前节点是否已经被破解(这里检查的是是否获得了管理员权限)。
37+
```csharp
38+
[Pathfinder.Meta.Load.Condition]
39+
public class TestCondition : Pathfinder.Action.PathfinderCondition
40+
{
41+
[XMLStorage]
42+
public string NodeID;
43+
[XMLStorage]
44+
public int PortNumber;
45+
46+
47+
48+
public override bool Check(object os_obj)
49+
{
50+
OS os = (OS)os_obj;
51+
if (os.connectedComp.PlayerHasAdminPermissions())
52+
{
53+
return true;
54+
}
55+
else
56+
{
57+
return false;
58+
}
59+
// return true if actions inside condition should be triggered
60+
// return false otherwise
61+
}
62+
}
63+
```
64+

Action.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# 自定义操作
2+
## 基础行动
3+
```csharp
4+
public class CustomAction : Pathfinder.Action.PathfinderAction
5+
{
6+
[XMLStorage]
7+
public string Attribute1;
8+
9+
[XMLStorage]
10+
public string Attribute2 = "Default value!";
11+
12+
public override void Trigger (object os_obj)
13+
{
14+
OS os = (OS)os_obj;
15+
os.write("Custom action triggered!");
16+
os.write(Attribute1);
17+
os.write(Attribute2);
18+
}
19+
}
20+
```
21+
### 可延迟的行动
22+
DelayablePathfinderAction是PathfinderAction的子类型,实现了DelayHost和Delay属性。(无需添加 [XMLStorage]
23+
public string Attribute1;属性用于存储延迟,他已经默认包含了)
24+
```csharp
25+
public class CustomDelayableAction : Pathfinder.Action.DelayablePathfinderAction
26+
{
27+
public override void Trigger (OS os)
28+
{
29+
if (DelayHost != null)
30+
{
31+
os.write("Custom action triggered after " + Delay + " seconds with host " + DelayHost + "!");
32+
}
33+
}
34+
}
35+
```
36+
### 注册
37+
操作可以手动注册,也可以使用操作属性注册。
38+
```csharp
39+
Pathfinder.Action.ActionManager.RegisterAction<CustomAction>("CustomActionXMLTag");
40+
```
41+
或者
42+
```csharp
43+
44+
[Pathfinder.Meta.Load.Action("CustomActionXMLTag")]
45+
public class CustomAction : PathfinderAction
46+
```
47+
### 将自定义操作添加到操作文件中
48+
```xml
49+
<CustomActionXMLTag Attribute1="Value" />
50+
```
51+
52+
Action更类似于一个用于连接Mod功能与Extension作者推进剧情的助手,它的及时性和延迟效果更适合用来触发一些临时性的事件。同时你也可以将一些Action或者原版没有的操作进行打包,方便在Extension中使用。
53+
54+
下一章我们会讲解它的触发条件的自定义方法。
55+
56+
这里,我们以一个简单的Action例子为例。他的效果是延迟后执行一个指令。
57+
58+
```csharp
59+
[Pathfinder.Meta.Load.Action("CommanderAction")]
60+
public class CommanderAction : Pathfinder.Action.DelayablePathfinderAction
61+
{
62+
public override void Trigger (OS os)
63+
{
64+
[XMLStorage]
65+
public string Command;
66+
67+
68+
if (DelayHost != null)
69+
{
70+
os.execute(Command);
71+
}
72+
}
73+
}
74+
```
75+

自定义的标签和检测.md

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
这部分很麻烦。
2+
3+
本人不建议任何新手在未学习完全部用法且熟练掌握的前提下尝试自定义标签和检测。
4+
5+
而且常有一些十分诡异的bug出现,甚至导致游戏崩溃。
6+
7+
建议新手先学习完所有用法,再尝试自定义标签和检测。
8+
9+
下面开始正式教程。
10+
11+
---
12+
# Hacknet 模组开发指南:标签检测与一次性触发系统
13+
14+
## 🎯 概述
15+
16+
本指南将教你创建一个 Hacknet 模组,实现以下功能:
17+
- 检测计算机是否包含特定标签
18+
- 运行自定义 EXE 程序输出特定信息
19+
- 确保每次连接节点只触发一次
20+
- 支持存档持久化
21+
22+
## 📁 文件架构
23+
24+
```
25+
YourMod/
26+
├── YourModMain.cs // 插件主入口
27+
├── ModCore.cs // 核心逻辑管理器
28+
├── YourModStorage.cs // 数据存储系统
29+
├── TestTagNode.cs // 标签节点定义
30+
├── TestTagLoader.cs // XML标签加载器
31+
└── TestTagExe.cs // 自定义可执行程序
32+
```
33+
34+
## 🔧 详细实现说明
35+
36+
### 1. 插件主入口 (YourModMain.cs)
37+
```csharp
38+
// 作用:模组启动入口,注册所有组件
39+
[BepInPlugin(ModGUID, ModName, ModVer)]
40+
public class YourModMain : HacknetPlugin
41+
{
42+
public override bool Load()
43+
{
44+
new ModCore(); // 初始化核心管理器
45+
// 注册自定义EXE程序
46+
Pathfinder.Executable.ExecutableManager.RegisterExecutable<TestTagExe>("#TestTagExe#");
47+
return true;
48+
}
49+
}
50+
```
51+
52+
**关键点:**
53+
- 使用 `BepInPlugin` 属性声明模组信息
54+
-`Load()` 方法中初始化所有组件
55+
- 必须使用 `ExecutableManager.RegisterExecutable` 注册EXE程序
56+
57+
### 2. 核心逻辑管理器 (ModCore.cs)
58+
```csharp
59+
// 作用:管理触发状态、处理存档事件
60+
public class ModCore
61+
{
62+
private static HashSet<string> triggeredComputers = new HashSet<string>();
63+
64+
public ModCore()
65+
{
66+
// 注册存档加载和保存事件
67+
EventManager<SaveComputerLoadedEvent>.AddHandler(OnComputerLoaded);
68+
EventManager<SaveComputerEvent>.AddHandler(OnComputerSave);
69+
}
70+
71+
public static bool CanTriggerOnComputer(string compId)
72+
{
73+
// 检查是否已触发,确保每次连接只触发一次
74+
if (triggeredComputers.Contains(compId))
75+
return false;
76+
triggeredComputers.Add(compId);
77+
return true;
78+
}
79+
}
80+
```
81+
82+
**关键点:**
83+
- 使用 `HashSet` 跟踪已触发的计算机
84+
- 通过事件系统处理存档的加载和保存
85+
- `CanTriggerOnComputer` 方法是触发控制的核心
86+
87+
### 3. 数据存储系统 (YourModStorage.cs)
88+
```csharp
89+
// 作用:提供全局数据访问接口
90+
public static class YourModStorage
91+
{
92+
private static Dictionary<string, TestTagNode> nodes = new Dictionary<string, TestTagNode>();
93+
94+
public static void AddOrUpdate(string compId, TestTagNode node)
95+
{
96+
nodes[compId] = node;
97+
}
98+
99+
public static bool HasNode(string compId)
100+
{
101+
return nodes.ContainsKey(compId);
102+
}
103+
}
104+
```
105+
106+
**关键点:**
107+
- 使用静态字典存储所有标签节点
108+
- 提供线程安全的访问方法
109+
- 计算机ID作为字典键值
110+
111+
### 4. 标签节点定义 (TestTagNode.cs)
112+
```csharp
113+
// 作用:定义标签的数据结构
114+
public class TestTagNode
115+
{
116+
public bool HasTriggered { get; set; } = false;
117+
118+
public static TestTagNode FromXml(ElementInfo info)
119+
{
120+
var node = new TestTagNode();
121+
// 从XML属性读取触发状态
122+
if (info.Attributes.TryGetValue("HasTriggered", out string triggeredStr))
123+
{
124+
node.HasTriggered = bool.Parse(triggeredStr);
125+
}
126+
return node;
127+
}
128+
}
129+
```
130+
131+
**关键点:**
132+
- `HasTriggered` 属性用于跨游戏会话的持久化
133+
- `FromXml` 方法处理存档数据的反序列化
134+
135+
### 5. XML标签加载器 (TestTagLoader.cs)
136+
```csharp
137+
// 作用:解析计算机XML中的自定义标签
138+
[ComputerExecutor("TestTag")]
139+
public class TestTagLoader : ContentLoader.ComputerExecutor
140+
{
141+
public override void Execute(EventExecutor exec, ElementInfo info)
142+
{
143+
string compId = Comp.idName ?? Comp.name;
144+
var node = new TestTagNode();
145+
YourModStorage.AddOrUpdate(compId, node);
146+
}
147+
}
148+
```
149+
150+
**关键点:**
151+
- 使用 `[ComputerExecutor("TestTag")]` 属性声明标签处理器
152+
- 在游戏加载计算机时自动调用
153+
- 将标签信息存入全局存储
154+
155+
### 6. 自定义可执行程序 (TestTagExe.cs)
156+
```csharp
157+
// 作用:实现具体的检测和输出逻辑
158+
public class TestTagExe : BaseExecutable
159+
{
160+
public TestTagExe(Rectangle location, OS operatingSystem, string[] args)
161+
: base(location, operatingSystem, args)
162+
{
163+
// 获取目标计算机
164+
var targetComp = os.connectedComp ?? os.thisComputer;
165+
string compId = targetComp.idName ?? targetComp.name;
166+
167+
// 检查标签存在性
168+
if (!YourModStorage.HasNode(compId))
169+
{
170+
os.write("NO TestTag!");
171+
needsRemoval = true;
172+
return;
173+
}
174+
175+
// 检查触发权限
176+
if (!ModCore.CanTriggerOnComputer(compId))
177+
{
178+
os.write("Computer TESTED!");
179+
needsRemoval = true;
180+
return;
181+
}
182+
183+
// 执行主要逻辑
184+
os.write("test complete");
185+
needsRemoval = true;
186+
}
187+
}
188+
```
189+
190+
**关键点:**
191+
- 继承 `BaseExecutable`
192+
- 在构造函数中完成所有逻辑(因为程序立即结束)
193+
- 使用 `needsRemoval = true` 让程序执行后自动移除
194+
- 三层检查机制确保正确触发
195+
196+
## 🎮 使用流程
197+
198+
### 1. 在计算机XML中添加标签
199+
```xml
200+
<computer name="TestComputer" ip="123.45.67.89" security="2">
201+
<!-- 其他计算机配置 -->
202+
<TestTag />
203+
</computer>
204+
```
205+
206+
### 2. 在游戏中运行EXE
207+
```
208+
run #TestTagExe#
209+
```
210+
211+
### 3. 预期输出结果
212+
213+
| 场景 | 输出内容 | 说明 |
214+
|------|----------|------|
215+
| 首次运行(有标签) | `test complete` | 正常触发 |
216+
| 重复运行(同一连接) | `Computer TESTED!` | 防止重复触发 |
217+
| 无标签计算机 | `NO TestTag!` | 标签检测失败 |
218+
| 重新连接后运行 | `test complete` | 连接重置后重新触发 |
219+
220+
## 🔄 触发机制详解
221+
222+
### 会话级别控制
223+
- **EXE实例级别**`hasTriggeredThisSession` 确保同一程序实例不重复触发
224+
- **连接会话级别**`ModCore.CanTriggerOnComputer()` 确保同一连接只触发一次
225+
- **游戏会话级别**`TestTagNode.HasTriggered` 记录历史触发状态(用于存档)
226+
227+
### 重置条件
228+
当满足以下条件时,触发状态会被重置:
229+
- 玩家使用 `disconnect` 命令
230+
- 连接到其他计算机
231+
- 游戏重新加载
232+
233+
### 修改标签名称
234+
将所有文件中的 `TestTag` 替换为你想要的标签名:
235+
```csharp
236+
// 在 TestTagLoader.cs 中
237+
[ComputerExecutor("YourCustomTag")]
238+
```
239+
240+
### 修改输出信息
241+
`TestTagExe.cs` 中修改 `os.write()` 的内容:
242+
```csharp
243+
os.write("你的自定义输出信息");
244+
```
245+
246+
247+
248+
## ⚠️ 注意事项
249+
250+
1. **命名空间一致性**:确保所有文件使用相同的命名空间
251+
2. **GUID唯一性**:修改 `ModGUID` 确保不与其他模组冲突
252+
3. **XML标签大小写**:XML标签名称区分大小写
253+
4. **存档兼容性**:修改标签结构时考虑旧存档的兼容性
254+

0 commit comments

Comments
 (0)