外观
资源会话 Scope
配套版本
本页资源会话接口使用 1.1.0 Host(Host API 1.10)与配套 SDK。1.0.0 不包含这些接口;升级时须同时核对 Host、SDK 和权限,不能只更换插件代码。下载见工具包,升级步骤见版本页。
插件需要连续操作背包、位置或容器时,用 Scope 一次申请整组资源。Host 统一协调同一机器人上的受管操作,本地插件和远程 EP遵守相同规则。Scope 关闭会停止接纳新动作,已经发生的游戏效果不会回滚;需要原子保存插件状态和动作意图时,使用存储事务。
声明资源与条件
python
from neomega_runtime import Resources, ScopeCondition, ScopeSpec
spec = ScopeSpec(
resources=[Resources.inventory_session(), Resources.position_control()],
conditions=[ScopeCondition.near(
target={"dimension": 0, "pos": [12.0, 64.0, 20.0]},
distance=6.0,
)],
)| 声明 | 含义 |
|---|---|
Resources.inventory_session() | 占用库存;Host 同时加入交互界面资源。 |
Resources.position_control() | 占用机器人位置控制。 |
Resources.container_session(dimension, x, y, z) | 指定整数维度与坐标的容器;Host 同时加入库存、交互界面和位置资源。 |
ScopeCondition.near(target, distance) | 要求机器人位于目标维度且距离不超过给定值;坐标须为三个有限数,距离须有限且不小于 0。 |
ScopeSpec(..., lease_ms=30000) | 申请资源会话及初始租约;SDK 自动续租,仍受 Host 最大存续时间限制。 |
整组资源一起授予,不支持在已有 Scope 中嵌套申请,也不支持持有资源后追加升级。near 会自动加入位置依赖:条件未满足时继续在申请期限内等待,执行受管动作时再次检查。它不会主动移动机器人,也不能阻止其他玩家或服务器改变位置。
手动申请与关闭
下面的函数接收受管插件回调提供的 ctx,返回联合证据和关闭句柄。request_id 应由调用方为本次业务分配并保留,不要给不同业务复用同一个 ID。
python
async def inspect_resources(ctx, request_id):
scope = await ctx.scopes.acquire(spec, request_id=request_id, timeout=30)
try:
with scope.context():
evidence = await scope.evidence("inventory", "position")
# 需要连续执行的 SDK 调用放在此处。
finally:
closed = await scope.close()
return evidence, closedscope.context() 让内部普通 SDK 调用继承 Scope 与已有服务调用关系。创建 Bus OpIntent 也应在该上下文内进行,动作的 session 使用 scope.handle.action_session。Scope 不会额外赋予动作权限。
正常授予的句柄状态为 active。关闭成功返回句柄后,检查 closed.state 和 closed.pending_operation_ids;只有 state == "closed" 表示该 Scope 已完成关闭。调用返回不等于所有在途动作已经结束,详见下方关闭与恢复。
自动关闭与回调
仅需要自动管理生命周期时,可以写成:
python
async def inspect_inventory(ctx):
async with ctx.scopes.open(
ScopeSpec(resources=[Resources.inventory_session()])
) as scope:
evidence = await scope.evidence("inventory")
return evidence, scope.handle手动取得的 Scope 也支持 async with scope:。上下文退出会尝试清理;若业务和清理同时出错,ScopeError.business_error 与 ScopeError.cleanup_error 保留两者,handle 保留已知句柄。
需要让调用方统一处理业务、清理两种结果时,使用 run:
python
async def collect(scope):
return await scope.evidence("inventory")
result = await ctx.scopes.run(
ScopeSpec(resources=[Resources.inventory_session()]), collect
)
# 分别处理 result.value、business_error、cleanup_error 与 handle。申请成功后,回调只执行一次。普通业务异常保存在 result.business_error,不会被自动重跑;取消在清理后继续传播,并通过异常的 scope_result 或 scope_error 保留恢复信息。申请本身失败仍会抛出异常。
| API | 返回与用途 |
|---|---|
await ctx.scopes.acquire(spec, request_id=..., timeout=30) | 手动取得 Scope。 |
ctx.scopes.open(spec, ...) / await ctx.scopes.run(spec, callback, ...) | 自动清理上下文 / 单次回调结果。 |
await scope.evidence(*resources) | 读取 Scope 所占资源的权威证据,返回解析后的 JSON 数据。 |
await scope.close(timeout=5) | 幂等关闭请求,返回本次观察到的句柄;检查是否仍未关闭。 |
await ctx.scopes.lookup(request_id) / await ctx.scopes.read(scope_id) | 查询原申请 / 原 Scope 的状态,不重新执行业务。 |
最小授权
将实际调用的精确成员加入 permissions.services,由管理员授予对应 service:<member>。例如,读取库存证据并保留失联恢复能力的 Scope 需要:
json
{
"permissions": {
"services": [
"scope.acquire", "scope.renew", "scope.close",
"scope.read", "scope.lookup", "scope.evidence"
]
}
}这是合并进现有 manifest 的权限片段,不是完整 manifest;依赖本页能力的插件设置 host_api: {"major": 1, "min_minor": 10}。renew 和 read 也会由 SDK 生命周期自动调用,不能只授权代码中显式出现的 acquire。具体业务成员仍需另行申请;单纯申请库存 Scope 不会授权移动物品。远程连接还需要EP 会话权限。
关闭与恢复
关闭先停止新动作接纳,再等待已经接纳的动作,包括排队中的动作。等待超过期限可能仍返回未关闭句柄;此时保留 Scope ID、operation_ids 和 pending_operation_ids,通过 read 与原动作回执继续核对。租约到期、插件退出或 EP 失联,也不会把尚未结束的效果当作已完成。
申请回复丢失时,SDK 查询原 request_id,不创建第二份申请。无法确认的申请会通过 ScopeError.request_id 暴露原 ID;重启恢复时还必须满足 Host 对原身份、代际和会话的约束。新远程连接不能凭相同 ID 接管旧会话。
动作回执的 unknown 表示结果无法确认。保留原意图、原 ID 与原期限,查询原回执;不要换幂等键再次提交。关闭 Scope 不会撤销已接纳动作、消除未知效果或自动退款,业务补偿由插件决定。
证据与普通视图
例如,两个 EP 显示的库存数量暂时不同,插件应在 Scope 中读取用于决策的 evidence,而不是把各自缓存当作锁内的最新状态。库存证据依据实际交互响应,位置证据依据相关联的查询响应;联合证据描述同一已观察数据边界,不承诺游戏服务器在同一瞬间的全局快照。
任意命令或原始协议包可能执行成功,但无法证明它影响的世界状态都已被观察。此时回执可能标记 state_proof=unavailable_for_world_effects 和 unresolved_effects=true,后续证据读取会拒绝该未解决的因果边界。不能把命令输出当作所有世界变化的读回证明。
普通 View 和可恢复投影仍用于观察;投影如何处理断流与不完整快照,见远程 EP 与投影。Go 用法见下节,使用公开工具包中的独立模块,无需私有 Host 源码。
Go 资源会话
1.1.0 公开包 github.com/OmineDev/neomega-sdk-go/scopes 提供相同的资源会话。Go Worker 使用已有 peer 建立客户端:
go
client := scopes.New(neomega.NewManagedBus(peer))其中 neomega 来自 github.com/OmineDev/neomega-sdk-go。适配器借用 Worker 的 Peer,不关闭它;插件停止前关闭投影、Scope 和 client,检查清理结果,再结束 Worker。远程进程使用远程 EP 客户端。
以下函数接收已建立的 *scopes.Client,使用调用方保留的申请 ID;Host 检查与 Python 相同的精确权限。
go
package pluginexample
import (
"context"
"encoding/json"
"errors"
"github.com/OmineDev/neomega-sdk-go/scopes"
)
func inspectInventory(ctx context.Context, client *scopes.Client, requestID string) (
evidence json.RawMessage, handle scopes.Handle, err error,
) {
session, err := client.AcquireRequest(ctx, requestID, scopes.Spec{
Resources: scopes.All(scopes.InventorySession()),
})
if err != nil {
return nil, handle, err
}
defer func() {
var cleanupErr error
handle, cleanupErr = session.Close()
err = errors.Join(err, cleanupErr)
}()
evidence, err = session.Evidence(session.Context(), "inventory")
return evidence, handle, err
}调用方同时检查返回的 err、handle.State 和 handle.PendingOperationIDs,不能把 Close() 返回当作世界效果已经完成。需要回调包装时:
go
result, err := client.Run(ctx, scopes.Spec{
Resources: scopes.All(scopes.InventorySession()),
}, func(session *scopes.Session) error {
_, err := session.Evidence(session.Context(), "inventory")
return err
})result.BusinessError、result.CleanupError 和 result.Handle 分别保留业务、清理和状态;返回的 err 合并错误。回调发生 panic 时先执行清理,再以 *scopes.Panic 携带原值及结果继续 panic,不自动重跑。
| Go API | 用途 |
|---|---|
scopes.New(bus) / scopes.NewRemote(ctx, bus) | 建立本地资源客户端 / 带续租的远程 EP 会话;调用方负责 client.Close()。 |
client.Acquire(ctx, spec) / AcquireRequest(ctx, requestID, spec) | 自动生成申请 ID / 使用已保存的 ID;后续用 Lookup 或 Read 查询。 |
session.Context() / session.Handle() | 受管调用上下文 / 当前句柄副本。 |
session.Evidence(ctx, resources...) / session.Close() | 读取 JSON 证据 / 幂等关闭并返回句柄及错误。 |
client.Run(ctx, spec, callback) | 单次回调与清理;返回 scopes.Result 和错误。 |
条件写为 scopes.Near(scopes.Target{Dimension: 0, Pos: [3]float64{12, 64, 20}}, 6),放入 scopes.Spec.Conditions;LeaseMillis 使用毫秒。受管 Bus 调用显式传递 session.Context(),动作账本会话取 session.Handle().ActionSession,不要传入旧的裸 context.Background() 丢掉 Scope 关系。远程连接见Go 远程客户端。