Skip to main content

Nixwrap

Unified Python SDK for Rocket League: save file data extraction, Stats API WebSocket client, tracker.gg stats, process detection, keyboard/controller input, RL log filem and a reusable GUI/overlay toolkit.

Built because EAC made traditional plugin development for online matches harder. Intended for streamers, tool developers, and anyone who wants RL data in Python.

Installation

From github

pip install git+https://github.com/nixvio64/nixwrap

# With optional extras:
pip install "nixwrap[tracker] @ git+https://github.com/nixvio64/nixwrap"   # tracker.gg
pip install "nixwrap[gui] @ git+https://github.com/nixvio64/nixwrap"       # overlay / GUI
pip install "nixwrap[input] @ git+https://github.com/nixvio64/nixwrap"     # keyboard + controller
pip install "nixwrap[all] @ git+https://github.com/nixvio64/nixwrap"      # everything

From PyPi

pip install nixwrap-rl

# With optional extras:
pip install nixwrap-rl[tracker]   # tracker.gg
pip install nixwrap-rl[gui]       # overlay / GUI
pip install nixwrap-rl[input]     # keyboard + controller
pip install nixwrap-rl[all]       # everything

Modules

Module Purpose Requires
nixwrap.save_file Decrypt + extract data from .save files pycryptodome
nixwrap.process Detect running RL (Steam vs Epic) psutil
nixwrap.stats Live Stats API WebSocket events -
nixwrap.tracker tracker.gg player rank/stats curl-cffi (optional)
nixwrap.input Keyboard, Xbox, DualSense hotkey detection dualsense-controller, keyboard (optional)
nixwrap.gui Overlay/GUI toolkit PySide6 (optional)
nixwrap.log Launch.log parser (session + match info) -
nixwrap.utils Constants, rank helpers, PsyNet config API -

Quick Start

0. Edit your DefaultStatsAPI.ini

Found at <RL Install Dir>\TAGame\Config\DefaultStatsAPI.ini set PacketSendRate value to something like 20 (1-120).

1. Save File Data Extraction

from nixwrap.save_file import load, find_save_file

# Auto-find the active save file
path = find_save_file()
save = load(path)

# Camera
print(save.camera.fov)           # 110.0
print(save.camera.stiffness)     # 0.55

# Controls (keyboard + gamepad)
print(save.controls.jump)                    # "SpaceBar"
print(save.gamepad_bindings.boost)           # "XboxTypeS_RightShoulder"

# Raw bindings for custom inspection
for b in save.gamepad_bindings.raw_bindings:
    print(f"  {b['Action']} -> {b['Key']}")

# Stats
print(save.stats.wins)           # 1234
print(save.xp.level)             # 27
print(save.xp.total_xp)          # 348223

# Skill data per playlist
for pid, skill in save.skills.items():
    print(f"Playlist {pid}: matches={skill.matches_played}, tier={skill.tier}")

# Inventory
for item in save.inventory:
    print(f"Product {item.product_id} (series {item.series_id})")

# Settings
print(save.video.res_width, save.video.res_height)
print(save.gameplay.controller_deadzone)
print(save.sound.master_volume)

# Quick chats, loadout sets, season, achievements, etc.
print(len(save.quick_chats), "quick chat bindings")
print(len(save.loadout_sets), "loadout presets")

raw = load_raw("path/to/savefile.save")
# raw["header"], raw["properties"], raw["objects"]

2. Process Detection

from nixwrap.process import find_rocket_league, is_rocket_league_focused

rl = find_rocket_league()
if rl:
    print(f"PID: {rl.pid}")
    print(f"Platform: {rl.platform.value}")      # steam or epic
    print(f"Root dir: {rl.root_dir}")
    print(f"Save path: {rl.save_data_path}")

if is_rocket_league_focused():
    print("RL window has focus")

3. Input Detection (Keyboard + Controller)

from nixwrap.input import (
    is_hotkey_pressed, is_key_pressed,
    get_xinput_state, XINPUT_A,
    setup_dualsense, start_dualsense_monitor,
    get_button_display,
)

# Keyboard
if is_key_pressed("tab"):
    print("Tab is held")

# XInput (Xbox / DS4 via DS4Windows)
state = get_xinput_state()
if state and state.is_pressed(XINPUT_A):
    print("A button pressed")

# DualSense (PS5)
controller = setup_dualsense()       # auto-finds bundled hidapi.dll
start_dualsense_monitor()            # background auto-reconnect

# Unified hotkey check (InGameRank-compatible config format)
config = {"is_controller": True, "controller_type": "xinput",
          "controller_button": XINPUT_A}
if is_hotkey_pressed(config):
    print("Hotkey held!")

# Button display names
print(get_button_display("xinput", XINPUT_A))           # "A"
print(get_button_display("dualsense", 0))               # "Cross"

4. Stats API (Live Events)

from nixwrap.stats import StatsClient

client = StatsClient()

# Callback mode
client.on("GoalScored", lambda evt: print(f"GOAL! {evt.scorer.name}"))
client.on("StatfeedEvent", lambda evt: print(f"{evt.main_target.name}: {evt.stat_type}"))
client.on_event(lambda evt: print(f"[{evt.event_type}]"))

with client:
    import time
    time.sleep(300)  # listen for 5 minutes

Async mode:

import asyncio
from nixwrap.stats import StatsClient

async def main():
    client = StatsClient()
    client.start()
    async for event in client.events():
        print(event.event_type)
        if event.event_type == "GoalScored":
            print(f"  {event.scorer.name} scored!")

asyncio.run(main())

5. Tracker API (Player Ranks)

from nixwrap.tracker import TrackerClient

client = TrackerClient()  # random browser fingerprint per request
stats = client.fetch("Steam|123456789|0", "PlayerName")

if stats.error:
    print(f"Error: {stats.error}")
else:
    print(f"Last updated: {stats.last_updated}")
    print(f"Lifetime: {stats.lifetime.wins} wins, {stats.lifetime.goals} goals")
    for pid, rank in stats.ranks.items():
        print(f"  {rank.playlist_name}: {rank.tier_name} {rank.division_name} "
              f"({rank.mmr} MMR, #{rank.rank_percentile}%)")
        if rank.win_streak:
            direction = "^" if rank.win_streak_type == "win" else "v"
            print(f"    Streak: {direction}{rank.win_streak}")

Results are cached for 5 minutes (tracker.gg update interval).

6. GUI / Overlay

from PySide6.QtWidgets import QApplication
from nixwrap.gui import (
    create_overlay, WindowConfig, WindowType,
    Painter, Color, THEME_DARK, FadeAnimation,
)

class MyOverlay:
    def __init__(self):
        self.win = create_overlay(600, 200, corner_radius=12, opacity=0.95)
        self.win._paint = self._paint
        self.win.snap_to_bottom_center(margin=20)

    def _paint(self, painter_q):
        p = Painter(painter_q)
        p.fill_rect(0, 0, 600, 200,
                     THEME_DARK.background.with_alpha(216), radius=12)
        p.stroke_rect(0, 0, 599, 199,
                       THEME_DARK.divider, width=1, radius=12)
        p.draw_text("Hello Rocket League!", 20, 40,
                     color=THEME_DARK.text_primary)

app = QApplication([])
overlay = MyOverlay()
app.exec()

7. Launch.log Parser

from nixwrap.log import parse_log

info = parse_log()

print(info.session.username)
print(info.session.rich_presence)

if info.game:
    print(info.game.playlist_id)
    print(info.game.playlist_name)
    print(info.game.game_class)
    print(info.game.map_name)
    print(info.game.server_ip, info.game.server_port)
    print(info.game.region)

info = parse_log(verify=False)  # skip Stats API check, return whatever the log has

# verify=True (default) checks the Stats API before returning game data
# so you don't get stale playlist/map info from a previous session.

8. PsyNet Config API

from nixwrap.utils import (
    fetch_psynet_config, get_online_playlists,
    get_maps_for_playlist, get_special_events,
)

config = fetch_psynet_config(939334844)

playlists = get_online_playlists(config)
print(playlists[11])   # Doubles
print(playlists[27])   # Hoops

maps = get_maps_for_playlist(config, 11)
print(len(maps))        # number of maps in rotation

for e in get_special_events(config):
    print(e["Title"])

Optional Dependencies

Extra Packages What you get
[tracker] curl-cffi better tracker.gg support
[gui] PySide6 Overlay windows, painter, colors, fonts, animations
[input] dualsense-controller, keyboard, bundled hidapi.dll Xbox (XInput), PS5 DualSense, keyboard hotkey detection
[all] all of the above Everything

Requirements

  • Python >= 3.10
  • pycryptodome >= 3.18 (save file AES)
  • psutil >= 5.9 (process detection)

Acknowledgements

Contributing

Do you want to make this library even better? Contributions are highly encouraged and appreciated.

Metadata

Release files for nixwrap-rl 0.1.3

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

Source distribution (sdist)

Source distribution for nixwrap-rl 0.1.3
File Size Uploaded Provenance Actions
nixwrap_rl-0.1.3.tar.gz 149.5 kB Without provenance Details for nixwrap_rl-0.1.3.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for nixwrap-rl 0.1.3
File Interpreter ABI Platform Actions
nixwrap_rl-0.1.3-py3-none-any.whl Python 3 none any Details for nixwrap_rl-0.1.3-py3-none-any.whl

Total release size: 308.3 kB

Release files / nixwrap_rl-0.1.3.tar.gz

Download URL nixwrap_rl-0.1.3.tar.gz
Size 149.5 kB
Tags Source
SHA-256 checksum
How to use checksums
fe5dc5af62e514d03ce5fff337f8b9801228d8bf6f17f6fc860e7efcb6038daf
BLAKE2b-256 checksum
How to use checksums
7a007331f604c2850d59a8d6ffa2da4dc0ab5e7f3c272f7451eb494775f1568e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.9

Release files / nixwrap_rl-0.1.3-py3-none-any.whl

Download URL nixwrap_rl-0.1.3-py3-none-any.whl
Size 158.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
08aa69a2f9c40f7a7c088b516cd24ef84f0666441c9801ce8336ce40cf04f89d
BLAKE2b-256 checksum
How to use checksums
c87cf12e1e60576462d5cee3af487884031d860ec7e8d96c997dfb42252895f8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.9

Security for nixwrap-rl 0.1.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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