From 58edaef2abc64c40208d8a704a50875bfffd029d Mon Sep 17 00:00:00 2001 From: min Date: Mon, 8 Jun 2026 06:28:45 +0300 Subject: [PATCH] =?UTF-8?q?fix(studio):=20Team=20Create=20=E2=80=94=20?= =?UTF-8?q?=D1=81=D0=B8=D0=BD=D1=85=D1=80=D0=BE=D0=BD=20=D1=81=D0=BA=D1=80?= =?UTF-8?q?=D0=B8=D0=BF=D1=82=D0=BE=D0=B2=20(upsertScript/removeScript)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Раньше синхронизировались только примитивы/модели/блоки, а скрипты нет — у соавтора было 0 скриптов. Перехватываем scene.upsertScript/removeScript, шлём op scriptUpsert/scriptRemove; applyRemoteOp применяет их у соавтора (список в Hierarchy обновляется штатным setInterval). Лимит соавторов 5 (MAX_COLLABORATORS в realtime StudioRoom, задеплоен на VM110). Co-Authored-By: Claude Opus 4.8 --- src/editor/engine/StudioCollab.js | 53 +++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/editor/engine/StudioCollab.js b/src/editor/engine/StudioCollab.js index 68ee0d4..ccb4c74 100644 --- a/src/editor/engine/StudioCollab.js +++ b/src/editor/engine/StudioCollab.js @@ -161,6 +161,48 @@ export class StudioCollab { return r; }; } + + // Скрипты — синхрон создания/редактирования (upsertScript) и удаления + // (removeScript). Скрипты живут в scene._scripts[] ({id, code, target, + // name}); UI зовёт scene.upsertScript / scene.removeScript. + const sc = this.scene; + if (sc && !sc.__collabScriptsPatched) { + sc.__collabScriptsPatched = true; + if (typeof sc.upsertScript === 'function') { + const origUpsert = sc.upsertScript.bind(sc); + sc.upsertScript = function (id, code, target, name) { + const r = origUpsert(id, code, target, name); + if (!self._applyingRemote) { + // id может быть сгенерён внутри upsertScript, если был null — + // достаём фактический из _scripts (последний с этим code). + let realId = id; + if (realId == null) { + const arr = sc._scripts || []; + realId = arr.length ? arr[arr.length - 1].id : null; + } + const rec = (sc._scripts || []).find((s) => s.id === realId); + if (rec) { + self.sendOp({ + op: 'scriptUpsert', + id: rec.id, + code: rec.code, + target: rec.target ?? null, + name: rec.name ?? null, + }); + } + } + return r; + }; + } + if (typeof sc.removeScript === 'function') { + const origRemoveScript = sc.removeScript.bind(sc); + sc.removeScript = function (id) { + const r = origRemoveScript(id); + if (!self._applyingRemote) self.sendOp({ op: 'scriptRemove', id }); + return r; + }; + } + } } /** Снять обёртки (при выходе из коллаба восстановить оригиналы — простой флаг). */ @@ -387,6 +429,17 @@ export function applyRemoteOp(scene, op) { case 'blockRemove': bm?.removeBlock?.(op.x, op.y, op.z); return; + case 'scriptUpsert': + // Создание/редактирование скрипта у соавтора. _applyingRemote уже + // выставлен (см. _applyRemoteOp) → обёртка upsertScript не зашлёт + // эхо обратно. _onSceneChange внутри обновит React-панели. + scene.upsertScript?.(op.id, op.code, op.target ?? null, op.name ?? null); + scene._onCollabScriptsChange?.(); + return; + case 'scriptRemove': + scene.removeScript?.(op.id); + scene._onCollabScriptsChange?.(); + return; } switch (t) { case 'add': {