Last active
January 10, 2021 20:48
-
-
Save bitwes/7d705a2c01b7ef06767fd2f69433e1cd to your computer and use it in GitHub Desktop.
General approach for handling the Apple's StoreKit event queue with Godot
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ------------------------------------------------------------------------------ | |
# iOS app store has a queue of events. Whenever a method that would expect an | |
# event to show up in this queue the queue_monitor is (re)started and | |
# will run for _max_wait seconds. The monitor will call the corrosponding event | |
# handler for any events it finds while running. When _max_wait seconds have | |
# elapsed then statuses are checked to see if anything is waiting on a response. | |
# If any are found then the status is changed to ERROR. | |
# | |
# There is some room for improvement here, but it seems to be working pretty | |
# well. There's a lot of edge cases to worry about but I haven't thought of | |
# any that you couldn't get through in the app (possibly with some difficulty). | |
# | |
# _max_wait should be on the order of minutes | |
# | |
# The monitor is started in _ready to catch any purchases that have not been | |
# finished. | |
# ------------------------------------------------------------------------------ | |
var PRODUCT_INFO_STATUS = { | |
IDLE = "idle", | |
ERROR = "error", | |
QUERYING = "querying", | |
COMPLETE = "complete" | |
} | |
var _product_info_status = PRODUCT_INFO_STATUS.IDLE | |
var _queue_event_handlers = { | |
"product_info":"_on_product_info_event", | |
"restore":"_on_restore_event", | |
"purchase":"_on_purchase_event" | |
} | |
var _purch_singleton = Engine.get_singleton("InAppStore") | |
# How often to pull events from the queue | |
var _check_wait_time = 1 | |
# timer to kick off pulling events from queue | |
var _monitor_timer = Timer.new() | |
# Used to find when _max_wait has elapsed | |
var _monitor_start_time = 0 | |
# Amount of time to wait for an event before considering | |
# it to be timed out. | |
var _max_wait = 180 | |
func _ready(): | |
add_child(_monitor_timer) | |
_monitor_timer.one_shot = false | |
_monitor_timer.wait_time = _check_wait_time | |
_monitor_timer.connect("timeout", self, "_monitor_queue") | |
_start_monitor() | |
func _start_monitor(): | |
_monitor_start_time = OS.get_ticks_msec() | |
if(_monitor_timer.is_stopped()): | |
_monitor_timer.start() | |
func _monitor_queue(): | |
while(_purch_singleton.get_pending_event_count() > 0): | |
var event = _purch_singleton.pop_pending_event() | |
if(_is_event_valid(event)): | |
if(_queue_event_handlers.has(event.type)): | |
print("[", event.type, "] event found: ") | |
callv(_queue_event_handlers[event.type], [event]) | |
else: | |
print("unknown event type: ", event.type) | |
print(event) | |
if(OS.get_ticks_msec() - _monitor_start_time > _max_wait * 1000): | |
_monitor_timer.stop() | |
_timeout_pending_requests() | |
func _timeout_pending_requests(): | |
# Sets a bunch of status variables based on whether or not any actions | |
# were still pending when the timeout occurred. | |
if(_product_info_status == PRODUCT_INFO_STATUS.QUERYING): | |
_product_info_status = PRODUCT_INFO_STATUS.ERROR | |
print("timed out waiting for product_info response") | |
# maybe emit some signal | |
func _on_purchase_event(event): | |
# handle purchase event | |
func _on_restore_event(event): | |
# handle restore event | |
func _on_product_info_event(event): | |
# if everything checks out then... | |
_product_info_status = PRODUCT_INFO_STATUS.COMPLETE | |
func do_product_info(): | |
# kick off request with _purch_singleton and if | |
# everything checks out then... | |
_product_info_status = PRODUCT_INFO_STATUS.QUERYING | |
_start_monitor() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment