Lần Đầu Làm Game Server
Lần đầu mình chạm vào lập trình game: hóa ra “game” cũng là backend, database và thuật toán
Trước đây mình nghĩ lập trình game chủ yếu là vẽ nhân vật, xử lý phím bấm, va chạm, thắng thua. Nhưng khi làm bài midterm này, mình mới hiểu: phần “chơi được” chỉ là một nửa.
Nửa còn lại là hệ thống dữ liệu phía sau: đăng nhập, lưu điểm, bảng xếp hạng, inventory, giao dịch… và mọi thứ đều cần đúng, nhanh, và khó bị gian lận.
Mình xem bài này như một bước “backend-first” cho game: gameplay chạy mượt trên client, còn server là nơi giữ sự thật dữ liệu. Nói cách khác: client có thể hiển thị mọi thứ, nhưng dữ liệu quan trọng phải được server quyết định và lưu.
1. Kiến trúc tổng quan: Client Godot + Server Java + SQLite
Client Godot xử lý:
UI/Scene: Login → Lobby → Mode chơi Classic/Adventure → Leaderboard/Inventory
Gameplay loop: di chuyển, spawn food, tính điểm, game over
Gửi request JSON lên server
Server Java xử lý:
HTTP API: /api/auth, /api/score, /api/adventure, /api/inventory, /api/trade
Xác thực đăng nhập/đăng ký
Lưu điểm, trả leaderboard
Quản lý inventory và trade bằng transaction
Database SQLite lưu:
users
scores
login_history
login_attempts
adventure_logs
inventory
Điểm “backend-first” ở đây là: mình nhìn game như một hệ thống có request/response, có schema, có luật dữ liệu, và có hiệu năng.
2. Khoảnh khắc “wow”: bấm Login trong game = gửi JSON như làm web backend
Ở Godot, khi bấm Login/Register, client gửi JSON lên server:
gdscript
var data = {
"cmd": command,
"user": username,
"pass": password
}
http_request.request(
AUTH_URL,
["Content-Type: application/json"],
HTTPClient.METHOD_POST,
JSON.stringify(data)
)Server Java nhận JSON, đọc cmd, rồi xử lý đăng ký/đăng nhập:
java
String cmd = json.get("cmd").getAsString();
if (cmd.equalsIgnoreCase("register")) {
// xử lý đăng ký
} else if (cmd.equalsIgnoreCase("login")) {
// xử lý đăng nhập
}Từ đây mình hiểu: làm game có tài khoản người chơi thực chất là làm client-server giống web/app, chỉ khác là client là game engine.
3. Database không chỉ “lưu”: nó dạy mình tư duy đúng/sai và an toàn dữ liệu
3.1. Schema và ràng buộc constraints
Database tạo bảng và ràng buộc như:
username UNIQUE NOT NULL để không trùng tài khoản
FOREIGN KEY để dữ liệu liên kết hợp lý
UNIQUE(username, item_type) để mỗi user chỉ có 1 dòng cho 1 loại item trong inventory
Ý nghĩa theo first principles:
Database tồn tại để mình không phải tin vào trí nhớ của chương trình.
Constraints tồn tại để mình không phải tin vào sự “tự giác” của code.
3.2. UPSERT cho inventory
Khi nhặt đồ, server dùng UPSERT để cộng dồn số lượng:
sql
INSERT INTO inventory(username, item_type, quantity)
VALUES (?, ?, ?)
ON CONFLICT(username, item_type)
DO UPDATE SET quantity = quantity + ?;Tại sao hay:
Không cần “check có tồn tại chưa” bằng 2 query.
Ít bug cạnh tranh race condition hơn cách tự viết logic if/else.
3.3. Transaction cho trade
Trade “bán Táo Vàng lấy điểm” dùng transaction:
Trừ item trong inventory
Tính điểm mới
Cập nhật scores
Nếu lỗi giữa chừng: rollback
Tư duy cần học:
Transaction là “bảo toàn dữ liệu”: hoặc thành công hết, hoặc quay lại trạng thái cũ.
4. Algorithm/Data Structures: game loop và leaderboard là bài thuật toán sống
4.1. Game loop và cấu trúc dữ liệu cho snake
Trong AdventureGame, thân rắn là mảng đóng vai trò hàng đợi:
insert(0, new_head) giống push front
pop_back() giống pop back
Đây là bài học thuật toán rất thật: chọn cấu trúc dữ liệu giúp code rõ ràng và chạy ổn.
4.2. Leaderboard: sort + giới hạn top N + cache RAM
Server load top điểm lên RAM bằng danh sách topList, trả cực nhanh mà không query DB mỗi lần:
Khi có điểm mới: cập nhật DB
Cập nhật RAM
Collections.sort(topList)
Nếu quá 10: remove(10)
Tại sao làm vậy:
Database mạnh ở lưu trữ, nhưng không nên bị gọi liên tục cho cùng một dữ liệu top 10.
RAM cache giúp phản hồi nhanh.
Đây là lúc mình bắt đầu hiểu bài toán hiệu năng.
4.3. Anti-cheat mindset
Nếu tin điểm từ client hoàn toàn, người chơi có thể sửa RAM hoặc cheat.
Vì vậy, “server là sự thật” là tư duy nền tảng cho game có điểm và bảng xếp hạng.
5. Những thứ mình học được
Mình đã học được:
Cách một game client gửi request JSON và nhận response
Cách server chia API theo handler/endpoint
Cách thiết kế schema cơ bản cho user/score/inventory
UPSERT, ORDER BY, GROUP BY, LIMIT
Transaction và rollback để giữ dữ liệu nhất quán
Tư duy cache RAM cho leaderboard
Tư duy thuật toán từ game loop: queue, sort, top N
6. Roadmap học tiếp
Algorithm/Data Structures cần học thêm:
Big-O cơ bản, vì insert(0, array) và sort đều có chi phí
Queue/Deque, có thể cài bằng linked list hoặc circular buffer
Sorting và top-K
PriorityQueue heap để giữ top K hiệu quả hơn sort toàn bộ
HashMap/Dictionary: map username -> score để update nhanh
Random & probability cho spawn loot/gacha
Database cần học thêm:
Index là gì, khi nào cần index
Ví dụ: username, scores(username), inventory(username, item_type)
Normalization cơ bản: dữ liệu nên đặt ở bảng nào cho hợp lý
Transaction isolation ở mức cơ bản
SQL injection và chuẩn hóa input
PreparedStatement là một bước đúng
Migrations: quản lý thay đổi schema theo thời gian
Kết luận: bài này khiến mình nhìn “lập trình game” khác đi
Sau bài này, mình không còn nghĩ game chỉ là gameplay.
Game là một hệ thống:
Có vòng lặp thời gian thực: game loop
Có dữ liệu cần đúng: database
Có thuật toán để chạy nhanh: top K, cache
Có kiến trúc client-server như một sản phẩm thật
Nếu mình viết lại bài này cho “lần đầu chạm vào game dev”, thì đây chính là điều đáng nhớ nhất:
Lập trình game là lập trình hệ thống, chỉ khác ở chỗ client là một thế giới sống.
Reader response
Bình luận
Bài viết này chưa có bình luận nào.
Tác giả
Duc Hoang
Golang Backend Developer
Toi dang xay dung blog ca nhan de ghi lai qua trinh hoc lap trinh va cac project thuc hanh.