diff --git a/steam/client/__init__.py b/steam/client/__init__.py index 5e3a65a..378de39 100644 --- a/steam/client/__init__.py +++ b/steam/client/__init__.py @@ -22,6 +22,7 @@ class SteamClient(CMClient, BuiltinBase): See `gevent-eventmitter `_ for details on how to work with events. """ + _reconnect_backoff_c = 0 current_jobid = 0 credential_location = None #: location for sentry username = None #: username when logged on @@ -86,6 +87,7 @@ class SteamClient(CMClient, BuiltinBase): result = EResult(msg.body.eresult) if result == EResult.OK: + self._reconnect_backoff_c = 0 self.logged_on = True self.set_persona(EPersonaState.Online) self.emit("logged_on") @@ -134,6 +136,25 @@ class SteamClient(CMClient, BuiltinBase): self.send(resp) + def reconnect(self, maxdelay=30, retry=0): + """Implements explonential backoff delay before attempting to connect. + It is otherwise identical to calling :meth:`steam.core.cm.CMClient.connect`. + The delay is reset upon a successful login. + + :param maxdelay: maximum delay in seconds before connect (0-120s) + :type maxdelay: :class:`int` + :param retry: see :meth:`steam.core.cm.CMClient.connect` + :type retry: :class:`int` + :return: successful connection + :rtype: :class:`bool` + """ + delay_seconds = 2**self._reconnect_backoff_c - 1 + + if delay_seconds < maxdelay: + self._reconnect_backoff_c = min(7, self._reconnect_backoff_c + 1) + + return self.connect(delay=delay_seconds, retry=retry) + def send(self, message): """ Send a message to CM diff --git a/steam/core/cm.py b/steam/core/cm.py index 50a5e7f..5f1d64c 100644 --- a/steam/core/cm.py +++ b/steam/core/cm.py @@ -50,7 +50,6 @@ class CMClient(EventEmitter): _recv_loop = None _heartbeat_loop = None - _reconnect_backoff_c = 0 _LOG = None def __init__(self, protocol=0): @@ -72,11 +71,13 @@ class CMClient(EventEmitter): self._LOG.debug("Emit event: %s" % repr(event)) super(CMClient, self).emit(event, *args) - def connect(self, retry=None): + def connect(self, retry=None, delay=0): """Initiate connection to CM. Blocks until connected unless ``retry`` is specified. :param retry: number of retries before returning. Unlimited when set to ``None`` :type retry: :class:`int` + :param delay: delay in secnds before connection attempt + :type delay: :class:`int` :return: successful connection :rtype: :class:`bool` """ @@ -88,6 +89,10 @@ class CMClient(EventEmitter): return self._connecting = True + if delay: + self._LOG.debug("Delayed connect: %d seconds" % delay) + gevent.sleep(delay) + self._LOG.debug("Connect initiated.") for i, server_addr in enumerate(self.servers): @@ -113,7 +118,7 @@ class CMClient(EventEmitter): self._connecting = False return True - def disconnect(self, reconnect=False, nodelay=False): + def disconnect(self): """Close connection .. note:: @@ -145,20 +150,7 @@ class CMClient(EventEmitter): self._reset_attributes() - if reconnect: - if nodelay: - delay_seconds = 0 - self._reconnect_backoff_c = 0 - else: - delay_seconds = 2**self._reconnect_backoff_c - 1 - self._reconnect_backoff_c = min(5, self._reconnect_backoff_c + 1) - - self.emit('reconnect', delay_seconds) - - gevent.spawn_later(delay_seconds, self.connect) - else: - self._reconnect_backoff_c = 0 - self.emit('disconnected') + self.emit('disconnected') def _reset_attributes(self): for name in ['connected', @@ -286,14 +278,14 @@ class CMClient(EventEmitter): if result is None: self.servers.mark_bad(self.current_server_addr) - gevent.spawn(self.disconnect, True) + gevent.spawn(self.disconnect) return eresult = result[0].body.eresult if eresult != EResult.OK: self._LOG.error("Failed to secure channel: %s" % eresult) - gevent.spawn(self.disconnect, True) + gevent.spawn(self.disconnect) return self.channel_key = key @@ -345,10 +337,9 @@ class CMClient(EventEmitter): EResult.ServiceUnavailable ): self.servers.mark_bad(self.current_server_addr) - self.disconnect(True) + self.disconnect() elif result == EResult.OK: self._seen_logon = True - self._reconnect_backoff_c = 0 self._LOG.debug("Logon completed")