Open Source Story: Agar.IO Clone

<- Quay về trang chủ

Những gì sắp kể ra ở đây là về hành trình của mình đến với thế giới open source, đây là một dự án làm cho vui nhưng rốt cuộc lại đóng vai trò khá quan trọng đối với con đường làm kĩ thuật của mình, nhất là khi nó cũng mở ra khá nhiều cơ hội lúc mình đặt chân đến Mỹ (khi có dịp mình sẽ nói về cái này sau). Từ đó đến giờ cũng khá lâu rồi, nên mình không thể nhớ hoàn toàn mọi chi tiết cũng như rất nhiều tư liệu trong quá trình viết game này đã bị mất, hôm nay quyết định viết ra chứ để lâu nữa có khi lại quên sạch luôn.


Vào khoảng nửa đầu năm 2015, thì trò chơi Agar.IO bắt đầu trở nên nổi tiếng và tình trạng chơi Agar trong giờ làm việc dần trở nên phổ biến, team mình lúc đó cũng không ngoại lệ

Thú thực thì công ty outsource, ở thời điểm mới thành lập, dự án cũng không có nhiều và gắt nên anh em cũng khá rảnh rỗi. Rảnh đến mức mình cài luôn Diablo III vào máy để chơi cơ mà.

Nhưng chơi game cả ngày thì cũng chán, thế là mình chuyển qua làm game ở trên công ty luôn (thời điểm đó mình vẫn còn hoạt động trong mảng game dev, đối với mình, làm game là đam mê, còn chuyện đi làm outsource cũng chỉ là vì cơm áo gạo tiền, quyết không bán mình đi làm game vì tiền, một phần khác thì vì cty outsource này trả lương cũng khá là cao).

Làm game gì bây giờ nhỉ? Lúc đó mình đang chơi 2 game là Diablo và Agar, làm Diablo thì đương nhiên là không làm nổi rồi, trình đếu đâu, thôi thì làm game Agar vậy.

Bản prototype đầu tiên

Tính từ lúc start đến lúc hoàn thành bản prototype chơi được đầu tiên là khoảng 1 buổi sáng, chắc tầm 3 tiếng, bản prototype khá đơn giản, hoàn toàn không có chế độ chơi online, nhưng các thành phần cơ bản của gameplay thì đều có đầy đủ:

  • Cơ chế dịch chuyển/điều khiển, collision detection (nói cho sang, chứ chỉ là kiểm tra một điểm có nằm trong một đường tròn không thôi), food spawning, cơ chế ăn food, cơ chế growth khi ăn.
  • Game được render trên một thẻ <canvas>, xóa toàn bộ màn hình sau mỗi lần di chuyển
  • Toàn bộ diện tích game là diện tích của màn hình hiện tại, chưa có scrolling và viewport

Thực ra ban đầu mình không có ý định làm bản online, mà dự định sẽ viết thêm bot và chơi offline. Nhưng mấy anh bạn trong team sau khi thấy mình làm ra được thế thì muốn mình share ra để chơi chung, nên đành phải viết thêm chức năng online.

Bản prototype tiếp theo: Multiplayer

Việc add thêm chức năng multiplayer cho một bản prototype game đang chạy offline cũng có nhiều cái thú vị cần nói tới, vì ngay từ bước này mình mắc phải nhiều sai lầm nghiêm trọng nhưng bản thân mình lúc đó chưa hề hay biết.

Về cơ bản thì logic của game vẫn được xử lý hoàn toàn trên phía client (!!!), server gần như chỉ làm nhiệm vụ nhận và broadcast các sự kiện được truyền lên từ các client.

Ví dụ có 2 player cùng kết nối vào game, khi player A di chuyển đến một vị trí (x, y) thì nó sẽ gửi một tín hiệu đến server, bảo là "Ê, tao chạy đến (x, y) nè", server sẽ nhận tín hiệu đó và thông báo cho tất cả những player khác rằng "Chúng mài ơi, thằng A nó chạy đến (x, y) nhá".

Hoặc khi player B ăn được một miếng food, thì nó sẽ thông báo lên server là "Này, tao vừa ăn được một cục, kích thước của tao giờ +1 rồi nhé", với bản chất là một cái loa phóng thanh, server cũng sẽ hét lên rằng "Bớ làng nước ơi thằng B nó tăng thêm một đơn vị rồi kìa".

Nếu quan tâm, các bạn có thể đọc thêm về toàn bộ những cách liên lạc từ các client lên server trong phiên bản đầu tiên này tại đây Game Architecture - huytd/agar.io-clone.

Sau khi có bản multiplayer, mình liền share cho mấy anh em trong team chạy thử, kết quả có vẻ như khá thành công, mình quyết định sẽ chia sẻ mã nguồn lên GitHub, kèm theo đó là viết một bản document thiệt hoành tráng mô tả cách xây dựng game của mình.

Lên sóng

Đây là commit đầu tiên khi game được publish lên GitHub: edf3fec.

Tiếp sau đó, mình share cái repo này lên Hacker News và nhận được khá nhiều sự chú ý của cộng đồng, đứng ở vị trí top 1 được hơn 1 ngày, kết quả là lượng star cho project tăng lên vùn vụt, đó cũng là lần đầu tiên mình tạo được nhiều sự chú ý đến vậy trên phạm vi ngoài nước.

Từ đó kéo theo rất nhiều chuyện vui, như là, một anh thầy giáo đến từ Brazil đã sử dụng source game của mình và deploy lên mạng nội bộ của trường cho học trò chơi, từ đó ảnh có thể ngắt hoàn toàn internet trong lớp, không cho lũ trẻ duyệt web trong giờ học nữa, cơ mà đám trẻ rất thích. Và rất nhiều người cũng gửi mail đề nghị mình làm freelance cho họ một bản game hoàn chỉnh để cạnh tranh với Agar.io, hoặc clone thành một trò có phong cách giống vậy, tất nhiên nhận ra tính chất chụp giựt của các con buôn, và tinh thần làm vì đam mê chứ không bán rẻ công sức, nên mình đã từ chối, giờ nghĩ lại thấy hơi tiếc tiền.

Bên cạnh đó cũng có rất nhiều bài học quý giá mà mình nhận được trong quá trình cộng tác với những người bạn không quen biết từ khắp nơi trên thế giới.

Đập đi làm lại

Gần như ngay lập tức sau khi mình publish source lên mạng, rất nhiều người đã thấy ngay sai lầm về mặt design trong kiến trúc client-server mà mình sử dụng, vì nó hoàn toàn quá dễ để hack.

Vì game logic được kiểm soát bằng cách để cho client gửi messages lên server, và server phân phát lại cho toàn game, một client hoàn toàn có thể hack tọa độ hoặc hack tăng kích thước lên cực bự, hoặc nếu thích thì có thể giết toàn bộ các player khác chỉ bằng việc gửi lên server vài messages đã được thay đổi.

Vì thế nên mình đã phải viết lại toàn bộ sau khi publish source lên GitHub (xem commit 547134c)

Việc kiểm soát logic của game (dịch chuyển, cập nhật tọa độ các player, collision detect, xử lý việc ăn và bị ăn của các player,...) phải được thực hiện hoàn toàn ở phía server, và client chỉ làm một nhiệm vụ duy nhất là render ra theo những gì server bảo.

Nếu mà nói về Networking Programming cho game thì nó rất là rộng, rộng đến mức có thể tách nó thành một phạm trù riêng và không liên quan gì đến chuyện làm game luôn, chính vì thế nên mình lười, không học cho nên đến lúc trước khi đụng phải vấn đề này, mình cũng chỉ biết mang máng là cách làm của mình nó sai lè ra đấy, nhưng không hình dung được phải làm sao cho nó đúng. Rất may, nhờ việc khoe cái ngu của mình lên mạng, nên mình đã chữa được cái ngu đấy của bản thân.

Một lần khác, project lại nhận thêm một issue nữa để bàn về vấn đề tại sao không sử dụng một cấu trúc dữ liệu khác tốt hơn thay cho việc dùng mảng để quản lý player.

Nói về vấn đề này, có thể hiểu nôm na rằng, ban đầu, tất cả các player được lưu vô một mảng ở trên server, và khi có sự tương tác xảy ra giữa các user, server sẽ phải kiểm tra bằng cách duyệt hết toàn bộ mảng đó:

// khởi tạo mảng chứa các player
const players = [];

// khi có player join vào game thì đưa vô mảng
server.on('playerJoined', (player) => players.push(player));

// kiểm tra va chạm giữa các players khác và player A
for (let player of players) {
	if (hitTest(playerA, player)) {
		// ... do something ...
	}
}

Tất nhiên làm như vậy thì rất ngu, vì server phải tốn công xử lý cho cả những user mà trên thực tế đang ở cách nhau rất xa (đầu screen và cuối screen chẳng hạn).

Sau khi thảo luận, mọi người thống nhất sẽ dùng Quadtree để quản lý các player theo tọa độ tương ứng, từ đó giúp cho việc tìm ra player nào đụng nhau hiệu quả hơn. Thú thực thì đây cũng là một thứ mình chưa bao giờ đụng đến, và chỉ được biết về nó sau cuộc thảo luận này, ngay cả việc implement cũng do một anh bạn tốt tính nào đó thực hiện giúp luôn .

Trên đây chỉ là ví dụ cho hàng tá thứ mà mình học được từ cộng đồng sau khi public mã nguồn của game lên GitHub.

Miniclip, DCMA và hưởng lợi

Phải nói thêm là, project của mình không phải là project duy nhất liên quan đến game Agar.IO ở thời điểm này, nhưng phần lớn các dự án khác trên GitHub lúc bấy giờ, bên cạnh các dự án written from scratch giống như mình làm, còn có các dự án kiểu rip off lại game cũ bằng cách dịch ngược client, deploy lại lên một server khác để gắn ads vào, hoặc viết lại client nhưng reverse engineering lại protocol để kết nối vào server chính,...

Sau khi game Agar.IO (bản gốc) được Miniclip mua lại, gần như ngay lập tức, họ gửi request đến GitHub để gỡ xuống hàng loạt các dự án liên quan đến Agar.IO hoặc có xài tên gọi Agar.IO

Lúc đó thì mình nghĩ, "thôi thế là xong phim con mẹ nó rồi", vì project của mình kiểu gì cũng sẽ nằm trong danh sách bị gỡ bỏ. Nhưng bất ngờ thay, sau một tuần thì mọi thứ vẫn an toàn.

Đồng thời, nhờ việc các "đối thủ cạnh tranh" bị tiêu diệt hết, và "đối thủ chính" đã bị hốt về tay Miniclip, hàng loạt những thủ đoạn kiếm tiền trắng trợn được đưa vào Agar.IO khiến cho cộng đồng bất mãn, và họ bắt đầu đi tìm một giải pháp vừa open source vừa có thể self-hosted được, tất nhiên ứng cử viên sáng giá không ai khác ngoài kẻ còn sót lại sau cơn bão dữ, là dự án của mình nên số lượng star cho dự án lại càng tăng lên từng ngày, từng ngày một.

Bên cạnh vai trò làm kẻ thay thế đại diện cho chính nghĩa trước thủ đoạn "xảo trá con buôn" của nhà phát hành game bản gốc, project của mình còn đóng vai trò là một nguồn tư liệu tham khảo cho nhiều developer khác khi họ có ý định bắt tay vào làm một game giống như Agar.IO - hẳn là bạn đang thắc mắc học hỏi được cái gì? mình cũng thắc mắc y thế khi thấy các repo khác mentioned tên mình và tên project của mình, chắc họ nhìn vào mình như là một case điển hình của code thối và kiến trúc tồi .

Về sau thì mình mới biết lý do project của mình còn sót lại là vì "người ta" không coi mình là đối thủ https://news.ycombinator.com/item?id=9976643. Thêm một bài học nữa, ở hiền thì gặp lành, chỉ cần mình đừng làm hại đến người ta, thì dù có tiền, người ta cũng sẽ để yên cho mình.

Vấn đề kiểm soát dự án

Tất nhiên không phải bài học miễn phí nào cũng dễ chịu, bên cạnh những người bạn vô danh sẵng lòng đem kiến thức của họ ra mở mang tầm mắt cho mình một cách nhiệt tình và nhã nhặn, có không ít những cá nhân thoải mái mạt sát, chửi đổng khi gặp những điều họ không vừa ý về project, hoặc lên các diễn đàn, website khác để nói xấu, cũng có vô số người tạo issue xong lặn đâu mất, 2, 3 năm sau mới vô comment lại

Tuy khó chịu thật, đôi lúc đọc issue xong chán chả buồn comment, nhưng nghĩ lại thì qua mỗi một trường hợp như thế, mình đều học được thêm về cách deal với từng loại người khác nhau trên internet, và quan trọng hơn nữa, đây là công sức và cống hiến của mình cho cộng đồng, ở đâu đó, dự án của mình đã ít nhiều tạo được một chút impact tốt đẹp (ví dụ như trường hợp anh thầy giáo Brazil ở trên), mình lấy đó làm động lực để tiếp tục fix bug

Nếu mà nói về một bài học đắt giá nhất, thì có lẽ đó là bài học về cách để kiểm soát dự án một cách hiệu quả.

Hồi đầu khi project vừa được ra mắt, mình rất là ba phải, ai tạo issue gì hay suggest cái gì mình cũng đều accept, gần như là merge hết không chừa một cái PR nào (thằng bé lần đầu được nhiều người tặng star và contribue cho dự án, nên gặp ai cho cái gì cũng hốt).

Và hậu quả thì chỉ sau vài tháng, toàn bộ codebase đã trở nên hoàn toàn lạ lẫm đối với mình , đến mức chính bản thân mình còn không hiểu được code nữa, còn thời gian dành cho việc fix bug do user report còn nhiều hơn cả thời gian làm việc ở công ty. Mặc dù đến lúc đó đã có thêm 2 bạn contributor khác cũng join vào để maintain dự án. Danh sách issue thì ngày một dài lên.

Nếu ở thời điểm đó mình biết cách nói không với các feature request, hay giữ thái độ cứng rắn hơn khi nhận được các pull request, biết cách sắp xếp và quản lý được roadmap cho dự án hiệu quả hơn thì có lẽ mình sẽ không bị quá tải và dẫn đến việc chán nản mà bỏ luôn dự án.

Tại sao lại bỏ?

Vì sao mình không làm tiếp dự án này?

Mặc dù những ai biết mình thì đều biết rằng mình nổi tiếng với việc làm giữa chừng là bỏ, nhưng nói thật là trong dự án này mình có lý do hẳn hoi đấy nhé.

Đầu tiên, phải nói đến đó là vision và motivation khi bắt đầu dự án. Mình hoàn toàn không có một định hướng gì rõ ràng cho dự án này, và mình bắt đầu làm nó chỉ vì rảnh quá không biết dùng thời gian để làm gì.

Và mình chỉ trở nên nghiêm túc với nó sau khi nhận được một vài sự chú ý nhất định.

Cho nên khi số lượng issue và feature request tăng lên, các vấn đề khó hơn xuất hiện càng nhiều, không còn quá quen thuộc với chính dự án do mình khởi xướng lên từ đầu, nên thời gian để fix bug cũng tăng lên, mức độ interest của mình đối với dự án càng lúc càng đi xuống.

Một lý do khác nữa, là mình không thực sự cảm thấy tự hào về dự án này, nhiều star để làm gì? trong khi bản thân dự án có kiến trúc cực kì non nớt, performance cực kì kém và các kĩ thuật sử dụng trong dự án cũng cực kì lởm. Điểm sáng duy nhất có lẽ là project rất đơn giản và đi kèm với nó là tài liệu mô tả khá chi tiết.

Những vấn đề còn bỏ ngỏ

Vẫn còn rất nhiều những vấn đề quan trọng mà mình chưa có cơ hội / và không còn hứng thú để giải quyết trong dự án này, roadmap còn rất nhiều những task chưa được tick, nhưng có lẽ một trong những vấn đề lớn nhất đó là performance khi có nhiều người cùng chơi - có rất nhiều lý do đằng sau chuyện này, phần lớn là từ phía xử lý logic game trên server, từ việc sử dụng JSON để xử lý tín hiệu trong game,...

Có lẽ một lúc nào đó mình sẽ viết lại hoàn toàn game này, hoặc cũng có thể là mình sẽ sửa sai bằng một project hoàn toàn mới không biết chừng (rồi cũng sẽ bị drop thôi ).


Dù sao thì đây cũng là một project thú vị, và hành trình đưa nó đến với cộng đồng cũng đã đem lại cho mình rất nhiều kinh nghiệm và bài học quý giá. Và tất nhiên đây không phải là trải nghiệm duy nhất của mình, còn rất nhiều những dự án khác nữa mà mình sẽ kể trong các bài viết tới, mong các bạn đón đọc.