Skip to content

Instantly share code, notes, and snippets.

@bitwes
Last active January 10, 2021 20:48
Show Gist options
  • Save bitwes/7d705a2c01b7ef06767fd2f69433e1cd to your computer and use it in GitHub Desktop.
Save bitwes/7d705a2c01b7ef06767fd2f69433e1cd to your computer and use it in GitHub Desktop.
General approach for handling the Apple's StoreKit event queue with Godot
# ------------------------------------------------------------------------------
# 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