Thiết Kế Gói Tài Nguyên
Thiết kế gói tài nguyên
Thông thường, các trò chơi sẽ đóng gói toàn bộ dữ liệu tài nguyên thành một tệp tin lớn, cho phép truy cập các tệp bên trong như các tệp tin thông thường khi game đang chạy. Làm thế nào để đóng gói và cập nhật tài nguyên hiệu quả là trọng tâm thiết kế hệ thống này.
Hiện nay có nhiều gói tài nguyên sử dụng định dạng phổ biến như ZIP, tuy nhiên cũng có nhiều hệ thống tự xây dựng định dạng riêng để phục vụ nhu cầu đặc thù như xử lý quan hệ tham chiếu giữa các tài nguyên. Khi số lượng tài nguyên quá lớn, người ta thường áp dụng cơ chế hash tên tệp gốc để tạo chỉ mục nhằm tăng tốc độ tìm kiếm. Định dạng MPQ của Blizzard và Asset Bundle của Unity3D đều áp dụng nguyên lý này.
Khi đóng gói, thông tin tên tệp gốc không còn cần thiết nữa. Ứng dụng có thể truy cập tài nguyên thông qua giá trị hash của tên tệp. Chính vì vậy, các công cụ giải nén MPQ của bên thứ ba thường cần một danh sách tên tệp bổ sung để ánh xạ ngược từ hash.
Vì sử dụng hash nên thiết kế hệ thống phải có phương án xử lý xung đột. Thiết kế của Asset Bundle có điểm yếu khi gặp xung đột trong quá trình tạo gói xác định (Deterministic), họ đơn giản từ chối đóng gói - một giải pháp không hợp lý. Đối với gói chứa vài nghìn tệp, giá trị hash 32-bit hoàn toàn đủ dùng nhưng tuyệt đối không được bỏ qua khả năng xung đột.
Phương pháp đơn giản là áp dụng hash kép với “muối”. Ví dụ khi phát hiện hai tên tệp A và B tạo ra cùng giá trị hash H, thay vì ghi trực tiếp dưới H, hệ thống sẽ đánh dấu xung đột và tạo một giá trị “muối” tham chiếu. Khi truy xuất, hệ thống sẽ tính lại hash[tên_tệp, muối] để phân biệt. Giá trị muối không nên tạo ngẫu nhiên mà nên sinh từ chuỗi cố định tăng dần để đảm bảo tính nhất quán giữa các lần đóng gói.
Lưu ý: Không nên đơn giản nối chuỗi muối vào tên tệp vì hầu hết thuật toán hash luồng dữ liệu sẽ tạo ra kết quả xung đột nếu hai chuỗi gốc có cùng giá trị hash. Cách đúng là sử dụng thuật toán cho phép thiết lập seed hoặc XOR vòng lặp giữa muối và tên tệp.
Vấn đề tham chiếu giữa các gói tài nguyên
Không thể chỉ dựa vào giá trị hash để tham chiếu giữa các gói vì hash không đảm bảo tính duy nhất tuyệt đối. Giải pháp tối ưu là lưu riêng danh sách tài nguyên được tham chiếu bên ngoài cùng bảng chỉ mục hash. Khi một gói cần tham chiếu tài nguyên ngoài, hệ thống sẽ ánh xạ từ hash tên tệp đến gói chứa tài nguyên đó.
Thiết kế cơ chế patch
Hầu như không cần hợp nhất patch vào gói gốc. Chỉ cần tạo gói mới chứa các tệp đã thay đổi. Gợi ý là patch nên giữ nguyên cấu trúc chỉ mục đầy đủ của gói gốc. Nếu patch không sửa tệp nào, chỉ cần đánh dấu tệp đó không nằm trong gói. Khi chạy game, hệ thống sẽ ưu tiên tải patch, nếu không tìm thấy sẽ quay về gói trước đó (có thể là patch hoặc gói đầy đủ).
Với hệ thống cập nhật thường xuyên, nên định kỳ tạo gói đầy đủ mới và sinh các patch từ phiên bản này đến các phiên bản sau. Người dùng có phiên bản cũ hơn gói đầy đủ mới nhất sẽ tải toàn bộ, ngược lại chỉ cần tải patch tăng dần. Quy trình này có thể tự động hóa hoàn toàn với công cụ thích hợp.
Công cụ đóng gói lý tưởng nên như thế nào?
Công cụ đóng gói cần được thiết kế như hệ thống kiểm soát phiên bản dữ liệu, tương tự Git. Nó cần hỗ trợ:
- Khởi tạo thư mục cục bộ thành kho tài nguyên
- Thêm/xóa tệp trong kho
- Xem danh sách tài nguyên đang theo dõi
- Đóng gói phiên bản hiện tại
- So sánh hai phiên bản để tạo patch
- Kết hợp nhiều patch thành gói đầy đủ
Hệ thống như vậy đảm bảo hiệu quả trong quản lý phiên bản tài nguyên phức tạp, đồng thời tối ưu quy trình phát hành và cập nhật game.