Skip to content

Commit 251d6b3

Browse files
committed
Better support for handling quit events for web and mobile games.
1 parent 889b539 commit 251d6b3

4 files changed

Lines changed: 52 additions & 23 deletions

File tree

README.md

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11

2+
23
# Quiver Analytics
34
[Quiver Analytics](https://quiver.dev/analytics/) allows you to collect analytics for games made with the [Godot engine](https://godotengine.org) in a privacy-friendly way. In just a few minutes, you can integrate Analytics in your game through this open source plugin and gain valuable insight into how players are interacting with your game. You also have fine-grained control over how your players' privacy is handled.
45

@@ -36,44 +37,48 @@ By default, player consent isn't required for anonymous data collection, but if
3637

3738
### Cleaning up before exiting the game
3839

39-
Before you quit the game, you have to tell the plugin to send a configurable "Quit game event" and flush any outstanding events. To do that, write this code anywhere you are exiting the game:
40+
Before you quit the game, you can tell the plugin to send a configurable "Quit game" event and flush any outstanding events. To do that, write this code anywhere you are exiting the game:
4041

4142
`await Analytics.handle_exit()`
4243

4344
Note that SceneTree.quit() will immediately exit the game so you want to make sure you wait for the above call to finish before calling the quit() function.
4445

46+
#### _Note for web and mobile games:_
47+
48+
If you are developing a web or mobile game, it might be difficult to call `handle_exit()` since you don't always have the option to run operations when a player backgrounds the app or closes the tab. To work around this, we have added some special logic to automatically add 'Quit game' events. Note that the timing of this event is an estimate and might be up to a minute off the actual quit time.
49+
4550
## Advanced Usage
4651
The following is not required, but you do have additional functionality if you need it.
4752

4853
### Managing consent
4954

5055
By default, the plugin won't ask for consent since no personally-identifying information is collected. However, you can enable opt-in data collection by going to Project Settings -> Quiver -> Analytics and set "Player Consent Required" to true. Now calls to `add_event()` will be ignored until you obtain consent. To manage consent, you can either use the built-in UI:
5156

52-
if Analytics.should_show_consent_dialog():
53-
Analytics.show_consent_dialog(parent_node)
57+
if Analytics.should_show_consent_dialog():
58+
Analytics.show_consent_dialog(parent_node)
5459

5560
This will decide whether consent has already been granted or denied and, if not, will spawn a consent dialog as a child of the parent_node.
5661

5762
If you'd like to use your own UI and manually handle consent management, you can use the following functions:
5863

59-
# Variable storing whether consent has been requested
60-
Analytics.consent_requested
61-
# Variable storing consent status
62-
Analytics.consent_granted
63-
# Function to call if consent was granted
64-
Analytics.approve_data_collection()
65-
# Function to call if consent was denied
66-
Analytics.deny_data_collection()
64+
# Variable storing whether consent has been requested
65+
Analytics.consent_requested
66+
# Variable storing consent status
67+
Analytics.consent_granted
68+
# Function to call if consent was granted
69+
Analytics.approve_data_collection()
70+
# Function to call if consent was denied
71+
Analytics.deny_data_collection()
6772

6873
### Customizing consent UI
6974
By default, the built-in consent UI will use whatever UI theme that has been set for your project. You can modify this by changing the properties of the ConsentDialog found in `/addons/quiver_analytics/consent_dialog.tscn`.
7075

7176
### Advanced Properties
7277
If you turn on Advanced Settings for Project Settings -> Quiver -> Analytics, you'll find the following properties:
7378

74-
Config File Path: where the config file is stored
75-
Auto Add Event on Launch: whether a "Launched game" event is sent automatically when the game starts
76-
Auto Add Event on Quit: whether a "Quit game" event is sent automatically after calling the `Analytics.handle_exit()` function.
79+
* "Config File Path": where the config file is stored
80+
* "Auto Add Event on Launch": whether a "Launched game" event is sent automatically when the game starts
81+
* "Auto Add Event on Quit": whether a "Quit game" event is sent automatically.
7782

7883
### Notes and Limitations
7984

addons/quiver_analytics/analytics.gd

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,18 @@ extends Node
1515
## Use this to pick a random player identifier
1616
const MAX_INT := 9223372036854775807
1717

18-
## The maximum rate of requests we can send to the server per minute.
19-
## If this limit is exceeded, requests will be dropped
20-
const MAX_REQUEST_RATE_PER_MINUTE := 50
18+
## The maximum rate we can add events to the queue.
19+
## If this limit is exceeded, requests will be dropped.
20+
const MAX_ADD_TO_EVENT_QUEUE_RATE := 50
2121

2222
## This controls the maximum size of the request queue that is saved to disk
23-
## in the situation the events weren't able to
24-
## In pathological cases, we may
23+
## in the situation the events weren't able to be successfully sent
24+
## In pathological cases, we may drop events if the queue grows too long.
2525
const MAX_QUEUE_SIZE_TO_SAVE_TO_DISK := 200
2626

2727
## The file to store queue events that weren't able to be sent due to network or server issues
2828
const QUEUE_FILE_NAME := "user://analytics_queue"
2929

30-
# XXX TODO CHANGEME
3130
## The server host
3231
const SERVER_PATH := "https://quiver.dev"
3332

@@ -37,6 +36,18 @@ const ADD_EVENT_PATH := "/analytics/events/add/"
3736
## Event names can't exceed this length
3837
const MAX_EVENT_NAME_LENGTH := 50
3938

39+
# The next two parameters guide how often we send artifical quit events.
40+
# We send these fake quit events because on certain platfomrms (mobile and web),
41+
# it can be hard to determine when a player ended the game (e.g. they background the app or close a tab).
42+
# So we just send periodic quit events with session IDs, which are reconciled by the server.
43+
44+
# We send a quit event this many seconds after launching the game.
45+
# We set this fairly low to handle immediate bounces from the game.
46+
const INITIAL_QUIT_EVENT_INTERVAL_SECONDS := 10
47+
48+
# This is the max interval between sending quit events
49+
const MAX_QUIT_EVENT_INTERVAL_SECONDS := 60
50+
4051
## Emitted when the sending the final events have been completed
4152
signal exit_handled
4253

@@ -61,10 +72,12 @@ var current_retry_time_seconds := min_retry_time_seconds
6172
var max_retry_time_seconds := 120.0
6273
var auto_add_event_on_launch := ProjectSettings.get_setting("quiver/analytics/auto_add_event_on_launch", true)
6374
var auto_add_event_on_quit := ProjectSettings.get_setting("quiver/analytics/auto_add_event_on_quit", true)
75+
var quit_event_interval_seconds := INITIAL_QUIT_EVENT_INTERVAL_SECONDS
76+
var session_id = abs(randi() << 32 | randi())
6477

6578
@onready var http_request := $HTTPRequest
6679
@onready var retry_timer := $RetryTimer
67-
80+
@onready var quit_event_timer := $QuitEventTimer
6881

6982
func _ready() -> void:
7083
# We attempt to load the saved configuration, if present
@@ -96,6 +109,8 @@ func _ready() -> void:
96109

97110
if auto_add_event_on_launch:
98111
add_event("Launched game")
112+
if auto_add_event_on_quit:
113+
quit_event_timer.start(quit_event_interval_seconds)
99114

100115
# if auto_add_event_on_quit:
101116
# get_tree().set_auto_accept_quit(false)
@@ -156,12 +171,13 @@ func add_event(name: String, properties: Dictionary = {}) -> void:
156171
requests_in_batch_count = 0
157172
else:
158173
requests_in_batch_count += 1
159-
if requests_in_batch_count > MAX_REQUEST_RATE_PER_MINUTE:
174+
if requests_in_batch_count > MAX_ADD_TO_EVENT_QUEUE_RATE:
160175
printerr("[Quiver Analytics] Event tracking was disabled temporarily because max event rate was exceeded.")
161176
return
162177

163178
# Auto-add the platform
164179
properties["$platform"] = OS.get_name()
180+
properties["$session_id"] = session_id
165181

166182
# Add the request to the queue and process the queue
167183
var request := {
@@ -273,6 +289,11 @@ func _on_retry_timer_timeout() -> void:
273289
_process_requests()
274290

275291

292+
func _on_quit_event_timer_timeout() -> void:
293+
add_event("Quit game")
294+
quit_event_interval_seconds = min(quit_event_interval_seconds + 10, MAX_QUIT_EVENT_INTERVAL_SECONDS)
295+
quit_event_timer.start(quit_event_interval_seconds)
296+
276297
#func _notification(what):
277298
# if what == NOTIFICATION_WM_CLOSE_REQUEST:
278299
# handle_exit()

addons/quiver_analytics/analytics.tscn

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,8 @@ timeout = 5.0
1212
[node name="RetryTimer" type="Timer" parent="."]
1313
one_shot = true
1414

15+
[node name="QuitEventTimer" type="Timer" parent="."]
16+
1517
[connection signal="request_completed" from="HTTPRequest" to="." method="_on_http_request_request_completed"]
1618
[connection signal="timeout" from="RetryTimer" to="." method="_on_retry_timer_timeout"]
19+
[connection signal="timeout" from="QuitEventTimer" to="." method="_on_quit_event_timer_timeout"]

addons/quiver_analytics/plugin.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
name="Quiver Analytics"
44
description="Get key insights into how players are interacting with your game while still respecting their privacy."
55
author="Quiver"
6-
version="0.1"
6+
version="0.2"
77
script="plugin.gd"

0 commit comments

Comments
 (0)