Краткий анонс новой библиотеки, позволяющей играм/приложениям, написанным на Python получать доступ к функциональности, предлагаемой платформой Steam.
Началось всё так: уезжая в отпуск я закинул себе на ноутбук новую версию Steam SDK. Просто так закинул, ради интереса: вдруг что-то в ней появилось. А закончилось тем, что сегодня я выложил на PyPI свежую разработку — steampak. Теперь обо всём по порядку.

Steam — это довольно известный сервис распространения цифрового содержимого (игры, программы, видео) от компании Valve. Компания Valve в девяностых делала его как платформу для распространения и обновления своих игр (Half-Life, Team Fortress, Counter-Strike), но с тех пор многое изменилось и на этой площадке публикуются тысячи продуктов других компаний и независимых разработчиков. Вы загружаете с сайта и ставите себе программу-клиент и через неё получаете доступ цифровому магазину, сообществам по интересам и вашей библиотеке приложений (игр).

Пару-тройку лет назад Valve сумела разглядеть в операционных системах отличных от Windows непаханое поле рынка и, одной из первых, нужно признать, начала направлять туда своих агрономов для подготовки почвы. Сегодня, судя по тому, какое количество игр (в том числе класса AAA) выходит с поддержкой Linux, а также учитывая то, что Valve начала выпускать машинки типа SteamBox, под управлением операционной системы, основанной на Debian, можно сказать, что поле начинает давать всходы.

На площадке Steam, кроме прочего, независимые разработчики и начинающие студии могут воспользоваться сервисом Greenlight — выставить свою игру (или прототип) на обозрение пользователям, и если она их заинтересует, то Valve предложит вам распространять её через Steam.

Для разработчиков игр Valve предлагает платформу Steamworks, через которую можно управлять своими приложениями в Steam. Кроме прочего, с сайта Steamworks можно загрузить архив с инструментарием для разработчика (SDK). Этот инструментарий включает в себя библиотеки для взаимодействия вашей игры с клиентом Steam под разные ОС, пример их использования в виде простенькой «игры» Spacewar и некоторые другие полезности.

Не секрет, что на Python можно писать игры (или использовать его для скриптования), интересные, удачные, достойно выглядящие. А вот бы ещё обучить такие игры взаимодействию со Steam. Среди полезностей, предоставляемых SDK, есть заголовочные .h файлы, описывающие сигнатуры предлагаемых Steam методов. И можно бы, воспользовавшись этой информацией, написать обвязку. Писать обвязки для библиотек, если вы помните, лучше при помощи ctypes, входящего в стандартную поставку Python. Одна загвоздка: Steam-клиент написан на C++, и если «обёртывание» обычных функций особого труда не составляет, то для обращения к атрибуту экземпляра класса ctypes инструментов не предоставляет (по большей части из за того, что ввиду отсутствия строго стандарта разные компиляторы C++ по разному располагают структуры в памяти). Загвоздку можно было бы обойти, путём написания собственной обёртки на C, которая сделала обращение к атрибутам объектов плоским, но это потребовалось бы поддерживать.

И тут в отпуске я обнаружил, что в Steam SDK, начиная с версии 1.32, выпущенной как раз ко дню моего рождения, сделали дополнительный плоский API в стиле C. Вот здесь я и не удержался.

Сразу оговорюсь, что это не перенос API как есть, а попытка переосмысления в парадигме Python. Прежде всего из-за того, что в оригинальном API, который нарабатывался годами, накопилось несколько, мягко говоря, спорных моментов.

Пример использования steampak:

    from steampak import SteamApi  # Это единая точка вход в API.

# Указываем путь до файла библиотеки.
LIBRARY_PATH = '/home/me/my_steam_game/libsteam_api.so'

# И идентификатор вашей игры, полученный в Steamworks.
# Можно не передавать его параметром, а просто положить
# файл 'steam_appid.txt' с этим идентификатором в директорию с игрой.
APP_ID = 480 # Это ID игры 'Spacewar', она идёт в SDK.

api = SteamApi(LIBRARY_PATH, app_id=APP_ID)

# Выведем имена друзей пользователя:
for user in api.friends():
print(user.name)

# Для интереса получим код страны:
print(api.utils.country_code)

# Поглядим, как у пользователя с достижениями по нашей игре:
for ach_name, ach in api.apps.current.achievements():
print('%s (%s): %s' % (ach.title, ach_name, ach.get_unlock_info()))

# И попробуем получить список игр (приложений) пользователя:
for app_id, app in api.apps.installed():
print('%s: %s' % (app_id, app.name))

# Под занавес не потушить API.
api.shutdown()

Если вам вдруг интересно, зачем я это делал, тем более в отпуске, то ответ состоит из двух частей: 1. разрабатывать на Python интересно везде; 2. я хочу, чтобы в Steam появлялись игры на Python. Только не надо больше «графических романов» в «японском» стиле, а то уже тошнит, правда.

На текущий момент steampak реализует далеко не всё, что умеет оригинальный API. Под большим вопросом стоит поддержка функциональности, использующей обратные вызовы — так Steam-клиент реализует асинхронную работу (опять же из-за того, что обработчиком обратных вызовов ожидается объект C++, а не обычная функция).

Если вам интересен проект, подключайтесь к разработке.

Да, вы можете проставить себе все достижения во всех ваших играх, но вы ведь не станете? %)

Категории

Язык
Стиль
Область
Интерпретатор
Платформа
ЯП
Проект

На заметку
У нас есть новостная группа в Telegram. Там же можно обсудить интересующие вопросы. Ссылка в самом низу страницы.