Hartz 4 - 等级差距与游戏时间经验值限制

Hartz 4 - 等级差距与游戏时间经验值限制

跑得比其他所有人远,或超过每日预算的玩家,将获得较少经验值,直至差距缩小。从10级开始,平日6小时、周末16小时额度内会有逐步下降的惩罚。仅服务端生效,客户端无需安装任何内容。

功能简介

两个服务器端限制,防止玩家进度过快:

  • 等级差距:当某个玩家等级比已知最低等级玩家高出10级以上时,该玩家将开始损失经验值。惩罚幅度线性增长,在差距达到20级时达到80%的上限。
  • 每日游戏时间:工作日限6小时,周五至周日限16小时。超过时限后,经验值获取降低70%。

两者通过乘法叠加。一个领先20级且用尽每日时限的玩家,仅能获得**6%**的正常经验值。所有参数均可配置。

模组会记录所有曾出现过的等级,重启后数据依然保留,因此新玩家下线时参考点不会消失。游戏时间按玩家每日统计,以凌晨4点为日界,确保深夜游戏仍计入前一个晚上。

当惩罚开始或解除时,玩家会收到聊天提示。

仅需服务端安装。客户端无需安装任何文件。

设计原理

最直观的实现方式是挂钩任何赋予经验值的函数并缩放数值。但在《幻兽帕鲁》1.0中无法实现,原因值得记录,以免后来者浪费一整个晚上。

已在版本 v1.0.1.100619 上验证:通过 UStruct:ForEachFunction 在运行时枚举类,并绑定候选挂钩,观察实际游戏过程中的触发情况:

PalPlayerState:GrantExpForParty                    MISSING
PalExpDatabase:AddExpValue_forPlayerParty_Server    存在,但从未触发
PalExpDatabase:DistributionExpValue_..._Server      存在,但从未触发
PalExpDatabase:AddExp_EnemyDeath                    触发——但只携带一个
                                                     PalDeadInfo 结构体。无经验值
                                                     和接收者信息。

因此,唯一触发的函数没有可缩放的数值,而带有可变 int32 ExpValue 的函数都是死代码。

故此模组不拦截经验值。它每隔几秒采样每个玩家的经验值,计算增益,然后扣回惩罚部分。这种方式独立于经验值赋予的内部路径,也意味着即使函数被重命名也能正常工作。

经验值写入本身是运行时解析的。当第一个玩家加入时,模组按顺序尝试四条路径,每条路径都用真实值 -1 和回读验证,并记录成功的那条:

[Hartz4] resolving XP writer -- controller via GetOwner() -> ...
[Hartz4] XP writer resolved: controller:Debug_AddPlayerExp_ToServer  (199 -> 198)

如果所有路径都失效,模组会明确提示,保持静默而非假装工作。

给其他模组作者的提示

  • 通过 pawn.CharacterParameterComponent:GetIndividualParameter() 读取玩家等级和经验值,再使用 :GetLevel() / :GetExp()。切勿直接操作 param.SaveParameter,那会导致无法捕获的原生崩溃。
  • PlayerState 的 PlayerUId 在刚连接后的一段时间内全为零。跳过这些状态,否则新加入的玩家会短暂被识别为未知玩家,导致所有人惩罚飙升。
  • LoopAsync 已弃用。LoopInGameThreadWithDelay 在游戏线程上运行,无需 ExecuteInGameThread 包装。
  • ioos 在《幻兽帕鲁》的 UE4SS Lua 环境中均可用,因此持久化和基于日期规则能正常工作。状态文件保存在服务器可执行文件同目录下。

未涵盖的功能

无战利品惩罚、无采矿惩罚。 并非遗漏——而是没有可用的挂钩。同样在1.0版本验证:

  • PalPlayerInventoryData:AddItem_ServerInternal 干净绑定后从未触发。物品不再通过该函数传递。
  • PalUtility:DropItem_FromEnemyDeath 触发并将击杀者作为actor传递,但仅接受两个actor指针。无数量可缩放,且掉落物尚不存在。
  • PalMapObjectPickableItemModelBase:RequestPickup_ServerInternal 与玩家ID一起触发——目前最有希望的线索——但堆叠数量位于物品容器中,其可写性尚未验证。

如果你找到路径,请提交问题,或直接使用代码。

安装

  1. 为《幻兽帕鲁》安装 UE4SS。
  2. Hartz4 文件夹放入 Pal/Binaries/Win64/ue4ss/Mods/
  3. 编辑 Hartz4/Scripts/config.lua
  4. 重启服务器。

Pal/Binaries/Win64/ue4ss/UE4SS.log 中确认:

[Hartz4] v1.0.0 loading
[Hartz4] state loaded: 0 known player(s), lowest level seen = nil
[Hartz4] polling every 15s (LoopInGameThreadWithDelay)

系统要求

UE4SS(《幻兽帕鲁》版本)。无需其他依赖。

许可证

MIT。