跳转到正文

瞄准、近战与弩状态 ​

TargetTracker 用当前实体包观察计算瞄准方向;TargetInputSession 在明确的单控制器条件下持续瞄准或维持已开始的物品使用;MeleeSession 组合一次近战输入。弩的蓄力和弹药变化由 CrossbowInventoryObservations 读取实际背包证据。

用目标引用、动作意图和后续观察组织控制流程。自动战斗策略、弹道预测和命中判断由插件实现。

调用前准备 ​

需求来源与权限
当前实体身份与位置ctx.actors.observe() 准备 framework.actor.observe;需要同名操作权限
当前手持引用、弩与弹药原生窗口 0 库存观察;不能用推算数量或未知槽位
当前输入 tick / revision原生 AuthInput 观察与动作审计,见移动接口
typed 动作framework.packet.send_typed;根据动作分别授权包 30(交互/攻击)、33(MouseOver)、44(SwingArm)、144(AuthInput),需要选槽时再加 31
协同控制与日志全部动作共享 ActionControl,并提供提交前可持久化的 journal

所有写操作还要求目标 Host 实现对应能力、进游戏且允许世界写入。操作权限和允许发送的包号都要授权;观察流还要单独授权。Host API 1.3 不是各 helper 都受支持的保证,插件不能把管理 HTTP 凭据当成 SDK 上下文。

组合读取动作前置证据 ​

text
prepare_action_guard_observation(operations, *, idempotency_key, deadline,
                                connection_id='', max_age_ms=500,
                                retained_fence=None) -> OperationIntent
action_guard_components(result) -> (death, inventory, auth_input)

从 neomega_runtime.action_guard 导入,操作权限为 framework.self.action_guard.observe。它在同一只读操作中依次读取自身死亡/身份观察(limit 为 1)、库存窗口 0 与保留的 typed AuthInput。返回 source='sequential_native_action_guard'、atomic=False,读取顺序固定为 death → inventory → auth_input;这是三份顺序快照,不能当作一个原子输入锁或新的时钟。

max_age_ms 必须为 1..500。可选 retained_fence 必须含 connection_id、正 uint64 revision、uint64 tick,可带 neutral 字典,用于核对当前接纳链;它不会改变原始数据年龄或放宽读取预算。action_guard_components() 仅在你已经保存外层成功 receipt 后,对内层 result 调用,验证三份组件都成功并按顺序返回原 provider 结果。组件 offset 是 Host 读取耗时边界,不能充当客户端单调时间,也不会直接生成 guard(stage) 所需的整套字段。

绑定一个目标生命周期 ​

text
TargetTracker(*, connection_id, scope_revision, unique_id, runtime_id,
              added_revision, max_position_age=.5, max_distance=64)
tracker.update(snapshot, origin, *, target_offset=(0,0,0)) -> dict
tracker.aim(snapshot, origin, neutral_input, *, target_offset=(0,0,0))
    -> (packet, solution)

从 neomega_runtime.target_control 导入。绑定来自同一 ActorSnapshot:connection_id、scope_revision 加目标行的 unique_id、runtime_id、added.revision。两种 ID 可接受合法十进制字符串;runtime ID 与 Add revision 是正 uint64,unique ID 是 int64。发生移除、死亡、runtime ID 复用、scope 变化或观察缺失时,tracker 会封闭并抛 ProtocolError,不会悄悄切换到另一个同 ID 实体。

快照必须来自 native_actor_packets,并且 complete=True、gap=False、truncated=False,最多 64 行;目标需要可见、未移除、未死亡且有有效位置。实际新鲜度检查使用 position.stamp.at,不是快照 sampled_at 或最近任意包时间。max_position_age 为 0.01..2 秒,默认 0.5。

origin 是已验证的瞄准起点三元组,通常由实际眼位适配;target_offset 各轴 ±4,由环境几何决定。max_distance 为 0.1..64,实际目标距离必须至少 0.001。返回 target_unique_id、target_runtime_id、target_position、position_revision、yaw、pitch、distance。

python
from neomega_runtime.target_control import TargetTracker

def bind_and_aim(snapshot, runtime_id, origin):
    rows = [row for row in snapshot['actors']
            if row['runtime_id'] == str(runtime_id)]
    if len(rows) != 1:
        raise ValueError('需要唯一且当前可观察的目标')
    target = rows[0]
    tracker = TargetTracker(
        connection_id=snapshot['connection_id'],
        scope_revision=snapshot['scope_revision'],
        unique_id=target['unique_id'], runtime_id=target['runtime_id'],
        added_revision=target['added']['revision'],
    )
    return tracker, tracker.update(snapshot, origin)

这段函数只计算角度,不发送数据。对同一目标后续使用原 tracker 的 update(),不要每帧重新绑定来绕过生命周期检查。aim() 在完整中性 AuthInput 副本上更新 Yaw/HeadYaw/Pitch 及存在的重复角度字段,返回 (packet, solution);不会制造 tick 或断言已发送。

持续瞄准与持续物品输入 ​

text
TargetInputSession(actions)
session.record() -> dict
async session.run(*, observe, journal, control, inventory, slot,
                  tracker=None, target_offset=(0,0,0), use=None, complete=None,
                  cancel=None, timeout=5, interval=.05, max_frames=100,
                  max_sample_age=.5, experimental_single_controller=False)
    -> dict

actions 可传 ctx.item_actions;inventory 和 slot 绑定此次手持引用。必须显式传 experimental_single_controller=True,所有同进程输入生产者共用 control,并确保没有其他插件或客户端同时发送输入。进程锁和观察 guard 不能提供 Host 原子独占。

异步 observe() 每次返回如下字段,由你持续排空的独立观察流和原生输入审计构成:

字段要求
ready、observations_valid必须都为 True,流缺口和重连时不得继续
sampled_monotonic、connection_id单调时钟采样时间和绑定的原生库存连接
selected_slot、inventory当前已观察到的选槽和库存快照;不能只因为发过 select 就认为选中
last_auth_input_tick、neutral_input完整中性帧的 Tick 必须恰为最新已观察 tick 加一;连续帧期间外部时钟变化会失败
actors、aim_origin使用 tracker 时必填,分别为实体快照和实际瞄准起点

timeout 为 0.1..10 秒,interval 为 0.05..1 秒,max_frames 为 1..200,max_sample_age 为 0.01..1 秒。间隔是提交间隔约束,不是目标效果完成计时器。journal(record) 必须异步持久化,日志完成后还会重新检查原样本年龄。

仅跟踪时传 tracker;持续使用时传未启动的 ItemActions.use_air() 作为 use,且必须提供 complete(sample)。它是同步判定函数,只有实际观察满足完成条件时返回字面值 True。use 最多发送一次,之后发新鲜中性帧;例如弩完成条件应使用完整类型化 chargedItem 与更新的弹药减少证据,而不是睡眠秒数。

python
from neomega_runtime.target_control import TargetInputSession

async def aim_briefly(ctx, tracker, inventory, slot, *,
                      observe, journal, control, cancel):
    session = TargetInputSession(ctx.item_actions)
    try:
        return await session.run(
            observe=observe, journal=journal, control=control,
            inventory=inventory, slot=slot, tracker=tracker,
            cancel=cancel, timeout=1, max_frames=10,
            experimental_single_controller=True,
        )
    finally:
        await journal(session.record())

此封装依赖上表的实际观察适配器,不是脱离游戏状态即可运行的自动瞄准脚本。record() 返回 state、mode、stopped_submitting、server_use_stopped=False、hit_verified=False、最近 solution 和每个 attempt 的 intent、started、operation ID、receipt。

正常状态包括 busy、dispatched(仅跟踪完成帧预算)、observed_complete、budget_exhausted(持续使用耗尽帧预算)、cancelled;异常可留下 stopped 或 unknown。取消只阻止后续提交,不自动释放、再次 use_air、射击或补中性清理。存在未知动作,或 use 已启动却没观察到完成时,control 会被封锁;先核对原动作和当前使用状态,再恢复。

一次近战组合 ​

text
MeleeSession.fields_for(action) -> tuple
MeleeSession.prepare(actions, inventory, slot, *, binding, mouse_position,
                     attack_position, clicked_position, swing_data, neutral_input,
                     idempotency_keys, deadline,
                     experimental_single_controller=False, action='attack')
melee.record() -> dict
async melee.dispatch(*, control, guard, journal, timeout=10,
                     max_sample_age=.5, poll_interval=.05) -> dict

从 neomega_runtime.combat_actions 导入。action='attack' 准备 MouseOver → attack → SwingArm → neutral 四步,各有不同 idempotency key;action='swing_only' 仅准备 SwingArm → neutral 两步,需要两个 key,三个交互几何参数必须全为 None。后者不会攻击目标。

binding 必须精确包含 MeleeSession.fields_for(action) 返回的字段:攻击时为 actor_uuid、actor_runtime_id、target_unique_id、target_runtime_id、dimension、inventory_connection_id、observer_epoch;挥手模式去掉两个 target_ 字段。mouse_position、attack_position、clicked_position、swing_data、neutral_input 均是调用方明确验证的协议输入。

异步 guard(stage) 返回 ready=True、observations_valid=True、sampled_monotonic、与 binding 完全一致的身份字段、selected_slot、held_item_ref 和 last_auth_input_tick。它必须在等待 operation receipt 期间也持续消费独立观察流;不能只在每一步调用时读取一个越来越旧的快照。

各发送阶段的 last_auth_input_tick 必须等于预备中性帧 Tick 减一,最后 'complete' 阶段必须等于中性帧 Tick。手持引用在 mouse_over/attack 阶段检查,挥手模式则在 swing 阶段检查。每步先查 guard,再提交并保存回执;失败不会继续,也没有自动补发清理。

成功 record() 的 state='dispatched',observed=False:只证明四步或两步发送回执成立,不证明目标受伤。异常后 state 为 stopped,这仍不代表服务端动作已停止。与 TargetInputSession 不同,MeleeSession 不自动封锁 control;调用者应根据保留的 attempts 和独立证据决定是否封锁,恢复时查询原 ID/key,不能重建 melee 续跑。

弩、装填与弹药观察 ​

text
CrossbowInventoryObservations(*, connection_id, connected_at, crossbow_slot,
                              crossbow_network_id, ammo_network_id,
                              ammo_slots, max_age_ms)
projection.apply(snapshot) -> dict
projection.close() -> None

从 neomega_runtime.crossbow_inventory 导入。它是只读投影,不负责装填计时、发送、释放或开火。NetworkID 必须按当前连接实际解析,弩与弹药 ID 均为正 int32 且不能相同;不能抄其他服务器的 ID。弩槽为 0..8,弹药槽为 0..35 的非空不重复列表,不能包含弩槽。

apply() 接受同连接、同 connected_at 的原生窗口 0 快照,检查 revision、槽位来源、类型化 NBT 和指纹。max_age_ms 是非负整数,限制槽位在快照采样时的年龄,不保证调用时仍新鲜。断线或验证失败会封闭投影,显式建立新连接后才重新创建。

输出含义
crossbow.chargecharged / uncharged / unknown。完整类型化 compound 缺少 chargedItem 才能判未装填;缺 NBT 或类型不匹配为 unknown
ammo各声明弹药槽独立状态与 ammo_count;不声称全背包完整
ammo_deltas只对比两次可知计数,且新槽观察 revision 大于前次快照 revision、观察时间晚于前次 sampled_at
charge_transition确认新观察支持时返回 {before, after},否则 None
current_state_guaranteed、fired_verified、hit_verified始终为 False;装填或弹药变化本身不能证明开火/命中

charged 目前要求类型化 chargedItem 中 Name='minecraft:arrow' 且为字符串,Count 为正 uint8。缺少这些证据、特殊箭种或未支持 NBT 形状时保留 unknown,不按外观名称推断。

python
from neomega_runtime.crossbow_inventory import CrossbowInventoryObservations

def watch_crossbow(first_snapshot, crossbow_id, arrow_id, bow_slot, ammo_slots):
    projection = CrossbowInventoryObservations(
        connection_id=first_snapshot['connection_id'],
        connected_at=first_snapshot['connected_at'],
        crossbow_slot=bow_slot, crossbow_network_id=crossbow_id,
        ammo_network_id=arrow_id, ammo_slots=ammo_slots, max_age_ms=500,
    )
    return projection, projection.apply(first_snapshot)

后续对同一 projection 调用 apply(new_snapshot) 才能形成变化证据。需要判断命中时,继续收集与目标生命周期匹配的伤害、生命或投射物观察,并明确其覆盖范围;不能把 attack 回执、挥手动画、弹药减少或实体消失单独作为击杀证明。

Neomega · 使用、开发与参考文档