Game ideas & features

Your next feature

Player statistics

Track rounds, kills and best scores in one session batch, with sum, maximum, minimum or latest-value rules.

SDK developRequires a player session
Godot
Illustration of Player statistics
Your visible result

Your game reports one complete session batch and displays the current counters and periods.

Video coming later

This step does not have a video yet. The guide and MCP request are ready to use.

Player statistics

Prepare the same project

  1. Choose the same project in the dashboard as in your game configuration.

    Player statistics →
  2. Create or reuse these test entries in the dashboard form. These are example values, not current project data.

    Statistic definitions
    kills: mode=sum, reset=none; best_score: mode=max, reset=none

    Sum adds each report. Maximum and minimum keep the best value; latest replaces it. Maximum, minimum and latest initialize from the first report. Values are signed whole numbers within the safe-integer range.

  3. Save, reload and verify the entries before running the example. Preserve unrelated definitions.

Build this feature

You can preview the guide. Running it requires an active project key. Prepare project

SDK installed, project configuration imported and player signed in.

This SDK example uses develop. Check whether the required APIs exist in your installed SDK.

Complete the Quickstart connection with the intended player. Call run_reviewed_action() from an explicit button or scene action. Opening the file alone sends no request.

GDScript
res://scripts/statistics.gd
horizOn call
1extends Node
2​
3# Existing configured SDK and authenticated player session required.
4# Invoke run_reviewed_action explicitly after reviewing this example.
5# This explicit action reports the shown persisted batch.
6func run_reviewed_action() -> void:
7 if not Horizon.auth.isSignedIn():
8 return
9 var snapshot: Dictionary = await Horizon.statistics.getStatistics()
10 if snapshot.is_empty():
11 push_error(Horizon.statistics.getLastErrorCode())
12 return
13 var pending_path: String = "user://statistics_" + snapshot.userId + ".dat"
14 var pending: Dictionary
15 if FileAccess.file_exists(pending_path):
16 var file := FileAccess.open(pending_path, FileAccess.READ)
17 if file == null:
18 return
19 var saved: Variant = file.get_var()
20 file.close()
21 if not saved is Dictionary:
22 return
23 pending = saved
24 else:
25 pending = {"operationId": Horizon.statistics.createOperationId(),
26 "updates": [{"key": "kills", "value": 3}, {"key": "best_score", "value": 250}]}
27 var file := FileAccess.open(pending_path, FileAccess.WRITE)
28 if file == null:
29 return
30 file.store_var(pending, false) # Variant storage preserves int64 values.
31 file.flush()
32 var saved_ok: bool = file.get_error() == OK
33 file.close()
34 if not saved_ok:
35 return
36 var original_bytes: PackedByteArray = FileAccess.get_file_as_bytes(pending_path)
37 if original_bytes.is_empty():
38 return
39 var result: Dictionary = await Horizon.statistics.updateStatistics(pending.updates, pending.operationId)
40 if result.is_empty():
41 # Keep the file and original ID, including after timeout or OPERATION_EXPIRED.
42 push_error(Horizon.statistics.getLastErrorCode())
43 return
44 # A delayed O1 success cannot remove another action's O2 original.
45 if FileAccess.get_file_as_bytes(pending_path) == original_bytes:
46 DirAccess.remove_absolute(ProjectSettings.globalize_path(pending_path))
47 print(result.values)
Open the complete SDK sample ↗

Test your new feature.

Your game reports one complete session batch and displays the current counters and periods.

Choose the same project in the dashboard as in your game configuration.

Endpoints
  • GET/statistics
  • GET/statistics/definitions
  • POST/statistics
All endpoints in the API reference
Our suggestion for your next step

Give every round a high score.

Save the score after a round and show who's ahead. Keep using your connection and player session.