액션이 실행되는 방식
장면이 액션을 보여 주는 순간부터 Vovó가 액션을 어떻게 다루는지입니다. Vovó가 보장하는 것과 액션이 기대해서는 안 되는 것을 다룹니다. 남에게 넘기거나 판매할 액션을 쓰는 사람을 위한 설명이며, 액션을 쓰는 방법은 액션에 있습니다.
자신만의 페이지
액션은 하나하나가 독립된 문서이며, 장면의 페이지가 마련해 주는 프레임에 들어갑니다. 액션의 HTML에 style.css와 script.js가 써 넣어지고 스크립트가 맨 끝에 오므로, HTML이 준비된 뒤에 스크립트가 실행됩니다. 액션 하나가 고장 나도 다른 액션은 함께 쓰러지지 않습니다. 장면은 액션을 장면의 순서대로 겹쳐 보여 주며, 첫 번째가 맨 아래에 놓입니다.
프레임은 클릭도 키도 받지 않습니다. 오버레이는 보는 것이지 누르는 것이 아닙니다. 방송 운영자가 하는 일은 패드, 액션의 패널, 꾸미기에서 하고, 이벤트로 액션에 전달됩니다.
액션은 열린 페이지가 자신을 보여 주는 동안에만, 그리고 활성 프로필에서만 이벤트를 듣습니다. 열린 페이지가 하나도 없으면 팔로우를 세는 것도 없습니다.
네트워크 없음, 브라우저에는 아무것도 남기지 않음
액션이 닿을 수 있는 것은 자신의 파일과 Vovó가 건네주는 것뿐입니다. 이미지, 소리, 동영상, 글꼴은 액션의 파일에서 이름으로 가져오거나 이벤트가 알려 준 주소(이모트, 커버, 꾸미기에서 고른 파일)에서 가져오며, 인터넷에서는 절대 가져오지 않습니다. 코드와 스타일은 script.js와 style.css뿐입니다. 파일이나 다른 곳의 스크립트와 스타일시트는 쓸 수 없고, eval도 워커도 없습니다. fetch, XMLHttpRequest, WebSocket은 액션 자신의 파일에 대해서도 거부되므로, 필요한 데이터는 스크립트에 넣습니다. 브라우저 저장소(localStorage, 쿠키, IndexedDB)는 없습니다. 액션이 간직할 것은 vovo.state에 간직합니다.
바깥에서 필요한 것은 선언한 권한 안에서 Vovó에게 요청합니다. 채팅 한 줄, 이름으로 부르는 웹훅, 패드의 소리, 공식 플레이어의 클립 같은 것입니다.
열린 모든 페이지가 실행합니다
같은 액션은 그 장면이 열린 모든 곳에서 실행됩니다. OBS, 장면 미리 보기 창, 브라우저 탭, 동시에 두 소스에서도 그렇습니다. 각 사본은 같은 이벤트를 받고 같은 것을 그립니다. 그래서 페이지는 그리고 재생하기만 하고, 한 번만 일어나야 하는 일은 Vovó에게 요청합니다. Vovó는 같은 이벤트를 처리하면서 요청한 페이지들에 대해 한 번만 합니다. 채팅 한 줄, 추첨, 음성, 웹훅, 패드의 소리, OBS의 소스, 액션 자체의 이벤트가 그렇습니다.
Vovó는 처리 중인 이벤트로 한 페이지의 호출과 다른 페이지의 호출을 구분합니다. 리스너가 끝난 뒤에 하는 호출(await 뒤, 애니메이션이 끝날 때, 타이머 안)은 { event: e }로 응답하는 이벤트를 밝힙니다. e는 리스너가 받은 객체입니다. 이것이 없으면 Vovó는 두 번째 페이지인지 두 번째 호출인지 구분할 수 없습니다.
vovo.on("raid", async (e) =>
{
const gift = await vovo.draw(["a hug", "a song", "a dance"]);
vovo.show("raid", { user: e.user, gift: gift.value });
vovo.chat.send(e.user + " gets " + gift.value + "!", { event: e });
});
페이지가 스스로 시작하는 일에는 뒤에 이벤트가 없습니다. 액션의 setInterval은 모든 페이지에서 돌고, 로드될 때 하는 일도 마찬가지입니다. 그러면 Vovó는 요청된 내용만으로 판단할 수 있으므로(5초 안의 같은 채팅 줄, 같은 음성, 같은 웹훅이나 자체 이벤트는 하나로 봅니다), 세는 일이나 한 번만 일어나야 하는 일은 timer 트리거에 둡니다. 이것은 앱의 것이며 모든 페이지에 하나입니다.
상태이지, 다시 재생이 아닙니다
vovo.state는 페이지가 아니라 Vovó가 간직합니다. 페이지가 로드되면(OBS 시작, 소스 새로 고침, 패닉 종료, Vovó 다시 열기) 스크립트가 실행되기 전에 상태를 전부 갖고 있으며, 한 페이지에서 설정한 키는 다른 페이지에도 곧바로, 아무 알림 없이 전달됩니다. 다른 페이지도 같은 이벤트를 스스로 처리했기 때문입니다. 이벤트는 절대 다시 재생되지 않습니다. 레이드가 왔을 때 없었던 페이지는 나중에도 그것을 알지 못합니다. 그래서 액션은 다시 보여 줘야 하는 것(목표의 숫자, 추첨의 이름들, 카운트다운이 끝나는 때)을 모두 상태에 두고, 로드될 때 그것으로 그립니다.
function draw()
{
document.getElementById("count").textContent = vovo.state.count || 0;
}
vovo.on("follow", () =>
{
vovo.state.count = (vovo.state.count || 0) + 1;
draw();
});
draw();
- 담긴 내용이 바뀌면 키를 다시 설정하세요:
vovo.state.names = names.concat([user]). 목록이나 객체를 제자리에서 바꾸면(push) 저장되지 않습니다. - 상태는 리스너 안에서, 어떤
await보다도 먼저 바꾸세요. 그러면 모든 페이지가 같은 상태에서 출발해 같은 상태에 이릅니다.await뒤에는 다른 페이지가 이미 썼을 수 있으니, 지금 있는 값에 좌우되지 않는 것(당첨자 이름, 시각)을 쓰고, 지금 값에 하나를 더하는 식으로는 절대 쓰지 마세요. - 키 하나에는 JSON으로 최대 64KB가 담기고, 키 이름은 최대 64자입니다.
현재 값과 계속 흐르는 측정값
네 가지 이벤트는 한 순간이면서 값이기도 합니다: obs.scene, media.track, twitch.viewers, screen.shared. 로드된 액션은 자신이 듣는 것마다 가장 최근 값을 방금 일어난 것처럼 곧바로 받으므로, 다음 곡을 기다리지 않고 재생 중인 곡을 보여 줍니다. 이것은 로드될 때마다, 모든 페이지에서, 뒤에 이벤트 없이 다시 옵니다. 그것으로 그리되, 한 번만 일어나야 하는 일로 응답하지는 마세요.
audio.level, screen.color, input.move, speech.text는 측정값이며 초당 최대 60번 옵니다. 전달되고 잊히며, 열린 페이지가 그것을 듣는 액션을 보여 주는 동안에만 읽습니다. 리스너는 가볍게 두고, 움직임은 CSS에 맡기세요.
로드, 다시 로드, 패닉
페이지는 언제든, 여러 번 로드될 수 있습니다. OBS는 장면이 보일 때 소스를 시작하고, 숨겨진 동안에는 종료할 수 있으며, 다시 보일 때 새로 고칠 수 있습니다. Vovó도 액션의 코드나 파일이 바뀌면 액션을 다시 로드합니다. 패닉은 모든 장면을 한꺼번에 비우고 액션을 다시 로드하며, 액션이 하던 일은 거기서 멈춥니다. 패닉이 끝나면 액션은 한 번 더 로드되어 상태에서 시작합니다. 패닉 중에는 액션에 아무것도 전달되지 않고, 그 사이에 온 것은 나중에도 재생되지 않습니다. 컨트롤의 값은 상태이므로 유지됩니다.
그러므로 액션은 앞선 페이지가 하다 만 일에 기대지 않고, 언제든 상태만으로 올바르게 떠야 합니다.
무대
액션은 짧은 변이 1080인 CSS 픽셀 무대에 그립니다. 16:9 방송은 해상도와 상관없이 1920 × 1080이고(4K 소스는 두 배 크기로 선명하게 그립니다), 세로 방송은 1080 × 1920이며, 다른 모양의 화면은 그 모양입니다. 액션의 문서는 무대와 같은 크기이고 투명하므로 100vw × 100vh가 화면 전체이고, 그리지 않은 곳에는 게임이 보입니다. 다른 사람의 방송을 위해 만드는 액션은 모양을 가정하지 않습니다. 가장자리 기준으로, 퍼센트로 배치하고, 세로 방송에는 @media (orientation: portrait)를 쓰세요.
OBS의 브라우저
OBS는 자체 브라우저로 장면을 보여 줍니다. Vovó의 편집기와 미리 보기 창의 브라우저보다 오래됐고(OBS 32에서는 Chromium 127), 그래픽 카드의 도움 없이 돌 수도 있습니다. 새로운 CSS나 JavaScript 기능은 기대기 전에 이 버전에서 되는지 확인하고, 넘기기 전에 OBS에서 직접 액션을 보세요. 거기서는 소리가 클릭 없이 바로 나고 방송으로 나갑니다. 스트리머만 들어야 하는 것은 액션에 넣지 않습니다.
OBS의 페이지에는 콘솔이 없습니다. 액션이 잡지 않은 오류와 Vovó가 호출에 대해 알려 주는 말은 액션의 이름과 줄 번호와 함께 Vovó의 콘솔에 옵니다. console.log는 오지 않습니다. 그것을 보려면 미리 보기 주소(vovo dev가 보여 줍니다)를 브라우저에서 열고 개발자 도구를 쓰세요.
속도와 한도
- 채팅: 한 줄 500자. 모든 액션을 합쳐 30초에 20줄, 액션마다 1초에 한 줄.
- 웹훅: JSON 16KB. 1분에 30번, 액션마다 1초에 한 번.
- 클립과 동영상: 액션마다 승인 대기 3개까지.
- 카메라: 한 번에 4대까지 보이고, 액션마다 카메라 설정은 4개까지.
- 음성 통화: 최대 300초.
- 진행 중으로 표시하는 버튼: 최대 15분.
- 상태: 키마다 64KB.
- 패키지: 파일 200개, 각각 32MB, 모두 합쳐 128MB.
index.html,style.css,script.js는 각각 2MB.
속도를 넘은 호출은 버려지며 줄을 서지 않습니다. 그 Promise는 이유와 함께 거부됩니다.
넘기기 전에
- 액션이 하는 온갖 일의 한가운데서 장면을 다시 로드하세요. 상태에서 올바르게 돌아옵니다.
- 장면을 OBS와 미리 보기 창 두 곳에서 열고 트리거를 하나씩 일으키세요. 채팅은 한 줄, 소리는 한 번, 숫자는 하나만 올라갑니다.
- 무언가를 보여 주는 동안 패닉을 켰다가 끄세요.
- 세로 프로필에서, 그리고 OBS에서 직접 시험하세요.
- 쓰는 권한만 요청하고, 동작에서 무엇인지 밝히세요: 만든 사람, 사이트, 한 줄 설명.
- ID를 붙이고 넘겨줄 때마다 버전을 올리세요. 그래야 업데이트가 대체할 액션을 찾고 어떤 버전인지 알려 줍니다.
- 어떤 라이선스로 넘겨주는지 밝히고, 조건을 직접 정했다면 전문도 넣으세요.