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
- RocketRP: AES key and binary format research
- tracker.gg: player stats API
- Rocket League Stats API: Psyonix/Epic
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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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.