Vấn đề nan giải khi bộ sưu tập game retro ngày càng phình to
Việc quản lý các tựa game retro rải rác trên nhiều thiết bị thường nhanh chóng vượt khỏi tầm kiểm soát. Trong suốt 15 năm, bộ sưu tập của tôi đã tăng vọt lên hơn 1.800 file ROM với tổng dung lượng 140 GB. Chúng nằm rải rác trên hai ổ cứng gắn ngoài, một chiếc NAS Synology đời cũ và một ổ NVMe phụ trên máy bàn tính. Cách đặt tên file thì thực sự là một thảm họa: một số ROM dùng quy chuẩn No-Intro gọn gàng, số khác lại mang các tag khó hiểu từ các nhóm phát hành từ tận năm 2004, và hầu như không có bìa đĩa (box art), sách hướng dẫn hay thông tin phát hành nào đi kèm.
Trải nghiệm chơi game cũng mệt mỏi không kém. Mỗi máy client lại cần một trình giả lập riêng. RetroArch thì phải thiết lập lại nút điều khiển trên từng chiếc laptop. Đường dẫn BIOS liên tục bị lỗi, còn file save thì chẳng bao giờ đồng bộ giữa chiếc mini-PC phòng khách với máy chơi game cầm tay. Mỗi lần đưa tay cầm cho bạn bè chơi cùng đồng nghĩa với việc mất toi 15 phút chỉ để gán lại nút bấm và sửa lỗi driver âm thanh.
Tôi khao khát một trải nghiệm mượt mà như Jellyfin dành cho game retro: lưu trữ tập trung trên máy chủ unRAID hoặc Ubuntu, tự động lập danh mục và quét metadata, rồi phát trực tiếp lên bất kỳ trình duyệt nào mà không cần cài đặt trình giả lập trên máy cá nhân.
Kiến trúc cốt lõi: Cách RomM xử lý giả lập
RomM (ROM Manager) mang đến chính xác quy trình làm việc đó. Được xây dựng trên nền Python với FastAPI, RomM kết hợp giao diện web hiện đại, phản hồi nhanh cùng các bộ scraper tự động và trình giả lập chạy trực tiếp trên trình duyệt. Nền tảng này vận hành dựa trên bốn thành phần cốt lõi:
- FastAPI Backend & Database: RomM chạy một API Python nhẹ nhàng kết hợp với MariaDB hoặc PostgreSQL. Cơ sở dữ liệu này quản lý danh mục ROM, trạng thái quét dữ liệu, tag tùy chỉnh và xác thực đa người dùng. Mức chiếm dụng tài nguyên rất thấp, thường chỉ dưới 250 MB RAM khi ở trạng thái nghỉ.
- Bộ cào dữ liệu (Scraper) IGDB & MobyGames: Trình quét sẽ phân tích tên file ROM của bạn và truy vấn API IGDB do Twitch cung cấp. Hệ thống sẽ tự động tải về ảnh bìa trước độ phân giải cao, bìa sau, mô tả trò chơi, thông tin nhà phát triển, thể loại và ngày phát hành mà không cần gắn tag thủ công.
- Giả lập không cần client thông qua EmulatorJS: RomM tích hợp sẵn EmulatorJS trực tiếp vào giao diện. Chỉ cần nhấn “Play”, RomM sẽ truyền luồng file ROM cùng libretro core phù hợp trực tiếp đến client. Trình duyệt sẽ chạy game cục bộ trong một canvas WebAssembly, xử lý tín hiệu tay cầm và âm thanh thông qua các API tiêu chuẩn của trình duyệt.
- Định danh thư mục chuẩn hóa (Directory Slugs): RomM nhận diện hệ máy console dựa trên tên thư mục. Các thư mục bắt buộc phải sử dụng các slug tiêu chuẩn—chẳng hạn như
snes,gba,megadrivevàpsx. Nếu tên thư mục của bạn khớp với quy ước, quá trình quét sẽ hoàn toàn tự động mà không cần sắp xếp thủ công.
Triển khai RomM bằng Docker Compose
Trong bài viết này, chúng ta sẽ triển khai RomM cùng MariaDB 11.4 trên máy chủ Ubuntu 24.04 LTS. Chúng ta sẽ tạo cấu trúc thư mục, tạo thông tin xác thực Twitch API để quét metadata tự động và khởi chạy stack bằng Docker Compose.
Bước 1: Chuẩn bị thư mục trên host và phân quyền
Tạo cấu trúc thư mục để lưu trữ trạng thái ứng dụng, cơ sở dữ liệu và thư viện game:
mkdir -p ~/homelab/romm/{config,database,resources}
mkdir -p ~/homelab/romm/library/roms/{snes,gba,megadrive,psx,n64}
mkdir -p ~/homelab/romm/library/bios
cd ~/homelab/romm
Mặc định RomM chạy dưới UID 1000. Hãy gán quyền sở hữu cho các thư mục lưu trữ ngay từ đầu để tránh lỗi phân quyền trong container khi tải ảnh bìa hoặc ghi file lưu game (save):
chown -R 1000:1000 ~/homelab/romm/config ~/homelab/romm/resources
Chuyển các file ROM vào đúng thư mục con tương ứng. Ví dụ: đặt các file Game Boy Advance .gba vào library/roms/gba/ và file Super Nintendo .sfc hoặc .smc vào library/roms/snes/. Sao chép các file BIOS cần thiết (như scph5501.bin cho PS1) trực tiếp vào library/bios/.
Bước 2: Tạo thông tin xác thực Twitch Developer cho IGDB
IGDB cung cấp cơ sở dữ liệu game rất phong phú, nhưng các yêu cầu truy vấn đều cần thông tin xác thực Twitch Developer:
- Đăng nhập vào Twitch Developer Console bằng tài khoản Twitch thông thường.
- Chọn Register Your Application.
- Đặt tên ứng dụng dễ nhớ (ví dụ:
my-homelab-romm). - Đặt OAuth Redirect URL thành
http://localhost. - Chọn Category là Application Integration rồi nhấn Create.
- Sao chép Client ID hiển thị trên màn hình, sau đó nhấn New Secret để tạo Client Secret.
Bước 3: Thiết lập Docker Compose Stack
Tạo một khóa xác thực ngẫu nhiên gồm 32 ký tự để mã hóa session trên web:
openssl rand -hex 32
Bây giờ, hãy tạo file docker-compose.yml và thay thế khóa vừa tạo cùng thông tin xác thực Twitch của bạn:
services:
romm-db:
image: mariadb:11.4
container_name: romm-db
restart: unless-stopped
environment:
- MARIADB_ROOT_PASSWORD=SetAStrongRootPasswordHere456
- MARIADB_DATABASE=romm
- MARIADB_USER=romm_user
- MARIADB_PASSWORD=SetAStrongDatabaseUserPassword123
volumes:
- ./database:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
romm:
image: rommapp/romm:latest
container_name: romm
restart: unless-stopped
depends_on:
romm-db:
condition: service_healthy
environment:
- DB_HOST=romm-db
- DB_NAME=romm
- DB_USER=romm_user
- DB_PASSWD=SetAStrongDatabaseUserPassword123
- ROMM_AUTH_SECRET_KEY=YOUR_OPENSSL_HEX_KEY_HERE
- IGDB_CLIENT_ID=YOUR_TWITCH_CLIENT_ID_HERE
- IGDB_CLIENT_SECRET=YOUR_TWITCH_CLIENT_SECRET_HERE
ports:
- "8080:8080"
volumes:
- ./resources:/romm/resources
- ./config:/romm/config
- ./library/roms:/romm/library/roms
- ./library/bios:/romm/library/bios
Bước 4: Khởi chạy Container và quét thư viện lần đầu
Khởi chạy các container cơ sở dữ liệu và ứng dụng ở chế độ chạy nền:
docker compose up -d
Theo dõi log khởi động để đảm bảo quá trình migration schema cơ sở dữ liệu diễn ra suôn sẻ:
docker logs -f romm
Khi log xuất hiện dòng Application startup complete, hãy mở trình duyệt và truy cập vào http://<ip-server-cua-ban>:8080. Điền thông tin theo hướng dẫn ban đầu để thiết lập tài khoản quản trị (admin) chính.
Để nạp dữ liệu vào thư viện, hãy nhấp vào Settings trên thanh điều hướng, chọn Scan, rồi bấm Scan Library. RomM sẽ duyệt qua các thư mục, nhận diện phần mở rộng của file, đối chiếu tên game với IGDB và tự động tải ảnh bìa về. Trên một máy chủ gia đình 4 nhân cơ bản, việc lập chỉ mục cho khoảng 350 tựa game SNES và GBA chỉ mất tầm 90 giây.
Kinh nghiệm thực tế và xử lý sự cố
Vận hành một máy chủ game nền web sẽ phát sinh một số trường hợp đặc thù liên quan đến quy ước đặt tên, định dạng đĩa và chính sách bảo mật của trình duyệt.
- Sử dụng quy chuẩn đặt tên No-Intro và Redump: RomM dựa vào độ tương đồng của tên file để tìm kiếm. Một file có tên
The Legend of Zelda - A Link to the Past (USA).sfcsẽ khớp ngay với IGDB với độ chính xác 100%. Ngược lại, một bản dump rút gọn có tên kiểuzelda_lttp_v1.smcsẽ không thể khớp tự động và buộc bạn phải tìm kiếm thủ công. - Chuyển đổi game nhiều đĩa sang playlist M3U: Các tựa game PS1 như Metal Gear Solid hay Final Fantasy VII thường trải dài trên nhiều đĩa. Hãy lưu trữ các file ảnh đĩa dưới dạng nén
.chdtrong một thư mục riêng cho từng game. Sau đó, tạo một file danh sách phát.m3unằm cùng thư mục, chứa tên file của từng đĩa trên mỗi dòng mới. Trỏ RomM vào file.m3unày. Cách làm này vừa tránh việc game bị nhân bản nhiều lần trong thư viện, vừa cho phép đổi đĩa mượt mà ngay trong EmulatorJS. - Chạy sau Reverse Proxy có HTTPS: Các trình duyệt Chromium và WebKit hiện đại sẽ chặn HTML5 Gamepad API và Fullscreen API trên các kết nối HTTP không an toàn (ngoại trừ
localhost). Nếu bạn truy cập RomM qua mạng nội bộ, tay cầm Bluetooth sẽ không thể nhận diện được. Hãy đặt RomM phía sau Traefik, Caddy hoặc Nginx Proxy Manager và cấp chứng chỉ TLS nội bộ từ Let’s Encrypt hoặc step-ca cục bộ. - Lưu game trên trình duyệt và đồng bộ hóa: Các file save SRAM trong game được lưu tạm trong bộ nhớ IndexedDB của trình duyệt và sẽ đồng bộ ngược về máy chủ RomM khi bạn thoát game. Nếu trình duyệt của bạn cài các tiện ích mở rộng chặn theo dõi quá gắt gao hoặc tự động xóa dữ liệu trang web khi đóng tab, bạn sẽ bị mất tiến trình chơi trước khi dữ liệu kịp đồng bộ. Hãy thêm tên miền RomM vào danh sách trắng (whitelist) trên uBlock Origin hoặc Privacy Badger.
Các bước tiếp theo
Giờ đây bạn đã có một máy chơi game nền web tự host gọn gàng, cho phép bất kỳ ai trong mạng nội bộ trải nghiệm các tựa game kinh điển chỉ sau vài giây. Từ bước này, bạn có thể cân nhắc cấu hình OIDC để tích hợp đăng nhập qua Authentik hoặc Keycloak, lên lịch backup tự động hàng đêm cho thư mục database và resources, hoặc ghim RomM lên dashboard Homepage của mình.

