Tài liệu Steamworks
Từng bước một: Thông số

Giới thiệu

Sau đây là hướng dẫn nhanh từng bước một để tích hợp thông số Steam vô cùng cơ bản vào ứng dụng của bạn trong vòng 10 phút với không quá 10 dòng mã được tích hợp vào code base của bạn. Steamworks SDK có một ứng dụng ví dụ tuyệt vời mang tên Spacewar để phô diễn toàn bộ tính năng Steam và nên là chặng dừng đầu tiên của bạn để xem cách vận hành của mọi tính năng Steam. Hướng dẫn này cô đọng thông tin tìm thấy trong Spacewar và API Thành tựu và Thông số, chỉ cung cấp những thông tin cần thiết cho Thông số Steam để đảm bảo tài liệu này súc tích nhất có thể. Xin lưu ý rằng có khá nhiều sự trùng lặp giữa thông số và thành tựu, vì thế nếu bạn tích hợp cả hai nên lưu ý rằng nhiều lệnh gọi có thể được hợp nhất.

Bước 1 - Xác định thông số cho trò chơi của bạn

Thông số đi theo từng ứng dụng và được cài đặt trên trang Thiết lập thông số trong backend của Steamworks App Admin. Sau đây là danh sách thông số từ ứng dụng ví dụ Spacewar trên Steamworks:
stats_spacewar.png

Bước 2 - Tóm lược thông số

Mã sau tồn tại độc lập với trò chơi và có thể thêm vào trò chơi nếu thích hợp. Class này vốn đã đầy đủ chức năng, nhưng cũng có thể dễ dàng mở rộng để đáp ứng nhiều nhu cầu hơn. Tất cả mã này được lấy trực tiếp từ file ví dụ Spacewar StatsAndAchievements.cpp/h.

File Header

Trước hết chúng tôi xác định một cấu trúc để giữ thông số dữ liệu nhận từ Steam, xác định loại thông số trong một enum thông dụng, sau đó cung cấp một macro để tạo các đối tượng thông số. Dữ liệu này phản ánh trực tiếp thông tin được tìm thấy tại trang Thiết lập thông số.
#define _STAT_ID( id,type,name ) { id, type, name, 0, 0, 0, 0 } enum EStatTypes { STAT_INT = 0, STAT_FLOAT = 1, STAT_AVGRATE = 2, }; struct Stat_t { int m_ID; EStatTypes m_eStatType; const char *m_pchStatName; int m_iValue; float m_flValue; float m_flAvgNumerator; float m_flAvgDenominator; };

Tiếp theo, chúng tôi xác định class helper sẽ bọc mọi lệnh gọi API thông số Steam cũng như tạo tất cả lệnh gọi lại trên Steam.
class CSteamStats { private: int64 m_iAppID; // AppID hiện tại Stat_t *m_pStats; // Dữ liệu thông số int m_iNumStats; // Số thông số bool m_bInitialized; // Đã gọi RequestStats và nhận lệnh gọi lại? public: CSteamStats(Stat_t *Stats, int NumStats); ~CSteamStats(); bool RequestStats(); bool StoreStats(); STEAM_CALLBACK( CSteamStats, OnUserStatsReceived, UserStatsReceived_t, m_CallbackUserStatsReceived ); STEAM_CALLBACK( CSteamStats, OnUserStatsStored, UserStatsStored_t, m_CallbackUserStatsStored ); };

File mã

Constructor

Tham số - Constructor đưa giá trị con trỏ qua một mảng thông số cùng với độ dài của mảng. Định dạng của mảng đó sẽ được bàn đến sau trong phần mã trò chơi chính.
Trả về - N/A
Chức năng - Constructor khởi chạy một số thành viên cùng lúc lấy AppID mà chúng ta hiện đang dùng. Thêm vào đó, nó cũng kết nối phương thức gọi lại để xử lý các lệnh gọi không đồng bộ đến Steam. Cuối cùng, lệnh gọi đầu tiên được thực hiện tới RequestStats() để lấy thông số và thành tựu của người dùng hiện tại.
CSteamStats::CSteamStats(Stat_t *Stats, int NumStats) : m_iAppID( 0 ), m_bInitialized( false ), m_CallbackUserStatsReceived( this, &CSteamStats::OnUserStatsReceived ), m_CallbackUserStatsStored( this, &CSteamStats::OnUserStatsStored ) { m_iAppID = SteamUtils()->GetAppID(); m_pStats = Stats; m_iNumStats = NumStats; RequestStats(); }

RequestStats()

Tham số - Không
Trả về - dữ liệu bool cho biết lệnh gọi có thành công hay không. Nếu lệnh gọi thất bại, rất có thể Steam chưa được khởi chạy. Hãy nhớ mở phần mềm Steam khi thực hiện lệnh gọi và gọi SteamAPI_Init trước.
Chức năng - Về cơ bản, phương thức này bọc lệnh gọi tới ISteamUserStats::RequestCurrentStats, đây là cuộc gọi không đồng bộ tới Steam yêu cầu cung cấp thông số của người dùng hiện tại. Lệnh gọi này cần được thực hiện trước khi đặt bất kỳ thông số hoặc thành tựu nào. Lệnh gọi đầu tới phương thức này được thực hiện trong constructor. Bạn có thể gọi lại bất kỳ lúc nào sau đó nếu bạn muốn kiểm tra thông số cập nhật hoặc thành tựu.
bool CSteamStats::RequestStats() { // Đã tải Steam chưa? Nếu chưa, thì không thể lấy thông số. if ( NULL == SteamUserStats() || NULL == SteamUser() ) { return false; } // Người dùng đã đăng nhập chưa? Nếu chưa, thì không thể lấy thông số. if ( !SteamUser()->BLoggedOn() ) { return false; } // Yêu cầu thông số người dùng. return SteamUserStats()->RequestCurrentStats(); }

StoreStats()

Tham số - Không
Trả về - dữ liệu bool cho biết lệnh gọi có thành công hay không. Nếu lệnh gọi thất bại, rất có thể Steam chưa được khởi chạy. Hãy nhớ mở phần mềm Steam khi thực hiện lệnh gọi và gọi SteamAPI_Init trước.
Chức năng - Về cơ bản, phương thức này bọc lệnh gọi tới ISteamUserStats::StoreStats, đây là cuộc gọi không đồng bộ tới Steam có chứa thông số của người dùng hiện tại trên máy chủ. Lệnh gọi này cần được thực hiện bất cứ lúc nào bạn muốn cập nhật thông số của người dùng.
bool CSteamStats::StoreStats() { if ( m_bInitialized ) { // nạp thông số for ( int iStat = 0; iStat < m_NumStats; ++iStat ) { Stat_t &stat = m_pStats[iStat]; switch (stat.m_eStatType) { case STAT_INT: SteamUserStats()->SetStat( stat.m_pchStatName, stat.m_iValue ); break; case STAT_FLOAT: SteamUserStats()->SetStat( stat.m_pchStatName, stat.m_flValue ); break; case STAT_AVGRATE: SteamUserStats()->UpdateAvgRateStat(stat.m_pchStatName, stat.m_flAvgNumerator, stat.m_flAvgDenominator ); // Kết quả trung bình được tính SteamUserStats()->GetStat(stat.m_pchStatName, &stat.m_flValue ); break; default: break; } } return SteamUserStats()->StoreStats(); } }

OnUserStatsReceived()

Tham số - N/A
Trả về - Trống
Chức năng - Phương thức này là một lệnh gọi lại, được kích hoạt bất cứ khi nào cố gắng yêu cầu thông số. Thông số được yêu cầu bằng RequestStats(). Phương thức này cập nhật biến thành viên m_Stats để phản ánh dữ liệu thông số mới nhất được trả về từ Steam.
void CSteamStats::OnUserStatsReceived( UserStatsReceived_t *pCallback ) { // có thể nhận lệnh gọi lại về thông số của trò chơi khác, hãy phớt lờ if ( m_iAppID == pCallback->m_nGameID ) { if ( k_EResultOK == pCallback->m_eResult ) { OutputDebugString( "Received stats and achievements from Steam\n" ); // nạp thông số for ( int iStat = 0; iStat < m_iNumStats; ++iStat ) { Stat_t &stat = m_Stats[iStat]; switch (stat.m_eStatType) { case STAT_INT: SteamUserStats()->GetStat(stat.m_pchStatName, &stat.m_iValue); break; case STAT_FLOAT: case STAT_AVGRATE: SteamUserStats()->GetStat(stat.m_pchStatName, &stat.m_flValue); break; default: break; } } m_bInitialized = true; } else { char buffer[128]; _snprintf( buffer, 128, "RequestStats - failed, %d\n", pCallback->m_eResult ); OutputDebugString( buffer ); } } }

OnUserStatsStored()

Tham số - N/A
Trả về - Trống
Chức năng - Phương pháp này là một lệnh gọi lại, được kích hoạt bất cứ khi nào cố gắng lưu trữ thông số trên Steam. Nếu bất kỳ thông số nào ta cố thiết lập trước đó phá vỡ ràng buộc, chúng sẽ được khôi phục về giá trị cũ để ta nạp lại giá trị.
void CSteamStats::OnUserStatsStored( UserStatsStored_t *pCallback ) { // có thể nhận lệnh gọi lại về thông số khác của trò chơi, hãy phớt lờ if ( m_iAppID == pCallback->m_nGameID ) { if ( k_EResultOK == pCallback->m_eResult ) { OutputDebugString( "StoreStats - success\n" ); } else if ( k_EResultInvalidParam == pCallback->m_eResult ) { // Một hoặc nhiều thông số được thiết lập phá vỡ ràng buộc. Đã được khôi phục, // và nên tái lập các giá trị ngay để giữ cho đồng bộ. OutputDebugString( "StoreStats - some failed to validate\n" ); // Giả một lệnh gọi lại tại đây để nạp lại giá trị. UserStatsReceived_t callback; callback.m_eResult = k_EResultOK; callback.m_nGameID = m_iAppID; OnUserStatsReceived( &callback ); } else { char buffer[128]; _snprintf( buffer, 128, "StoreStats - failed, %d\n", pCallback->m_eResult ); OutputDebugString( buffer ); } } }

Bước 3 - Tích hợp vào trò chơi

Đây là danh sách đầy đủ các đoạn mã bạn cần để tích hợp vào trò chơi tại những vị trí thích hợp.

Các Define và Global

Dưới đây là danh sách các mục cần thiết để xây dựng theo các thông số, và một mảng các thông số trò chơi cụ thể và con trỏ chung cho object helper. Xin lưu ý rằng thông số trùng với thông tin trên trang quản trị Steamworks.
... #include "steam_api.h" #include "isteamuserstats.h" #include "SteamStats.h" // Mảng thông số sẽ giữ dữ liệu về nội dung và trạng thái của chúng Stat_t g_Stats[] = { _STAT_ID( 1, STAT_INT, "NumGames"), _STAT_ID( 2, STAT_INT, "NumWins"), _STAT_ID( 3, STAT_INT, "NumLosses"), _STAT_ID( 4, STAT_FLOAT, "FeetTraveled"), _STAT_ID( 5, STAT_AVGRATE, "AverageSpeed"), _STAT_ID( 7, STAT_FLOAT, "MaxFeetTraveled"), }; // Truy cập chung đến object Stats CSteamStats* g_SteamStats = NULL; ...

Khởi chạy

Lệnh gọi tới SteamAPI_Init khởi chạy toàn bộ Steam và phải được thực hiện trước hết. Nếu lệnh đó thành công, hãy tạo object helper để vượt qua mảng thông số cùng với kích thước mảng.
... // Khởi chạy Steam bool bRet = SteamAPI_Init(); // Tạo đối object SteamStats nếu Steam khởi chạy thành công if (bRet) { g_SteamStats = new CSteamStats(g_Stats, 6); } ...

Xử lý lệnh gọi lại

Để đảm bảo xử lý tất cả lệnh gọi lại trên Steam, ta cần thường xuyên bơm tin nhắn mới. Thực hiện bằng cách thêm lệnh gọi này vào vòng lặp trò chơi.
... SteamAPI_RunCallbacks(); ...

Lưu thông số

Thông số được lưu bằng cách thực hiện một lệnh gọi tới StoreStats().
... if (g_SteamStats) g_SteamStats->StoreStats(); ...

Tắt

Lệnh gọi tới SteamAPI_Shutdown có lẽ đã tồn tại trong mã của bạn. Nó tắt Steam và phải được gọi trước khi thoát ứng dụng. Cuối cùng, xóa object helper đã tạo.
... // Tắt Steam SteamAPI_Shutdown(); // Xóa object SteamStats if (g_SteamStats) delete g_SteamStats; ...

Bước 4 - Thử nghiệm và xử lý sự cố

Mã mẫu này xuất thông tin gỡ lỗi tới console gỡ lỗi, giúp bạn biết lệnh gọi nào thành công hoặc thất bại. Sau đây là vài thông báo lỗi điển hình và cách sửa:

Ứng dụng này không thể khỏi chạy vì không tìm thấy steam_api.dll. Cài đặt lại ứng dụng có thể giải quyết vấn đề này.
Đảm bảo rằng steam_api.dll ở cùng thư mục với file thực thi.

[S_API FAIL] SteamAPI_Init() failed; unable to locate a running instance of Steam, or a local steamclient.dll
Rất có thể không có phần mềm Steam nào đang chạy. Khởi động Steam và đăng nhập.

[S_API FAIL] SteamAPI_Init() failed; no appID found.
Rất có thể tệp steam_appid.txt không ở đúng vị trí. Đặt nó vào thư mục nguồn và đảm bảo rằng nó chứa mã appID của bạn.