SPT 模组开发指南
本文档基于官方示例项目 server-mod-examples 下载 和核心库 server-csharp/Libraries 下载,为开发者提供完整的模组开发指南。
目录
1. 环境设置
1.1 必备工具
1.2 项目配置
在 .csproj 文件中添加必要的 NuGet 依赖和配置项:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <!-- 目标框架版本,必须设置为 net9.0 --> <TargetFramework>net9.0</TargetFramework> <!-- 根命名空间,建议以下划线开头避免与其他模组冲突 --> <RootNamespace>_MyMod</RootNamespace> <!-- 启用隐式 using 指令,简化代码编写 --> <ImplicitUsings>enable</ImplicitUsings> <!-- 启用可空类型检查,提高代码安全性 --> <Nullable>enable</Nullable> <!-- 输出类型为类库(DLL) --> <OutputType>Library</OutputType> <!-- 模组版本号,遵循 SEMVER 规范 --> <Version>0.0.1</Version> <!-- 输出路径配置,便于部署到 SPT 服务器 --> <OutputPath>bin\$(Configuration)\$(ProjectName)\$(AssemblyName)\</OutputPath> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> </PropertyGroup> <ItemGroup> <!-- SPT 核心库:提供模组基础功能和 API --> <PackageReference Include="SPTarkov.Server.Core" Version="4.0.0" /> <!-- DI 注解库:提供依赖注入相关的属性和接口 --> <PackageReference Include="SPTarkov.DI.Annotations" Version="4.0.0" /> <!-- 语义化版本库:用于版本号解析和比较 --> <PackageReference Include="SemanticVersioning" Version="2.0.0" /> <!-- 可选:Harmony 库,用于运行时方法注入和 Hook --> <PackageReference Include="Harmony" Version="2.2.2" /> </ItemGroup> </Project>
关键配置说明:
-
TargetFramework: 必须设置为
net9.0,与 SPT Server 的运行时版本保持一致 -
RootNamespace: 建议以下划线开头,避免与其他模组或系统命名空间冲突
-
OutputPath: 配置输出路径为
bin\$(Configuration)\$(ProjectName)\$(AssemblyName)\,便于直接复制到 SPT 的user/mods/目录 -
SPTarkov.Server.Core: SPT 模组开发的核心库,提供日志、数据库、配置等服务
-
SPTarkov.DI.Annotations: 提供
[Injectable]等依赖注入注解 -
SemanticVersioning: 用于处理模组版本号和兼容性检查
2. 项目结构
推荐的模组项目结构:
My-Mod/ # 项目根目录 ├── My-Mod.csproj # 项目文件 ├── ModMetadata.cs # 模组元数据(必需) ├── MyModMain.cs # 主入口类 ├── Services/ # 自定义服务(可选) │ └── MyService.cs ├── Handlers/ # 事件处理器(可选) │ └── MyHandler.cs ├── config.json # 自定义配置(可选) ├── data/ # 数据文件(可选) │ └── example.json └── bin/ # 编译输出目录 └── Release/ # Release 构建配置 └── My-Mod/ # 目标框架 ├── My-Mod.dll # 编译后的模组程序集(必需) ├── config.json # 自定义配置(可选) └── data/ # 数据文件(可选)
3. 核心概念
3.1 ModMetadata(模组元数据)
每个模组必须定义 ModMetadata 记录:
using SPTarkov.Server.Core.Models.Spt.Mod;
using SPTarkov.Server.Core.Models.Utils;
public record ModMetadata : AbstractModMetadata
{
// 模组唯一标识(建议使用反向域名格式)
public override string ModGuid { get; init; } = "com.yourname.mymod";
// 模组名称
public override string Name { get; init; } = "My Awesome Mod";
// 作者
public override string Author { get; init; } = "Your Name";
// 版本(遵循 SEMVER)
public override SemanticVersioning.Version Version { get; init; } = new("1.0.0");
// 兼容的 SPT 版本
public override SemanticVersioning.Range SptVersion { get; init; } = new("~4.0.0");
// 依赖的其他模组(可选)
public override Dictionary<string, SemanticVersioning.Range>? ModDependencies { get; init; }
// 不兼容的模组(可选)
public override List<string>? Incompatibilities { get; init; }
// 是否包含资源包
public override bool? IsBundleMod { get; init; } = false;
// 许可证
public override string License { get; init; } = "MIT";
}
3.2 依赖注入(DI)
SPT 使用依赖注入模式,通过构造函数注入服务:
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.DI;
[Injectable]
public class MyService(ISptLogger<MyService> logger)
{
public void DoSomething()
{
logger.Info("Doing something...");
}
}
3.3 加载顺序
通过 [Injectable] 属性的 TypePriority 参数控制加载顺序:
// 在数据库加载后执行
[Injectable(TypePriority = OnLoadOrder.PostDBModLoader + 1)]
public class MyMod : IOnLoad { ... }
常用加载顺序值:
|
顺序值 |
含义 |
|---|---|
|
|
SPT 加载器启动前 |
|
|
数据库加载完成后 |
|
|
水印初始化时 |
|
|
SPT 加载器完成后 |
4. 基础模板
4.1 最小模组模板
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.DI;
using SPTarkov.Server.Core.Models.Spt.Mod;
using SPTarkov.Server.Core.Models.Utils;
// 模组元数据
public record ModMetadata : AbstractModMetadata
{
public override string ModGuid { get; init; } = "com.yourname.minimalmod";
public override string Name { get; init; } = "Minimal Mod";
public override string Author { get; init; } = "Your Name";
public override SemanticVersioning.Version Version { get; init; } = new("1.0.0");
public override SemanticVersioning.Range SptVersion { get; init; } = new("~4.0.0");
public override string License { get; init; } = "MIT";
}
// 主入口类
[Injectable(TypePriority = OnLoadOrder.PostDBModLoader + 1)]
public class MinimalMod(ISptLogger<MinimalMod> logger) : IOnLoad
{
public Task OnLoad()
{
logger.Success("Minimal Mod loaded successfully!");
return Task.CompletedTask;
}
}
4.2 带配置的模组模板
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.DI;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Spt.Mod;
using SPTarkov.Server.Core.Models.Utils;
using System.Reflection;
public record ModMetadata : AbstractModMetadata { /* ... */ }
public record MyConfig
{
public required string Setting1 { get; set; }
public int Setting2 { get; set; } = 100;
}
[Injectable(TypePriority = OnLoadOrder.PostDBModLoader + 1)]
public class MyMod(
ISptLogger<MyMod> logger,
ModHelper modHelper) : IOnLoad
{
public Task OnLoad()
{
// 获取模组目录路径
var modPath = modHelper.GetAbsolutePathToModFolder(Assembly.GetExecutingAssembly());
// 读取配置文件
var config = modHelper.GetJsonDataFromFile<MyConfig>(modPath, "config.json");
logger.Success($"Loaded config: Setting1={config.Setting1}, Setting2={config.Setting2}");
return Task.CompletedTask;
}
}
5. 常用服务详解
5.1 ISptLogger(日志服务)
logger.Success("操作成功"); // 绿色成功消息
logger.Warning("警告信息"); // 黄色警告消息
logger.Error("错误信息"); // 红色错误消息
logger.Info("普通信息"); // 白色信息消息
logger.Critical("严重错误"); // 严重错误消息
logger.Debug("调试信息"); // 仅写入日志文件
logger.LogWithColor("自定义颜色", LogTextColor.Red, LogBackgroundColor.Black);
5.2 DatabaseService(数据库服务)
using SPTarkov.Server.Core.Services;
public class MyMod(DatabaseService databaseService) : IOnLoad
{
public Task OnLoad()
{
// 获取全局配置
var globals = databaseService.GetGlobals();
// 获取藏身处数据
var hideout = databaseService.GetHideout();
// 获取 Bot 数据
var bots = databaseService.GetBots();
// 获取地图数据
var locations = databaseService.GetLocations();
// 获取物品模板
var templates = databaseService.GetTemplates();
return Task.CompletedTask;
}
}
5.3 ConfigServer(配置服务)
using SPTarkov.Server.Core.Servers;
using SPTarkov.Server.Core.Models.Spt.Config;
public class MyMod(ConfigServer configServer) : IOnLoad
{
private readonly BotConfig _botConfig = configServer.GetConfig<BotConfig>();
private readonly HideoutConfig _hideoutConfig = configServer.GetConfig<HideoutConfig>();
private readonly WeatherConfig _weatherConfig = configServer.GetConfig<WeatherConfig>();
public Task OnLoad()
{
// 藏身处建造时间
_hideoutConfig.OverrideCraftTimeSeconds = 60;
// 天气
_weatherConfig.OverrideSeason = Season.WINTER;
return Task.CompletedTask;
}
}
5.4 ModHelper(模组辅助工具)
using SPTarkov.Server.Core.Helpers;
using System.Reflection;
public class MyMod(ModHelper modHelper) : IOnLoad
{
public Task OnLoad()
{
// 获取模组绝对路径
string modPath = modHelper.GetAbsolutePathToModFolder(Assembly.GetExecutingAssembly());
// 读取 JSON 配置
var config = modHelper.GetJsonDataFromFile<MyConfig>(modPath, "config.json");
// 写入 JSON 数据
modHelper.WriteJsonDataToFile(outputData, modPath, "output.json");
return Task.CompletedTask;
}
}
6. 进阶功能
6.1 修改游戏数据库
public class DatabaseEditor(
ISptLogger<DatabaseEditor> logger,
DatabaseService databaseService) : IOnLoad
{
public Task OnLoad()
{
// 修改全局设置
var globals = databaseService.GetGlobals();
globals.Configuration.SavagePlayCooldown = 1; // Scav冷却1秒
// 修改藏身处
var hideout = databaseService.GetHideout();
var toilet = hideout.Areas.FirstOrDefault(a => a.Type == HideoutAreas.WaterCloset);
if (toilet != null)
{
foreach (var stage in toilet.Stages.Values)
{
stage.ConstructionTime = 60; // 建造时间60秒
stage.Requirements.Clear(); // 清空需求
}
}
// 修改 Bot
var bots = databaseService.GetBots();
if (bots.Types.TryGetValue("assault", out var scav))
{
scav.FirstNames.Clear(); // 清空需求
scav.FirstNames.Add("Gary"); // Scav都叫Gary
}
logger.Success("Database edited successfully!");
return Task.CompletedTask;
}
}
6.2 添加自定义商人
using SPTarkov.Server.Core.Routers;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
[Injectable(TypePriority = OnLoadOrder.PostDBModLoader + 1)]
public class CustomTraderMod(
ModHelper modHelper,
ImageRouter imageRouter,
ConfigServer configServer,
TimeUtil timeUtil,
AddCustomTraderHelper traderHelper) : IOnLoad
{
public Task OnLoad()
{
var modPath = modHelper.GetAbsolutePathToModFolder(Assembly.GetExecutingAssembly());
// 加载商人基础配置
var traderBase = modHelper.GetJsonDataFromFile<TraderBase>(modPath, "data/base.json");
// 添加头像路由
imageRouter.AddRoute(traderBase.Avatar.Replace(".jpg", ""),
Path.Combine(modPath, "data/cat.jpg"));
// 设置刷新时间
var traderConfig = configServer.GetConfig<TraderConfig>();
traderHelper.SetTraderUpdateTime(traderConfig, traderBase,
timeUtil.GetHoursAsSeconds(1), timeUtil.GetHoursAsSeconds(2));
// 添加到数据库
traderHelper.AddTraderWithEmptyAssortToDb(traderBase);
traderHelper.AddTraderToLocales(traderBase, "My Trader", "Welcome to my shop!");
// 加载商品配置
var assort = modHelper.GetJsonDataFromFile<TraderAssort>(modPath, "data/assort.json");
traderHelper.OverwriteTraderAssort(traderBase.Id, assort);
return Task.CompletedTask;
}
}
6.3 创建聊天机器人
using SPTarkov.Server.Core.Helpers.Dialogue;
using SPTarkov.Server.Core.Models.Eft.Dialog;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Services;
[Injectable]
public class MyChatBot(MailSendService mailSendService) : IDialogueChatBot
{
public UserDialogInfo GetChatBot()
{
return new UserDialogInfo
{
Id = "myChatBot",
Aid = 9999999,
Info = new UserDialogDetails
{
Nickname = "MyBot",
Side = "Usec",
Level = 69,
MemberCategory = MemberCategory.Sherpa
}
};
}
public ValueTask<string> HandleMessage(MongoId sessionId, SendMessageRequest request)
{
// 回复玩家消息
mailSendService.SendUserMessageToPlayer(sessionId, GetChatBot(),
$"You said: {request.Text}");
return ValueTask.FromResult(request.DialogId);
}
}
6.4 使用 Harmony 注入
using HarmonyLib;
using SPTarkov.Server.Core.Utils;
[Injectable(TypePriority = OnLoadOrder.PreSptModLoader + 1)]
public class HarmonyPatchExample(ISptLogger<HarmonyPatchExample> logger) : IOnLoad
{
public Task OnLoad()
{
var harmony = new Harmony("com.yourname.harmonypatch");
// 查找并 Patch 目标方法
var originalMethod = AccessTools.Method(typeof(TargetClass), "TargetMethod");
var prefixMethod = AccessTools.Method(typeof(HarmonyPatchExample), nameof(MyPrefix));
harmony.Patch(originalMethod, new HarmonyMethod(prefixMethod));
logger.Success("Harmony patches applied!");
return Task.CompletedTask;
}
static bool MyPrefix()
{
// 在原方法执行前执行
return true; // 返回 false 跳过原方法
}
}
7. 调试与测试
7.1 本地测试
方法一:使用 IDE 构建
-
构建模组:在 Visual Studio 中使用 Release 模式构建
-
复制文件:将
bin/Release/My-Mod/目录复制到 SPT 服务器的user/mods/目录下 -
启动服务器:运行 SPT 服务器,查看日志确认模组加载
方法二:使用命令行构建 (新手推荐)
-
打开命令提示符(CMD 或 PowerShell)
-
进入项目目录:
cd E:\TKF\My-Mod
-
执行构建命令:
dotnet build -c Release
-
复制文件:将
bin/Release/My-Mod/目录复制到 SPT 服务器的user/mods/目录下 -
启动服务器:运行 SPT 服务器,查看日志确认模组加载
7.2 日志调试
使用 ISptLogger 输出调试信息:
logger.Debug($"Player level: {player.Level}");
logger.Info($"Item count: {items.Count}");
日志文件位于:SPT_Data/Server/logs/
8. 发布与分发
8.1 打包模组
-
构建 Release 版本
-
压缩
bin/Release/My-Mod/目录为 ZIP 文件 -
包含以下文件:
-
模组 DLL 文件
-
config.json(如有) -
README.md(说明文档) -
data/(数据文件) -
其他资源文件
8.2 模组发布
推荐发布平台:
参考资料
-
官方示例: https://github.com/sp-tarkov/server-mod-examples?tab=readme-ov-file – 包含多个示例模组,用于学习和参考
-
核心库: https://github.com/sp-tarkov/server-csharp/tree/main/Libraries – 包含 SPT 服务器的核心库,用于开发模组
-
SPT Wiki: https://wiki.sp-tarkov.com/ – 提供详细的 SPT 服务器开发文档
隐藏内容需要回复可以看见









666
666
6666
66666666666666666
666666
太强了
6666
强啊