delta.dat
Tài nguyên game nằm gọn trong hai archive: AvatarStar.pde nặng 1,27 GB chứa model với map, và một
language pack cho mỗi thứ tiếng, chẳng hạn AvatarStar_vi_vn.pde khoảng 33 MB. Không có cơ chế nào
khác thì sửa một dòng text tiếng Việt cũng bắt người chơi tải lại đủ 33 MB, còn sửa một cái mesh là
1,27 GB.
delta.dat sinh ra để khỏi phải làm vậy. Định dạng này của chính nhà phát hành chứ không phải thứ ta
tự chế ra, kênh Đài Loan ship kiểu này nhiều năm nay rồi.
Nó là payload của chế độ PatchMode="Patch". Chưa rõ chế độ đó khác Replace chỗ nào thì đọc
Replace và Patch trước, trang này đi vào bên trong chính file delta.dat.
Không phải binary delta
Phần tiêu đề “Không phải binary delta”Trong client không có bsdiff, xdelta hay vcdiff. Không tính offset, không có window, không có checksum nào đem ra đối chiếu. Đơn vị nhỏ nhất gửi được là một file nằm trong archive, không phải một dải byte. Sửa một byte trong mesh 8 KB thì vẫn gửi đủ 8 KB.
Thô như vậy nhưng vẫn ăn đứt cách cũ. Số đo ngày 2026-08-29:
| Cách gửi | Trên đường truyền |
|---|---|
PatchMode="Replace" cho AvatarStar.pde |
1.270.980.608 B |
PatchMode="Patch", thêm một file Lua 129 byte |
45.056 B |
Nặng nhẹ là do phần thay đổi, không phải do archive to hay nhỏ. Vì vậy nên xếp tài nguyên đúng chỗ: thứ riêng cho từng thứ tiếng để trong language pack, thứ dùng chung để trong base archive. Trước đây mọi người cố nhồi nội dung vào pack 51 MB cho khỏi phải đụng cú 1,27 GB, mà đúng ra cú đó chưa bao giờ cần phải có.
delta.dat cũng là một file .pde
Phần tiêu đề “delta.dat cũng là một file .pde”Không có container riêng nào cả. delta.dat là archive .pde như hai archive nó sắp vá. Mở ra thì
thấy:
/patch.lst danh sách thao tác, text thuần/AvatarStar.pde/ payload cho archive này/AvatarStar_vi_vn.pde/ payload cho archive kia/client.exe file ghi thẳng ra ổ đĩaThư mục con nào trùng tên với một archive thì mọi thứ bên trong thuộc về archive đó. Còn lại ghi thẳng vào thư mục client.
patch.lst: danh sách lệnh
Phần tiêu đề “patch.lst: danh sách lệnh”patch.lst là phần điều khiển, nó nói cho client biết phải làm gì với từng file:
P AvatarStar.pdeP AvatarStar_vi_vn.pdeU AvatarStar.pde\scripts\sys\deltaProbe.luaU AvatarStar_vi_vn.pde\scripts\sys\deltaProbe.luaText thuần, cắt theo \n. Mỗi dòng biến thành một node 0x21c byte, trong đó byte đầu là chữ
opcode còn đường dẫn bắt đầu từ offset 2. Nói cách khác dòng nào cũng có dạng cứng: một chữ
cái, một dấu cách, rồi đường dẫn.
Có năm opcode:
| Op | Nghĩa | Applier thật sự làm gì |
|---|---|---|
P <archive> |
mở archive ra làm mục tiêu | đăng ký tên archive. Từ đó op nào có đường dẫn bắt đầu bằng <archive>\ sẽ áp bên trong archive đó thay vì ghi ra đĩa |
A <path> |
thêm | CreateDirectoryA tạo thư mục nếu thiếu, rồi mở, cấp phát, đọc hết, ghi hết |
U <path> |
ghi đè | y hệt A nhưng bỏ bước tạo thư mục |
D <path> |
xóa | DeleteFileA |
S <number> |
tiến độ | atoi con số rồi ghi vào một biến global đếm tiến độ |
A và U khác nhau đúng một chuyện
Phần tiêu đề “A và U khác nhau đúng một chuyện”Cả hai đều chép nội dung file y hệt nhau. A chỉ khác ở chỗ gọi thêm CreateDirectoryA trước khi
chép. Nên chọn A khi đường dẫn là mới toanh, chọn U khi thư mục đã có sẵn.
Đo thật: một dòng A trỏ vào /mesh/prop/deltatest/ làm số thư mục trong AvatarStar.pde nhảy từ
946 lên 947, còn số file từ 20.227 lên 20.229.
Và U lên một path đã có để lại đúng một entry. Nó thay tại chỗ chứ không đẻ ra bản trùng nằm
cạnh bản cũ.
P phải đứng trước
Phần tiêu đề “P phải đứng trước”Thiếu dòng P AvatarStar.pde thì AvatarStar.pde\weapon\smg_92.lua.cache không còn là đường dẫn
bên trong archive nữa, nó thành đường dẫn file trên đĩa và client sẽ tạo ra một thư mục tên
AvatarStar.pde.
Định tuyến chạy bằng cách tra đường dẫn, không phải theo P gần nhất. Client lấy thành phần đầu
của đường dẫn so với danh sách archive đã đăng ký: trùng thì chui vào archive, không trùng thì ghi ra
thư mục client. Nhờ vậy U client.exe ghi thẳng ra đĩa mà không cần đóng khối P nào lại.
Gõ sai một chữ là mất nguyên dòng
Phần tiêu đề “Gõ sai một chữ là mất nguyên dòng”B, C, E tới O, Q, R, T, và mọi chữ nằm ngoài khoảng A–U đều rơi vào nhánh default
rồi bị bỏ qua, không báo một tiếng nào. Không có dialog, không có log.
File trong delta.dat nằm nguyên xi
Phần tiêu đề “File trong delta.dat nằm nguyên xi”Applier chép byte, hết. Nó không bọc lại file vào container .cache, cũng không tính hay kiểm idHash
nào cả.
Nghĩa là file thuộc họ có bọc container phải được bọc sẵn, kèm idHash đúng cho đường dẫn đích, từ lúc đóng gói delta. Applier sẽ vui vẻ cài một container có hash sai, rồi tới lượt engine lặng lẽ từ chối asset đó lúc load. Không ai báo ở khâu nào cả. Xem Thêm một khẩu súng để biết cách kiểm trước khi ship.
Client không tự tìm ra cái gì đã đổi
Phần tiêu đề “Client không tự tìm ra cái gì đã đổi”Client tải delta.dat về, đọc patch.lst, rồi thi hành đúng từng dòng. Nó không so gì hết, cũng
không kiểm tra xem file cũ có giống như ta tưởng không.
Cho nên chọn cái gì vào danh sách là việc của phía ta. diff_pde.py lo phần đó: so hai bản build của
cùng một archive rồi in ra danh sách op.
Chỗ này có một cái bẫy. Mỗi delta được dựng cho đúng một bước nhảy version, từ bản X sang bản Y.
Client đang ở bản W cũ hơn X vài bản mà vớ phải delta này thì một dòng A có thể trỏ vào entry nó đã
có, còn một dòng U lại trỏ vào entry nó chưa có. Chưa ai đo xem client xử lý hai trường hợp đó ra
sao. Nhà phát hành né bằng cách nối 61 block liên tiếp, SrcVer của block sau bằng đúng DstVer
của block trước, nên delta nào cũng rơi xuống đúng bản nguồn của nó.
Vì sao so hai archive lại nhanh
Phần tiêu đề “Vì sao so hai archive lại nhanh”So hai archive 1,27 GB trong 6,4 giây nghe khó tin, nhưng có lý do.
Archive mã hóa bằng một pad cố định 4096 byte căn theo page, tức là page nào cũng là
plaintext XOR pad[0:4096], nằm ở đâu trong file cũng vậy. Hai page trùng ciphertext khi và chỉ khi
trùng plaintext. Muốn biết một file có đổi hay không thì so ciphertext là đủ, khỏi giải mã. Chỉ page
directory với FAT mới phải giải, và cũng chỉ để đi cây thư mục.
Con số 6,4 giây là trường hợp xấu nhất về mặt băm, vì không có gì đổi nên file nào cũng phải băm hết, nhưng lại là trường hợp tốt nhất về I/O vì cùng một file và page cache còn nóng. Một cặp file lạnh thật sự là khoảng 2,5 GB đọc từ đĩa. Cứ ước theo tốc độ đĩa, đừng lấy con số này ra dùng.
Cái giá: archive không bao giờ nhỏ lại
Phần tiêu đề “Cái giá: archive không bao giờ nhỏ lại”Mỗi lần U ghi đè một entry, applier viết chuỗi page mới xuống cuối file rồi bỏ luôn chuỗi cũ. Không
có free list, không tái sử dụng. Đo trên một lần chạy thật:
| Kích thước file | Page địa chỉ được | Page còn đi tới được | Page mồ côi | |
|---|---|---|---|---|
AvatarStar.pde bản ship |
1.270.980.608 | 309.995 | 309.995 | 0 |
| sau khi thay 2 mesh | 1.272.340.480 | 310.326 | 310.049 | 277 (1,1 MB) |
283.920 byte nội dung mới làm file phình thêm 1.359.872 byte, gấp khoảng bốn tới năm lần.
Delta vẫn đáng dùng, 320 KB trên đường truyền thì vẫn hơn 1,27 GB. Nhưng thỉnh thoảng phải rebuild
archive bằng một bản Replace đầy đủ, coi như bảo trì định kỳ. Muốn biết đã đến lúc chưa thì đi
cây thư mục, đếm page còn đi tới được, rồi so với tổng số page địa chỉ được.
Khi nào thật sự cần patch client
Phần tiêu đề “Khi nào thật sự cần patch client”Phần lớn việc nội dung không đụng tới kênh patch. Chỉ số vũ khí, giá, quest, buff, danh mục shop
đều nằm trong các bảng SYS_* mà server đọc từ database. Sửa chúng thì chạy SQL rồi restart service,
người chơi không phải tải gì.
Kênh patch chỉ cần khi đụng tới text, icon, model, map, hoặc chính client.exe.
Nhớ cái ranh giới này lúc lên kế hoạch. Ví dụ ở trang Thêm một khẩu súng chia đúng như vậy: art của khẩu súng đi qua delta, dòng trong shop đi qua SQL.
Đọc tiếp
Phần tiêu đề “Đọc tiếp”- Ship một bản update là quy trình từng bước.
- Tra cứu là bảng field và bảng opcode.
Bản nháp đang review. Nội dung nội bộ, đừng chia sẻ ra ngoài nhóm.