This commit is contained in:
2026-08-24 17:22:02 +03:00
parent bd22229430
commit f1605470ff
4 changed files with 75 additions and 23 deletions

View File

@@ -4153,12 +4153,10 @@ function startCustomTooltips() {
}
function vmixCountdownSyncCommands(input, selectedName, millisecondsProvider, action = "start") {
// BUILD106: Runtime is the ONLY source of truth for game/penalty time.
// Important vMix detail: SetCountdown changes the countdown Duration; it does not
// reliably replace the current position of a countdown that was previously started
// or suspended. Therefore every operator state change performs a hard reseed:
// freeze title rendering -> reset native countdown -> set Runtime value -> unfreeze.
// Start/Resume then starts from that freshly seeded Runtime value.
// BUILD107: Runtime remains the source of truth, but normal operator control no
// longer uses StopCountdown because vMix defines it as Stop + Reset.
// ChangeCountdown updates the CURRENT countdown position; SetCountdown only changes
// Duration. PauseCountdown is used only for an actual Runtime pause transition.
const target = { Input: String(input || "").trim(), SelectedName: String(selectedName || "").trim() };
const renderTarget = { Input: target.Input };
const currentValue = () => {
@@ -4167,24 +4165,32 @@ function startCustomTooltips() {
};
if (!target.Input || !target.SelectedName) return [];
const reseedStopped = [
{ Function: "PauseRender", ...renderTarget },
{ Function: "StopCountdown", ...target },
{ Function: "SetCountdown", ...target, Value: currentValue },
{ Function: "ResumeRender", ...renderTarget },
];
if (action === "stop" || action === "pause" || action === "set") {
// Do not trust the native vMix current position on Pause. Freeze at the exact
// Runtime value and leave the native countdown stopped. The next Resume will
// hard-reseed again before StartCountdown.
return reseedStopped;
if (action === "pause" || action === "stop") {
// Space pause: toggle the native countdown into Pause, then pin its current value
// to the exact Runtime time. No StopCountdown reset is allowed in this path.
return [
{ Function: "PauseRender", ...renderTarget },
{ Function: "PauseCountdown", ...target },
{ Function: "ChangeCountdown", ...target, Value: currentValue },
{ Function: "ResumeRender", ...renderTarget },
];
}
if (action === "set") {
// Scoreboard/F1 pre-sync while Runtime is not running: update only the current
// vMix position. Do not toggle PauseCountdown because it is a Pause/Resume toggle.
return [
{ Function: "PauseRender", ...renderTarget },
{ Function: "ChangeCountdown", ...target, Value: currentValue },
{ Function: "ResumeRender", ...renderTarget },
];
}
// Start/Resume: set the native CURRENT position from Runtime and then run it.
// This avoids both StopCountdown reset and SetCountdown duration-only semantics.
return [
{ Function: "PauseRender", ...renderTarget },
{ Function: "StopCountdown", ...target },
{ Function: "SetCountdown", ...target, Value: currentValue },
{ Function: "ChangeCountdown", ...target, Value: currentValue },
{ Function: "ResumeRender", ...renderTarget },
{ Function: "StartCountdown", ...target },
];
@@ -13915,7 +13921,7 @@ function renderHockeyPenaltyDashboard(node, component, runtime) {
<div class="shortcut-finish-action-list">${timerFinishActionRows(step)}</div>
<div class="shortcut-vmix-inventory-note"><span>${escapeHtml(shortcutInventoryLabel())}</span><small>Для каждого countdown теперь обязательно выбирается конкретный Text / SelectedName. Это исключает отправку времени в первый текстовый элемент по умолчанию.</small></div>
<p class="shortcut-step-note">Режим <b>Countdown vMix</b>: веб-таймер является единственным источником времени. Перед Start/Resume vMix выполняет <code>StopCountdown → SetCountdown(время Runtime) → StartCountdown</code>; при Pause/Stop vMix выполняет <code>StopCountdown → SetCountdown(время Runtime)</code> и остаётся остановленным на точном веб-времени. Команды идут в Agent без ACK отдельными кадрами строго по порядку. Каждую секунду значение в vMix не передаётся. <b>Text mirror</b> оставлен только для совместимости со старыми титрами. Для верхнего счёта используется одна penalty-плашка: при реальном большинстве она показывает ближайшее изменение численного состава; при чистом равном обоюдном удалении сама по себе не появляется.</p>
<p class="shortcut-step-note">Режим <b>Countdown vMix</b>: веб-таймер является источником времени. Перед Start/Resume vMix выполняет <code>ChangeCountdown(время Runtime) → StartCountdown</code>; при Pause/Stop выполняет <code>PauseCountdown → ChangeCountdown(время Runtime)</code>. <code>StopCountdown</code> в обычном управлении не используется, потому что он сбрасывает countdown. Команды идут в Agent без ACK отдельными кадрами строго по порядку. Каждую секунду значение в vMix не передаётся. <b>Text mirror</b> оставлен только для совместимости со старыми титрами. Для верхнего счёта используется одна penalty-плашка: при реальном большинстве она показывает ближайшее изменение численного состава; при чистом равном обоюдном удалении сама по себе не появляется.</p>
</div>`;
} else if (step.type === "delay") {
body.innerHTML = `<div class="shortcut-step-grid"><label>Задержка, мс<input type="number" min="0" max="10000" step="10" data-step-field="milliseconds" value="${Number(step.milliseconds) || 0}"></label></div>`;