Panduan ini menunjukkan cara menerapkan game tersimpan menggunakan
snapshot API yang disediakan oleh Layanan game Google Play. API ini dapat ditemukan dalam paket
com.google.android.gms.games.snapshot
dan com.google.android.gms.games
.
Sebelum memulai
Jika Anda belum melakukannya, sebaiknya tinjau konsep game Game Tersimpan.
- Pastikan untuk mengaktifkan dukungan game tersimpan untuk game Anda di Konsol Google Play.
- Download dan tinjau contoh kode game tersimpan di halaman contoh Android.
- Pahami rekomendasi yang dijelaskan dalam Checklist Kualitas.
Mendapatkan klien snapshot
Untuk mulai menggunakan snapshot API, game Anda harus mendapatkan
objek SnapshotsClient
terlebih dahulu. Anda dapat melakukannya dengan memanggil
metode Games.getSnapshotsClient()
dan meneruskan
aktivitas serta GoogleSignInAccount
untuk pemutar saat ini. Untuk mempelajari cara
mengambil informasi akun pemain, lihat
Login di Game Android.
Menentukan cakupan Drive
Snapshot API bergantung pada Google Drive API untuk penyimpanan game tersimpan. Untuk
mengakses Drive API, aplikasi Anda harus menentukan cakupan
Drive.SCOPE_APPFOLDER
saat mem-build klien login dengan Google.
Berikut adalah contoh cara melakukannya di metode
onResume()
untuk aktivitas login Anda:
private GoogleSignInClient mGoogleSignInClient; @Override protected void onResume() { super.onResume(); signInSilently(); } private void signInSilently() { GoogleSignInOptions signInOption = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN) // Add the APPFOLDER scope for Snapshot support. .requestScopes(Drive.SCOPE_APPFOLDER) .build(); GoogleSignInClient signInClient = GoogleSignIn.getClient(this, signInOption); signInClient.silentSignIn().addOnCompleteListener(this, new OnCompleteListener<GoogleSignInAccount>() { @Override public void onComplete(@NonNull Task<GoogleSignInAccount> task) { if (task.isSuccessful()) { onConnected(task.getResult()); } else { // Player will need to sign-in explicitly using via UI } } }); }
Menampilkan game tersimpan
Anda dapat mengintegrasikan snapshot API di mana pun game Anda menyediakan opsi kepada pemain untuk menyimpan atau memulihkan progresnya. Game Anda mungkin menampilkan opsi tersebut di titik penyimpanan/pemulihan yang ditentukan, atau memungkinkan pemain untuk menyimpan atau memulihkan progres kapan saja.
Setelah pemain memilih opsi simpan/pulihkan dalam game, game Anda dapat secara opsional menampilkan layar yang meminta pemain untuk memasukkan informasi game tersimpan yang baru atau memilih game tersimpan yang sudah ada untuk dipulihkan.
Untuk menyederhanakan pengembangan, snapshot API menyediakan antarmuka pengguna (UI) pilihan game tersimpan default yang dapat Anda gunakan secara langsung. UI pilihan game tersimpan memungkinkan pemain membuat game tersimpan baru, melihat detail tentang game tersimpan yang ada, dan memuat game tersimpan sebelumnya.
Untuk meluncurkan UI Game Tersimpan default:
- Panggil
SnapshotsClient.getSelectSnapshotIntent()
untuk mendapatkanIntent
guna meluncurkan UI pilihan game tersimpan default. - Panggil
startActivityForResult()
dan teruskanIntent
tersebut. Jika panggilan berhasil, game akan menampilkan UI pilihan game tersimpan, beserta opsi yang Anda tentukan.
Berikut adalah contoh cara meluncurkan UI pilihan game tersimpan default:
private static final int RC_SAVED_GAMES = 9009; private void showSavedGamesUI() { SnapshotsClient snapshotsClient = Games.getSnapshotsClient(this, GoogleSignIn.getLastSignedInAccount(this)); int maxNumberOfSavedGamesToShow = 5; Task<Intent> intentTask = snapshotsClient.getSelectSnapshotIntent( "See My Saves", true, true, maxNumberOfSavedGamesToShow); intentTask.addOnSuccessListener(new OnSuccessListener<Intent>() { @Override public void onSuccess(Intent intent) { startActivityForResult(intent, RC_SAVED_GAMES); } }); }
Jika pemain memilih untuk membuat game tersimpan baru atau memuat game tersimpan yang sudah ada,
UI akan mengirimkan permintaan ke Layanan game Google Play. Jika permintaan berhasil,
Layanan game Google Play akan menampilkan informasi untuk membuat atau memulihkan game tersimpan melalui
callback
onActivityResult()
. Game Anda dapat mengganti callback ini untuk memeriksa apakah terjadi error selama permintaan.
Cuplikan kode berikut menunjukkan contoh implementasi
onActivityResult()
:
private String mCurrentSaveName = "snapshotTemp"; /** * This callback will be triggered after you call startActivityForResult from the * showSavedGamesUI method. */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { if (intent != null) { if (intent.hasExtra(SnapshotsClient.EXTRA_SNAPSHOT_METADATA)) { // Load a snapshot. SnapshotMetadata snapshotMetadata = intent.getParcelableExtra(SnapshotsClient.EXTRA_SNAPSHOT_METADATA); mCurrentSaveName = snapshotMetadata.getUniqueName(); // Load the game data from the Snapshot // ... } else if (intent.hasExtra(SnapshotsClient.EXTRA_SNAPSHOT_NEW)) { // Create a new snapshot named with a unique string String unique = new BigInteger(281, new Random()).toString(13); mCurrentSaveName = "snapshotTemp-" + unique; // Create the new snapshot // ... } } }
Menulis game tersimpan
Untuk menyimpan konten ke game tersimpan:
- Buka snapshot secara asinkron melalui
SnapshotsClient.open()
. Kemudian, ambil objekSnapshot
dari hasil tugas dengan memanggilSnapshotsClient.DataOrConflict.getData()
. - Ambil instance
SnapshotContents
melaluiSnapshotsClient.SnapshotConflict
. - Panggil
SnapshotContents.writeBytes()
untuk menyimpan data pemain dalam format byte. - Setelah semua perubahan ditulis, panggil
SnapshotsClient.commitAndClose()
untuk mengirim perubahan ke server Google. Dalam panggilan metode, game Anda dapat secara opsional memberikan informasi tambahan untuk memberi tahu Layanan game Google Play cara menampilkan game tersimpan ini kepada pemain. Informasi ini ditunjukkan dalam objekSnapshotMetaDataChange
, yang dibuat game Anda menggunakanSnapshotMetadataChange.Builder
.
Cuplikan berikut menunjukkan cara game Anda dapat melakukan perubahan pada game tersimpan:
private Task<SnapshotMetadata> writeSnapshot(Snapshot snapshot, byte[] data, Bitmap coverImage, String desc) { // Set the data payload for the snapshot snapshot.getSnapshotContents().writeBytes(data); // Create the change operation SnapshotMetadataChange metadataChange = new SnapshotMetadataChange.Builder() .setCoverImage(coverImage) .setDescription(desc) .build(); SnapshotsClient snapshotsClient = Games.getSnapshotsClient(this, GoogleSignIn.getLastSignedInAccount(this)); // Commit the operation return snapshotsClient.commitAndClose(snapshot, metadataChange); }
Jika perangkat pemain tidak terhubung ke jaringan saat aplikasi memanggil
SnapshotsClient.commitAndClose()
, Layanan game Google Play akan menyimpan data game tersimpan secara lokal di
perangkat. Setelah perangkat dihubungkan kembali, Layanan game Google Play akan menyinkronkan perubahan game tersimpan
yang di-cache secara lokal ke server Google.
Memuat game tersimpan
Untuk mengambil game tersimpan dari pemain yang saat ini login:
- Buka snapshot secara asinkron melalui
SnapshotsClient.open()
. Kemudian, ambil objekSnapshot
dari hasil tugas dengan memanggilSnapshotsClient.DataOrConflict.getData()
. Atau, game Anda juga dapat mengambil snapshot tertentu melalui UI pilihan game tersimpan, seperti yang dijelaskan dalam Menampilkan game tersimpan. - Ambil instance
SnapshotContents
melaluiSnapshotsClient.SnapshotConflict
. - Panggil
SnapshotContents.readFully()
untuk membaca konten snapshot.
Cuplikan berikut menunjukkan cara memuat game tersimpan tertentu:
Task<byte[]> loadSnapshot() { // Display a progress dialog // ... // Get the SnapshotsClient from the signed in account. SnapshotsClient snapshotsClient = Games.getSnapshotsClient(this, GoogleSignIn.getLastSignedInAccount(this)); // In the case of a conflict, the most recently modified version of this snapshot will be used. int conflictResolutionPolicy = SnapshotsClient.RESOLUTION_POLICY_MOST_RECENTLY_MODIFIED; // Open the saved game using its name. return snapshotsClient.open(mCurrentSaveName, true, conflictResolutionPolicy) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { Log.e(TAG, "Error while opening Snapshot.", e); } }).continueWith(new Continuation<SnapshotsClient.DataOrConflict<Snapshot>, byte[]>() { @Override public byte[] then(@NonNull Task<SnapshotsClient.DataOrConflict<Snapshot>> task) throws Exception { Snapshot snapshot = task.getResult().getData(); // Opening the snapshot was a success and any conflicts have been resolved. try { // Extract the raw data from the snapshot. return snapshot.getSnapshotContents().readFully(); } catch (IOException e) { Log.e(TAG, "Error while reading Snapshot.", e); } return null; } }).addOnCompleteListener(new OnCompleteListener<byte[]>() { @Override public void onComplete(@NonNull Task<byte[]> task) { // Dismiss progress dialog and reflect the changes in the UI when complete. // ... } }); }
Menangani konflik game tersimpan
Saat menggunakan snapshot API dalam game, beberapa perangkat dapat melakukan pembacaan dan penulisan di game tersimpan yang sama. Jika koneksi jaringan terputus untuk sementara dan kemudian terhubung kembali, hal ini dapat menyebabkan konflik data saat game tersimpan yang disimpan di perangkat lokal pemain tidak sinkron dengan versi jarak jauh yang tersimpan di server Google.
Snapshot API memberikan mekanisme resolusi konflik yang menyajikan serangkaian game tersimpan yang mengalami konflik pada waktu baca dan memungkinkan Anda menerapkan strategi penyelesaian yang sesuai untuk game Anda.
Saat Layanan game Google Play mendeteksi konflik data, metode
SnapshotsClient.DataOrConflict.isConflict()
akan menampilkan nilai true
. Dalam hal ini, class
SnapshotsClient.SnapshotConflict
akan menyediakan dua versi game tersimpan:
- Versi server: Versi terbaru yang diketahui oleh Layanan game Google Play agar akurat untuk perangkat pemain; dan
- Versi lokal: Versi modifikasi yang terdeteksi di salah satu perangkat pemain yang berisi konten atau metadata yang mengalami konflik. Versi ini mungkin tidak sama dengan versi yang Anda coba simpan.
Game Anda harus menentukan cara menyelesaikan konflik dengan memilih salah satu versi yang disediakan atau menggabungkan data dari kedua versi game tersimpan.
Untuk mendeteksi dan menyelesaikan konflik game tersimpan:
- Panggil
SnapshotsClient.open()
. Hasil tugas berisi classSnapshotsClient.DataOrConflict
. - Panggil metode
SnapshotsClient.DataOrConflict.isConflict()
. Jika hasilnya benar, Anda memiliki konflik yang harus diselesaikan. - Panggil
SnapshotsClient.DataOrConflict.getConflict()
untuk mengambil instanceSnaphotsClient.snapshotConflict
. - Panggil
SnapshotsClient.SnapshotConflict.getConflictId()
untuk mengambil ID konflik yang secara unik mengidentifikasi konflik yang terdeteksi. Game Anda memerlukan nilai ini untuk mengirim permintaan penyelesaian konflik di lain waktu. - Panggil
SnapshotsClient.SnapshotConflict.getConflictingSnapshot()
untuk mendapatkan versi lokal. - Panggil
SnapshotsClient.SnapshotConflict.getSnapshot()
untuk mendapatkan versi server. - Untuk menyelesaikan konflik game tersimpan, pilih versi yang ingin Anda simpan ke server sebagai
versi akhir, lalu teruskan ke metode
SnapshotsClient.resolveConflict()
.
Cuplikan berikut menunjukkan dan memberikan contoh cara game Anda dapat menangani konflik game tersimpan dengan memilih game tersimpan yang terakhir diubah sebagai versi final untuk disimpan:
private static final int MAX_SNAPSHOT_RESOLVE_RETRIES = 10; Task<Snapshot> processSnapshotOpenResult(SnapshotsClient.DataOrConflict<Snapshot> result, final int retryCount) { if (!result.isConflict()) { // There was no conflict, so return the result of the source. TaskCompletionSource<Snapshot> source = new TaskCompletionSource<>(); source.setResult(result.getData()); return source.getTask(); } // There was a conflict. Try resolving it by selecting the newest of the conflicting snapshots. // This is the same as using RESOLUTION_POLICY_MOST_RECENTLY_MODIFIED as a conflict resolution // policy, but we are implementing it as an example of a manual resolution. // One option is to present a UI to the user to choose which snapshot to resolve. SnapshotsClient.SnapshotConflict conflict = result.getConflict(); Snapshot snapshot = conflict.getSnapshot(); Snapshot conflictSnapshot = conflict.getConflictingSnapshot(); // Resolve between conflicts by selecting the newest of the conflicting snapshots. Snapshot resolvedSnapshot = snapshot; if (snapshot.getMetadata().getLastModifiedTimestamp() < conflictSnapshot.getMetadata().getLastModifiedTimestamp()) { resolvedSnapshot = conflictSnapshot; } return Games.getSnapshotsClient(theActivity, GoogleSignIn.getLastSignedInAccount(this)) .resolveConflict(conflict.getConflictId(), resolvedSnapshot) .continueWithTask( new Continuation< SnapshotsClient.DataOrConflict<Snapshot>, Task<Snapshot>>() { @Override public Task<Snapshot> then( @NonNull Task<SnapshotsClient.DataOrConflict<Snapshot>> task) throws Exception { // Resolving the conflict may cause another conflict, // so recurse and try another resolution. if (retryCount < MAX_SNAPSHOT_RESOLVE_RETRIES) { return processSnapshotOpenResult(task.getResult(), retryCount + 1); } else { throw new Exception("Could not resolve snapshot conflicts"); } } }); }
Mengubah game tersimpan untuk resolusi konflik
Jika Anda ingin menggabungkan data dari beberapa game tersimpan atau mengubah Snapshot
yang ada untuk disimpan ke server sebagai versi final yang telah diselesaikan, ikuti langkah-langkah berikut:
- Panggil
SnapshotsClient.open()
. - Panggil
SnapshotsClient.SnapshotConflict.getResolutionSnapshotsContent()
untuk mendapatkan objekSnapshotContents
baru. - Gabungkan data dari
SnapshotsClient.SnapshotConflict.getConflictingSnapshot()
danSnapshotsClient.SnapshotConflict.getSnapshot()
ke dalam objekSnapshotContents
dari langkah sebelumnya. - Secara opsional, buat instance
SnapshotMetadataChange
jika ada perubahan pada kolom metadata. - Panggil
SnapshotsClient.resolveConflict()
. Dalam panggilan metode Anda, teruskanSnapshotsClient.SnapshotConflict.getConflictId()
sebagai argumen pertama, serta objekSnapshotMetadataChange
danSnapshotContents
yang sebelumnya Anda ubah sebagai argumen kedua dan ketiga. - Jika panggilan
SnapshotsClient.resolveConflict()
berhasil, API akan menyimpan objekSnapshot
ke server dan mencoba membuka objek Snapshot di perangkat lokal Anda.- Jika ada konflik,
SnapshotsClient.DataOrConflict.isConflict()
akan menampilkantrue
. Dalam hal ini, game Anda harus kembali ke langkah 2 dan mengulangi langkah-langkah untuk mengubah snapshot hingga konflik terselesaikan. - Jika tidak ada konflik,
SnapshotsClient.DataOrConflict.isConflict()
akan menampilkanfalse
dan objekSnapshot
terbuka untuk diubah oleh game Anda.
- Jika ada konflik,