nixwrap-rl 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. nixwrap_rl-0.1.0/LICENSE +21 -0
  2. nixwrap_rl-0.1.0/PKG-INFO +277 -0
  3. nixwrap_rl-0.1.0/README.md +246 -0
  4. nixwrap_rl-0.1.0/pyproject.toml +52 -0
  5. nixwrap_rl-0.1.0/setup.cfg +4 -0
  6. nixwrap_rl-0.1.0/src/nixwrap/__init__.py +3 -0
  7. nixwrap_rl-0.1.0/src/nixwrap/_version.py +3 -0
  8. nixwrap_rl-0.1.0/src/nixwrap/gui/__init__.py +77 -0
  9. nixwrap_rl-0.1.0/src/nixwrap/gui/_animations.py +257 -0
  10. nixwrap_rl-0.1.0/src/nixwrap/gui/_app.py +152 -0
  11. nixwrap_rl-0.1.0/src/nixwrap/gui/_colors.py +151 -0
  12. nixwrap_rl-0.1.0/src/nixwrap/gui/_geometry.py +171 -0
  13. nixwrap_rl-0.1.0/src/nixwrap/gui/_images.py +94 -0
  14. nixwrap_rl-0.1.0/src/nixwrap/gui/_painter.py +204 -0
  15. nixwrap_rl-0.1.0/src/nixwrap/gui/_text.py +76 -0
  16. nixwrap_rl-0.1.0/src/nixwrap/gui/_themes.py +40 -0
  17. nixwrap_rl-0.1.0/src/nixwrap/gui/_window.py +308 -0
  18. nixwrap_rl-0.1.0/src/nixwrap/input/__init__.py +51 -0
  19. nixwrap_rl-0.1.0/src/nixwrap/input/_bindings.py +88 -0
  20. nixwrap_rl-0.1.0/src/nixwrap/input/_dualsense.py +220 -0
  21. nixwrap_rl-0.1.0/src/nixwrap/input/_keyboard.py +72 -0
  22. nixwrap_rl-0.1.0/src/nixwrap/input/_xinput.py +116 -0
  23. nixwrap_rl-0.1.0/src/nixwrap/input/hidapi.dll +0 -0
  24. nixwrap_rl-0.1.0/src/nixwrap/process/__init__.py +25 -0
  25. nixwrap_rl-0.1.0/src/nixwrap/process/_detector.py +150 -0
  26. nixwrap_rl-0.1.0/src/nixwrap/process/_window.py +89 -0
  27. nixwrap_rl-0.1.0/src/nixwrap/py.typed +0 -0
  28. nixwrap_rl-0.1.0/src/nixwrap/save_file/__init__.py +207 -0
  29. nixwrap_rl-0.1.0/src/nixwrap/save_file/_accessors.py +767 -0
  30. nixwrap_rl-0.1.0/src/nixwrap/save_file/_binary_parser.py +228 -0
  31. nixwrap_rl-0.1.0/src/nixwrap/save_file/_binary_serializer.py +107 -0
  32. nixwrap_rl-0.1.0/src/nixwrap/save_file/_cli.py +42 -0
  33. nixwrap_rl-0.1.0/src/nixwrap/save_file/_crypto.py +66 -0
  34. nixwrap_rl-0.1.0/src/nixwrap/save_file/_file_io.py +192 -0
  35. nixwrap_rl-0.1.0/src/nixwrap/save_file/_models.py +321 -0
  36. nixwrap_rl-0.1.0/src/nixwrap/stats/__init__.py +25 -0
  37. nixwrap_rl-0.1.0/src/nixwrap/stats/_client.py +238 -0
  38. nixwrap_rl-0.1.0/src/nixwrap/stats/_models.py +169 -0
  39. nixwrap_rl-0.1.0/src/nixwrap/stats/_parser.py +290 -0
  40. nixwrap_rl-0.1.0/src/nixwrap/tracker/__init__.py +20 -0
  41. nixwrap_rl-0.1.0/src/nixwrap/tracker/_cache.py +80 -0
  42. nixwrap_rl-0.1.0/src/nixwrap/tracker/_client.py +370 -0
  43. nixwrap_rl-0.1.0/src/nixwrap/tracker/_models.py +78 -0
  44. nixwrap_rl-0.1.0/src/nixwrap/utils/__init__.py +56 -0
  45. nixwrap_rl-0.1.0/src/nixwrap/utils/_constants.py +80 -0
  46. nixwrap_rl-0.1.0/src/nixwrap/utils/_platforms.py +66 -0
  47. nixwrap_rl-0.1.0/src/nixwrap/utils/_ranks.py +88 -0
  48. nixwrap_rl-0.1.0/src/nixwrap_rl.egg-info/PKG-INFO +277 -0
  49. nixwrap_rl-0.1.0/src/nixwrap_rl.egg-info/SOURCES.txt +50 -0
  50. nixwrap_rl-0.1.0/src/nixwrap_rl.egg-info/dependency_links.txt +1 -0
  51. nixwrap_rl-0.1.0/src/nixwrap_rl.egg-info/requires.txt +15 -0
  52. nixwrap_rl-0.1.0/src/nixwrap_rl.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 nixvio64
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,277 @@
1
+ Metadata-Version: 2.4
2
+ Name: nixwrap-rl
3
+ Version: 0.1.0
4
+ Summary: Unified SDK for Rocket League save file data extraction, Stats API WebSocket client, tracker.gg stats, process detection, input (keyboard/controller), and GUI overlay toolkit.
5
+ Author: nixvio64
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/nixvio64/nixwrap
8
+ Project-URL: Source, https://github.com/nixvio64/nixwrap
9
+ Project-URL: Issues, https://github.com/nixvio64/nixwrap/issues
10
+ Keywords: rocket-league,save-file,stats-api,overlay,tracker,bakkesmod
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Topic :: Games/Entertainment
16
+ Requires-Python: ==3.11
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: pycryptodome>=3.18
20
+ Requires-Dist: psutil>=5.9
21
+ Provides-Extra: tracker
22
+ Requires-Dist: curl-cffi>=0.6; extra == "tracker"
23
+ Provides-Extra: gui
24
+ Requires-Dist: PySide6>=6.5; extra == "gui"
25
+ Provides-Extra: input
26
+ Requires-Dist: dualsense-controller>=0.3; extra == "input"
27
+ Requires-Dist: keyboard>=0.13; extra == "input"
28
+ Provides-Extra: all
29
+ Requires-Dist: nixwrap[gui,input,tracker]; extra == "all"
30
+ Dynamic: license-file
31
+
32
+ # Nixwrap
33
+
34
+ Unified Python SDK for Rocket League: save file data extraction, Stats API WebSocket client, tracker.gg stats, process detection, keyboard/controller input, and a reusable GUI/overlay toolkit.
35
+
36
+ Built because EAC made traditional plugin development for online matches harder. Intended for streamers, tool developers, and anyone who wants RL data in Python.
37
+ ```bash
38
+ pip install git+https://github.com/nixvio64/nixwrap
39
+
40
+ # With optional extras:
41
+ pip install "nixwrap[tracker] @ git+https://github.com/nixvio64/nixwrap" # tracker.gg
42
+ pip install "nixwrap[gui] @ git+https://github.com/nixvio64/nixwrap" # overlay / GUI
43
+ pip install "nixwrap[input] @ git+https://github.com/nixvio64/nixwrap" # keyboard + controller
44
+ pip install "nixwrap[all] @ git+https://github.com/nixvio64/nixwrap" # everything
45
+
46
+ ```
47
+
48
+ ## Modules
49
+
50
+ | Module | Purpose | Requires |
51
+ |--------|---------|----------|
52
+ | `nixwrap.save_file` | Decrypt + extract data from `.save` files | `pycryptodome` |
53
+ | `nixwrap.process` | Detect running RL (Steam vs Epic) | `psutil` |
54
+ | `nixwrap.stats` | Live Stats API WebSocket events | - |
55
+ | `nixwrap.tracker` | tracker.gg player rank/stats | `curl-cffi` (optional) |
56
+ | `nixwrap.input` | Keyboard, Xbox, DualSense hotkey detection | `dualsense-controller`, `keyboard` (optional) |
57
+ | `nixwrap.gui` | Overlay/GUI toolkit | `PySide6` (optional) |
58
+ | `nixwrap.utils` | Constants and helpers | - |
59
+
60
+ ---
61
+
62
+ ## Quick Start
63
+
64
+ ### 0. Edit your DefaultStatsAPI.ini
65
+
66
+ Found at ```<RL Install Dir>\TAGame\Config\DefaultStatsAPI.ini``` set PacketSendRate value to something like 20 (1-120).
67
+
68
+ ### 1. Save File Data Extraction
69
+
70
+ ```python
71
+ from nixwrap.save_file import load, find_save_file
72
+
73
+ # Auto-find the active save file
74
+ path = find_save_file()
75
+ save = load(path)
76
+
77
+ # Camera
78
+ print(save.camera.fov) # 110.0
79
+ print(save.camera.stiffness) # 0.55
80
+
81
+ # Controls (keyboard + gamepad)
82
+ print(save.controls.jump) # "SpaceBar"
83
+ print(save.gamepad_bindings.boost) # "XboxTypeS_RightShoulder"
84
+
85
+ # Raw bindings for custom inspection
86
+ for b in save.gamepad_bindings.raw_bindings:
87
+ print(f" {b['Action']} -> {b['Key']}")
88
+
89
+ # Stats
90
+ print(save.stats.wins) # 1234
91
+ print(save.xp.level) # 27
92
+ print(save.xp.total_xp) # 348223
93
+
94
+ # Skill data per playlist
95
+ for pid, skill in save.skills.items():
96
+ print(f"Playlist {pid}: matches={skill.matches_played}, tier={skill.tier}")
97
+
98
+ # Inventory
99
+ for item in save.inventory:
100
+ print(f"Product {item.product_id} (series {item.series_id})")
101
+
102
+ # Settings
103
+ print(save.video.res_width, save.video.res_height)
104
+ print(save.gameplay.controller_deadzone)
105
+ print(save.sound.master_volume)
106
+
107
+ # Quick chats, loadout sets, season, achievements, etc.
108
+ print(len(save.quick_chats), "quick chat bindings")
109
+ print(len(save.loadout_sets), "loadout presets")
110
+ ```
111
+
112
+ ### 2. Process Detection
113
+
114
+ ```python
115
+ from nixwrap.process import find_rocket_league, is_rocket_league_focused
116
+
117
+ rl = find_rocket_league()
118
+ if rl:
119
+ print(f"PID: {rl.pid}")
120
+ print(f"Platform: {rl.platform.value}") # steam or epic
121
+ print(f"Root dir: {rl.root_dir}")
122
+ print(f"Save path: {rl.save_data_path}")
123
+
124
+ if is_rocket_league_focused():
125
+ print("RL window has focus")
126
+ ```
127
+
128
+ ### 3. Input Detection (Keyboard + Controller)
129
+
130
+ ```python
131
+ from nixwrap.input import (
132
+ is_hotkey_pressed, is_key_pressed,
133
+ get_xinput_state, XINPUT_A,
134
+ setup_dualsense, start_dualsense_monitor,
135
+ get_button_display,
136
+ )
137
+
138
+ # Keyboard
139
+ if is_key_pressed("tab"):
140
+ print("Tab is held")
141
+
142
+ # XInput (Xbox / DS4 via DS4Windows)
143
+ state = get_xinput_state()
144
+ if state and state.is_pressed(XINPUT_A):
145
+ print("A button pressed")
146
+
147
+ # DualSense (PS5)
148
+ controller = setup_dualsense() # auto-finds bundled hidapi.dll
149
+ start_dualsense_monitor() # background auto-reconnect
150
+
151
+ # Unified hotkey check (InGameRank-compatible config format)
152
+ config = {"is_controller": True, "controller_type": "xinput",
153
+ "controller_button": XINPUT_A}
154
+ if is_hotkey_pressed(config):
155
+ print("Hotkey held!")
156
+
157
+ # Button display names
158
+ print(get_button_display("xinput", XINPUT_A)) # "A"
159
+ print(get_button_display("dualsense", 0)) # "Cross"
160
+ ```
161
+
162
+ ### 4. Stats API (Live Events)
163
+
164
+ ```python
165
+ from nixwrap.stats import StatsClient
166
+
167
+ client = StatsClient()
168
+
169
+ # Callback mode
170
+ client.on("GoalScored", lambda evt: print(f"GOAL! {evt.scorer.name}"))
171
+ client.on("StatfeedEvent", lambda evt: print(f"{evt.main_target.name}: {evt.stat_type}"))
172
+ client.on_event(lambda evt: print(f"[{evt.event_type}]"))
173
+
174
+ with client:
175
+ import time
176
+ time.sleep(300) # listen for 5 minutes
177
+ ```
178
+
179
+ **Async mode:**
180
+
181
+ ```python
182
+ import asyncio
183
+ from nixwrap.stats import StatsClient
184
+
185
+ async def main():
186
+ client = StatsClient()
187
+ client.start()
188
+ async for event in client.events():
189
+ print(event.event_type)
190
+ if event.event_type == "GoalScored":
191
+ print(f" {event.scorer.name} scored!")
192
+
193
+ asyncio.run(main())
194
+ ```
195
+
196
+ ### 5. Tracker API (Player Ranks)
197
+
198
+ ```python
199
+ from nixwrap.tracker import TrackerClient
200
+
201
+ client = TrackerClient() # random browser fingerprint per request
202
+ stats = client.fetch("Steam|123456789|0", "PlayerName")
203
+
204
+ if stats.error:
205
+ print(f"Error: {stats.error}")
206
+ else:
207
+ print(f"Last updated: {stats.last_updated}")
208
+ print(f"Lifetime: {stats.lifetime.wins} wins, {stats.lifetime.goals} goals")
209
+ for pid, rank in stats.ranks.items():
210
+ print(f" {rank.playlist_name}: {rank.tier_name} {rank.division_name} "
211
+ f"({rank.mmr} MMR, #{rank.rank_percentile}%)")
212
+ if rank.win_streak:
213
+ direction = "^" if rank.win_streak_type == "win" else "v"
214
+ print(f" Streak: {direction}{rank.win_streak}")
215
+ ```
216
+
217
+ Results are cached for 5 minutes (tracker.gg update interval).
218
+
219
+ ### 6. GUI / Overlay
220
+
221
+ ```python
222
+ from PySide6.QtWidgets import QApplication
223
+ from nixwrap.gui import (
224
+ create_overlay, WindowConfig, WindowType,
225
+ Painter, Color, THEME_DARK, FadeAnimation,
226
+ )
227
+
228
+ class MyOverlay:
229
+ def __init__(self):
230
+ self.win = create_overlay(600, 200, corner_radius=12, opacity=0.95)
231
+ self.win._paint = self._paint
232
+ self.win.snap_to_bottom_center(margin=20)
233
+
234
+ def _paint(self, painter_q):
235
+ p = Painter(painter_q)
236
+ p.fill_rect(0, 0, 600, 200,
237
+ THEME_DARK.background.with_alpha(216), radius=12)
238
+ p.stroke_rect(0, 0, 599, 199,
239
+ THEME_DARK.divider, width=1, radius=12)
240
+ p.draw_text("Hello Rocket League!", 20, 40,
241
+ color=THEME_DARK.text_primary)
242
+
243
+ app = QApplication([])
244
+ overlay = MyOverlay()
245
+ app.exec()
246
+ ```
247
+
248
+ ---
249
+
250
+ ## Optional Dependencies
251
+
252
+ | Extra | Packages | What you get |
253
+ |-------|----------|-------------|
254
+ | `[tracker]` | `curl-cffi` | better tracker.gg support |
255
+ | `[gui]` | `PySide6` | Overlay windows, painter, colors, fonts, animations |
256
+ | `[input]` | `dualsense-controller`, `keyboard`, bundled `hidapi.dll` | Xbox (XInput), PS5 DualSense, keyboard hotkey detection |
257
+ | `[all]` | all of the above | Everything |
258
+
259
+ ---
260
+
261
+ ## Requirements
262
+
263
+ - **Python** >= 3.10
264
+ - **pycryptodome** >= 3.18 (save file AES)
265
+ - **psutil** >= 5.9 (process detection)
266
+
267
+
268
+ ---
269
+
270
+ ## Acknowledgements
271
+
272
+ - [RocketRP](https://github.com/Drogebot/RocketRP): AES key and binary format research
273
+ - [tracker.gg](https://tracker.gg/): player stats API
274
+ - Rocket League Stats API: [Psyonix/Epic](https://www.rocketleague.com/en/developer/stats-api)
275
+
276
+ ## Contributing
277
+ Do you want to make this library even better? Contributions are highly encouraged and appreciated.
@@ -0,0 +1,246 @@
1
+ # Nixwrap
2
+
3
+ Unified Python SDK for Rocket League: save file data extraction, Stats API WebSocket client, tracker.gg stats, process detection, keyboard/controller input, and a reusable GUI/overlay toolkit.
4
+
5
+ Built because EAC made traditional plugin development for online matches harder. Intended for streamers, tool developers, and anyone who wants RL data in Python.
6
+ ```bash
7
+ pip install git+https://github.com/nixvio64/nixwrap
8
+
9
+ # With optional extras:
10
+ pip install "nixwrap[tracker] @ git+https://github.com/nixvio64/nixwrap" # tracker.gg
11
+ pip install "nixwrap[gui] @ git+https://github.com/nixvio64/nixwrap" # overlay / GUI
12
+ pip install "nixwrap[input] @ git+https://github.com/nixvio64/nixwrap" # keyboard + controller
13
+ pip install "nixwrap[all] @ git+https://github.com/nixvio64/nixwrap" # everything
14
+
15
+ ```
16
+
17
+ ## Modules
18
+
19
+ | Module | Purpose | Requires |
20
+ |--------|---------|----------|
21
+ | `nixwrap.save_file` | Decrypt + extract data from `.save` files | `pycryptodome` |
22
+ | `nixwrap.process` | Detect running RL (Steam vs Epic) | `psutil` |
23
+ | `nixwrap.stats` | Live Stats API WebSocket events | - |
24
+ | `nixwrap.tracker` | tracker.gg player rank/stats | `curl-cffi` (optional) |
25
+ | `nixwrap.input` | Keyboard, Xbox, DualSense hotkey detection | `dualsense-controller`, `keyboard` (optional) |
26
+ | `nixwrap.gui` | Overlay/GUI toolkit | `PySide6` (optional) |
27
+ | `nixwrap.utils` | Constants and helpers | - |
28
+
29
+ ---
30
+
31
+ ## Quick Start
32
+
33
+ ### 0. Edit your DefaultStatsAPI.ini
34
+
35
+ Found at ```<RL Install Dir>\TAGame\Config\DefaultStatsAPI.ini``` set PacketSendRate value to something like 20 (1-120).
36
+
37
+ ### 1. Save File Data Extraction
38
+
39
+ ```python
40
+ from nixwrap.save_file import load, find_save_file
41
+
42
+ # Auto-find the active save file
43
+ path = find_save_file()
44
+ save = load(path)
45
+
46
+ # Camera
47
+ print(save.camera.fov) # 110.0
48
+ print(save.camera.stiffness) # 0.55
49
+
50
+ # Controls (keyboard + gamepad)
51
+ print(save.controls.jump) # "SpaceBar"
52
+ print(save.gamepad_bindings.boost) # "XboxTypeS_RightShoulder"
53
+
54
+ # Raw bindings for custom inspection
55
+ for b in save.gamepad_bindings.raw_bindings:
56
+ print(f" {b['Action']} -> {b['Key']}")
57
+
58
+ # Stats
59
+ print(save.stats.wins) # 1234
60
+ print(save.xp.level) # 27
61
+ print(save.xp.total_xp) # 348223
62
+
63
+ # Skill data per playlist
64
+ for pid, skill in save.skills.items():
65
+ print(f"Playlist {pid}: matches={skill.matches_played}, tier={skill.tier}")
66
+
67
+ # Inventory
68
+ for item in save.inventory:
69
+ print(f"Product {item.product_id} (series {item.series_id})")
70
+
71
+ # Settings
72
+ print(save.video.res_width, save.video.res_height)
73
+ print(save.gameplay.controller_deadzone)
74
+ print(save.sound.master_volume)
75
+
76
+ # Quick chats, loadout sets, season, achievements, etc.
77
+ print(len(save.quick_chats), "quick chat bindings")
78
+ print(len(save.loadout_sets), "loadout presets")
79
+ ```
80
+
81
+ ### 2. Process Detection
82
+
83
+ ```python
84
+ from nixwrap.process import find_rocket_league, is_rocket_league_focused
85
+
86
+ rl = find_rocket_league()
87
+ if rl:
88
+ print(f"PID: {rl.pid}")
89
+ print(f"Platform: {rl.platform.value}") # steam or epic
90
+ print(f"Root dir: {rl.root_dir}")
91
+ print(f"Save path: {rl.save_data_path}")
92
+
93
+ if is_rocket_league_focused():
94
+ print("RL window has focus")
95
+ ```
96
+
97
+ ### 3. Input Detection (Keyboard + Controller)
98
+
99
+ ```python
100
+ from nixwrap.input import (
101
+ is_hotkey_pressed, is_key_pressed,
102
+ get_xinput_state, XINPUT_A,
103
+ setup_dualsense, start_dualsense_monitor,
104
+ get_button_display,
105
+ )
106
+
107
+ # Keyboard
108
+ if is_key_pressed("tab"):
109
+ print("Tab is held")
110
+
111
+ # XInput (Xbox / DS4 via DS4Windows)
112
+ state = get_xinput_state()
113
+ if state and state.is_pressed(XINPUT_A):
114
+ print("A button pressed")
115
+
116
+ # DualSense (PS5)
117
+ controller = setup_dualsense() # auto-finds bundled hidapi.dll
118
+ start_dualsense_monitor() # background auto-reconnect
119
+
120
+ # Unified hotkey check (InGameRank-compatible config format)
121
+ config = {"is_controller": True, "controller_type": "xinput",
122
+ "controller_button": XINPUT_A}
123
+ if is_hotkey_pressed(config):
124
+ print("Hotkey held!")
125
+
126
+ # Button display names
127
+ print(get_button_display("xinput", XINPUT_A)) # "A"
128
+ print(get_button_display("dualsense", 0)) # "Cross"
129
+ ```
130
+
131
+ ### 4. Stats API (Live Events)
132
+
133
+ ```python
134
+ from nixwrap.stats import StatsClient
135
+
136
+ client = StatsClient()
137
+
138
+ # Callback mode
139
+ client.on("GoalScored", lambda evt: print(f"GOAL! {evt.scorer.name}"))
140
+ client.on("StatfeedEvent", lambda evt: print(f"{evt.main_target.name}: {evt.stat_type}"))
141
+ client.on_event(lambda evt: print(f"[{evt.event_type}]"))
142
+
143
+ with client:
144
+ import time
145
+ time.sleep(300) # listen for 5 minutes
146
+ ```
147
+
148
+ **Async mode:**
149
+
150
+ ```python
151
+ import asyncio
152
+ from nixwrap.stats import StatsClient
153
+
154
+ async def main():
155
+ client = StatsClient()
156
+ client.start()
157
+ async for event in client.events():
158
+ print(event.event_type)
159
+ if event.event_type == "GoalScored":
160
+ print(f" {event.scorer.name} scored!")
161
+
162
+ asyncio.run(main())
163
+ ```
164
+
165
+ ### 5. Tracker API (Player Ranks)
166
+
167
+ ```python
168
+ from nixwrap.tracker import TrackerClient
169
+
170
+ client = TrackerClient() # random browser fingerprint per request
171
+ stats = client.fetch("Steam|123456789|0", "PlayerName")
172
+
173
+ if stats.error:
174
+ print(f"Error: {stats.error}")
175
+ else:
176
+ print(f"Last updated: {stats.last_updated}")
177
+ print(f"Lifetime: {stats.lifetime.wins} wins, {stats.lifetime.goals} goals")
178
+ for pid, rank in stats.ranks.items():
179
+ print(f" {rank.playlist_name}: {rank.tier_name} {rank.division_name} "
180
+ f"({rank.mmr} MMR, #{rank.rank_percentile}%)")
181
+ if rank.win_streak:
182
+ direction = "^" if rank.win_streak_type == "win" else "v"
183
+ print(f" Streak: {direction}{rank.win_streak}")
184
+ ```
185
+
186
+ Results are cached for 5 minutes (tracker.gg update interval).
187
+
188
+ ### 6. GUI / Overlay
189
+
190
+ ```python
191
+ from PySide6.QtWidgets import QApplication
192
+ from nixwrap.gui import (
193
+ create_overlay, WindowConfig, WindowType,
194
+ Painter, Color, THEME_DARK, FadeAnimation,
195
+ )
196
+
197
+ class MyOverlay:
198
+ def __init__(self):
199
+ self.win = create_overlay(600, 200, corner_radius=12, opacity=0.95)
200
+ self.win._paint = self._paint
201
+ self.win.snap_to_bottom_center(margin=20)
202
+
203
+ def _paint(self, painter_q):
204
+ p = Painter(painter_q)
205
+ p.fill_rect(0, 0, 600, 200,
206
+ THEME_DARK.background.with_alpha(216), radius=12)
207
+ p.stroke_rect(0, 0, 599, 199,
208
+ THEME_DARK.divider, width=1, radius=12)
209
+ p.draw_text("Hello Rocket League!", 20, 40,
210
+ color=THEME_DARK.text_primary)
211
+
212
+ app = QApplication([])
213
+ overlay = MyOverlay()
214
+ app.exec()
215
+ ```
216
+
217
+ ---
218
+
219
+ ## Optional Dependencies
220
+
221
+ | Extra | Packages | What you get |
222
+ |-------|----------|-------------|
223
+ | `[tracker]` | `curl-cffi` | better tracker.gg support |
224
+ | `[gui]` | `PySide6` | Overlay windows, painter, colors, fonts, animations |
225
+ | `[input]` | `dualsense-controller`, `keyboard`, bundled `hidapi.dll` | Xbox (XInput), PS5 DualSense, keyboard hotkey detection |
226
+ | `[all]` | all of the above | Everything |
227
+
228
+ ---
229
+
230
+ ## Requirements
231
+
232
+ - **Python** >= 3.10
233
+ - **pycryptodome** >= 3.18 (save file AES)
234
+ - **psutil** >= 5.9 (process detection)
235
+
236
+
237
+ ---
238
+
239
+ ## Acknowledgements
240
+
241
+ - [RocketRP](https://github.com/Drogebot/RocketRP): AES key and binary format research
242
+ - [tracker.gg](https://tracker.gg/): player stats API
243
+ - Rocket League Stats API: [Psyonix/Epic](https://www.rocketleague.com/en/developer/stats-api)
244
+
245
+ ## Contributing
246
+ Do you want to make this library even better? Contributions are highly encouraged and appreciated.
@@ -0,0 +1,52 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "nixwrap-rl"
7
+ version = "0.1.0"
8
+ description = "Unified SDK for Rocket League save file data extraction, Stats API WebSocket client, tracker.gg stats, process detection, input (keyboard/controller), and GUI overlay toolkit."
9
+ readme = "README.md"
10
+ requires-python = "==3.11"
11
+ license = {text = "MIT"}
12
+ authors = [{name = "nixvio64"}]
13
+ keywords = ["rocket-league", "save-file", "stats-api", "overlay", "tracker", "bakkesmod"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Topic :: Games/Entertainment",
20
+ ]
21
+
22
+ dependencies = [
23
+ "pycryptodome>=3.18",
24
+ "psutil>=5.9",
25
+ ]
26
+
27
+ [project.urls]
28
+ Homepage = "https://github.com/nixvio64/nixwrap"
29
+ Source = "https://github.com/nixvio64/nixwrap"
30
+ Issues = "https://github.com/nixvio64/nixwrap/issues"
31
+
32
+ [project.optional-dependencies]
33
+ tracker = [
34
+ "curl-cffi>=0.6",
35
+ ]
36
+ gui = [
37
+ "PySide6>=6.5",
38
+ ]
39
+ input = [
40
+ "dualsense-controller>=0.3",
41
+ "keyboard>=0.13",
42
+ ]
43
+ all = [
44
+ "nixwrap[tracker,gui,input]",
45
+ ]
46
+
47
+ [tool.setuptools.packages.find]
48
+ where = ["src"]
49
+
50
+ [tool.setuptools.package-data]
51
+ nixwrap = ["py.typed"]
52
+ "nixwrap.input" = ["hidapi.dll"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """Unified SDK for Rocket League: save files, stats, tracker, input, GUI."""
2
+
3
+ from nixwrap._version import __version__
@@ -0,0 +1,3 @@
1
+ """nixwrap version."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,77 @@
1
+ """Overlay/GUI toolkit. Needs PySide6 (pip install nixwrap[gui])."""
2
+
3
+
4
+ def __getattr__(name: str):
5
+ """Lazy-load all public names, checking for PySide6."""
6
+ _ensure_pyside6()
7
+ # Import the real module
8
+ if name in _EXPORTS:
9
+ mod_name, attr = _EXPORTS[name]
10
+ mod = __import__(f"nixwrap.gui.{mod_name}", fromlist=[attr])
11
+ return getattr(mod, attr)
12
+ raise AttributeError(f"module 'nixwrap.gui' has no attribute {name!r}")
13
+
14
+
15
+ def _ensure_pyside6() -> None:
16
+ try:
17
+ import PySide6 # noqa: F401
18
+ except ImportError:
19
+ raise ImportError(
20
+ "The nixwrap.gui module requires PySide6.\n"
21
+ "Install with: pip install nixwrap[gui]\n"
22
+ "Or directly: pip install PySide6"
23
+ ) from None
24
+
25
+
26
+ # Map public names to (module, attr)
27
+ _EXPORTS: dict[str, tuple[str, str]] = {
28
+ # _window
29
+ "BaseWindow": ("_window", "BaseWindow"),
30
+ "WindowConfig": ("_window", "WindowConfig"),
31
+ "WindowType": ("_window", "WindowType"),
32
+ "create_overlay": ("_window", "create_overlay"),
33
+ "create_frameless_window": ("_window", "create_frameless_window"),
34
+ "create_window": ("_window", "create_window"),
35
+ # _painter
36
+ "Painter": ("_painter", "Painter"),
37
+ # _colors
38
+ "Color": ("_colors", "Color"),
39
+ "Gradient": ("_colors", "Gradient"),
40
+ "TEAM_BLUE": ("_colors", "TEAM_BLUE"),
41
+ "TEAM_ORANGE": ("_colors", "TEAM_ORANGE"),
42
+ "BACKGROUND_DARK": ("_colors", "BACKGROUND_DARK"),
43
+ "DIVIDER_GRAY": ("_colors", "DIVIDER_GRAY"),
44
+ "TEXT_MUTED": ("_colors", "TEXT_MUTED"),
45
+ "TEXT_LIGHT": ("_colors", "TEXT_LIGHT"),
46
+ "TEXT_WHITE": ("_colors", "TEXT_WHITE"),
47
+ "ACCENT_BLUE": ("_colors", "ACCENT_BLUE"),
48
+ # _geometry
49
+ "LayoutCalculator": ("_geometry", "LayoutCalculator"),
50
+ "ScreenInfo": ("_geometry", "ScreenInfo"),
51
+ "get_primary_screen": ("_geometry", "get_primary_screen"),
52
+ "get_all_screens": ("_geometry", "get_all_screens"),
53
+ "get_screen_containing": ("_geometry", "get_screen_containing"),
54
+ # _text
55
+ "FontConfig": ("_text", "FontConfig"),
56
+ "FontManager": ("_text", "FontManager"),
57
+ "font_manager": ("_text", "font_manager"),
58
+ # _images
59
+ "ImageCache": ("_images", "ImageCache"),
60
+ "image_cache": ("_images", "image_cache"),
61
+ # _animations
62
+ "FadeAnimation": ("_animations", "FadeAnimation"),
63
+ "SlideAnimation": ("_animations", "SlideAnimation"),
64
+ "PropertyAnimation": ("_animations", "PropertyAnimation"),
65
+ # _themes
66
+ "Theme": ("_themes", "Theme"),
67
+ "THEME_DARK": ("_themes", "THEME_DARK"),
68
+ "THEME_LIGHT": ("_themes", "THEME_LIGHT"),
69
+ # _app
70
+ "QtApp": ("_app", "QtApp"),
71
+ "get_rl_monitor": ("_app", "get_rl_monitor"),
72
+ "snap_to_rl_monitor": ("_app", "snap_to_rl_monitor"),
73
+ "is_rl_active": ("_app", "is_rl_active"),
74
+ }
75
+
76
+
77
+ __all__ = list(_EXPORTS.keys())