• 注册
  • 游戏攻略 游戏攻略 关注:1885 内容:69

    SPT 4.0 服务端Mod开发教程

  • 查看作者
  • 打赏作者
  • 10
  • 游戏攻略
  • 初窥堂奥

    SPT 模组开发指南

    本文档基于官方示例项目 server-mod-examples 下载 和核心库 server-csharp/Libraries 下载,为开发者提供完整的模组开发指南。

    目录

    1. 环境设置

    2. 项目结构

    3. 核心概念

    4. 基础模板

    5. 常用服务详解

    6. 进阶功能

    7. 调试与测试

    8. 发布与分发


    1. 环境设置

    1.1 必备工具

    • .NET 9 SDK下载 – 用于编译 C# 代码

    • Notepad  – 用于开发编写代码

    • SPT Server下载 – 用于测试模组

    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 { ... }

    常用加载顺序值:

    顺序值

    含义

    OnLoadOrder.PreSptModLoader

    SPT 加载器启动前

    OnLoadOrder.PostDBModLoader

    数据库加载完成后

    OnLoadOrder.Watermark

    水印初始化时

    OnLoadOrder.PostSptModLoader

    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 构建

    1. 构建模组:在 Visual Studio 中使用 Release 模式构建

    2. 复制文件:将 bin/Release/My-Mod/ 目录复制到 SPT 服务器的 user/mods/ 目录下

    3. 启动服务器:运行 SPT 服务器,查看日志确认模组加载

    方法二:使用命令行构建 (新手推荐)

    1. 打开命令提示符(CMD 或 PowerShell)

    2. 进入项目目录

      cd E:\TKF\My-Mod
    3. 执行构建命令

      dotnet build -c Release
    4. 复制文件:将 bin/Release/My-Mod/ 目录复制到 SPT 服务器的 user/mods/ 目录下

    5. 启动服务器:运行 SPT 服务器,查看日志确认模组加载

    7.2 日志调试

    使用 ISptLogger 输出调试信息:

    logger.Debug($"Player level: {player.Level}");
    logger.Info($"Item count: {items.Count}");

    日志文件位于:SPT_Data/Server/logs/


    8. 发布与分发

    8.1 打包模组

    1. 构建 Release 版本

    2. 压缩 bin/Release/My-Mod/ 目录为 ZIP 文件

    3. 包含以下文件:

      • 模组 DLL 文件

      • config.json(如有)

      • README.md(说明文档)

      • data/(数据文件)

      • 其他资源文件

    8.2 模组发布

    推荐发布平台:


    参考资料


    隐藏内容需要回复可以看见

    回复
    炉火纯青
    打赏了1金币
    回复

    666

    回复
    功行圆满
    VIP5
    打赏了1金币
    回复
    渐入佳境

    666

    回复
    略有小成

    6666

    回复
    渐入佳境

    66666666666666666

    回复
    渐入佳境

    666666

    回复
    初来乍到

    太强了

    回复
    自成一派

    6666

    回复
    圆转纯熟

    强啊

    回复

    请登录之后再进行评论

    登录
    离线版教程
  • 今日 0
  • 内容 1082
  • 关注 1885
  • 聊天
    关注 7

    【招募】GRIFFIN TKF项目开工 期待你的加入 || 你是否想加入格里芬书写自己与人形的故事

    捐助我们

    • 微信
    • 支付宝
  • 签到
  • 任务
  • 发布
  • 模式切换
  • 偏好设置
  • 帖子间隔 侧栏位置: