Browse Source

added pythonic api for access to steam leaderboads

pull/41/head
Rossen Georgiev 9 years ago
parent
commit
05102196c6
  1. 147
      steam/client/builtins/misc.py
  2. 21
      steam/enums/common.py

147
steam/client/builtins/misc.py

@ -2,10 +2,19 @@
Various features that don't have a category
"""
import logging
import sys
from eventemitter import EventEmitter
from steam.core.msg import MsgProto, get_um
from steam.enums import EResult, ELeaderboardDataRequest, ELeaderboardSortMethod, ELeaderboardDisplayType
from steam.enums.emsg import EMsg
from steam.util import WeakRefKeyDict
from steam.util.throttle import ConstantRateLimit
if sys.version_info < (3,):
_range = xrange
else:
_range = range
class Misc(object):
def __init__(self, *args, **kwargs):
@ -33,6 +42,31 @@ class Misc(object):
self.send(message)
def get_leaderboard(self, app_id, name):
"""Find a leaderboard
:param app_id: application id
:type app_id: :class:`int`
:param name: leaderboard name
:type name: :class:`str`
:return: leaderboard instance
:rtype: :class:`SteamLeaderboard`
:raises: :class:`LookupError` on message timeout or error
"""
message = MsgProto(EMsg.ClientLBSFindOrCreateLB)
message.header.routing_appid = app_id
message.body.app_id = app_id
message.body.leaderboard_name = name
message.body.create_if_not_found = False
resp = self.send_job_and_wait(message, timeout=15)
if not resp:
raise LookupError("Didn't receive response within 15seconds :(")
if resp.eresult != EResult.OK:
raise LookupError(EResult(resp.eresult))
return SteamLeaderboard(self, app_id, name, resp)
class SteamUnifiedMessages(EventEmitter):
"""Simple API for send/recv of unified messages
@ -126,3 +160,116 @@ class SteamUnifiedMessages(EventEmitter):
return None
else:
return resp[0]
class SteamLeaderboard(object):
"""Steam leaderboard object.
Generated via :meth:`Misc.get_leaderboard()`
Works more or less like a :class:`list` to access entries.
.. note::
Each slice will produce a message to steam.
Steam and protobufs might not like large slices.
Avoid accessing individual entries by index and instead use iteration or well sized slices.
Example usage:
.. code:: python
lb = client.get_leaderboard(...)
print len(lb)
for entry in lb[:100]: # top 100
pass
"""
app_id = 0
name = '' #: leaderboard name
id = 0 #: leaderboard id
entry_count = 0
sort_method = ELeaderboardSortMethod.NONE #: :class:`steam.enums.common.ELeaderboardSortMethod`
display_type = ELeaderboardDisplayType.NONE #: :class:`steam.enums.common.ELeaderboardDisplayType`
data_request = ELeaderboardDataRequest.Global #: :class:`steam.enums.common.ELeaderboardDataRequest`
def __init__(self, steam, app_id, name, data):
self._steam = steam
self.app_id = app_id
for field in data.DESCRIPTOR.fields:
if field.name.startswith('leaderboard_'):
self.__dict__[field.name.replace('leaderboard_', '')] = getattr(data, field.name)
self.sort_method = ELeaderboardSortMethod(self.sort_method)
self.display_type = ELeaderboardDisplayType(self.display_type)
def __repr__(self):
return "<%s(%d, %s, %d, %s, %s)>" % (
self.__class__.__name__,
self.app_id,
self.name,
len(self),
self.sort_method,
self.display_type,
)
def __len__(self):
return self.entry_count
def get_entries(self, start=0, end=0, data_request=ELeaderboardDataRequest.Global):
"""Get leaderboard entries.
:param start: start entry, not index (e.g. rank 1 is `start=1`)
:type start: :class:`int`
:param end: end entry, not index (e.g. only one entry then `start=1,end=1`)
:type end: :class:`int`
:param data_request: data being requested
:type data_request: :class:`steam.enums.common.ELeaderboardDataRequest`
:return: a list of entries, see `CMsgClientLBSGetLBEntriesResponse`
:rtype: :class:`list`
:raises: :class:`LookupError` on message timeout or error
"""
message = MsgProto(EMsg.ClientLBSGetLBEntries)
message.body.app_id = self.app_id
message.body.leaderboard_id = self.id
message.body.range_start = start
message.body.range_end = end
message.body.leaderboard_data_request = data_request
resp = self._steam.send_job_and_wait(message, timeout=15)
if not resp:
raise LookupError("Didn't receive response within 15seconds :(")
if resp.eresult != EResult.OK:
raise LookupError(EResult(resp.eresult))
return resp.entries
def __getitem__(self, x):
if isinstance(x, slice):
stop_max = len(self)
start = 0 if x.start is None else x.start if x.start >= 0 else max(0, x.start + stop_max)
stop = stop_max if x.stop is None else x.stop if x.stop >= 0 else max(0, x.stop + stop_max)
step = x.step or 1
if step < 0:
start, stop = stop, start
step = abs(step)
else:
start, stop, step = x, x + 1, 1
if start >= stop: return []
entries = self.get_entries(start+1, stop, self.data_request)
return [entries[i] for i in _range(0, len(entries), step)]
def __iter__(self):
def entry_generator():
with ConstantRateLimit(1, 1, use_gevent=True) as r:
for i in _range(0, len(self), 500):
entries = self[i:i+500]
if not entries:
raise StopIteration
for entry in entries:
yield entry
r.wait()
return entry_generator()

21
steam/enums/common.py

@ -346,6 +346,27 @@ class EClientPersonaStateFlag(SteamIntEnum):
ClanTag = 1024
Facebook = 2048
class ELeaderboardDataRequest(SteamIntEnum):
Global = 0
GlobalAroundUser = 1
Friends = 2
Users = 3
class ELeaderboardSortMethod(SteamIntEnum):
NONE = 0
Ascending = 1
Descending = 2
class ELeaderboardDisplayType(SteamIntEnum):
NONE = 0
Numeric = 1
TimeSeconds = 2
TimeMilliSeconds = 3
class ELeaderboardUploadScoreMethod(SteamIntEnum):
NONE = 0
KeepBest = 1
ForceUpdate = 2
# Do not remove
from sys import modules

Loading…
Cancel
Save