ResoniteLink
ResoniteLink позволяет разработчикам программно управлять Resonite VR-мирами. Используйте CLI или TypeScript-библиотеку для автоматизации изменений слотов/компонентов, импорта ассетов и управления ProtoFlux через WebSocket API.
ResoniteLink — это MCP-сервер и CLI-инструмент, предназначенный для всестороннего взаимодействия с мирами Resonite VR через протокол WebSocket ResoniteLink. Он позволяет разработчикам программно управлять основными элементами, такими как слоты, компоненты и ресурсы в экземпляре Resonite. Помимо базового управления объектами, инструмент поддерживает продвинутые операции, такие как импорт текстур из локальных файлов или сырых пиксельных данных, а также предоставляет точный контроль над узлами ProtoFlux для построения сложной внутримировой логики. Его можно бесшовно интегрировать как MCP-сервер для таких платформ, как Claude Code/Desktop, или использовать как автономный интерфейс командной строки или библиотеку TypeScript для создания собственной автоматизации и разработки приложений.
Ключевые особенности
Варианты использования
ResoniteLink WebSocket プロトコルを使用して Resonite VR ワールドを操作するための MCP サーバ & CLI ツール。
npm install
npm run buildプロジェクトルートの .mcp.json に設定済み。Claude Code を再起動すると自動的にMCPサーバーが利用可能になります。
claude_desktop_config.json に以下を追加:
{
"mcpServers": {
"resonitelink": {
"command": "node",
"args": ["C:/Users/neo/GitHub/resolink_mcp/dist/mcp-server.js"],
"env": {
"RESONITE_WS_URL": "ws://localhost:29551"
}
}
}
}| ツール | 説明 |
|---|---|
connect |
Resonite に接続 |
disconnect |
接続を切断 |
get_slot |
スロット情報を取得 |
find_slot |
名前でスロットを検索 |
add_slot |
スロットを追加 |
remove_slot |
スロットを削除 |
update_slot |
スロットを更新 |
add_component |
コンポーネントを追加 |
get_component |
コンポーネント情報を取得 |
update_component |
コンポーネントを更新 |
remove_component |
コンポーネントを削除 |
import_texture_file |
テクスチャをファイルからインポート |
search_components |
コンポーネントを検索 |
get_component_info |
コンポーネント詳細を取得 |
list_categories |
カテゴリ一覧 |
search_by_category |
カテゴリで検索 |
search_by_member |
メンバー名で検索 |
get_component_source |
ソースコードを取�� |
grep_source |
ソースを全文検索 |
CLI は WebSocket URL を以下の優先順位で決定します:
- コマンドライン引数
--url(最優先) - 環境変数
RESONITELINK_URL - デフォルト値:
ws://localhost:29551
環境変数で設定する例:
# Windows (PowerShell)
$env:RESONITELINK_URL = "ws://localhost:29469"
# Windows (コマンドプロンプト)
set RESONITELINK_URL=ws://localhost:29469
# Linux/Mac
export RESONITELINK_URL=ws://localhost:29469環境変数を設定すれば、毎回 --url オプションを指定する必要がなくなります。
# Rootスロット情報を取得
node dist/cli.js root --depth 1
# 特定のスロット情報を取得
node dist/cli.js get-slot --id <slotId> --depth 1
# コンポーネント情報も含めて取得
node dist/cli.js get-slot --id <slotId> --components
# 名前でスロットを検索
node dist/cli.js find --name MyObject
# スロットを追加
node dist/cli.js add-slot --name NewSlot --position 0,1,0
# 親スロットを指定してスロットを追加
node dist/cli.js add-slot --parent <parentId> --name MySlot --position 0,1,0 --scale 1,1,1
# コンポーネントを追加
node dist/cli.js add-component --slot <slotId> --type "[FrooxEngine]FrooxEngine.BoxMesh"
# スロットを更新
node dist/cli.js update-slot --id <slotId> --name NewName --position 0,2,0
# スロットを削除
node dist/cli.js remove-slot --id <slotId>
# コンポーネントを取得
node dist/cli.js get-component --id <componentId>
# コンポーネントを削除
node dist/cli.js remove-component --id <componentId>
# スロット階層をツリー表示
node dist/cli.js tree --depth 3
# WebSocket URLを指定(異なるポートの場合)
node dist/cli.js root --url ws://localhost:9422 --depth 2import { ResoniteLinkClient } from './src/index.js';
const client = new ResoniteLinkClient({ url: 'ws://localhost:29551' });
await client.connect();
// スロットを追加
await client.addSlot({ name: 'MyObject', position: { x: 0, y: 1, z: 0 } });
// コンポーネントを追加
await client.addComponent({
containerSlotId: slotId,
componentType: '[FrooxEngine]FrooxEngine.BoxMesh'
});
client.disconnect();const client = new ResoniteLinkClient({
url: 'ws://localhost:29551', // WebSocket URL(必須)
debug: true, // コンソールにログ出力
logFile: 'debug.log', // ファイルにログ出力
requestTimeout: 30000, // リクエストタイムアウト(ミリ秒、デフォルト: 30000)
autoReconnect: false, // 自動再接続
reconnectInterval: 5000, // 再接続間隔(ミリ秒)
});問題が発生した場合、デバッグログを有効にすると SEND/RECV メッセージを確認できます:
const client = new ResoniteLinkClient({
url: 'ws://localhost:29551',
debug: true, // コンソール出力
logFile: 'debug.log', // ファイル出力
});ログ例:
[2026-01-08T06:10:24.504Z] SEND: { "$type": "addSlot", "messageId": "..." }
[2026-01-08T06:10:24.506Z] RECV: { "success": true, "messageId": "...", "error": null }
レスポンスが返らない場合(不正な形式のデータ送信時など)、タイムアウトでエラーが発生します:
const client = new ResoniteLinkClient({
url: 'ws://localhost:29551',
requestTimeout: 10000, // 10秒でタイムアウト
});タイムアウト発生時は Error: Request timeout after 10000ms: updateComponent (...) のようなエラーがスローされます。
ResoniteLink のアセットインポートAPIを使用して、テクスチャをインポートできます。
// Resoniteホストのローカルファイルからテクスチャをインポート
const result = await client.importTexture2DFile({
filePath: 'C:/path/to/texture.png'
});
if (result.success) {
console.log('Asset URL:', result.assetURL);
// assetURL を StaticTexture2D などに設定可能
}// RGBAピクセルデータからテクスチャをインポート
const width = 256;
const height = 256;
const rawData = Buffer.alloc(width * height * 4); // RGBA, 4 bytes per pixel
// ピクセルデータを生成(例: グラデーション)
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const offset = (y * width + x) * 4;
rawData[offset] = x; // R
rawData[offset + 1] = y; // G
rawData[offset + 2] = 128; // B
rawData[offset + 3] = 255; // A
}
}
const result = await client.importTexture2DRawData({
width,
height,
colorProfile: 'sRGB', // 'sRGB' または 'Linear'
rawData
});
if (result.success) {
console.log('Asset URL:', result.assetURL);
}filePathは Resonite が動作しているホストのローカルパスassetURLはセッション内でのみ有効(一時的なURL)- サポートされる形式: PNG, JPG など���般的な画像形式
- 生データは RGBA フォーマット(1ピクセル4バイト)
Resonite ワールドの Root 以下には、削除してはいけないシステムオブジェクトがあります。
| オブジェクト名 | 説明 |
|---|---|
Controllers |
コントローラー入力システム |
Roles |
ユーザーロール管理 |
SpawnArea |
ユーザーのスポーン位置 |
Light |
ワールドの照明 |
Skybox |
空・背景 |
User <...> |
接続中のユーザー(削除するとキックされる) |
__TEMP |
一時オブジェクト管理 |
Undo Manager |
アンドゥ履歴 |
Assets |
共有アセット |
Clipboard Importer |
クリップボードインポート機能 |
const SYSTEM_OBJECTS = [
'Controllers', 'Roles', 'SpawnArea', 'Light', 'Skybox',
'__TEMP', 'Undo Manager', 'Assets', 'Clipboard Importer'
];
// システムオブジェクトとUserはスキップ
if (SYSTEM_OBJECTS.includes(name) || name.startsWith('User ')) {
continue;
}[FrooxEngine]FrooxEngine.ComponentName
| コンポーネント | 用途 |
|---|---|
BoxMesh |
直方体メッシュ |
SphereMesh |
球体メッシュ |
CylinderMesh |
円柱メッシュ |
ConeMesh |
円錐メッシュ |
BevelBoxMesh |
角丸直方体 |
RampMesh |
スロープ |
FrameMesh |
フレーム |
TorusMesh |
トーラス |
CapsuleMesh |
カプセル |
MeshRenderer |
メッシュ描画 |
PBS_Metallic |
PBRマテリアル |
Light |
ライト |
await client.updateComponent({
id: materialId,
members: {
AlbedoColor: { $type: 'colorX', value: { r: 1, g: 0, b: 0, a: 1, profile: 'sRGB' } },
Smoothness: { $type: 'float', value: 0.5 },
Metallic: { $type: 'float', value: 0.2 },
}
});Enum 型のメンバーを更新する場合、以下の形式を使用する必要があります:
{
$type: 'enum', // 小文字の 'enum'
value: 'Alpha', // 文字列で値を指定(数値ではない)
enumType: 'BlendMode' // Enum の型名
}| 値 | 説明 |
|---|---|
Opaque |
不透明(デフォルト) |
Cutout |
カットアウト(アルファテスト) |
Alpha |
半透明(アルファブレンド) |
await client.updateComponent({
id: materialId,
members: {
BlendMode: { $type: 'enum', value: 'Alpha', enumType: 'BlendMode' },
AlbedoColor: { $type: 'colorX', value: { r: 0.6, g: 0.75, b: 0.9, a: 0.3, profile: 'sRGB' } },
}
});| 値 | 説明 |
|---|---|
Directional |
ディレクショナルライト |
Point |
ポイントライト |
Spot |
スポットライト |
await client.updateComponent({
id: lightId,
members: {
LightType: { $type: 'enum', value: 'Point', enumType: 'LightType' },
Intensity: { $type: 'float', value: 2.0 },
Range: { $type: 'float', value: 10.0 },
}
});$typeは必ず小文字の'enum'('Enum'ではない)valueは数値ではなく文字列で指定enumTypeを省略すると動作しない場合がある- 正しくない形式を送信するとレスポンスが返らずタイムアウトする
| $type | 説明 | 例 |
|---|---|---|
float |
浮動小数点 | { $type: 'float', value: 0.5 } |
int |
整数 | { $type: 'int', value: 10 } |
bool |
真偽値 | { $type: 'bool', value: true } |
float2 |
2Dベクトル | { $type: 'float2', value: { x: 1, y: 1 } } |
float3 |
3Dベクトル | { $type: 'float3', value: { x: 1, y: 2, z: 3 } } |
floatQ |
クォータニオン | { $type: 'floatQ', value: { x: 0, y: 0, z: 0, w: 1 } } |
colorX |
色 | { $type: 'colorX', value: { r: 1, g: 0, b: 0, a: 1, profile: 'sRGB' } } |
enum |
列挙型 | { $type: 'enum', value: 'Alpha', enumType: 'BlendMode' } |
reference |
参照 | { $type: 'reference', targetId: 'Reso_XXXXX' } |
list |
リスト | { $type: 'list', elements: [...] } |
empty |
出力メンバー | { $type: 'empty', id: 'Reso_XXXXX' } |
ResoniteLink の更新により、ProtoFlux ノードの出力メンバーが $type: "empty" として返されるようになりました。
ProtoFlux ノードには入力(SyncRef)と出力(NodeValueOutput)があります:
- 入力: 値を受け取る(reference型で返される)
- 出力: 値を出力する(以前は返されなかった → 今は empty 型で返される)
{
"componentType": "...GlobalTransform",
"members": {
"Instance": { "$type": "reference", "targetId": null, ... },
"GlobalPosition": { "$type": "empty", "id": "Reso_82E" },
"GlobalRotation": { "$type": "empty", "id": "Reso_82F" },
"GlobalScale": { "$type": "empty", "id": "Reso_830" }
}
}出力メンバーの id を使って、他のノードの入力に接続��きます:
// GlobalTransform の GlobalPosition 出力を取得
const globalTransformComp = slotData.data?.components?.find(c =>
c.componentType?.includes('GlobalTransform')
);
const globalPositionId = globalTransformComp.members.GlobalPosition.id; // "Reso_82E"
// ValueSub の A 入力に GlobalPosition を接続
await client.updateComponent({
id: subComp.id,
members: {
A: { $type: 'reference', targetId: globalPositionId }, // 出力IDを直接参照!
}
});| 項目 | 以前 | 現在 |
|---|---|---|
| 出力メンバー | JSONに含まれない | $type: "empty" で返される |
| 複数出力ノードの参照 | コンポーネントID全体を参照 → エラ��� | 出力IDを個別に参照 → 成功 |
これにより、GlobalTransform のような複数出力を持つノードも正しく接続できるようになりました。
MeshRenderer の Materials リストを更新するには2段階の操作が必要です。
ResoniteLink の制限により、リスト要素への参照設定は以下の動作をします:
- 1回目の更新: リストに新しい要素が追加されるが、
targetIdは null になる - 2回目の更新: 要素の
idを指定することで、既存要素のtargetIdを設定できる
つまり、要素の追��と参照の設定は別々の操作として行う必要があります。
// 1. まずリストに要素を追加(この時点では targetId は null になる)
await client.updateComponent({
id: rendererId,
members: {
Materials: {
$type: 'list',
elements: [{ $type: 'reference', targetId: materialId }]
}
}
});
// 2. 追加された要素のIDを取得
const rendererData = await client.getComponent(rendererId);
const elementId = rendererData.data.members.Materials.elements[0].id;
// 3. 要素のIDを指定して、参照を設定
await client.updateComponent({
id: rendererId,
members: {
Materials: {
$type: 'list',
elements: [{ $type: 'reference', id: elementId, targetId: materialId }]
}
}
});idフィールドを省略すると、新しい要素が追加される(既存要素は更新されない)idフィールドを指定すると、その ID を持つ既存要素が更新される- 1回目で
targetIdを指定しても無視され、null になる
ProtoFlux ノード(ジェネリック型コンポーネント)を追加するには、特定の形式が必要です。
[ProtoFluxBindings]FrooxEngine.FrooxEngine.ProtoFlux.CoreNodes.<コンポーネント名><型>
| 項目 | 正しい形式 | 間違った形式 |
|---|---|---|
| アセンブリ名 | [ProtoFluxBindings] |
[FrooxEngine] |
| 名前空間 | FrooxEngine.FrooxEngine.ProtoFlux.CoreNodes |
FrooxEngine.ProtoFlux.CoreNodes |
| ジェネリック型 | <bool>, <int>, <float> |
<System.Boolean>, `1[System.Boolean] |
- 名前空間:
FrooxEngineが2回繰り返される - 型指定: C# エイリアス(
bool,int,float)を使用する(System.Booleanではない) - 記法:
<>形式を使用する(.NET のバッククォート記法`1[...]ではない)
スロットやユーザーなどの複合型を型パラメータに使う場合は、アセンブリ名付きの完全修飾名を使用する:
[ProtoFluxBindings]...RefObjectInput<[FrooxEngine]FrooxEngine.Slot>
| 型 | 正しい形式 | 間違った形式 |
|---|---|---|
| Slot | <[FrooxEngine]FrooxEngine.Slot> |
<Slot>, <FrooxEngine.Slot> |
| User | <[FrooxEngine]FrooxEngine.User> |
<User> |
| IButton | <[FrooxEngine]FrooxEngine.IButton> |
<IButton> |
重要: プリミティブ型(int, float, bool など)はエイリアスをそのまま使い、複合型(Slot, User など)は [FrooxEngine]FrooxEngine.TypeName 形式を使う。
| ノード | componentType |
|---|---|
| ValueInput<int> | [ProtoFluxBindings]FrooxEngine.ProtoFlux.Runtimes.Execution.Nodes.ValueInput<int> |
| ValueAdd<int> | [ProtoFluxBindings]FrooxEngine.ProtoFlux.Runtimes.Execution.Nodes.Operators.ValueAdd<int> |
| ValueDisplay<int> | [ProtoFluxBindings]FrooxEngine.ProtoFlux.Runtimes.Execution.Nodes.ValueDisplay<int> |
| WorldTimeFloat | [ProtoFluxBindings]FrooxEngine.ProtoFlux.Runtimes.Execution.Nodes.FrooxEngine.Time.WorldTimeFloat |
| AxisAngle_floatQ | [ProtoFluxBindings]FrooxEngine.ProtoFlux.Runtimes.Execution.Nodes.Math.Quaternions.AxisAngle_floatQ |
| HSV_ToColorX | [ProtoFluxBindings]FrooxEngine.ProtoFlux.Runtimes.Execution.Nodes.Color.HSV_ToColorX |
| ValueFieldDrive<floatQ> | [ProtoFluxBindings]FrooxEngine.FrooxEngine.ProtoFlux.CoreNodes.ValueFieldDrive<floatQ> |
| ValueFieldDrive<colorX> | [ProtoFluxBindings]FrooxEngine.FrooxEngine.ProtoFlux.CoreNodes.ValueFieldDrive<colorX> |
| ValueFieldDrive<bool> | [ProtoFluxBindings]FrooxEngine.FrooxEngine.ProtoFlux.CoreNodes.ValueFieldDrive<bool> |
// ValueFieldDrive<floatQ> を追加(回転ドライブ用)
await client.addComponent({
containerSlotId: slotId,
componentType: '[ProtoFluxBindings]FrooxEngine.FrooxEngine.ProtoFlux.CoreNodes.ValueFieldDrive<floatQ>'
});
// WorldTimeFloat を追加(時間取得用)
await client.addComponent({
containerSlotId: slotId,
componentType: '[ProtoFluxBindings]FrooxEngine.ProtoFlux.Runtimes.Execution.Nodes.FrooxEngine.Time.WorldTimeFloat'
});
// HSV_ToColorX を追加(色変換用)
await client.addComponent({
containerSlotId: slotId,
componentType: '[ProtoFluxBindings]FrooxEngine.ProtoFlux.Runtimes.Execution.Nodes.Color.HSV_ToColorX'
});// ValueAdd の A, B 入力に ValueInput ノードを接続
await client.updateComponent({
id: addCompId,
members: {
A: { $type: 'reference', targetId: input1CompId },
B: { $type: 'reference', targetId: input2CompId },
}
});
// ValueDisplay の Input に ValueAdd の出力を接続
await client.updateComponent({
id: displayCompId,
members: {
Input: { $type: 'reference', targetId: addCompId },
}
});| コンポーネント | 用途 |
|---|---|
ValueFieldDrive<T> |
フィールドをドライブ |
ReferenceFieldDrive<T> |
参照フィールドをドライブ |
GlobalValue<T> |
グローバル値 |
GlobalReference<T> |
グローバル参照 |
ProtoFlux ノードを追加する際は、各ノードを別々のスロットに配置し、適切な位置に並べることで視認性が向上します。
Resonite の座標系:
- X軸: 左右(右が正)
- Y軸: 上下(上が正)
- Z軸: 前後(手前が正)
ProtoFlux ノードは**左から右(X軸方向)**に配置するのが一般的です。
左 ────────────────────────────────────────────→ 右 (X軸)
[入力ノード群] → [処理ノード] → [出力ノード] → [ドライブノード]
x=-1.5 x=-1.0 x=-0.5 x=0
Y軸で上下にずらして配置:
// 入力1(上側)
await client.addSlot({ name: 'Input1', position: { x: -1.5, y: 0.15, z: 0 } });
// 入力2(下側)
await client.addSlot({ name: 'Input2', position: { x: -1.5, y: -0.15, z: 0 } });
// 処理ノード(中央)
await client.addSlot({ name: 'Process', position: { x: -1.0, y: 0, z: 0 } });// 親スロット
const fluxSlot = await client.addSlot({ name: 'Flux', position: { x: 0, y: 2, z: 0 } });
// 各ノードを左から右に配置
const nodes = [
{ name: 'AxisInput', x: -0.6, y: 0.15 }, // 回転軸入力
{ name: 'TimeNode', x: -0.6, y: -0.15 }, // 時間取得
{ name: 'AxisAngleNode', x: -0.3, y: 0 }, // 軸角度→クォータニオン変換
{ name: 'DriveNode', x: 0, y: 0 }, // 回転ドライブ
];
for (const node of nodes) {
await client.addSlot({
name: node.name,
parentId: fluxSlot.data.id,
position: { x: node.x, y: node.y, z: 0 }
});
}| 項目 | 推奨値 |
|---|---|
| ノード間の水平間隔 | 0.3〜0.5 |
| 分岐時の垂直間隔 | 0.15〜0.3 |
| 親スロットからの相対座標 | 使用する |
- 親スロット(例:
Flux)を作成し、その下に各ノードを配置 - 位置は親スロットからの相対座標になる
- データフローが左から右に流れるように配置
- 分岐がある場��は Y軸で上下にずらす
一部の ProtoFlux ノードは追加できない場合があります:
- 複雑なジェネリック制約を持つノード
- 特殊な初期化が必要なノード
回避策:
- Resonite 内で手動で ProtoFlux を作成
- 既存の ProtoFlux をテンプレートとして複製
- PackedObject として保存したものをインポート
# コンポーネント名で検索
node dist/cli.js search --query Mesh
# コンポーネント詳細を表示
node dist/cli.js info --name PBS_Metallic
# カテゴリ一覧
node dist/cli.js categories
# カテゴリで検索
node dist/cli.js category --query "Materials"
# メンバー名で検索
node dist/cli.js member --query Smoothness
# ソースコード全文検索
node dist/cli.js grep --query "SyncPlayback"
# ソースコード表示
node dist/cli.js source --name BoxMesh# ProtoFlux 1+1 を作成(ValueInput → ValueAdd → ValueDisplay)
node dist/scripts/create-flux-add.js ws://localhost:58971
# 東京タワー(詳細版)を作成
node dist/scripts/create-tokyo-tower-detailed.js ws://localhost:58971
# 東京スカイツリーを作成
node dist/scripts/create-skytree.js ws://localhost:58971
# 東京タワーを削除
node dist/scripts/delete-tokyo-tower.js ws://localhost:58971
# モダンハウス(内装付き)を作成
node dist/scripts/create-house3.js
# 街を作成
node dist/scripts/create-town.js
# 雲を作成
node dist/scripts/create-clouds.js
# すべて削除して床だけ��する
node dist/scripts/reset-to-floor.jsMIT
ResoniteLink — MCP-сервер и CLI для управления Resonite VR
ResoniteLink — это MCP-сервер и CLI-инструмент для управления мирами Resonite VR через WebSocket-протокол. Позволяет программно создавать, изменять и удалять слоты, компоненты, текстуры и многое другое.
Установка
npm install
npm run build
Использование в качестве MCP-сервера
Claude Code
Настройка уже добавлена в .mcp.json в корне проекта. После перезапуска Claude Code сервер станет доступен автоматически.
Claude Desktop
В claude_desktop_config.json добавьте:
{
"mcpServers": {
"resonitelink": {
"command": "node",
"args": ["C:/Users/neo/GitHub/resolink_mcp/dist/mcp-server.js"],
"env": {
"RESONITE_WS_URL": "ws://localhost:29551"
}
}
}
}
Инструменты MCP
| Инструмент | Описание |
|---|---|
connect |
Подключиться к Resonite |
disconnect |
Отключиться |
get_slot |
Получить информацию о слоте |
find_slot |
Найти слот по имени |
add_slot |
Добавить новый слот |
remove_slot |
Удалить слот |
update_slot |
Обновить слот |
add_component |
Добавить компонент |
get_component |
Получить информацию о компоненте |
update_component |
Обновить компонент |
remove_component |
Удалить компонент |
import_texture_file |
Импортировать текстуру из файла |
search_components |
Поиск компонентов |
get_component_info |
Детальная информация о компоненте |
list_categories |
Список категорий |
search_by_category |
Поиск по категории |
search_by_member |
Поиск по имени члена |
get_component_source |
Исходный код компонента |
grep_source |
Полнотекстовый поиск по исходникам |
Использование в качестве CLI
Указание WebSocket URL
Порядок приоритета:
- Аргумент
--url(наивысший) - Переменная окружения
RESONITELINK_URL - Значение по умолчанию:
ws://localhost:29551
Пример установки переменной:
export RESONITELINK_URL=ws://localhost:29469
Примеры команд
# Получить корневой слот
node dist/cli.js root --depth 1
# Найти слот по имени
node dist/cli.js find --name MyObject
# Добавить слот
node dist/cli.js add-slot --name NewSlot --position 0,1,0
# Добавить компонент
node dist/cli.js add-component --slot <slotId> --type "[FrooxEngine]FrooxEngine.BoxMesh"
# Обновить слот
node dist/cli.js update-slot --id <slotId> --name NewName --position 0,2,0
# Удалить слот
node dist/cli.js remove-slot --id <slotId>
# Древовидное отображение
node dist/cli.js tree --depth 3
Использование в качестве библиотеки
import { ResoniteLinkClient } from './src/index.js';
const client = new ResoniteLinkClient({ url: 'ws://localhost:29551' });
await client.connect();
// Добавить слот
await client.addSlot({ name: 'MyObject', position: { x: 0, y: 1, z: 0 } });
// Добавить компонент
await client.addComponent({
containerSlotId: slotId,
componentType: '[FrooxEngine]FrooxEngine.BoxMesh'
});
client.disconnect();
Конфигурация клиента
const client = new ResoniteLinkClient({
url: 'ws://localhost:29551', // обязательный
debug: true, // лог в консоль
logFile: 'debug.log', // лог в файл
requestTimeout: 30000, // таймаут запроса (мс)
autoReconnect: false, // авто-переподключение
reconnectInterval: 5000 // интервал переподключения
});
Отладка
Включите debug: true для просмотра сообщений SEND/RECV. Пример:
[2026-01-08T06:10:24.504Z] SEND: { "$type": "addSlot", "messageId": "..." }
[2026-01-08T06:10:24.506Z] RECV: { "success": true, "messageId": "...", "error": null }
Импорт текстур
Из файла
const result = await client.importTexture2DFile({
filePath: 'C:/path/to/texture.png'
});
if (result.success) {
// Используйте result.assetURL для StaticTexture2D
}
Из сырых данных
const width = 256, height = 256;
const rawData = Buffer.alloc(width * height * 4); // RGBA
// заполните rawData
const result = await client.importTexture2DRawData({
width, height,
colorProfile: 'sRGB', // или 'Linear'
rawData
});
Примечания:
filePath— путь на хосте, где работает Resonite.assetURLдействителен только в рамках сессии.- Поддерживаемые форматы: PNG, JPG.
- Сырые данные: RGBA (4 байта на пиксель).
Важные замечания
Системные объекты (не удалять!)
Следующие объекты в корне мира удалять нельзя: Controllers, Roles, SpawnArea, Light, Skybox, User <...>, __TEMP, Undo Manager, Assets, Clipboard Importer. Удаление User приведёт к кику.
Пример безопасной фильтрации:
const SYSTEM_OBJECTS = ['Controllers','Roles','SpawnArea','Light','Skybox','__TEMP','Undo Manager','Assets','Clipboard Importer'];
if (SYSTEM_OBJECTS.includes(name) || name.startsWith('User ')) continue;
Формат компонентов
Компоненты указываются в виде [FrooxEngine]FrooxEngine.ComponentName. Часто используемые:
- BoxMesh, SphereMesh, CylinderMesh, ConeMesh, MeshRenderer, PBS_Metallic, Light и др.
Типы полей (members)
| $type | Описание | Пример |
|---|---|---|
| float | Число с плавающей | { $type: 'float', value: 0.5 } |
| int | Целое | { $type: 'int', value: 10 } |
| bool | Булево | { $type: 'bool', value: true } |
| float2 | 2D-вектор | { $type: 'float2', value: { x: 1, y: 1 } } |
| float3 | 3D-вектор | { $type: 'float3', value: { x: 1, y: 2, z: 3 } } |
| floatQ | Кватернион | { $type: 'floatQ', value: { x: 0, y: 0, z: 0, w: 1 } } |
| colorX | Цвет | { $type: 'colorX', value: { r: 1, g: 0, b: 0, a: 1, profile: 'sRGB' } } |
| enum | Перечисление | { $type: 'enum', value: 'Alpha', enumType: 'BlendMode' } |
| reference | Ссылка | { $type: 'reference', targetId: 'Reso_XXXXX' } |
| list | Список | { $type: 'list', elements: [...] } |
| empty | Выходной член | { $type: 'empty', id: 'Reso_XXXXX' } |
Важно: Для enum $type пишется строчными буквами ('enum'), значение — строка, а enumType — имя перечисления.
Особенности работы с ProtoFlux
- Выходные члены ProtoFlux-узлов теперь возвращаются с
$type: "empty"и содержатid. Этот id можно использовать для подключения к входам других узлов.
Обновление списка Materials (2 шага)
В MeshRenderer обновление Materials требует двух вызовов:
- Добавить элемент (targetId станет null)
- Получить id элемента и установить targetId
// Шаг 1
await client.updateComponent({
id: rendererId,
members: { Materials: { $type: 'list', elements: [{ $type: 'reference', targetId: materialId }] } }
});
// Шаг 2: получить id и обновить
const rendererData = await client.getComponent(rendererId);
const elementId = rendererData.data.members.Materials.elements[0].id;
await client.updateComponent({
id: rendererId,
members: { Materials: { $type: 'list', elements: [{ $type: 'reference', id: elementId, targetId: materialId }] } }
});
Добавление ProtoFlux-компонентов
Формат: [ProtoFluxBindings]FrooxEngine.FrooxEngine.ProtoFlux.CoreNodes.<Имя><Тип>.
- Для примитивов:
<bool>,<int>,<float>(C#-алиасы). - Для составных типов (Slot, User и т.д.):
[FrooxEngine]FrooxEngine.Slot.
Пример:
await client.addComponent({
containerSlotId: slotId,
componentType: '[ProtoFluxBindings]FrooxEngine.FrooxEngine.ProtoFlux.CoreNodes.ValueFieldDrive<floatQ>'
});
Примеры проверенных компонентов
| Узел | componentType |
|---|---|
| ValueInput | [ProtoFluxBindings]...ValueInput<int> |
| ValueAdd | [ProtoFluxBindings]...ValueAdd<int> |
| WorldTimeFloat | [ProtoFluxBindings]...WorldTimeFloat |
| AxisAngle_floatQ | [ProtoFluxBindings]...AxisAngle_floatQ |
| HSV_ToColorX | [ProtoFluxBindings]...HSV_ToColorX |
| ValueFieldDrive<floatQ> | [ProtoFluxBindings]...ValueFieldDrive<floatQ> |
| ValueFieldDrive<colorX> | [ProtoFluxBindings]...ValueFieldDrive<colorX> |
| ValueFieldDrive | [ProtoFluxBindings]...ValueFieldDrive<bool> |
(Полный список компонентов и их типов можно найти в исходном коде или документации Resonite.)
Как разработчики могут автоматизировать задачи в VR-мирах Resonite с помощью ResoniteLink?
Разработчики могут автоматизировать задачи, используя обширную библиотеку TypeScript от ResoniteLink для создания собственных скриптов или взаимодействуя напрямую через интерфейс командной строки (CLI). Это позволяет динамически управлять слотами, компонентами, ассетами и даже сложной логикой ProtoFlux.
Поддерживает ли ResoniteLink управление ассетами, например, импорт текстур?
Да, ResoniteLink предоставляет надежные функции управления ассетами. Вы можете легко импортировать текстуры из локальных файлов или необработанных данных пикселей непосредственно в ваши VR-миры Resonite, что позволяет динамически создавать и обновлять контент.
Подходит ли ResoniteLink для создания собственных инструментов или интеграций для Resonite?
Безусловно. Благодаря мощной библиотеке TypeScript, ResoniteLink является идеальным решением для разработчиков, желающих создать собственную автоматизацию, специализированные инструменты или интегрировать управление VR-мирами Resonite во внешние приложения и рабочие процессы.
Что такое ResoniteLink и какова его основная функция?
ResoniteLink — это универсальный инструмент, предназначенный для программного взаимодействия с VR-мирами Resonite. Он использует протокол WebSocket, позволяя разработчикам управлять, автоматизировать и контролировать различные аспекты своих VR-сред, предлагая как сервер, так и интерфейс командной строки.
Какой уровень контроля ResoniteLink предоставляет над внутриигровой логикой и компонентами?
ResoniteLink обеспечивает расширенный контроль над внутренней структур
Connect
Connect to Resonite via ResoniteLink WebSocket
Parameters
1Disconnect
Disconnect from Resonite
Get Slot
Get information about a slot by ID
Parameters
4Find Slot
Find a slot by name within a parent slot
Parameters
4Add Slot
Add a new slot to the world
Parameters
4Remove Slot
Remove a slot from the world. WARNING: Do not remove system slots like Controllers, Roles, SpawnArea, Light, Skybox, Assets, etc.
Parameters
1Update Slot
Update slot properties (position, rotation, scale, name, active)
Parameters
6scaleobjectisActivebooleanpositionobjectList Children
List child slots (id and name only) - lightweight alternative to get_slot
Parameters
2List Components
List components on a slot (id and type only) - lightweight
Parameters
1Add Component
Add a component to a slot. Component type format: [FrooxEngine]FrooxEngine.ComponentName
Parameters
2Get Component
Get component details by ID
Parameters
2Update Component
Update component member values
Parameters
2Remove Component
Remove a component by ID
Parameters
1Import Texture File
Import a texture from a file on the local file system (Resonite host). Returns assetURL that can be assigned to static asset providers.
Parameters
1Search Components
Search for Resonite components by name in decompiled source
Parameters
2Get Component Info
Get detailed info about a specific Resonite component
Parameters
1List Categories
List all available component categories
Search By Category
Search components by category
Parameters
2Search By Member
Search components that have a specific member name
Parameters
2Get Component Source
Get the full source code of a component
Parameters
1Grep Source
Search all source files for a text pattern
Комментарии
Комментариев пока нет. Будьте первым.