七日杀V3.0 各系统变更与代码/XML修正清单-个人总结经验
目标版本:7 Days to Die V3.0.0 b252 | 创建时间:2026-06-16
前言
真的,我现在对 TFP 这帮人已经形成一种生理反应了:看他们版本号一跳,我血压也跟着跳。你说他们懒吧,他们能把底层接口、路径、字段、方法签名从里到外翻个底儿朝天,连 Discord 的字段都给你体面送走了,勤快得不行。你说他们勤奋吧,V3.0 玩俩小时愣是没发现和 V2 有半毛钱区别,多的那个幽默合成系统甚至不如 大佬做的MOD。
我在迁移自己 MOD 的过程中,陆续积攒了一批 V3.0版本更替信息点。它们不保证覆盖所有情况,但至少能帮你减少翻查代码的工作量。下面是我的记录,你可以逐条核对,或者直接丢给 AI 辅助修理。
1. Discord 禁用逻辑:旧字段 patch 改为启动参数方案
适用场景
MOD 试图阻止 Discord 自动登录、Discord 初始化或 Discord 账号绑定弹窗。
问题
旧版本常见做法是 patch Discord 设置加载与初始化流程,并直接写 Discord 内部字段。V3.0 后这些字段和流程存在兼容风险,继续 patch 旧字段容易触发 MissingField / MissingMethod / Harmony 绑定失败。
之前
[HarmonyPatch(typeof(DiscordManager.DiscordSettings), nameof(DiscordManager.DiscordSettings.Load))]
public static class DiscordSettingsLoadPatch
{
public static void Postfix(DiscordManager.DiscordSettings __result)
{
if (__result == null)
{
return;
}
__result.DiscordDisabled = true;
__result.DiscordFirstTimeInfoShown = true;
__result.LastAccountType = DiscordManager.EDiscordAccountType.None;
__result.AccessToken = null;
__result.RefreshToken = null;
__result.AutoJoinVoiceMode = DiscordManager.EAutoJoinVoiceMode.None;
}
}
[HarmonyPatch(typeof(DiscordManager), nameof(DiscordManager.Init))]
public static class DiscordManagerInitPatch
{
public static bool Prefix(DiscordManager __instance)
{
if (__instance != null && __instance.Settings != null)
{
__instance.Settings.DiscordDisabled = true;
__instance.Settings.DiscordFirstTimeInfoShown = true;
__instance.Settings.AutoJoinVoiceMode = DiscordManager.EAutoJoinVoiceMode.None;
}
Log.Out("[ExampleMod] Blocked DiscordManager.Init.");
return false;
}
}
现在
public class ModApi : IModApi
{
public void InitMod(Mod _modInstance)
{
ExampleDiscordCompatibilityBootstrap.EnsureInitialized();
}
}
public static class ExampleDiscordCompatibilityBootstrap
{
private static bool initialized;
public static void EnsureInitialized()
{
if (initialized)
{
return;
}
initialized = true;
Log.Out("[ExampleMod] V3.0 compatibility mode: skipping legacy Discord Harmony patches. Use the game's -NoDiscord launch option.");
}
}
结论:V3.0 下不建议继续 patch Discord 内部字段。更稳妥的公开经验是:删除旧 Discord Harmony patch,改为提示用户使用游戏启动参数 -NoDiscord。
2. 背包 / 工具带交互:库存刷新事件更名
适用场景:自动整理背包、隐藏背包槽、自动存入货币、快捷存取、背包按钮、工具带按钮等功能。
问题:V2 常用 XUiM_PlayerInventory.onBackpackItemsChanged / onToolbeltItemsChanged 作为 Harmony patch 目标或直接调用。V3.0 中这两个入口改为 dispatchBackpackItemsChanged / dispatchToolbeltItemsChanged。
之前
[HarmonyPatch(typeof(XUiM_PlayerInventory), "onBackpackItemsChanged")]
public static class Patch_PlayerInventory_OnBackpackItemsChanged
{
private static void Postfix(XUiM_PlayerInventory __instance)
{
if (ExampleInventoryService.Enabled && !ExampleInventoryService.IsUpdating)
{
ExampleInventoryService.RefreshOrNormalize(__instance);
}
}
}
[HarmonyPatch(typeof(XUiM_PlayerInventory), "onToolbeltItemsChanged")]
public static class Patch_PlayerInventory_OnToolbeltItemsChanged
{
private static void Postfix(XUiM_PlayerInventory __instance)
{
if (ExampleInventoryService.Enabled && !ExampleInventoryService.IsUpdating)
{
ExampleInventoryService.RefreshOrNormalize(__instance);
}
}
}
手动刷新时也常见旧调用:
inventory.Backpack.SetSlots(slots); inventory.onBackpackItemsChanged(); inventory.RefreshCurrency();
现在
[HarmonyPatch(typeof(XUiM_PlayerInventory), "dispatchBackpackItemsChanged")]
public static class Patch_PlayerInventory_DispatchBackpackItemsChanged
{
private static void Postfix(XUiM_PlayerInventory __instance)
{
if (ExampleInventoryService.Enabled && !ExampleInventoryService.IsUpdating)
{
ExampleInventoryService.RefreshOrNormalize(__instance);
}
}
}
[HarmonyPatch(typeof(XUiM_PlayerInventory), "dispatchToolbeltItemsChanged")]
public static class Patch_PlayerInventory_DispatchToolbeltItemsChanged
{
private static void Postfix(XUiM_PlayerInventory __instance)
{
if (ExampleInventoryService.Enabled && !ExampleInventoryService.IsUpdating)
{
ExampleInventoryService.RefreshOrNormalize(__instance);
}
}
}
手动刷新改为:
inventory.Backpack.SetSlots(slots); inventory.dispatchBackpackItemsChanged(); inventory.RefreshCurrency();
结论:所有背包 / 工具带刷新相关 patch 目标与直接调用都应检查。旧 onBackpackItemsChanged / onToolbeltItemsChanged 改为 dispatchBackpackItemsChanged / dispatchToolbeltItemsChanged。
3. 背包实体来源:不要依赖旧 Bag.entity
适用场景:根据玩家实体计算背包大小、技能加成、背包栏位、装备栏状态或本地玩家数据。
问题:旧代码可能从 Bag.entity 取得所属实体。V3.0 中该路径不可用。
之前
EntityAlive owner = bag != null ? bag.entity : null; EntityPlayerLocal player = owner as EntityPlayerLocal;
现在
EntityPlayerLocal player = null;
if (GameManager.Instance != null && GameManager.Instance.World != null)
{
player = GameManager.Instance.World.GetPrimaryPlayer();
}
如果代码已在 XUi 上下文中,也可以优先从 UI 上下文取本地玩家:
EntityPlayerLocal player = null;
if (xui != null && xui.playerUI != null)
{
player = xui.playerUI.entityPlayer;
}
结论:背包类不应再假设自己持有玩家实体。优先从 XUi 上下文或 GameManager.Instance.World.GetPrimaryPlayer() 取得本地玩家。
4. 方块受击 patch:Block.OnBlockDamaged 参数变化
适用场景:矿物探测、方块伤害记录、采集统计、命中特效、方块坐标追踪等功能。
问题:V2 patch 可能绑定 _clrIdx 与 _blockPos。V3.0 的 Block.OnBlockDamaged 不再提供这两个旧参数,改为传入 BlockValueRef。
之前
[HarmonyPatch(typeof(Block), "OnBlockDamaged")]
public static class Patch_Block_OnBlockDamaged
{
public static void Prefix(int _clrIdx, Vector3i _blockPos, BlockValue _blockValue, int _damagePoints, int _entityIdThatDamaged)
{
ExampleBlockHitTracker.Record(_blockPos, _blockValue, _damagePoints, _entityIdThatDamaged, _clrIdx);
}
}
现在
[HarmonyPatch(typeof(Block), "OnBlockDamaged")]
public static class Patch_Block_OnBlockDamaged
{
public static void Prefix(BlockValueRef _bvRef, BlockValue _blockValue, int _damagePoints, int _entityIdThatDamaged)
{
Vector3i blockPos;
if (!_bvRef.TryGetBlockPos(out blockPos))
{
blockPos = _bvRef.ToBlockPos(GameManager.Instance != null ? GameManager.Instance.World : null);
}
ExampleBlockHitTracker.Record(blockPos, _blockValue, _damagePoints, _entityIdThatDamaged, -1);
}
}
结论:旧 _blockPos 改为从 BlockValueRef 解析。旧 _clrIdx 不再可靠,调试输出可保留 -1 作为占位。
5. 命中信息:旧 HitInfoDetails.clrIdx 不再可用
适用场景:准星命中方块、HUD 指示、方块调试显示、射线命中详情输出等功能。
问题:旧代码可能读取 HitInfoDetails.clrIdx。V3.0 中该字段不可用。
之前
HitInfoDetails details = localPlayer.HitInfoDetails;
int clrIdx = details.clrIdx;
Log.Out("hit block clrIdx=" + clrIdx);
现在
HitInfoDetails details = localPlayer.HitInfoDetails;
Log.Out("hit block details available; clrIdx removed in V3.0");
如果需要方块坐标,应改从当前可用的 hit position / block position / BlockValueRef 路径重新推导,而不是继续依赖 clrIdx。
结论:删除 HitInfoDetails.clrIdx 访问。需要坐标时按 V3.0 实际可用命中数据重新取。
6. 击飞 / 布娃娃:EModelBase.DoRagdoll 新增模式参数
适用场景:自定义弹射物、爆炸冲击、近战击飞、激光击退、范围伤害击退等功能。
问题:V2 调用 DoRagdoll 时首参直接传眩晕时间或击飞时间。V3.0 新增首参 EModelBase.RagdollMode。
之前
if (targetEntity != null && targetEntity.emodel != null)
{
targetEntity.emodel.DoRagdoll(
Mathf.Max(0f, stunDuration),
EnumBodyPartHit.Torso,
forceVector,
targetEntity.position,
false);
}
现在
if (targetEntity != null && targetEntity.emodel != null)
{
targetEntity.emodel.DoRagdoll(
EModelBase.RagdollMode.Default,
Mathf.Max(0f, stunDuration),
EnumBodyPartHit.Torso,
forceVector,
targetEntity.position,
false);
}
结论:旧 DoRagdoll(stunTime, bodyPart, force, pos, remote) 改为 DoRagdoll(EModelBase.RagdollMode.Default, stunTime, bodyPart, force, pos, remote)。
7. 击杀经验:EntityPlayer.AddKillXP 新增使用物品参数
适用场景:自定义投射物、召唤物、AOE、DoT、陷阱或非原版攻击链路需要补发击杀经验。
问题:V2 常见调用只传击杀目标和经验倍率。V3.0 新增 ItemValue itemUsed 参数。
之前
EntityPlayer killerPlayer = killerEntity as EntityPlayer;
if (killerPlayer != null)
{
killerPlayer.AddKillXP(targetEntity, 1f);
}
现在
EntityPlayer killerPlayer = killerEntity as EntityPlayer;
if (killerPlayer != null)
{
ItemValue itemUsed = killerEntity.inventory != null ? killerEntity.inventory.holdingItemItemValue : null;
killerPlayer.AddKillXP(targetEntity, itemUsed, 1f);
}
结论:V3.0 下补传当前使用物品。没有可用物品时传 null。
8. XUi 拖拽窗口:xui.dragAndDrop 改为 xui.DragAndDropWindow
适用场景:自定义背包槽、自定义装备槽、自定义工作站槽、拖拽物品到自定义 UI、从自定义 UI 取回物品。
问题:V2 常见代码访问 xui.dragAndDrop.CurrentStack、xui.dragAndDrop.PickUpType、xui.dragAndDrop.InMenu。V3.0 中入口改为 xui.DragAndDropWindow。
之前
public override void OnOpen()
{
base.OnOpen();
if (base.xui != null && base.xui.dragAndDrop != null)
{
base.xui.dragAndDrop.InMenu = true;
}
base.RefreshBindings(false);
}
public override void OnClose()
{
base.OnClose();
if (base.xui != null && base.xui.dragAndDrop != null)
{
base.xui.dragAndDrop.InMenu = false;
}
}
private ItemStack GetCursorStack()
{
return base.xui != null && base.xui.dragAndDrop != null
? base.xui.dragAndDrop.CurrentStack
: ItemStack.Empty.Clone();
}
现在
public override void OnOpen()
{
base.OnOpen();
if (base.xui != null && base.xui.DragAndDropWindow != null)
{
base.xui.DragAndDropWindow.InMenu = true;
}
base.RefreshBindings();
}
public override void OnClose()
{
base.OnClose();
if (base.xui != null && base.xui.DragAndDropWindow != null)
{
base.xui.DragAndDropWindow.InMenu = false;
}
}
private ItemStack GetCursorStack()
{
return base.xui != null && base.xui.DragAndDropWindow != null
? base.xui.DragAndDropWindow.CurrentStack
: ItemStack.Empty.Clone();
}
结论:xui.dragAndDrop 全部替换为 xui.DragAndDropWindow。
9. XUi 绑定刷新:RefreshBindings(false) 改为无参
适用场景:所有自定义 XUiController、背包槽、按钮、标签、HUD、工作站窗口。
问题:V2 常见调用 RefreshBindings(false)。V3.0 当前可用签名为无参 RefreshBindings()。
之前
this.RefreshData(); base.RefreshBindings(false);
子控件刷新:
for (int i = 0; i < childControllers.Count; i++)
{
childControllers[i].RefreshBindings(false);
}
现在
this.RefreshData(); base.RefreshBindings();
子控件刷新:
for (int i = 0; i < childControllers.Count; i++)
{
childControllers[i].RefreshBindings();
}
结论:批量搜索 RefreshBindings(。如果只有布尔参数,改为无参。
10. XUi 输入:OnPressed / OnScrolled override 改为事件绑定
适用场景:自定义按钮、自定义物品槽、自定义滚轮区域、自定义列表、可拖拽 UI 元素。
问题:V2 可通过 override OnPressed(int) / OnScrolled(float) 接收输入。V3.0 更适合在 Init() 中绑定 OnPress / OnScroll 事件。
之前
public override void OnPressed(int _mouseButton)
{
ItemStack cursorStack = GetCursorStack();
if (cursorStack == null || cursorStack.IsEmpty())
{
base.OnPressed(_mouseButton);
return;
}
this.SwapItem();
}
public override void OnScrolled(float _delta)
{
base.OnScrolled(_delta);
this.ScrollList(_delta);
}
现在
public override void Init()
{
base.Init();
base.OnPress += this.HandlePressed;
base.OnScroll += this.HandleScrolled;
}
private void HandlePressed(XUiController _sender, int _mouseButton)
{
ItemStack cursorStack = GetCursorStack();
if (cursorStack == null || cursorStack.IsEmpty())
{
base.Pressed(_mouseButton);
return;
}
this.SwapItem();
}
private void HandleScrolled(XUiController _sender, float _delta)
{
this.ScrollList(_delta);
}
结论:旧输入 override 改事件绑定。空光标时如果需要保留原按钮行为,可调用 base.Pressed(_mouseButton)。
11. XUi 文件结构:Config/XUi 拆为 V3 新目录
适用场景:任何带 XUi patch 的 MOD,包括 HUD、背包、物品详情、工作站、自定义菜单。
问题:V2 MOD 通常把 xui.xml、windows.xml、controls.xml 放在 Config/XUi/。V3.0 官方拆为:
Config/XUi_Common Config/XUi_InGame Config/XUi_Menu
游戏内 HUD / 背包 / 工作站一般迁移到:
Config/XUi_InGame
之前
Config/XUi/xui.xml Config/XUi/windows.xml Config/XUi/controls.xml
现在
Config/XUi_InGame/xui.xml Config/XUi_InGame/windows.xml Config/XUi_InGame/templates.xml
如果是通用模板,可按实际官方模板位置迁移到:
Config/XUi_Common/templates.xml
结论:不要继续新增 Config/XUi/。先判断 UI 属于游戏内、菜单还是通用模板,再放入对应目录。
12. XUi XPath:ruleset 层移除
适用场景:向原版 toolbelt、backpack、HUD window group 或自定义 window group 注入窗口。
问题:V2 常见 XPath 指向 /xui/ruleset[@name='default']。V3.0 XUi_InGame/xui.xml 根节点直接是
之前
<append xpath="/xui/ruleset[@name='default']/window_group[@name='toolbelt']"> <window name="windowExampleHud" /> </append> <append xpath="/xui/ruleset[@name='default']"> <window_group name="example_window_group" controller="XUiC_ExampleWindowGroup, ExampleAssembly" close_compass_on_open="true" defaultSelected="btnExample"> <window name="windowExample" /> </window_group> </append>
现在
<append xpath="/xui/window_group[@name='toolbelt']"> <window name="windowExampleHud" /> </append> <append xpath="/xui"> <window_group name="example_window_group" controller="XUiC_ExampleWindowGroup, ExampleAssembly" close_compass_on_open="true" defaultselected="btnExample"> <window name="windowExample" /> </window_group> </append>
结论:/xui/ruleset[@name='default'] 改为 /xui;/xui/ruleset[@name='default']/window_group[…] 改为 /xui/window_group[…];defaultSelected 改为官方小写 defaultselected。
13. XUi 模板:controls.xml 改为 templates.xml
适用场景:向 item_stack、背包物品格、装备格、自定义按钮模板、自定义控件模板注入 XML。
问题:V2 使用 controls.xml 与 /controls XPath。V3.0 官方改为 templates.xml 与 /templates。
之前
<insertAfter xpath="/controls/item_stack/rect/sprite[@name='background']">
<sprite name="exampleIcon" depth="12" width="30" height="30"
atlas="UIAtlas" sprite="ui_game_symbol_check"
pos="8,0" pivot="center"
foregroundlayer="true"
visible="{exampleiconvisible}" />
</insertAfter>
<append xpath="/controls">
<examplebutton depth="3" pos="0,0" height="32" width="160">
<rect name="${name}" depth="${depth}" pos="${pos}" width="${width}" height="${height}" controller="SimpleButton">
<button name="clickable" sprite="menu_empty" />
<label name="btnLabel" text="${caption}" />
</rect>
</examplebutton>
</append>
现在
<insertAfter xpath="/templates/item_stack/rect/sprite[@name='background']">
<sprite name="exampleIcon" depth="12" width="30" height="30"
atlas="UIAtlas" sprite="ui_game_symbol_check"
pos="8,0" pivot="center"
foregroundlayer="true"
visible="{exampleiconvisible}" />
</insertAfter>
<append xpath="/templates">
<examplebutton depth="3" pos="0,0" height="32" width="160">
<rect name="${name}" depth="${depth}" pos="${pos}" width="${width}" height="${height}" controller="SimpleButton">
<button name="clickable" sprite="menu_empty" />
<label name="btnLabel" text="${caption}" />
</rect>
</examplebutton>
</append>
结论:controls.xml 改为 templates.xml;/controls/… 改为 /templates/…;不要假设模板仍在旧位置,先对照 V3 官方 XUi_Common/templates.xml 与 XUi_InGame/templates.xml。
14. XML 属性名:点号属性不再安全
适用场景:任何 XML property name=”A.B”,尤其动作属性、工具分类、嵌套配置。
问题:V3.0 DynamicProperties.ValidateKey() 禁止 property name 中包含 .。旧写法会导致 XML loader 硬错误,并进一步导致其他 XML 文件引用同一个物品时报“物品不存在”。
之前
<property class="Action0"> <property name="Class" value="DynamicMelee" /> <property name="Damage_type" value="Slashing" /> <property name="Sphere" value=".15" /> <property name="Sound_start" value="melee_swing_light" /> <property name="ToolCategory.Butcher" value="0" param1="4.7" /> <property name="UseGrazingHits" value="true" /> </property>
现在
<property class="Action0"> <property name="Class" value="DynamicMelee" /> <property name="Damage_type" value="Slashing" /> <property name="Sphere" value=".15" /> <property name="Sound_start" value="melee_swing_light" /> <property class="ToolCategory"> <property name="Butcher" value="0" param1="4.7" /> </property> <property name="UseGrazingHits" value="true" /> </property>
把 property name=”A.B” 迁移为嵌套结构:
<property class="A"> <property name="B" value="..." /> </property>
15. loot group 名称变化:旧背包组名需要核对
适用场景:向原版 loot group 追加物品、奖励包、背包类 loot、容器掉落。
问题:部分旧 loot group 名称在 V3.0 中已更名。典型例子:旧 groupZpackRegular 不再存在,V3.0 应使用 groupZpackReg。
之前
<append xpath="/lootcontainers/lootgroup[@name='groupZpackRegular']"> <item name="exampleItem" prob="0.05" /> </append>
现在
<append xpath="/lootcontainers/lootgroup[@name='groupZpackReg']"> <item name="exampleItem" prob="0.05" /> </append>
结论:V3.0 升级时不要只看 XML 语法通过,还要核对 XPath 目标是否真实存在。
16. Localization:文件名与表头变化
适用场景:任何带本地化文本的 MOD。
问题:V2 常见 Localization.txt 表头没有 KeepLoaded。V3.0 官方使用 Localization.csv,表头新增 KeepLoaded,且 english 为小写。
之前
Key,File,Type,UsedInMainMenu,NoTranslate,english,Context / Alternate Text,german,spanish,french,italian,japanese,koreana,polish,brazilian,russian,turkish,schinese,tchinese exampleKey,items,Item,,,Example Text,,,,,,,,,,,,示例文本,
现在
Key,File,Type,UsedInMainMenu,NoTranslate,KeepLoaded,english,Context / Alternate Text,german,spanish,french,italian,japanese,koreana,polish,brazilian,russian,turkish,schinese,tchinese exampleKey,items,Item,,,,Example Text,,,,,,,,,,,,示例文本,
结论:Localization.txt 改为 Localization.csv;增加 KeepLoaded 列;用 CSV parser 处理,不要手写拼列;清理空 key / 空行;中文内容写入后用文件回读或 hash 验证,不依赖终端显示。
17. entitygroups.xml:旧文本格式迁移为 XML 元素
适用场景:向实体组添加僵尸、动物、事件实体、特殊刷怪组。
问题:V3.0 官方 entitygroups.xml 回归 proper XML element 格式。旧 text based format 可能暂时兼容,但不建议继续依赖。
之前
<entitygroup name="ExampleGroup"> zombieExampleA zombieExampleB,0.3 </entitygroup>
现在
<entitygroup name="ExampleGroup"> <e n="zombieExampleA" /> <e n="zombieExampleB" p="0.3" /> </entitygroup>
结论:后续 patch 优先使用
18. 建议的 V3.0 迁移检查清单
DLL / C#: - 搜索 DiscordSettings / DiscordDisabled / DiscordFirstTimeInfoShown。 - 搜索 onBackpackItemsChanged / onToolbeltItemsChanged。 - 搜索 Bag.entity。 - 搜索 Block.OnBlockDamaged patch 参数。 - 搜索 HitInfoDetails.clrIdx。 - 搜索 DoRagdoll 调用。 - 搜索 AddKillXP 调用。 - 搜索 xui.dragAndDrop。 - 搜索 RefreshBindings(false)。 - 搜索 OnPressed / OnScrolled override。 XML: - 检查 Config/XUi 是否需要迁移到 XUi_InGame / XUi_Common / XUi_Menu。 - 检查 /xui/ruleset[@name='default']。 - 检查 controls.xml 与 /controls XPath。 - 检查 defaultSelected 大小写。 - 检查 property name 中是否包含点号。 - 检查 XPath 目标是否仍存在。 - 检查 Localization.txt -> Localization.csv。 - 检查 Localization 表头是否包含 KeepLoaded。 - 检查 entitygroups.xml 是否仍是旧文本格式。








鄂公网安备42011202002543号



用心了
代表作:七日杀反作弊、七日杀云黑、七日杀BOT、七日杀云商城
感谢大佬的付出,赞一个!
感谢大佬的付出,赞一个!
感谢大佬的付出,赞一个!
希赫工作室
感谢大佬的付出,赞一个!
一个热爱七日杀沉浸式真实生存模组开发的mod制作爱好者
满满的干货,无私的奉献,感谢为社区的无私付出。
一个热爱七日杀沉浸式真实生存模组开发的mod制作爱好者
必须支持一波!
B站UP主!有40年游戏经验的达人 ! 《所罗门宝藏》作者。
B站UP主!有40年游戏经验的达人 ! 《所罗门宝藏》作者。