Skip to main content

pybfbc2stats

ci License Package Last commit

Python 🐍 library for retrieving statistics of Battlefield: Bad Company 2 players. Possible thanks to previous work by Luigi Auriemma and nemo.

Features

  • lookup players/personas by id or name
  • search for players/personas by name (with wildcard support)
  • retrieve all available player statistics
  • retrieve player dogtag records
  • retrieve player leaderboards
  • retrieve server list
  • retrieve individual game server's details for (including player list)
  • support for all platforms (PC, Xbox 360, PS3)
  • full support for async Python

Installation

Simply install the package via pip.

$ pip install pybfbc2stats

Usage

TLS 1.0

The FESL backend only supports TSL 1.0. So, you can only use this library in an environment that allows Python to use TLS 1.0. The easiest and least intrusive way to enable TLS 1.0 support is to set an OPENSSL_CONF environment variable that contains the absolute path to the included openssl.cnf. On Linux, you can set it by running this in the project directory:

export OPENSSL_CONF=$(realpath openssl.cnf)

Basic example

The following examples show how to find a player/persona by name and retrieve their stats using the default as well as the async client.

Retrieve stats using the default FESL client

from urllib.parse import quote

from pybfbc2stats import FeslClient, Platform, Namespace


def main():
    with FeslClient('ea_account_name', 'ea_account_password', Platform.pc) as client:
        quoted_name = quote('Krut0r')
        persona = client.lookup_username(quoted_name, Namespace.pc)
        stats = client.get_stats(int(persona['userId']))
        print(stats)


if __name__ == '__main__':
    main()

Retrieve stats using the async FESL client

import asyncio
from urllib.parse import quote

from pybfbc2stats import AsyncFeslClient, Platform, Namespace


async def main():
    async with AsyncFeslClient('ea_account_name', 'ea_account_password', Platform.pc) as client:
        quoted_name = quote('Krut0r')
        persona = await client.lookup_username(quoted_name, Namespace.pc)
        stats = await client.get_stats(int(persona['userId']))
        print(stats)


if __name__ == '__main__':
    asyncio.run(main())

Retrieve the server list using the default theater client

from pybfbc2stats import FeslClient, TheaterClient, Platform

def main():
    # First, get theater details and login key (lkey) from FESL
    with FeslClient('ea_account_name', 'ea_account_password', Platform.ps3) as feslClient:
        theater_hostname, theater_port = feslClient.get_theater_details()
        lkey = feslClient.get_lkey()
    
    # Now use the theater client to get the server list
    with TheaterClient(theater_hostname, theater_port, lkey, Platform.ps3) as theaterClient:
        lobbies = theaterClient.get_lobbies()
        servers = []
        for lobby in lobbies:
            lobby_servers = theaterClient.get_servers(int(lobby['LID']))
            servers.extend(lobby_servers)
        print(servers)

if __name__ == '__main__':
    main()

Retrieve the server list using the async theater client

import asyncio
from pybfbc2stats import AsyncFeslClient, AsyncTheaterClient, Platform

async def main():
    # First, get theater details and login key (lkey) from FESL
    async with AsyncFeslClient('ea_account_name', 'ea_account_password', Platform.ps3) as feslClient:
        theater_hostname, theater_port = await feslClient.get_theater_details()
        lkey = await feslClient.get_lkey()
    
    # Now use the theater client to get the server list
    async with AsyncTheaterClient(theater_hostname, theater_port, lkey, Platform.ps3) as theaterClient:
        lobbies = await theaterClient.get_lobbies()
        servers = []
        for lobby in lobbies:
            lobby_servers = await theaterClient.get_servers(int(lobby['LID']))
            servers.extend(lobby_servers)
        print(servers)

if __name__ == '__main__':
    asyncio.run(main())

Client methods

Both the default and the async clients offer the same methods with the same signatures.

Once connected, the clients read from the connection in the background (a thread for the default clients, a task for the async clients) and automatically respond to the backend's ping/memcheck packets. A client therefore stays connected while idle. Clients may also be used for multiple requests in parallel (from multiple threads/tasks), requests are matched to their responses using transaction ids. If the connection is lost, pending requests fail with a ConnectionError. Subsequent requests will automatically reconnect the client and re-authenticate it.

Advanced usage: if you replace a client's connection (e.g. to use a custom host), do so before making the first request. The connection must not be changed once the client has started reading from it.

[Async]FeslClient(username, password, platform, timeout)

Create a new [Async]FeslClient instance.

Note: The account has to be valid for Bad Company 2. If your account does not work, you can create a new one using ealist: .\ealist.exe -A -a [username] [password] bfbc2-pc (the created account will work for all platforms).

Arguments

Argument Type Opt/Required Note
username str Required
password str Required
platform Platform Required One of: Platform.pc, Platform.ps3 (Xbox 360 is not yet supported)
timeout float Optional How long to wait for data before raising a timeout exception (timeout is applied per socket operation, meaning the timeout is applied to each read from/write to the underlying connection to the FESL backend)

[Async]FeslClient.hello()

Send the initial "hello" packet to the FESL server.


[Async]FeslClient.memcheck()

Send the response to the FESL server's "memcheck" challenge.


[Async]FeslClient.login()

Send the login details to the FESL server.


[Async]FeslClient.get_lkey()

Get the login key (lkey) used to authenticate on theater backend.


[Async]FeslClient.get_theater_details()

Get the hostname and port of the theater backend for the client's platform.


[Async]FeslClient.lookup_usernames(usernames, namespace)

Lookup a list of url encoded/quoted usernames and return any matching personas (only exact name matches are returned).

Note: Since this method accepts a namespace argument, it can lookup usernames in any namespace (on any platform), regardless of which Platform was used to create the client instance.

Arguments

Argument Type Opt/Required Note
usernames List[str] Required List of url encoded/quoted usernames
namespace Namespace Required One of: Namespace.pc, Namespace.ps3, Namespace.xbox360

Example

from urllib.parse import quote

from pybfbc2stats import FeslClient, Platform, Namespace

client = FeslClient('ea_account_name', 'ea_account_password', Platform.pc)
names = ['SickLittleMonkey', '[SuX] DeLuXe']
quoted = [quote(name) for name in names]
persona = client.lookup_usernames(quoted, Namespace.pc)

[Async]FeslClient.lookup_username(username, namespace)

Lookup a single url encoded/quoted username and return any matching persona (only exact name matches are returned).

Note: Since this method accepts a namespace argument, it can lookup usernames in any namespace (on any platform), regardless of which Platform was used to create the client instance.

Arguments

Argument Type Opt/Required Note
username str Required Url encoded/quoted username
namespace Namespace Required One of: Namespace.pc, Namespace.ps3, Namespace.xbox360

Example

from urllib.parse import quote

from pybfbc2stats import FeslClient, Platform, Namespace

client = FeslClient('ea_account_name', 'ea_account_password', Platform.ps3)
persona = client.lookup_username(quote('Major Brainhurt'), Namespace.pc)

[Async]FeslClient.lookup_user_ids(user_ids, namespace)

Lookup a list of user ids and return any matching personas.

Note: Since this method accepts a namespace argument, it can lookup user ids in any namespace (on any platform), regardless of which Platform was used to create the client instance.

Arguments

Argument Type Opt/Required Note
user_ids List[int] Required
namespace Namespace Required One of: Namespace.pc, Namespace.ps3, Namespace.xbox360

Example

from pybfbc2stats import FeslClient, Platform, Namespace

client = FeslClient('ea_account_name', 'ea_account_password', Platform.pc)
persona = client.lookup_user_ids([232302860, 233866102], Namespace.xbox360)

[Async]FeslClient.lookup_user_id(user_id, namespace)

Lookup a single user id and return any matching persona.

Note: Since this method accepts a namespace argument, it can lookup user ids in any namespace (on any platform), regardless of which Platform was used to create the client instance.

Arguments

Argument Type Opt/Required Note
user_id int Required
namespace Namespace Required One of: Namespace.pc, Namespace.ps3, Namespace.xbox360

Example

from pybfbc2stats import FeslClient, Platform, Namespace

client = FeslClient('ea_account_name', 'ea_account_password', Platform.pc)
persona = client.lookup_user_id(232302860, Namespace.xbox360)

[Async]FeslClient.search_name(screen_name, namespace)

Find personas given a url encoded/quoted (partial) name. You can use * as a trailing wildcard.

Note: The FESL backend returns an error both if a) no matching results were found and b) too many matching results were found. So, be careful with wildcard characters in combination with short partial names.

Arguments

Argument Type Opt/Required Note
screen_name str Required Url encoded/quoted (partial) name
namespace Namespace Required One of: Namespace.pc, Namespace.ps3, Namespace.xbox360

Example

from urllib.parse import quote

from pybfbc2stats import FeslClient, Platform

client = FeslClient('ea_account_name', 'ea_account_password', Platform.pc)
results = client.search_name(quote('[=BL=] larryp11'))

[Async]FeslClient.get_stats(userid, keys)

Retrieve a given list of stats attributes for a given player id on the client instance's platform.

Arguments

Argument Type Opt/Required Note
userid int Required
keys List[bytes] Optional By default, all available attributes are retrieved (see STATS_KEYS constant for details)

Example

from pybfbc2stats import FeslClient, Platform

client = FeslClient('ea_account_name', 'ea_account_password', Platform.ps3)
stats = client.get_stats(223789857, [b'accuracy', b'kills', b'deaths', b'score', b'time'])

[Async]FeslClient.get_leaderboard(min_rank, max_rank, sort_by, keys)

Retrieve a given range of players on the leaderboard with the given list of stats, sorted by a given key, on the client instance's platform.

Note: There does not seem to be a hard limit to either the rank rage size nor the number of stats keys that can be retrieved for each player. You will, however, need to increase your client-wide timeout if you are planning to retrieve large chunks of the leaderboard or lots of stats attributes.

Arguments

Argument Type Opt/Required Note
min_rank int Optional Minimum placement/rank on the leaderboard (1-250000)
max_rank int Optional Maximum placement/rank on the leaderboard (1-250001)
sort_by bytes Optional Key to sort leaderboard by, must be one of deaths, elo, kills, rank, score, time, veteran
keys List[bytes] Optional By default, only deaths, kills, score and time are retrieved (see STATS_KEYS constant for additional available keys)

Example

from pybfbc2stats import FeslClient, Platform

client = FeslClient('ea_account_name', 'ea_account_password', Platform.pc)
leaderboard = client.get_leaderboard(1, 50, b'time')

[Async]FeslClient.get_dogtags(userid)

Retrieve a list of players the given player id has taken dogtags from.

Note: The FESL backend returns an error both if a) the given user id does not exist and b) the user has not yet taken any dogtags.

Arguments

Argument Type Opt/Required
userid int Required

Example

from pybfbc2stats import FeslClient, Platform

client = FeslClient('ea_account_name', 'ea_account_password', Platform.ps3)
dogtags = client.get_dogtags(223789857)

[Async]TheaterClient(host, port, lkey, platform, timeout)

Create a new [Async]TheaterClient instance.

Arguments

Argument Type Opt/Required Note
host str Required IP/hostname of the theater backend for the platform (can be retrieved via FESL)
port int Required Port of the theater backend for the platform (can be retrieved via FESL)
lkey str Required Login key (lkey) (retrieved via FESL)
platform Platform Required One of: Platform.pc, Platform.ps3 (Xbox 360 is not yet supported)
timeout float Optional How long to wait for data before raising a timeout exception (timeout is applied per socket operation, meaning the timeout is applied to each read from/write to the underlying connection to the FESL backend)

[Async]TheaterClient.connect()

Initialize the connection to the Theater backend by sending the initial CONN/hello packet.


[Async]TheaterClient.authenticate()

Authenticate against/log into the Theater backend using the lkey retrieved via FESL.


[Async]TheaterClient.get_lobbies()

Retrieve all available game (server) lobbies.

Example

from pybfbc2stats import TheaterClient, Platform

client = TheaterClient('bfbc2-ps3-server.theater.ea.com', 18336, 'your_lkey', Platform.ps3)
lobbies = client.get_lobbies()

[Async]TheaterClient.get_servers(lobby_id)

Retrieve all available game servers from the given lobby.

Arguments

Argument Type Opt/Required Note
lobby_id int Required Id of the game server lobby

Example

from pybfbc2stats import TheaterClient, Platform

client = TheaterClient('bfbc2-ps3-server.theater.ea.com', 18336, 'your_lkey', Platform.ps3)
servers = client.get_servers(257)

[Async]TheaterClient.get_server_details(lobby_id, game_id)

Retrieve full details and player list for a given server.

Arguments

Argument Type Opt/Required Note
lobby_id int Required Id of the game server lobby the server is hosted in
game_id int Required Game (server) id

Example

from pybfbc2stats import TheaterClient, Platform

client = TheaterClient('bfbc2-ps3-server.theater.ea.com', 18336, 'your_lkey', Platform.ps3)
general, detailed, players = client.get_server_details(257, 120018)

[Async]TheaterClient.get_current_server(user_id)

Retrieve full details and player list for a given user's current server (server they are currently playing on, raises a PlayerNotFound exception if the player is not currently playing online).

Arguments

Argument Type Opt/Required Note
user_id int Required Id of the user whose current server to get

Example

from pybfbc2stats import TheaterClient, Platform

client = TheaterClient('bfbc2-ps3-server.theater.ea.com', 18336, 'your_lkey', Platform.ps3)
general, detailed, players = client.get_current_server(227528903)

Metadata

Release files for pybfbc2stats 0.9.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pybfbc2stats 0.9.1
File Size Uploaded
pybfbc2stats-0.9.1.tar.gz 63.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pybfbc2stats 0.9.1
File Interpreter ABI Platform
pybfbc2stats-0.9.1-py3-none-any.whl Python 3 none any Details

Total release size: 113.8 kB

Release files / pybfbc2stats-0.9.1.tar.gz

Download URL pybfbc2stats-0.9.1.tar.gz
Size 63.1 kB
Tags Source
SHA-256 checksum
How to use checksums
ea1be9176958e2841e12d901b8260915014a41a9a00401638bc15c6eb5ff98de
BLAKE2b-256 checksum
How to use checksums
9f6bcb87c5d8f8711e1dcd2b3f0d1fb1c35ec2c9ab0564e48b01f6d245bcabdf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / pybfbc2stats-0.9.1-py3-none-any.whl

Download URL pybfbc2stats-0.9.1-py3-none-any.whl
Size 50.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
37dc7c14ea1d7a822c1bc470333945b7855da633bba6bb41710da26202997a5f
BLAKE2b-256 checksum
How to use checksums
6457caa9c7a886ff4978d5812d2410ff2140f6eab2c22456a3d6f0472bfdc344
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

0.9.1 This release

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.4

2 release files

0.6.3

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.15

2 release files

0.3.12

2 release files

0.3.11

2 release files

0.3.10

2 release files

0.3.9

2 release files

0.3.8

2 release files

0.3.7

2 release files

0.3.6

2 release files

0.3.5

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.8

2 release files

0.2.7

2 release files

0.2.6

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page