PDA

View Full Version : [Guide] Tạo một hệ thống Cơ Sở Dữ Liệu Tài Khoản đơn giản



trungviet
08-01-13, 12:37 PM
Box Server Chiến Quốc đã tồn tại khá lâu rồi mà vẫn chưa thấy ai động chạm đến vấn đề này, nay xin mạng phép mở đầu chủ đề, anh em cùng thảo luận cho vui :-" .
Guide này sẽ được cập nhật từ từ cho anh em có thời gian nghiên cứu :"> .

Cơ Sở Dữ Liệu (CSDL) là gì?


- Đại loại ở đây, do server Chiến Quốc không hỗ trợ kết nối trực tiếp đến các CSDL như MySQL, MSSQL nên CSDL đối với Server Chiến Quốc là một tập tin có thể thêm tài khoản, sau đó đọc tập tin này từ server để kiểm tra sự tồn tại của tài khoản hoặc đúng sai của mật khẩu.

Yêu cầu 1:

Để tạo một CSDL, bước đầu tiên là phải có CSDL (Troll nhau tí =)) ).
Giải quyết:

Tạo CSDL bằng thao tác: Mở thư mục current (Server Chiến Quốc), tạo một tập tin với tên tập tin là Database.dat (Hoặc tên gì tùy các bạn muốn). Nhiệm vụ của tập tin này là lưu trữ các dữ liệu sẽ được ghi vào (Tài khoản, mật khẩu blah blah tùy vào ý muốn, ở đây là tài khoản) để có thể đọc bất cứ lúc nào (Với cách này thì không chỉ dùng cho mỗi Server Chiến Quốc mà còn thể dùng cho rất nhiều server khác).
Yêu cầu 2:

Khi đã có CSDL, việc tiếp theo là gì? Chả nhẽ cứ để đấy là hệ thống có thể tự đọc được và gọi đây là CSDL :-B ? Ồ không :-| ! Chúng ta phải dùng phối hợp các hàm, thuật toán để Ghi, Đọc dữ liệu cho CSDL theo từng bước.
Giải quyết:

Vào thư mục current/sys/sys, tạo một tập tin script với tên database.c, sau đó mở tập tin này bằng một trình soạn thảo văn bản (Nếu muốn sử dụng Tiếng Việt trong tập tin này thì nên sử dụng các trình soạn thảo hỗ trợ encode UTF-8 nhưng lưu lại với encode ANSI), ở đây mình dùng Notepad++.
Viết vào nội dung như sau:


int is_legal_user(object me, string id, string passwd) {
int i, size;
string readData, *accountArray, *dataArray;
readData = read_file("Database.dat"); // Bước này sẽ đọc nội dung tập tin Database.dat đã tạo ở bước 1
accountArray = explode(readData, "\n"); // Đọc Database theo từng dòng
for (i = 0, size = sizeof(accountArray); i < size; i++) { // Vòng lặp kiểm tra từng dòng
dataArray = explode(accountArray[i], " "); // Ở mỗi dòng, tài khoản sẽ được lưu theo dạng: "taikhoan matkhau", ngăn nhau bằng một dấu " " nên ta sẽ chạy hàm explode để cắt ra. Vậy ta sẽ được ở biến dataArray: dataArray[0] = "taikhoan", dataArray[1] = "matkhau"
}
send_user(me, "%c%s", '!', "Tài khoản này không tồn tại! "); // Thông báo kết quả lên màn hình
send_user(me, "%c%c", 0xff, 0x11); // Làm trống ô nhập tài khoản và mật khẩu
return 0; // Trả về giá trị 0 khi gọi hàm (Đăng nhập thất bại)
}
Với đoạn code trên, những gì ta có là một hàm có thể đọc được dữ liệu từ Database.dat đã tạo ở bước 1, và chuyển dữ liệu thành từng mảng array tương ứng với từng dòng. Phân tích mỗi dòng, tách ra tài khoản, mật khẩu để so sánh.

Còn nữa...

P/S: Đã cố gắng viết đơn giản nhất cho newbie, nếu có cái nào không hiểu thì có thể google hoặc comment tại đây nhé :-s .

trungviet
17-01-13, 09:56 AM
Tiếp tục guide...

Ở bài viết #1 (<b><font color=red>[Chỉ có thành viên mới xem link được. <a href="register.php"> Nhấp đây để đăng ký thành viên......</a>]</font></b>), những gì ta có được là đoạn code:


int is_legal_user(object me, string id, string passwd) {
int i, size;
string readData, *accountArray, *dataArray;
readData = read_file("Database.dat");
accountArray = explode(readData, "\n");
for (i = 0, size = sizeof(accountArray); i < size; i++) {
dataArray = explode(accountArray[i], " ");
}
send_user(me, "%c%s", '!', "Tài khoản này không tồn tại! ");
send_user(me, "%c%c", 0xff, 0x11);
return 0;
}

Nhưng chỉ với code thế này thì chưa thể check tài khoản được, thế nên cần chèn tiếp vào các đoạn code sau:

1/ Tạo biến string sError:

Chèn vào:


string readData, *accountArray, *dataArray;
Để thành:


string readData, *accountArray, *dataArray, sError;


2/ Chèn code so sánh:

Tìm đến dòng:


dataArray = explode(accountArray[i], " ");
Chèn vào bên dưới:


if (strlwr(id) == strlwr(dataArray[0])) { // Biến id là tài khoản cần kiểm tra, dataArray[0] là tài khoản đang kiểm tra để so sánh, hàm strlwr() để chuyển toàn bộ ký tự trong hàm về ký tự không in hoa (VD: TaiKhoan => taikhoan). Phần này có nghĩa là nếu tài khoản cần kiểm tra (id) giống với tài khoản đang kiểm tra (dataArray[0])...
// Kiểm tra mật khẩu
if (passwd != dataArray[1]) { // Nếu mật khẩu không giống...
sError = "Mật khẩu không đúng! ";
}
if (!sError) {
return 1; // Trả về giá trị 1 (Ở phần này có thể hiểu nôm na 1 = Tài khoản đúng), đồng thời kết thúc vòng lặp.
}
break; // Dừng vòng lặp. Ở code này thì có nghĩa là nếu tài khoản có tồn tại thì sẽ dừng vòng lặp kiểm tra.
}

3/ Và kết quả là:


int is_legal_user(object me, string id, string passwd) {
int i, size;
string readData, *accountArray, *dataArray, sError;
readData = read_file("Database.dat");
accountArray = explode(readData, "\n");
for (i = 0, size = sizeof(accountArray); i < size; i++) {
dataArray = explode(accountArray[i], " ");
if (id == dataArray[0]) {
if (passwd != dataArray[1]) {
sError = "Mật khẩu không đúng! ";
}
if (!sError) {
return 1;
}
break;
}
}
send_user(me, "%c%s", '!', "Tài khoản này không tồn tại! ");
send_user(me, "%c%c", 0xff, 0x11);
return 0;
}


Còn nữa...

P/S: Mỗi dòng code đều có chú thích, nếu ai không hiểu có thể xem lại từ đầu.

trungviet
09-11-14, 05:56 AM
Tiếp tục guide...

Nhân dịp cuối tuần rảnh rỗi, ghé thăm CLBGamesVN thấy guide này còn dở dang, quyết định dành ra chút thời gian để hoàn thành nốt bài viết.

Ở bài viết #2 (<b><font color=red>[Chỉ có thành viên mới xem link được. <a href="register.php"> Nhấp đây để đăng ký thành viên......</a>]</font></b>), những gì ta có được là đoạn code:


int is_legal_user(object me, string id, string passwd) {
int i, size;
string readData, *accountArray, *dataArray, sError;
readData = read_file("Database.dat");
accountArray = explode(readData, "\n");
for (i = 0, size = sizeof(accountArray); i < size; i++) {
dataArray = explode(accountArray[i], " ");
if (strlwr(id) == strlwr(dataArray[0])) {
if (passwd != dataArray[1]) {
sError = "Mật khẩu không đúng! ";
}
if (!sError) {
return 1;
}
break;
}
}
send_user(me, "%c%s", '!', "Tài khoản này không tồn tại! ");
send_user(me, "%c%c", 0xff, 0x11);
return 0;
}

Nhìn sơ qua cũng có thể thấy chúng ta đã gần như hoàn tất về việc tạo ra một hàm check mật khẩu đúng hay sai. Và để chính thức đưa hàm này vào sử dụng, ta cần phải tiếp tục hoàn thành:

1/ Thông báo kết quả sau khi kiểm tra:

Chèn vào:


if (sError) {
send_user(me, "%c%s", '!', sError);
send_user(me, "%c%c", 0xff, 0x11);
return 0;
}
Phía trên đoạn:


send_user(me, "%c%s", '!', "Tài khoản này không tồn tại! ");
send_user(me, "%c%c", 0xff, 0x11);
return 0;
}

Và kết quả là:


int is_legal_user(object me, string id, string passwd) {
int i, size;
string readData, *accountArray, *dataArray, sError;
readData = read_file("Database.dat");
accountArray = explode(readData, "\n");
for (i = 0, size = sizeof(accountArray); i < size; i++) {
dataArray = explode(accountArray[i], " ");
if (strlwr(id) == strlwr(dataArray[0])) {
if (passwd != dataArray[1]) {
sError = "Mật khẩu không đúng! ";
}
if (!sError) {
return 1;
}
break;
}
}
if (sError) {
send_user(me, "%c%s", '!', sError);
send_user(me, "%c%c", 0xff, 0x11);
return 0;
}
send_user(me, "%c%s", '!', "Tài khoản này không tồn tại! ");
send_user(me, "%c%c", 0xff, 0x11);
return 0;
}

2/ Đưa hàm vào sử dụng:

Mở file sys/sys/login.c, tìm đến dòng:


// ±êʶÃÜÂëÊÇ·ñºÏ·¨
if( !CHECK_D->is_legal_id( me, lowId = strlwr(id) ) ) return;
// if( !CHECK_D->is_legal_passwd(me, passwd) ) return;

Và chèn vào bên dưới:


if (!"/sys/sys/database"->is_legal_user(me, strlwr(id), passwd)) return; // Nếu kết quả trả về khi gọi hàm is_legal_user() là 0 thì ngừng quá trình đăng nhập.


3/ Thêm/xóa tài khoản và thay đổi mật khẩu:

Về cơ bản, chúng ta đã hoàn thành hệ thống check tài khoản. Và bây giờ, để thêm mới tài khoản, chúng ta sẽ mở file Database.dat đã tạo ở trên, thêm tài khoản vào với dạng "taikhoan matkhau":


taikhoan01 matkhau
taikhoan02 matkhau
test01 123456
test02 654321
Đến đây việc xóa tài khoản và đổi mật khẩu có lẽ các bạn cũng đã có thể tự hoàn thành.

Hết.

BONUS:

Code hoàn chỉnh và tối ưu hóa hơn so với code trong guide, mật khẩu được md5 mã hóa (Tuy nhiên vẫn không tránh khỏi việc tài khoản càng nhiều thời gian check càng lâu và ngốn ram, có thể làm crash Game Server nếu nhiều truy cập cùng 1 lúc như bot login. Cứ yên tâm là trường hợp này sẽ chỉ xảy ra khi lượng tài khoản đạt trên 5 con số :)) ):


/*
* Database system by trungviet (database.c)
*/

#define ACCOUNT_DB "database/account_db.dat"

int is_legal_user(object me, string id, string passwd) {
string *line, error_msg;
int i, size;
string db_id, db_passwd;
int db_banned;
line = explode(read_file(ACCOUNT_DB), "\n");
for (i = 0, size = sizeof(line); i < size; i++) {
if (line[i][0] == '#' || line[i] == "") continue;
if (!sscanf(line[i], "%s %s %d", db_id, db_passwd, db_banned)) continue;
if (strlwr(id) == strlwr(db_id)) {
if (md5(passwd) != db_passwd) error_msg = "Mật khẩu không đúng! ";
if (!error_msg && db_banned > 0) error_msg = "Tài khoản này đã bị cấm! ";
if (!error_msg) return 1;
break;
}
}
if (!error_msg) error_msg = "Tài khoản này không tồn tại! ";
send_user(me, "%c%s", '!', error_msg);
send_user(me, "%c%c", 0xff, 0x11);
return 0;
}
"account_db.dat" đi kèm:


#id passwd(md5) banned
test001 e10adc3949ba59abbe56e057f20f883e 0
test002 e10adc3949ba59abbe56e057f20f883e 0
test003 e10adc3949ba59abbe56e057f20f883e 1
test004 e10adc3949ba59abbe56e057f20f883e 0
test005 e10adc3949ba59abbe56e057f20f883e 0

P/S: e10adc3949ba59abbe56e057f20f883e = 123456. Có thể tạo password md5 online tại <b><font color=red>[Chỉ có thành viên mới xem link được. <a href="register.php"> Nhấp đây để đăng ký thành viên......</a>]</font></b>


P/S: Ngoài ra mình cũng muốn nhắn là hiện tại mình đã đi làm và không còn thời gian cho box Chiến Quốc Server nữa, những tài liệu các bạn cần gần như đã có trong box này nên khi các bạn pm yahoo mình để hỏi về vấn đề fix, code hay cài đặt Chiến Quốc mình sẽ không trả lời. Chúc box Chiến Quốc Server sẽ ngày càng phát triển hơn :D .