corrigindo estado atual da sala, aparentemente, todos os clientes possuem o mesmo estado de sala com steps e samples de áudio

This commit is contained in:
JotaChina committed 2025-11-26 19:36:54 -03:00
1 parent 6eb81089f7
commit 3f714c88de
3 files changed
+265 -238

No files matched your search

+102 -59
View File
@@ -38,7 +38,9 @@ import {
handleLocalProjectReset,
syncPatternStateToServer,
BLANK_PROJECT_XML,
generateXmlFromStateExported,
} from "./file.js";
import { renderAll, showToast } from "./ui.js"; // showToast()
import { updateStepUI, renderPatternEditor } from "./pattern/pattern_ui.js";
import { PORT_SOCK } from "./config.js";
@@ -170,18 +172,17 @@ socket.on("connect_error", (err) => {
);
});
// -----------------------------------------------------------------------------
// Atualizar os artefatos da plataforma
// -----------------------------------------------------------------------------
socket.on("system_update", (data) => {
if (data.type === "RELOAD_SAMPLES") {
console.log(
`[System Update] Recebida ordem para recarregar samples: ${data.message}`
);
// Certifique-se de que esta função existe e faz o que é esperado:
// 1. Fetch dos novos manifestos (metadata/samples-manifest.json etc.)
// 2. Renderiza a interface do navegador de samples
loadAndRenderSampleBrowser();
}
// Se houver outras notificações, adicione-as aqui (ex: RELOAD_PROJECTS)
});
// -----------------------------------------------------------------------------
@@ -193,11 +194,28 @@ socket.on("load_project_state", async (projectXml) => {
if (isLoadingProject) return;
isLoadingProject = true;
try {
await parseMmpContent(projectXml); //
// 1. Carrega o XML que veio do servidor (pode estar desatualizado)
await parseMmpContent(projectXml);
// 🔥 CORREÇÃO: Força a restauração da sessão LOCAL logo após carregar o XML
// Isso garante que suas alterações locais (BPM, steps) "ganhem" do servidor
if (window.ROOM_NAME) {
const raw = sessionStorage.getItem(`temp_state_${window.ROOM_NAME}`);
if (raw) {
const parsed = JSON.parse(raw);
const hasLocalAudio = parsed.audioSnapshot?.clips?.length > 0 ||
parsed.audioSnapshot?.tracks?.length > 0;
if (hasLocalAudio) {
console.log("Re-aplicando sessão local sobre o XML do servidor...");
await loadStateFromSession();
}
}
}
renderAll();
showToast("🎵 Projeto carregado com sucesso", "success");
// --- INÍCIO DA CORREÇÃO ---
const hasAudio =
(appState.audio?.clips?.length || 0) > 0 ||
(appState.audio?.tracks?.length || 0) > 0;
@@ -207,13 +225,12 @@ socket.on("load_project_state", async (projectXml) => {
"Projeto XML carregado, sem áudio. Pedindo snapshot de áudio..."
);
// 🔥 FIX CRÍTICO: Libera a trava IMEDIATAMENTE antes de pedir
// Libera a trava IMEDIATAMENTE antes de pedir
// Caso contrário, a resposta chega muito rápido e cai no bloqueio "justReset=true"
appState.global.justReset = false;
sendAction({ type: "AUDIO_SNAPSHOT_REQUEST" });
}
// --- FIM DA CORREÇÃO ---
} catch (e) {
console.error("Erro ao carregar projeto:", e);
@@ -301,9 +318,6 @@ export function sendAction(action) {
action.__senderId = socket.id;
action.__senderName = USER_NAME;
// =================================================================
// 👇 INÍCIO DA CORREÇÃO (Expandir `isTransport` para SET_SYNC_MODE)
// =================================================================
const isTransport =
action.type === "TOGGLE_PLAYBACK" ||
action.type === "STOP_PLAYBACK" ||
@@ -312,10 +326,7 @@ export function sendAction(action) {
action.type === "STOP_AUDIO_PLAYBACK" ||
action.type === "SET_LOOP_STATE" ||
action.type === "SET_SEEK_TIME" ||
action.type === "SET_SYNC_MODE"; // 👈 Adicionado
// =================================================================
// 👆 FIM DA CORREÇÃO
// =================================================================
action.type === "SET_SYNC_MODE";
if (inRoom && isTransport) {
const leadTimeMs = 200;
@@ -375,12 +386,16 @@ export function sendAction(action) {
}, 900);
}
// HELPERS POP-UPS
// -----------------------------------------------------------------------------
// POP-UPS
// -----------------------------------------------------------------------------
function basenameNoExt(path) {
if (!path) return "";
const base = String(path).split(/[\\/]/).pop();
return base.replace(/\.[^/.]+$/, "");
}
function trackLabel(track, idx) {
const name =
track?.name ||
@@ -389,12 +404,14 @@ function trackLabel(track, idx) {
track?.sample?.displayName;
return name ? `(Faixa ${idx + 1})` : `Faixa ${idx + 1}`;
}
function actorOf(action) {
const n = action.__senderName || action.userName;
if (n && typeof n === "string") return n;
const sid = action.__senderId ? String(action.__senderId) : "";
return `Alicer-${sid.slice(0, 4) || "????"}`;
}
function instrumentLabel(track, tIdx) {
const name =
track?.name ||
@@ -421,7 +438,9 @@ function schedulePatternRerender() {
});
}
// RECEBER BROADCAST
// -----------------------------------------------------------------------------
// BROADCAST
// -----------------------------------------------------------------------------
socket.on("feedback", (msg) => {
console.log("[Servidor]", msg);
showToast(msg, "info");
@@ -444,7 +463,9 @@ socket.on("action_broadcast", (payload) => {
handleActionBroadcast(action);
});
// PROCESSAR AÇÕES RECEBIDAS
// -----------------------------------------------------------------------------
// PROCESSAR AÇÕES
// -----------------------------------------------------------------------------
let isLoadingProject = false;
async function handleActionBroadcast(action) {
if (!action || !action.type) return;
@@ -618,26 +639,7 @@ async function handleActionBroadcast(action) {
await parseMmpContent(action.xml); //
renderAll();
showToast("🎶 Projeto sync", "success");
// --- INÍCIO DA CORREÇÃO ---
// O problema é esta chamada. Ela cria um "eco" (SYNC_PATTERN_STATE)
// que força um segundo 'parseMmpContent' e quebra o estado.
// REMOVA ou COMENTE este bloco:
/*
if (window.ROOM_NAME) {
console.log(
"LOAD_PROJECT: Sincronizando novo estado com o servidor..."
);
syncPatternStateToServer(); //
}
*/
// EM VEZ DISSO, apenas salve o estado na sessão local.
// O `syncPatternStateToServer` agora é responsabilidade do servidor
// (que já recebeu o LOAD_PROJECT) ou de outras ações.
saveStateToSession();
// --- FIM DA CORREÇÃO ---
} catch (e) {
console.error("Erro LOAD_PROJECT:", e);
showToast("❌ Erro projeto", "error");
@@ -661,15 +663,9 @@ async function handleActionBroadcast(action) {
case "RESET_ROOM":
console.log("Socket: Recebendo comando de RESET_ROOM do servidor."); //
// --- INÍCIO DA CORREÇÃO ---
// Faltava esta linha para definir quem executou a ação
const who = actorOf(action);
// --- FIM DA CORREÇÃO ---
handleLocalProjectReset(); //
showToast(`🧹 Reset por ${who}`, "warning"); //
handleLocalProjectReset();
showToast(`🧹 Reset por ${who}`, "warning");
// Salva o estado localmente também!
saveStateToSession(); //
break;
@@ -768,31 +764,78 @@ async function handleActionBroadcast(action) {
// Notes
case "TOGGLE_NOTE": {
const { trackIndex: ti, patternIndex: pi, stepIndex: si, isActive } = action;
const t = appState.pattern.tracks[ti];
if (t) {
t.patterns[pi] = t.patterns[pi] || { steps: [] };
t.patterns[pi].steps[si] = isActive;
try { updateStepUI(ti, pi, si, isActive); } catch {}
if (!isFromSelf) schedulePatternRerender();
}
// ✔ ADICIONE ISTO
if (window.ROOM_NAME && isFromSelf) {
const xml = generateXmlFromStateExported();
sendAction({
type: "SYNC_PATTERN_STATE",
xml
});
}
saveStateToSession();
break;
}
case "UPDATE_PATTERN_NOTES": {
const {
trackIndex: ti,
patternIndex: pi,
stepIndex: si,
isActive,
notes,
steps: incomingSteps,
} = action;
const t = appState.pattern.tracks[ti];
if (t) {
t.patterns[pi] = t.patterns[pi] || { steps: [] };
t.patterns[pi].steps[si] = isActive;
if (!t) break;
// Garante o pattern
if (!t.patterns[pi]) t.patterns[pi] = { steps: [], notes: [] };
// 1) Atualiza notas
t.patterns[pi].notes = Array.isArray(notes) ? notes : [];
// 2) Se steps vieram junto, atualiza
if (Array.isArray(incomingSteps)) {
t.patterns[pi].steps = incomingSteps;
}
// 3) Atualiza interface
try {
schedulePatternRerender();
} catch (e) {
console.warn("Erro no render UPDATE_PATTERN_NOTES", e);
}
// 4) Sync online somente se fui EU quem enviei
const isFromSelf = action.__senderId === socket.id;
if (window.ROOM_NAME && isFromSelf) {
try {
updateStepUI(ti, pi, si, isActive);
} catch {}
if (!isFromSelf) {
schedulePatternRerender();
const xml = generateXmlFromStateExported(); // ← CORREÇÃO
sendAction({
type: "SYNC_PATTERN_STATE",
xml,
});
} catch (e) {
console.error("Erro gerando XML UPDATE_PATTERN_NOTES", e);
}
}
const who = actorOf(action);
const v = isActive ? "+" : "-";
showToast(`🎯 ${who} ${v} nota ${ti + 1}.${pi + 1}.${si + 1}`, "info");
// Salva o estado localmente também!
// 5) Salva no sessionStorage
saveStateToSession();
break;
}
// Samples
case "SET_TRACK_SAMPLE": {
const ti = action.trackIndex;