@byvik/capacitor-play-games 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 byVik
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,479 @@
1
- # Temporary Holding Version
1
+ # @byvik/capacitor-play-games
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Google Play Games Services **v2** for Capacitor: sign-in, leaderboards, achievements and cloud saved games.
4
+
5
+ - Built on `play-services-games-v2`, the SDK Google currently supports. No `GoogleApiClient`, no legacy Google Sign-In.
6
+ - Saved games (snapshots), so a player's progress follows their Google account across devices and reinstalls.
7
+ - Leaderboard standing: the player's rank and the number of scores in one call.
8
+ - Android only. On iOS and web every method rejects, so guard calls with `Capacitor.getPlatform() === 'android'`.
9
+
10
+ Tested on a physical device (Android 12) against a real Play Games Services project: sign-in, player, score submission, standing, achievements, saved games and Google's leaderboard and achievement screens.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ npm install @byvik/capacitor-play-games
16
+ npx cap sync
17
+ ```
18
+
19
+ Requires Capacitor 8.
20
+
21
+ ## Setup
22
+
23
+ 1. In the [Play Console](https://play.google.com/console), open your game and go to **Play Games Services → Setup and management → Configuration**. Create the project and add an Android credential for every key that signs your app: the debug key, your upload key and the Play App Signing key. A missing SHA-1 is the usual reason sign-in fails with `errorCode: 10`.
24
+
25
+ 2. Copy the **Project ID** shown at the top of that page into `android/app/src/main/res/values/strings.xml`:
26
+
27
+ ```xml
28
+ <string name="game_services_project_id" translatable="false">123456789012</string>
29
+ ```
30
+
31
+ 3. Reference it inside `<application>` in `android/app/src/main/AndroidManifest.xml`:
32
+
33
+ ```xml
34
+ <meta-data
35
+ android:name="com.google.android.gms.games.APP_ID"
36
+ android:value="@string/game_services_project_id" />
37
+ ```
38
+
39
+ 4. To use `saveGame` and `loadGame`, turn on **Saved Games** in the same configuration page.
40
+
41
+ While the game is unpublished, only accounts listed under **Testers** can sign in.
42
+
43
+ ### Variables
44
+
45
+ Set these in `android/variables.gradle` to override the defaults:
46
+
47
+ - `playServicesGamesV2Version`: version of `com.google.android.gms:play-services-games-v2` (default: `20.1.2`)
48
+
49
+ ## Usage
50
+
51
+ ```ts
52
+ import { PlayGames } from '@byvik/capacitor-play-games';
53
+
54
+ // On launch: the SDK has already tried a silent sign-in.
55
+ const { isAuthenticated } = await PlayGames.isAuthenticated();
56
+
57
+ // From a "Sign in" button:
58
+ const result = await PlayGames.signIn();
59
+ if (!result.isAuthenticated) {
60
+ console.warn('Not signed in', result.errorCode, result.errorMessage);
61
+ }
62
+
63
+ // Leaderboards
64
+ await PlayGames.submitScore({ leaderboardId: 'CgkI...', score: 4200 });
65
+ await PlayGames.showLeaderboard({ leaderboardId: 'CgkI...', timeSpan: 'daily' });
66
+ const { rank, numScores } = await PlayGames.getLeaderboardStanding({
67
+ leaderboardId: 'CgkI...',
68
+ timeSpan: 'daily',
69
+ });
70
+
71
+ // Achievements
72
+ await PlayGames.unlockAchievement({ achievementId: 'CgkI...' });
73
+ await PlayGames.setAchievementSteps({ achievementId: 'CgkI...', steps: 37 });
74
+ await PlayGames.showAchievements();
75
+
76
+ // Saved games
77
+ await PlayGames.saveGame({ name: 'progress', data: JSON.stringify(save) });
78
+ const { data } = await PlayGames.loadGame({ name: 'progress' });
79
+ if (data !== null) {
80
+ const cloudSave = JSON.parse(data);
81
+ }
82
+ ```
83
+
84
+ ### Errors
85
+
86
+ `isAuthenticated()` and `signIn()` never reject; they resolve with `isAuthenticated: false`. Every other method rejects when Play Games reports a failure, and the error's `code` is the [Google API status code](https://developers.google.com/android/reference/com/google/android/gms/games/GamesClientStatusCodes) as a string.
87
+
88
+ ## API
89
+
90
+ <docgen-index>
91
+
92
+ * [`isAuthenticated()`](#isauthenticated)
93
+ * [`signIn()`](#signin)
94
+ * [`getCurrentPlayer()`](#getcurrentplayer)
95
+ * [`openPlayGamesApp()`](#openplaygamesapp)
96
+ * [`submitScore(...)`](#submitscore)
97
+ * [`getLeaderboardStanding(...)`](#getleaderboardstanding)
98
+ * [`showLeaderboard(...)`](#showleaderboard)
99
+ * [`showAllLeaderboards()`](#showallleaderboards)
100
+ * [`unlockAchievement(...)`](#unlockachievement)
101
+ * [`revealAchievement(...)`](#revealachievement)
102
+ * [`incrementAchievement(...)`](#incrementachievement)
103
+ * [`setAchievementSteps(...)`](#setachievementsteps)
104
+ * [`showAchievements()`](#showachievements)
105
+ * [`saveGame(...)`](#savegame)
106
+ * [`loadGame(...)`](#loadgame)
107
+ * [Interfaces](#interfaces)
108
+ * [Type Aliases](#type-aliases)
109
+
110
+ </docgen-index>
111
+
112
+ <docgen-api>
113
+ <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
114
+
115
+ ### isAuthenticated()
116
+
117
+ ```typescript
118
+ isAuthenticated() => Promise<AuthResult>
119
+ ```
120
+
121
+ Whether a player is signed in. Shows no UI and never rejects.
122
+
123
+ The SDK tries a silent sign-in when the app starts, so this is the call
124
+ to make before drawing anything that depends on the session.
125
+
126
+ **Returns:** <code>Promise&lt;<a href="#authresult">AuthResult</a>&gt;</code>
127
+
128
+ **Since:** 0.1.0
129
+
130
+ --------------------
131
+
132
+
133
+ ### signIn()
134
+
135
+ ```typescript
136
+ signIn() => Promise<AuthResult>
137
+ ```
138
+
139
+ Ask the player to sign in. This is the only call that shows Google's
140
+ sign-in UI, so make it from a button the player pressed. Never rejects:
141
+ a cancelled or failed sign-in resolves with `isAuthenticated: false`.
142
+
143
+ **Returns:** <code>Promise&lt;<a href="#authresult">AuthResult</a>&gt;</code>
144
+
145
+ **Since:** 0.1.0
146
+
147
+ --------------------
148
+
149
+
150
+ ### getCurrentPlayer()
151
+
152
+ ```typescript
153
+ getCurrentPlayer() => Promise<Player>
154
+ ```
155
+
156
+ The signed-in player. Rejects when nobody is signed in.
157
+
158
+ **Returns:** <code>Promise&lt;<a href="#player">Player</a>&gt;</code>
159
+
160
+ **Since:** 0.1.0
161
+
162
+ --------------------
163
+
164
+
165
+ ### openPlayGamesApp()
166
+
167
+ ```typescript
168
+ openPlayGamesApp() => Promise<void>
169
+ ```
170
+
171
+ Open the Play Games app, or its store listing when it is not installed.
172
+
173
+ Useful when `signIn()` cannot succeed because the device has no Google
174
+ account or the player has no Play Games profile: both are created there.
175
+
176
+ **Since:** 0.1.0
177
+
178
+ --------------------
179
+
180
+
181
+ ### submitScore(...)
182
+
183
+ ```typescript
184
+ submitScore(options: SubmitScoreOptions) => Promise<void>
185
+ ```
186
+
187
+ Submit a score to a leaderboard.
188
+
189
+ | Param | Type |
190
+ | ------------- | ----------------------------------------------------------------- |
191
+ | **`options`** | <code><a href="#submitscoreoptions">SubmitScoreOptions</a></code> |
192
+
193
+ **Since:** 0.1.0
194
+
195
+ --------------------
196
+
197
+
198
+ ### getLeaderboardStanding(...)
199
+
200
+ ```typescript
201
+ getLeaderboardStanding(options: LeaderboardStandingOptions) => Promise<LeaderboardStanding>
202
+ ```
203
+
204
+ The player's rank and score, and the number of scores in the
205
+ leaderboard, read together for one time span and collection.
206
+
207
+ | Param | Type |
208
+ | ------------- | --------------------------------------------------------------------------------- |
209
+ | **`options`** | <code><a href="#leaderboardstandingoptions">LeaderboardStandingOptions</a></code> |
210
+
211
+ **Returns:** <code>Promise&lt;<a href="#leaderboardstanding">LeaderboardStanding</a>&gt;</code>
212
+
213
+ **Since:** 0.1.0
214
+
215
+ --------------------
216
+
217
+
218
+ ### showLeaderboard(...)
219
+
220
+ ```typescript
221
+ showLeaderboard(options: LeaderboardOptions) => Promise<void>
222
+ ```
223
+
224
+ Open Google's UI for one leaderboard. Resolves when the UI opens.
225
+
226
+ | Param | Type |
227
+ | ------------- | ----------------------------------------------------------------- |
228
+ | **`options`** | <code><a href="#leaderboardoptions">LeaderboardOptions</a></code> |
229
+
230
+ **Since:** 0.1.0
231
+
232
+ --------------------
233
+
234
+
235
+ ### showAllLeaderboards()
236
+
237
+ ```typescript
238
+ showAllLeaderboards() => Promise<void>
239
+ ```
240
+
241
+ Open Google's UI listing every leaderboard of the game.
242
+
243
+ **Since:** 0.1.0
244
+
245
+ --------------------
246
+
247
+
248
+ ### unlockAchievement(...)
249
+
250
+ ```typescript
251
+ unlockAchievement(options: AchievementOptions) => Promise<void>
252
+ ```
253
+
254
+ Unlock an achievement. Queued by the SDK when offline.
255
+
256
+ | Param | Type |
257
+ | ------------- | ----------------------------------------------------------------- |
258
+ | **`options`** | <code><a href="#achievementoptions">AchievementOptions</a></code> |
259
+
260
+ **Since:** 0.1.0
261
+
262
+ --------------------
263
+
264
+
265
+ ### revealAchievement(...)
266
+
267
+ ```typescript
268
+ revealAchievement(options: AchievementOptions) => Promise<void>
269
+ ```
270
+
271
+ Reveal a hidden achievement. Queued by the SDK when offline.
272
+
273
+ | Param | Type |
274
+ | ------------- | ----------------------------------------------------------------- |
275
+ | **`options`** | <code><a href="#achievementoptions">AchievementOptions</a></code> |
276
+
277
+ **Since:** 0.1.0
278
+
279
+ --------------------
280
+
281
+
282
+ ### incrementAchievement(...)
283
+
284
+ ```typescript
285
+ incrementAchievement(options: AchievementStepsOptions) => Promise<void>
286
+ ```
287
+
288
+ Add steps to an incremental achievement. Queued by the SDK when offline.
289
+
290
+ | Param | Type |
291
+ | ------------- | --------------------------------------------------------------------------- |
292
+ | **`options`** | <code><a href="#achievementstepsoptions">AchievementStepsOptions</a></code> |
293
+
294
+ **Since:** 0.1.0
295
+
296
+ --------------------
297
+
298
+
299
+ ### setAchievementSteps(...)
300
+
301
+ ```typescript
302
+ setAchievementSteps(options: AchievementStepsOptions) => Promise<void>
303
+ ```
304
+
305
+ Set the absolute progress of an incremental achievement. Play ignores a
306
+ value lower than the one it already has, which makes this the safe choice
307
+ when your own save holds the real counter.
308
+
309
+ | Param | Type |
310
+ | ------------- | --------------------------------------------------------------------------- |
311
+ | **`options`** | <code><a href="#achievementstepsoptions">AchievementStepsOptions</a></code> |
312
+
313
+ **Since:** 0.1.0
314
+
315
+ --------------------
316
+
317
+
318
+ ### showAchievements()
319
+
320
+ ```typescript
321
+ showAchievements() => Promise<void>
322
+ ```
323
+
324
+ Open Google's achievements UI. Resolves when the UI opens.
325
+
326
+ **Since:** 0.1.0
327
+
328
+ --------------------
329
+
330
+
331
+ ### saveGame(...)
332
+
333
+ ```typescript
334
+ saveGame(options: SaveGameOptions) => Promise<void>
335
+ ```
336
+
337
+ Write a saved game to the player's Google account, creating it if needed.
338
+ Conflicts between devices are resolved by keeping the most recently
339
+ modified copy.
340
+
341
+ | Param | Type |
342
+ | ------------- | ----------------------------------------------------------- |
343
+ | **`options`** | <code><a href="#savegameoptions">SaveGameOptions</a></code> |
344
+
345
+ **Since:** 0.1.0
346
+
347
+ --------------------
348
+
349
+
350
+ ### loadGame(...)
351
+
352
+ ```typescript
353
+ loadGame(options: LoadGameOptions) => Promise<LoadGameResult>
354
+ ```
355
+
356
+ Read a saved game. Resolves with `data: null` when it does not exist and
357
+ rejects when it could not be read.
358
+
359
+ | Param | Type |
360
+ | ------------- | ----------------------------------------------------------- |
361
+ | **`options`** | <code><a href="#loadgameoptions">LoadGameOptions</a></code> |
362
+
363
+ **Returns:** <code>Promise&lt;<a href="#loadgameresult">LoadGameResult</a>&gt;</code>
364
+
365
+ **Since:** 0.1.0
366
+
367
+ --------------------
368
+
369
+
370
+ ### Interfaces
371
+
372
+
373
+ #### AuthResult
374
+
375
+ | Prop | Type | Description | Since |
376
+ | --------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- |
377
+ | **`isAuthenticated`** | <code>boolean</code> | Whether a player is signed in to Play Games Services. | 0.1.0 |
378
+ | **`errorCode`** | <code>number</code> | Google API status code. Only present when the SDK reported an error. `10` (DEVELOPER_ERROR) means the app is misconfigured: the SHA-1 of this build or its package name is missing from the OAuth credentials in the Play Console. `16` and `12501` mean the player cancelled. | 0.1.0 |
379
+ | **`errorMessage`** | <code>string</code> | Error message from the SDK. Only present when the SDK reported an error. | 0.1.0 |
380
+
381
+
382
+ #### Player
383
+
384
+ | Prop | Type | Description | Since |
385
+ | ----------------- | ------------------- | ------------------------------------------------ | ----- |
386
+ | **`playerId`** | <code>string</code> | | 0.1.0 |
387
+ | **`displayName`** | <code>string</code> | The name the player shows in Play Games. | 0.1.0 |
388
+ | **`title`** | <code>string</code> | The player's Play Games title, if they have one. | 0.1.0 |
389
+
390
+
391
+ #### SubmitScoreOptions
392
+
393
+ | Prop | Type | Description | Default | Since |
394
+ | ------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | ----- |
395
+ | **`leaderboardId`** | <code>string</code> | | | 0.1.0 |
396
+ | **`score`** | <code>number</code> | Raw score, an integer. For time leaderboards it is in milliseconds and for currency leaderboards in 1/1,000,000ths of the main unit. | | 0.1.0 |
397
+ | **`immediate`** | <code>boolean</code> | Wait for the server to confirm the score. With `false` the SDK queues the score, even offline, and the call resolves straight away. With `true` the call resolves once the server has the score and rejects when it cannot be reached; use it when you read the standing right after submitting. | <code>false</code> | 0.1.0 |
398
+
399
+
400
+ #### LeaderboardStanding
401
+
402
+ | Prop | Type | Description | Since |
403
+ | --------------- | --------------------------- | --------------------------------------------------------------------------------------------- | ----- |
404
+ | **`rank`** | <code>number \| null</code> | The player's rank, or `null` when unknown (for example, no score in this time span). | 0.1.0 |
405
+ | **`score`** | <code>number \| null</code> | The player's raw score, or `null` when unknown. | 0.1.0 |
406
+ | **`numScores`** | <code>number \| null</code> | How many scores the leaderboard has in this time span and collection, or `null` when unknown. | 0.1.0 |
407
+
408
+
409
+ #### LeaderboardStandingOptions
410
+
411
+ | Prop | Type | Description | Default | Since |
412
+ | ----------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----- |
413
+ | **`forceReload`** | <code>boolean</code> | Skip the local cache and ask the server. The cached number of scores can be stale, but Google rate-limits forced reloads: keep it for moments when fresh data matters. | <code>false</code> | 0.1.0 |
414
+
415
+
416
+ #### LeaderboardOptions
417
+
418
+ | Prop | Type | Description | Default | Since |
419
+ | ------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | ----- |
420
+ | **`leaderboardId`** | <code>string</code> | | | 0.1.0 |
421
+ | **`timeSpan`** | <code><a href="#leaderboardtimespan">LeaderboardTimeSpan</a></code> | | <code>'allTime'</code> | 0.1.0 |
422
+ | **`collection`** | <code><a href="#leaderboardcollection">LeaderboardCollection</a></code> | `'friends'` needs the player's permission to read their friends list. Until they have granted it, `getLeaderboardStanding` rejects with code `'26703'` (CONSENT_REQUIRED). | <code>'public'</code> | 0.1.0 |
423
+
424
+
425
+ #### AchievementOptions
426
+
427
+ | Prop | Type | Since |
428
+ | ------------------- | ------------------- | ----- |
429
+ | **`achievementId`** | <code>string</code> | 0.1.0 |
430
+
431
+
432
+ #### AchievementStepsOptions
433
+
434
+ | Prop | Type | Since |
435
+ | ------------------- | ------------------- | ----- |
436
+ | **`achievementId`** | <code>string</code> | 0.1.0 |
437
+ | **`steps`** | <code>number</code> | 0.1.0 |
438
+
439
+
440
+ #### SaveGameOptions
441
+
442
+ | Prop | Type | Description | Since |
443
+ | ----------------- | ------------------- | --------------------------------------------------------------------------------------------------- | ----- |
444
+ | **`name`** | <code>string</code> | Unique name of the saved game: 1 to 100 characters from `a-z`, `A-Z`, `0-9`, `-`, `.`, `_` and `~`. | 0.1.0 |
445
+ | **`data`** | <code>string</code> | The contents, stored as UTF-8. Play allows up to 3 MB per saved game. | 0.1.0 |
446
+ | **`description`** | <code>string</code> | Description shown in the Play Games saved games UI. | 0.1.0 |
447
+
448
+
449
+ #### LoadGameResult
450
+
451
+ | Prop | Type | Description | Since |
452
+ | ---------- | --------------------------- | ----------------------------------------------------------------------- | ----- |
453
+ | **`data`** | <code>string \| null</code> | The saved contents, or `null` when no saved game with that name exists. | 0.1.0 |
454
+
455
+
456
+ #### LoadGameOptions
457
+
458
+ | Prop | Type | Since |
459
+ | ---------- | ------------------- | ----- |
460
+ | **`name`** | <code>string</code> | 0.1.0 |
461
+
462
+
463
+ ### Type Aliases
464
+
465
+
466
+ #### LeaderboardTimeSpan
467
+
468
+ <code>'daily' | 'weekly' | 'allTime'</code>
469
+
470
+
471
+ #### LeaderboardCollection
472
+
473
+ <code>'public' | 'friends'</code>
474
+
475
+ </docgen-api>
476
+
477
+ ## License
478
+
479
+ MIT
@@ -0,0 +1,54 @@
1
+ ext {
2
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
3
+ playServicesGamesV2Version = project.hasProperty('playServicesGamesV2Version') ? rootProject.ext.playServicesGamesV2Version : '20.1.2'
4
+ }
5
+
6
+ buildscript {
7
+ repositories {
8
+ google()
9
+ mavenCentral()
10
+ }
11
+ dependencies {
12
+ classpath 'com.android.tools.build:gradle:8.13.0'
13
+ }
14
+ }
15
+
16
+ apply plugin: 'com.android.library'
17
+
18
+ android {
19
+ namespace = "com.byvik.capacitor.playgames"
20
+ compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
21
+ defaultConfig {
22
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
23
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
24
+ versionCode 1
25
+ versionName "1.0"
26
+ }
27
+ buildTypes {
28
+ release {
29
+ minifyEnabled false
30
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
31
+ }
32
+ }
33
+ lintOptions {
34
+ abortOnError = false
35
+ }
36
+ compileOptions {
37
+ sourceCompatibility JavaVersion.VERSION_21
38
+ targetCompatibility JavaVersion.VERSION_21
39
+ }
40
+ }
41
+
42
+
43
+ repositories {
44
+ google()
45
+ mavenCentral()
46
+ }
47
+
48
+
49
+ dependencies {
50
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
51
+ implementation project(':capacitor-android')
52
+ implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
53
+ implementation "com.google.android.gms:play-services-games-v2:$playServicesGamesV2Version"
54
+ }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>