bài viết được trích của bạn tiến nguyễn trong unityviet
=======================================================
Như vậy chúng ta đã cùng nhau trãi qua loạt bài viết về save game, để kết thúc series này hôm nay mình sẽ trình bày vấn đề và giải pháp đơn giản thực hiện vấn đề: Đồng bộ dữ liệu game lên server và cho phép user chơi trên nhiều máy khác nhau.
Mô hình đồng bộ dữ liệu
Mô hình đồng bộ dữ liệu
Trong quá trình đồ bộ dữ liệu ở mức đơn giản thì chúng ta có 2 thành phần đó là:
– Client: game client (Android, iOS, WP8), thành phần này phụ trách đẩy dữ liệu lên server và cập nhật mới khi có thông báo, kiểm tra tính hợp lệ của dữ liệu (có thể sử dụng thư viện từ build, hoặc SDK nếu sử dụng services của bên thứ 3 cung cấp).
– Server: thành phần kiểm tra tính hợp lệ, lưu trữ dữ liệu và cho phép truy xuất dữ liệu (có thể sử dụng các services của bên thứ 3 cung cấp). Trong phạm vi bài viết này mình sẽ sử dụng service inhouse (tự build).
Chuẩn bị:
– Server:
Hệ thống UserId (Đăng ký, đăng nhập, session).
Sử dụng PHP & MySQL để mở API cho phép client put, get thông tin dựa trên giao thức HTTP (dành cho các game đơn gian, việc truy xuất dữ liệu không liên tục).
Sử dụng Socket Server(Websocket, Socket C/C++, Socket Java.
…
– Client:
Module (đăng ký, đăng nhập, kiểm tra session)
HTTPRequest (sử dụng đối tượng WWW của Unity).
Socket client
…
Chi tiết:
– Phần server (implement sử dụng giao thức HTTP bằng PHP và MySQL):
Phương thức Put (lưu trữ dữ liệu) với URL:
http://api.stgame.vn/put...on=xxxxxx&data=yyyyyPhuong thức Get(lấy dữ liệu) với URL:
http://api.stgame.vn/get...on=xxxxxx&data=yyyyyParameter được mã hóa đề đảm bảo tính bảo mật (mã hóa theo thuật toán giống nhau ở client và server, dùng public key và private key như facebook SDK)
Implement chi tiết sẽ được trình bài ở series cụa thể về lưu trư game data ở server.
– Phần client (implement đơn gian module sync data sử dụng HTTP) với các thành phần sử dụng:
EventDelegate
Coroutine của MonoBehaviour (Unity)
WWW (Unity)
SimpleJSON
Mã:
using System.Collections;
using System.Collections.Generic;
using SimpleJSON;
public class SimpleSyncData : MonoBehaviour {
public delegate void CallbackFunction(string message);
public List<EventDelegate> onConnectFail = new List<EventDelegate>();
public List<EventDelegate> onSyncDataDone = new List<EventDelegate>();
public void syncGameDataFromServer(string data) {
if(UserInfoLocal.Instance.isLogin && !UserInfoLocal.Instance.isUpdated) {
string urlGetGameData = "http://api.stgame.vn/get?session=" + UserInfoLocal.getDataSession() + "&data=" + data;
StartCoroutine(sendHttpRequest(urlGetGameData, onSyncGameDataFromDone));
}
}
public void syncGameDataToServer(string data) {
if(UserInfoLocal.Instance.isLogin && UserInfoLocal.Instance.isUpdated) {
string urlPutGameData = "http://api.stgame.vn/put?session=" + UserInfoLocal.getDataSession() + "&data=" + data;
StartCoroutine(sendHttpRequest(urlPutGameData, onSyncGameDataToDone));
}
}
public IEnumerator sendHttpRequest(string url, CallbackFunction callback) {
WWW www = new WWW(url);
float timer = 0;
bool failed = false;
while(!www.isDone){
if(timer > maxWaitingTime){
failed = true;
break;
}
timer += Time.deltaTime;
yield return null;
}
if(failed) {
www.Dispose();
fireEventConnectFail();
} else {
if(string.IsNullOrEmpty(www.error)){
callback(www.text);
} else {
fireEventConnectFail();
}
}
}
public JSONNode processDataFromServer(string data) {
if(!string.IsNullOrEmpty(data)) {
string jsonStr = StCypher.Decrypt(data);
if(!string.IsNullOrEmpty(jsonStr)) {
Debug.Log (jsonStr);
JSONNode jsonNode = JSONNode.Parse(jsonStr);
if(jsonNode != null && !string.IsNullOrEmpty(jsonNode["error"].Value)) {
return jsonNode;
}
}
}
fireEventConnectFail();
return null;
}
public void onSyncGameDataFromDone(string data) {
JSONNode jsonNode = processDataFromServer(data);
if(jsonNode != null) {
int error = jsonNode["error"].AsInt;
if(error >= ErrorCode.Err_None) {
if(jsonNode["data"] != null) {
JSONNode gameData = JSONNode.Parse(jsonNode["data"]);
if(gameData != null && UserInfoLocal.Instance.onReceiveGameData(gameData)) {
fireEventSyncDataDone();
} else {
syncGameDataToServer();
}
} else {
syncGameDataToServer();
}
} else {
switch(error) {
case ErrorCode.Err_Not_Exists:
{
//Update state data
//Sync data to server
syncGameDataToServer();
break;
}
case ErrorCode.Err_Invalid_Session:
{
//Sign out
break;
}
}
}
} else {
//fail
fireEventConnectFail();
}
}
public void onSyncGameDataToDone(string data) {
JSONNode jsonNode = processDataFromServer(data);
if(jsonNode != null) {
int error = jsonNode["error"].AsInt;
if(error >= ErrorCode.Err_None) {
fireEventSyncDataDone();
} else {
switch(error) {
case ErrorCode.Err_Write_Failed:
case ErrorCode.Err_Invalid_Params:
{
//Write fail
//Sign out
break;
}
default:
{
fireEventConnectFail();
break;
}
}
}
} else {
//fail
fireEventConnectFail();
}
}
public void fireEventSyncDataDone() {
EventDelegate.Execute(onSyncDataDone);
}
public void fireEventConnectFail() {
EventDelegate.Execute(onConnectFail);
}
}
– Vậy là chúng ta đã kết thúc loạt bài viết về save game, thật sự thì vấn đề này khá rộng lớn và chứ đụng nhiều sơ sót trong quá trình thực hiện, bài viết chỉ mang tính chất tham khảo ý tưởng, khi đưa vào sản phẩm thực sự chúng ta cần cải tiến rất nhiều (có điều kiện mình sẽ nói thêm về chủ đề này).
– Nếu có bất kỳ thắc mắc nào các bạn có thể comment trực tiếp để mọi người cùng thảo luận và học hỏi lẫn nhau.
Cảm ơn các bạn đã quan tâm theo dõi và hẹn gặp lại các bạn ở những series tiếp theo.
Nếu thấy thích thì nhấn nút Like bên dưới, thấy hay thì share giúp mình. Bye bye ^^.