外观
瞄准、近战与弩状态
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)
-> dictactions 可传 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.charge | charged / 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 回执、挥手动画、弹药减少或实体消失单独作为击杀证明。