Molang — 表达式工具

Molang 是 Minecraft 内置的表达式语言。RoninNetease 提供了类型安全的读写封装,支持命名变量Query 变量实体变量的增删改查。


1. 概述

architect.utils.molang
├── types.py     ← 抽象基类(MolangReadable, MolangMutable 等)
├── common.py    ← NamedVariable, NamedEntityVariable
├── client.py    ← QueryVariable, ReactiveQueryVariable, @MolangQuery, MolangClient
└── server.py    ← NamedProperty, MolangServer

2. 抽象类型 — types.py

定义了 Molang 读写操作的接口:

基类 方法
MolangReadable getValue(self, actorId, defaultValue)
MolangMutable(MolangReadable) setValue(self, actorId, value)
EntityMolangReadable getValue(self, defaultValue)
EntityMolangMutable(EntityMolangReadable) setValue(self, value)

3. 命名变量 — common.py

3.1 NamedVariable — 实体变量(v.{name}

from architect.utils.molang.common import NamedVariable

# 创建变量
myVar = NamedVariable('speed')     # 自动加前缀 → 'v.speed'

# 读取
val = myVar.getValue(actorId, defaultValue=0)

# 写入(自动执行 Molang 赋值表达式)
myVar.setValue(actorId, 1.5)       # 等效 v.speed = 1.5

# 获取完整 Molang 名称
print(myVar.getMolangName())       # 'v.speed'

3.2 NamedEntityVariable — 无 actor 参数的实体变量

from architect.utils.molang.common import NamedEntityVariable

# 构造函数直接绑定实体和变量名
var = NamedEntityVariable(entityId, 'speed', defaultValue=0)

# 读写无需传 actorId
val = var.getValue()
var.setValue(2.0)

# 获取 Molang 名称
print(var.getName())   # 'v.speed'

4. 客户端 Query 变量 — client.py

4.1 QueryVariable — query.mod 变量

from architect.utils.molang.client import QueryVariable

# 创建并注册 query.mod 变量
qv = QueryVariable('playerLevel', defaultValue=0)
# 自动注册为 'query.mod.playerLevel'

# 读写
val = qv.getValue(actorId)
qv.setValue(actorId, 10)

# 监听值变化
qv.OnValueChanged.on(lambda actorId, value: print('Changed:', actorId, value))

4.2 ReactiveQueryVariable — 自动计算变量

from architect.utils.molang.client import ReactiveQueryVariable

# 定义计算函数(返回计算值)
def calcLevel(actorId):
    return getComponent(actorId, PlayerStats).level

# 创建响应式变量
rqv = ReactiveQueryVariable('playerLevel', calc=calcLevel)
rqv.update(actorId)  # 手动触发一次更新

4.3 @MolangQuery — Query 变量装饰器

from architect.utils.molang.client import MolangQuery

@MolangQuery
def playerHealth(actorId):
    """普通 query 变量,只在本地使用"""
    return getComponent(actorId, Health).hp

@MolangQuery(shared=True)
def playerLevel(actorId):
    """共享变量:变化时广播到其他客户端"""
    return getComponent(actorId, PlayerStats).level

@MolangQuery 装饰的函数作为 ReactiveQueryVariable 的计算函数。shared=True 时会通过 MolangClient.broadcastQuery() 发送到服务端再广播给其他客户端。

4.4 evalMolang — 一次性 Molang 表达式求值

用于在客户端对 Molang 表达式进行一次性求值,无需注册持久变量:

from architect.utils.molang.client import evalMolang

# 求值 Molang 表达式,返回计算结果
value = evalMolang(entityId, 'query.health')
value = evalMolang(entityId, 'query.mod.playerLevel')

# 带错误处理的求值
def onError(err):
    print('Molang 表达式出错:', err)
    return 0  # 出错时返回默认值

value = evalMolang(entityId, 'v.broken_expr', onerror=onError)  # 返回 0
参数 类型 说明
id str 实体 ID
expr str 完整的 Molang 表达式字符串
onerror callable / None 可选错误回调;若为 None,错误时抛出 SyntaxError

evalMolang 适用于需要临时求值 Molang 表达式的场景(如调试、一次性计算),无需像 QueryVariable 那样创建持久注册。

4.5 MolangClient 子系统

from architect.utils.molang.client import MolangClient

# 服务端会自动注册 @SubsystemClient MolangClient
# 它在 onRender 中自动更新所有 ReactiveQueryVariable
# 并监听 'ronin_molang_query' 自定义事件同步共享变量

5. 服务端 — server.py

5.1 NamedProperty — 实体属性

from architect.utils.molang.server import NamedProperty

prop = NamedProperty('strength')   # 通过 v.property('strength') 操作

# 读取(使用 EvalMolangExpression)
val = prop.getValue(actorId)       # 可能返回 None(出错时)

# 写入(使用 SetPropertyValue)
prop.setValue(actorId, 100)

5.2 MolangServer 子系统

from architect.utils.molang.server import MolangServer

# 自动注册,处理以下功能:
# - 监听 'ronin_molang_query' 事件 → 广播到所有客户端
# - sendQuery(id, name, value) → 向所有客户端发送查询

6. 完整示例

# 客户端:将组件值暴露为 Molang 变量
from architect.utils.molang.client import QueryVariable, MolangQuery
from architect.utils.molang.common import NamedVariable

@MolangQuery
def playerHealth(actorId):
    """本地 query 变量,每渲染帧自动同步"""
    health = getComponent(actorId, Health)
    return health.hp if health else 0

# 创建 v.score 实体变量
score = NamedVariable('score')
score.setValue(actorId, 1000)
print(score.getValue(actorId))  # 1000

# 在 Molang JSON 中使用:v.score、query.mod.playerHealth

下一步