WebAssembly로 빌드를 하면 웹에 올릴 수 있다!
사실 유니티같은 멀쩡한 게임 엔진에서 그런 것처럼 compile target만 바꾸면 짠 하고 그냥 될 줄 알았는데 저희 나약한 bevy는 신경쓸 게 많아서 훨씬 오래 걸렸습니다…
(있을지는 모르겠지만) bevy 온라인 게임의 webassembly compile을 할 누군가를 위해 뭐가 필요했는지 적어 두고자 합니다.
0. Rust conditional compilation
킹갓 러스트는 multiple targets 지원이 아주 잘 되어 있습니다.
#[cfg]를 통해 target에 따라 코드가 포함되거나 안 되게 하면 됩니다.
#[cfg(not(target_arch = "wasm32"))] // 타겟이 wasm이 아닌 경우
fn asdf() { /* normal implementation */ }
#[cfg(target_arch = "wasm32")] // 타겟이 wasm인 경우
fn asdf() { /* wasm-specific implementation */ }
1. bevy
https://bevy-cheatbook.github.io/platforms/wasm.html
요거 따르면 쉽게 됩니다. 이거 할 때까지는 희망에 차 있었음…
2. tokio runtime
이 게임의 클라이언트 자체는 bevy ECS로 오브젝트 및 시스템끼리 알아서 상호작용하는 형태지만, 카드 게임인 만큼 서버로 요청 받고 결과 반환, 카드 룰 적용 등을 하기 위해 순차적인 중심 로직 처리가 필요했고, 이를 위해 bevy-tokio-tasks로 tokio runtime을 ECS 옆에 하나 돌렸습니다. (tokio는 rust async 플랫폼, 대충 1프레임당 로직이 도는 게 아니라 서버로부터 요청을 받을 때마다 처리하는 event-driven 스레드 하나 썼다는 얘기)
근데 얘 wasm compile이 안 되더라고요. 정확히는 tokio가 wasm을 완벽히 지원하지 않는데 지원하지 않는 쪽 기능을 썼어요. (기본적으로 wasm은 스레드가 하나라서 전체 피쳐가 다 지원되기 쉽지 않은 듯)
다행히도 저랑 똑같은 걸 겪고 해결책을 만든 사람이 있어서 그걸로 바꿔 썼습니다. github star 8개짜리 조그만 플젝인데 이거 없으면 진짜 코드 뜯어고치느라 고생 많이 했을 것 같습니다. 압도적 감사… 얘는 build target이 wasm이면 async task를 tokio 대신 javascript Promises에 기반한 wasm_bindgen_futures를 통해 실행합니다.
3. WebSocket
브라우저에서 돌릴려면 당연하지만 시스템 콜을 못 하고, 따라서 TCP socket도 만들 수 없습니다. 대신 네트워킹을 하기 위해 WebSocket이라는 걸 제공하는데, (제가 이해한 바로는) 원래 웹에 접속할 때 http 통신을 하기 위해 이루어지는 TCP connection에 꼽사리를 껴서 통신할 수 있게 해 주는 그런 거 같습니다.
이걸 쓰는 건 좋은데, 문제는 WebSocket은 TCP byte stream 위에 레이어를 하나 얹어서 message단위로 통신을 합니다. 근데 제 게임 네트워킹 코드는 byte stream에 직렬화된 struct를 그대로 보내는 식으로 구현되어 있어서, WebSocket layer 위에 다시 byte stream layer를 얹는 짓거리를 해야 했습니다. 사용 crate는 ws_stream_wasm랑 ws_stream_tungstenite.
그리고 rust async 라이브러리들이 춘추전국시대처럼 나뉘어져 다 지들만의 표준을 따르고 있어서 컴파일되도록 하는 데에 좀 애먹었습니다.
웹빌드 할 생각이 있으면 처음부터 웹소켓에 맞춰 코드 짜야 할 것 같습니다.
4. Time
시간도 syscall이므로 wasm에서 직접적으로 지원하지 않습니다. 신기하게 컴파일이 안 되는 것도 아니고 std::time::Instant
로 현재 시간을 불러 오려고 하면 그대로 runtime panic을 띄웁니다.
wasmtimer 쓰면 그래도 쉽게 해결됩니다.
현 상황
일부 이미지가 잘 안 보이고, connection이 끊길 때 게임이 제대로 종료가 안 되는 등의 문제가 아직 남아 있긴 한데, 일단 웹에서 돌아가긴 합니다.
이제 gitlab ci/cd로 진짜 웹사이트로 빌드해 볼 생각입니다.