MENU

【歪门邪道】拯救消失的东方女仆

May 13, 2025 • 瞎折腾

最近又开始玩MC了,自己弄了个整合包,以AE2、Mek和沉浸工程为主。自从玩过了东方女仆mod和TACZ枪械mod了,我就再也离不开这两个mod了——女仆能拿枪,这就无敌了啊。可是这个整合包玩着玩着,我突然发现我女仆没了?用卷轴定位说是在未加载区块上,可是我tp到了对应的座标,就是不见我的女仆。于是一场惊心动魄的救援行动就此展开——

背景

因为有和朋友联机的需求,所以开了个服务器。因为当时正在搞乙烯发电厂,打算开创造找个远一点(指7万方块之外)的地方设计一下怎么搞。结果忘记让女仆待在家里,就跟着传送了过来,她在旁边突突突打怪,我也没管她。我记得当天夜里一路玩到凌晨两点,虽然设计出了16个燃气发电机、发电容量为1MFE/t的设计,但现实中我都困的不行了,于是就tp回家,改成生存就下线了。下线之后NAS有个更新,需要重启服务器,自然也有需要重启容器中的游戏服务端,我不知道这是否是女仆丢失的原因。但我查阅了github上的issue区,只找到一篇相关的问题报告,但模组作者没有回应,大哥自己把issue关了。

救援计划

发现女仆消失后,我立刻放下手中的任务,开始思索如何把女仆带回来。目前来看,仆人铃和小号都无法召回女仆,因为模组认为女仆在未加载区块上。如果生成新的女仆,那么原来的女仆还是会存在,并且一直处于这种幽灵状态。由于仆人铃在绑定时会显示uuid,于是我推断模组应该是给每个女仆都设定了uuid作为id,如果我能让模组重新生成一个具有相同uuid的女仆,就算不能把原来的女仆带回来,至少也能顶掉原来的女仆进而修正这个幽灵状态。索性这个mod是开源的,而我正好略懂一些kotlin/jvm的编程技巧,于是我便冲到GitHub上查阅源码。

根据源码可知,女仆的数据是存储在nbt标签中的,所以只要我们能够提取到女仆的UUID,然后把它移花接木到魂符上就行了。

实施救援

由于不想把事情搞得太麻烦,所以我是根据我自己的情况进行的修复。如果大家也遇到了类似的情况,可以发动自己的脑筋,再不济去解析一下游戏的存档,总能找到uuid的。

由于我先前绑定过仆人铃,而仆人铃刚好就是把女仆的uuid存在nbt里的,所以主手拿着仆人铃,执行以下命令:

/data get entity @p SelectedItem

这个命令会在聊天窗口显示nbt数据,但游戏内不好复制,于是我从启动器的控制台日志里面复制出了结果:

{
  id: "touhou_little_maid:servant_bell",
  tag: {
    ServantBellTip: "女仆名称", 
    ServantBellUuid: [I; -1136390084, -1170784113, -1271669764, -260320852]
  }, 
  Count: 1b
}

虽然长得很像json,但实际不是。总之,可以看到里面有个ServantBellUuid,后面虽然不是uuid的常见形式,但熟悉uuid的话应该能看出,这是把字节数据当成整数给表示出来了。

下面我们就可以找一个魂符来移花接木了。其实理论上用胶片也行,但是你得手动复活。

退出服务器,我创建了一个单人的存档,召唤了一个女仆,然后把它收进魂符里面,使用上面的命令查看nbt数据:

{id: "touhou_little_maid:smart_slab_has_maid", tag: {MaidInfo: {CanUpdate: 1b, MaidExperience: 0, SoundPackId: "littlemaid_peco", MaidBackpackData: {}, LeftHanded: 0b, MaidAIChatData: {ChatTemperature: -1.0d, ChatModel: "", CustomSetting: "", TtsModel: "", TtsSiteName: "", ChatSiteName: "", OwnerName: "", TtsLanguage: ""}, AbsorptionAmount: 0.0f, MaidHunger: 0, Invulnerable: 0b, IsYsmModel: 0b, ForgeCaps: {"tacz:synced_entity_data": [], "mekanism:radiation": {radiation: 1.0E-7d}, "curios:inventory": {Curios: []}}, MaidSchedulePos: {Work: {Z: -1374, X: -320, Y: 63}, Dimension: "minecraft:overworld", Configured: 0b, Idle: {Z: -1374, X: -320, Y: 63}, Sleep: {Z: -1374, X: -320, Y: 63}}, ModelId: "geckolib:foxmaid", ArmorDropChances: [0.085f, 0.085f, 0.085f, 0.085f], MaidBackpackType: "touhou_little_maid:middle_backpack", HandDropChances: [0.085f, 0.085f], MaidBaubleInventory: {Size: 9, Items: []}, ForcedAge: 0, MaidIsRideable: 1b, MaidScriptBook: [], MaidScheduleMode: "DAY", ArmorItems: [{id: "minecraft:iron_boots", Count: 1b, tag: {Damage: 59}}, {id: "minecraft:iron_leggings", Count: 1b, tag: {Damage: 59}}, {id: "minecraft:iron_chestplate", Count: 1b, tag: {Damage: 59}}, {id: "minecraft:iron_helmet", Count: 1b, tag: {Damage: 59}}], FavorabilityManagerCounter: {WorkMeal: 2352, Sleep: 8780, Death: 0}, StructureSpawn: 0b, MaidTaskInventory: {Size: 9, Items: []}, MaidInventory: {Size: 36, Items: [{Slot: 0, Count: 22b, id: "minecraft:bread"}, {Slot: 5, Count: 1b, tag: {AllTypeCreative: 1b, AmmoCount: 2147483614}, id: "tacz:ammo_box"}]}, MaidIsPickup: 0b, YsmRoamingVars: {}, UUID: [I; -1136390084, -1170784113, -1271669764, -260320852], ForgeData: {}, StruckByLightning: 0b, YsmRoamingUpdateFlag: 0, Motion: [0.0d, -0.0784000015258789d, 0.0d], MaidGameSkillData: {}, CanPickUpLoot: 0b, Fire: -1s, MaidFavorability: 384, PersistenceRequired: 0b, DeathTime: 0s, MaidTaskDataMaps: {}, OnGround: 1b, YsmModelId: "", MaidHideInventory: {Size: 1, Items: []}, KillRecord: {Slime: 0, Wither: 2, KillRecord: 2, EnderDragon: 0}, Attributes: [{Name: "gunsmithlib:bullet_speed", Base: 0.0d}, {Name: "minecraft:generic.max_health", Base: 80.0d}, {Name: "gunsmithlib:bullet_damage", Base: 0.0d}, {Name: "forge:entity_gravity", Base: 0.08d}, {Name: "tacz:tacz.bullet_resistance", Base: 0.0d}, {Name: "minecraft:generic.armor", Base: 0.0d}, {Name: "minecraft:generic.armor_toughness", Base: 0.0d}, {Name: "forge:step_height_addition", Base: 0.0d}, {Name: "minecraft:generic.attack_damage", Base: 6.0d}, {Name: "forge:swim_speed", Base: 1.0d}, {Name: "minecraft:generic.movement_speed", Base: 0.699999988079071d}], Brain: {memories: {}}, Owner: [I; 玩家uuid], Age: 0, Sitting: 0b, YsmModelTexture: "", Rotation: [-100.0f, 0.0f], HurtByTimestamp: 104992, CitadelData: {}, MaidSubConfig: {SoundFreq: 0.19999993f, PickupType: 2, BackpackShow: 0b, OpenDoor: 1b, ChatBubbleShow: 1b, ActiveClimbing: 1b, OpenFenceGate: 1b, BackItemShow: 1b}, MaidTask: "touhou_little_maid:gun_attack", HandItems: [{id: "tacz:modern_kinetic_gun", Count: 1b, tag: {AttachmentEXTENDED_MAG: {id: "tacz:attachment", tag: {AttachmentId: "tacz:light_extended_mag_3"}, Count: 1b}, HasBulletInBarrel: 1b, GunCurrentAmmoCount: 3, GunFireMode: "SEMI", GunId: "classicr:tti_g34"}}, {id: "mekanismadditions:light_blue_balloon", Count: 1b}], Air: 300s, YsmModelName: '{"text":""}', FallDistance: 0.0f, YsmRouletteAnim: "empty", MaidIsHome: 0b, Pos: [-324.85620222628216d, 63.0d, -1372.3034760980975d], Health: 80.0f, HurtTime: 0s, FallFlying: 0b, InLove: 0, PortalCooldown: 0}}, Count: 1b}
眼尖的读者可能看出来了,你这魂符的uuid怎么和之前女仆的一样啊?那当然了,因为我这篇文章是事后复盘,既然没办法复现bug,那就只能力图还原一下当时的情景了。

这怎么这么多一大坨?当我试图移花接木的时候,我发现无论是rcon还是游戏内的聊天框,都说命令太长。那就只能精简掉一些不必要的字段了,可是哪些字段不必要呢?我看了看代码,觉得有点麻烦,于是以实践检验真理。在我的不断尝试下,发现大部分字段都可以精简,只留下女仆uuid和玩家的uuid即可。

于是我使用如下命令,将我工具栏的第二格覆盖为一个有女仆数据的魂符:

/item replace entity @p hotbar.1 with touhou_little_maid:smart_slab_has_maid{MaidInfo: {CanUpdate: 1b, MaidIsPickup: 1b, UUID: [I; 463257288, 837635870, -1609583147, 1128655388], Owner: [I; 玩家uuid]}}

这个命令足够短,可以直接在游戏的聊天框内输入。只要把其中UUID的部分换成你女仆的uuid即可。关于Owner,由于我是正版玩家,而且服务器也开了正版验证,因此无论是单机还是服务器,uuid都是不变的,复制出来直接拿来用就好了。但如果你是离线模式,或者服务器没开正版验证,你就得自己想办法去确定自己的uuid了,好在也不难。

万事具备,就差一个右键了。

对着地面使用魂符,虽然数据全都没了,但我的女仆又回来了!于是接下来就是创造模式的理赔时间了——好感度丢了?直接创造往上加!物品没了?创造模式丢什么拿什么,没丢的也能拿,就算是精神损失费了!这就是1967年冬天,平帐的好机会啊(不是)!

后记

虽然一开始女仆丢了很着急,但好在最后有惊无险地救回来了。不过这种方法是否会有后遗症或者副作用,我暂且不得而知。从目前来看,似乎一切正常,因此我和朋友就继续搭建乙烯发电厂去了。

-全文完-


知识共享许可协议
【歪门邪道】拯救消失的东方女仆天空 Blond 采用 知识共享 署名 - 非商业性使用 - 相同方式共享 4.0 国际 许可协议进行许可。
本许可协议授权之外的使用权限可以从 https://skyblond.info/about.html 处获得。

Archives QR Code
QR Code for this page
Tipping QR Code