# CoolAPI — Unity SDK for Cool GameX

Add **achievements, stats, leaderboards, cloud saves, DLC and ownership checks** to a
Unity game in minutes. Free and **MIT-licensed** — use it in any game, commercial or not.

✅ Unity 2021.3+ · ✅ no dependencies · ✅ async/await · ✅ works offline · ✅ Windows / macOS / Linux

> Full docs: **https://coolgamex.com/sdk.html** · Version history: [CHANGELOG](CHANGELOG.md) · Current: **v2.0.0**

---

## Setup in 3 steps

1. **Import** — *Assets › Import Package › Custom Package…* → `CoolGameX-SDK.unitypackage`.
   The **Setup window** opens by itself (or *Tools › Cool GameX › Setup*).
2. **Configure** — click *Create settings*, enter your **game slug** (as shown in the
   Creator dashboard) and click *Test connection*.
3. **Sign in** — enter your Cool GameX email and password in the Setup window. Play Mode
   now runs as you, so you can test unlocks right away.

That's it. The SDK starts itself when the game launches — no `Initialize()` call and no
scene object needed. When players start your game from the Cool GameX launcher, their
session is picked up automatically.

Optional but recommended: click **Generate IDs** to get compile-checked constants:

```csharp
CoolGameXManager.Unlock(Ids.Achievements.SpeedRunner);   // a typo is now a compile error
```

---

## Achievements

```csharp
using CoolGameX;

CoolGameXManager.Unlock("speed_runner");                  // fire-and-forget

UnlockResult r = await CoolAPI.Achievements.UnlockDetailed("speed_runner");
if (r.NewlyUnlocked) PlayFanfare();                        // first time only
// r.Queued == true → the device was offline; it will sync automatically

// Incremental: store progress and unlock at the target
await CoolAPI.Achievements.Progress("kill_100", kills, 100);

// Everything, with the player's unlock state
AchievementInfo[] all = await CoolAPI.Achievements.GetAll();
bool has = CoolAPI.Achievements.IsUnlocked("speed_runner");

// React to unlocks (your own banner / sound)
CoolAPI.Achievements.Unlocked += a => Debug.Log($"{a.name} +{a.points}");
```

Unlocks are idempotent — call them as often as you like.

## Stats

```csharp
CoolGameXManager.AddStat("enemies_killed");               // +1, fire-and-forget
int total = await CoolAPI.Stats.Add("enemies_killed", 5); // returns the new total
await CoolAPI.Stats.Set("deepest_floor", 12);
int kills = await CoolAPI.Stats.Get("enemies_killed");
StatEntry[] all = await CoolAPI.Stats.GetAll();
```

Stats need no setup — a stat exists the first time you set it.

## Leaderboards

```csharp
SubmitResult r = await CoolAPI.Leaderboards.Submit("high_score", 9001);
if (r.Improved) ShowNewBest(r.Rank);

// Lower is better (speedruns):
await CoolAPI.Leaderboards.Submit("fastest_run", 83_250, SortOrder.Ascending);

LeaderboardPage top = await CoolAPI.Leaderboards.GetTop("high_score", 10);
foreach (var e in top.entries) Debug.Log($"#{e.rank} {e.username} {e.score}");

LeaderboardPage mine = await CoolAPI.Leaderboards.GetAroundMe("high_score", range: 3);
```

Boards are created on the first submit. By default only a player's best score is kept.

## Cloud saves

```csharp
[System.Serializable] class SaveData { public int level; public float health; }

await CoolAPI.CloudSave.Save("slot1", new SaveData { level = 7, health = 80 });
SaveData data = await CoolAPI.CloudSave.Load<SaveData>("slot1");   // null/default if empty
SaveSlot[] slots = await CoolAPI.CloudSave.List();
await CoolAPI.CloudSave.Delete("slot1");
```

Up to 50 slots of 512 KB per player per game. `Save` returns `false` on failure so you
can keep a local copy and retry.

## DLC & ownership

```csharp
if (await CoolAPI.DLC.Owns(Ids.DLC.HauntedExpansion)) UnlockExpansion();
string[] owned = await CoolAPI.DLC.ListOwned();

bool fullGame = await CoolAPI.Ownership.OwnsGame();       // demo vs. full content
```

## Player

```csharp
PlayerProfile me = await CoolAPI.Player.GetProfile();
welcomeText.text = $"Welcome back, {CoolAPI.Player.Name}!";
CoolAPI.SignedIn += p => Debug.Log($"Hi {p.username}");
```

## Playtime

Nothing to do. By default (*Auto*) the launcher times games it starts, and the SDK
tracks playtime only when the game runs on its own — so minutes are never counted
twice. Change it in the Setup window (*Playtime: Auto / Always / Never*).

---

## Reliability

- **Offline queue** — unlocks, stat changes and playtime made offline are saved on the
  device and synced automatically when the connection returns.
- **Retries** — idempotent requests retry automatically on network hiccups.
- **Secure sessions** — games started from the launcher get a token scoped to that one
  game (it can't touch the player's account). It lasts 12 hours and the SDK refreshes it
  automatically.
- **Status** — `CoolAPI.IsConnected`, `CoolAPI.IsAuthenticated`, `CoolAPI.LastError`,
  `CoolAPI.ConnectionChanged`, and `CoolAPI.StatusLine()`.

## Debugging

Press **F8** in Play Mode (or a development build) for the **debug overlay**: connection,
session expiry, queued changes, one-click test unlocks for every achievement and the
recent SDK log. Set `Log level` in the Setup window for more console output.

---

## Manual control (optional)

Turn off *Auto-start* in the Setup window and start the SDK yourself:

```csharp
bool signedIn = await CoolAPI.InitializeAsync();          // uses the settings asset
```

Or add the **Cool GameX Manager** component to your first scene — its fields override the
project settings. The 1.x API (`CoolAPI.Initialize(slug, url)`,
`Auth.LoginWithToken`, `Playtime.StartSession/EndSession`) is still supported.

## Installing without the .unitypackage

- **Zip** — unzip `CoolGameX-SDK.zip` and drop the `CoolGameX` folder into `Assets/`.
- **Package Manager** — *Window › Package Manager › + › Add package from disk…* and pick
  this folder's `package.json`.

---

## API reference

| `CoolAPI` | |
| --- | --- |
| `InitializeAsync(settings?)` | Configure, connect and sign in. Returns true when signed in. |
| `Configure(settings?)` / `ConnectAsync(token?)` | The two halves of `InitializeAsync`. |
| `IsInitialized`, `IsConnected`, `IsAuthenticated`, `LaunchedFromLauncher` | State. |
| `ConnectionChanged`, `SignedIn` | Events. |
| `LogLevel`, `LastError`, `LastStatus`, `StatusLine()` | Diagnostics. |

| `CoolAPI.Achievements` | |
| --- | --- |
| `Unlock(slug)` / `UnlockDetailed(slug)` | Unlock (idempotent; queued offline). |
| `Progress(slug, current, target)` / `GetProgress(slug)` | Incremental achievements. |
| `GetAll(refresh?)`, `IsUnlocked(slug)`, `Find(slug)` | Definitions + unlock state. |
| `Unlocked`, `OnAchievementUnlocked` | First-unlock events. |

| `CoolAPI.Stats` | `Set`, `Add`, `Get`, `GetAll`, `GetAllJson` |
| --- | --- |
| **`CoolAPI.Leaderboards`** | `Submit`, `GetTop`, `GetAroundMe` |
| **`CoolAPI.CloudSave`** | `Save`, `Save<T>`, `Load`, `Load<T>`, `Delete`, `List` |
| **`CoolAPI.DLC`** | `Owns`, `ListOwned`, `ClearCache` |
| **`CoolAPI.Ownership`** | `OwnsGame(refresh?)` |
| **`CoolAPI.Player`** | `GetProfile`, `Current`, `Name` |
| **`CoolAPI.Auth`** | `LoginWithToken`, `Refresh`, `RefreshIfNeeded`, `Logout`, `IsGameToken`, `ExpiresAtUtc` |
| **`CoolAPI.Queue`** | `PendingCount`, `Flush`, `Clear`, `Changed` |
| **`CoolGameXManager`** (static) | `Unlock`, `UnlockAsync`, `SetStat`, `AddStat`, `SubmitScore`, `AchievementUnlocked`, `Ready` |

## Backend endpoints

| Method | Path | Auth |
| --- | --- | --- |
| `POST` | `/api/achievements/unlock` | session |
| `POST` | `/api/stats/set` · `/api/stats/add` | session |
| `GET` | `/api/stats` | session |
| `POST` | `/api/playtime/update` | session |
| `POST` | `/api/leaderboards/submit` | session |
| `GET` | `/api/leaderboards/top` · `/api/leaderboards/me` | none · session |
| `GET` `PUT` `DELETE` | `/api/cloudsave` (+ `/list`) | session |
| `GET` | `/api/dlc/owned` | session |
| `GET` | `/api/sdk/me` · `/ownership` · `/achievements` · `/catalog` | session |
| `POST` | `/api/sdk/refresh` · `/api/sdk/editor-login` | session · none |

## License

MIT
