rust-map-parser 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 (134) hide show
  1. rust_map_parser-0.1.0/LICENSE +21 -0
  2. rust_map_parser-0.1.0/MANIFEST.in +8 -0
  3. rust_map_parser-0.1.0/PKG-INFO +538 -0
  4. rust_map_parser-0.1.0/README.md +516 -0
  5. rust_map_parser-0.1.0/docs/export-config.md +756 -0
  6. rust_map_parser-0.1.0/docs/export-result.md +527 -0
  7. rust_map_parser-0.1.0/docs/images/heatmap-ores.png +0 -0
  8. rust_map_parser-0.1.0/docs/images/map-render.jpg +0 -0
  9. rust_map_parser-0.1.0/docs/images/no-build-zones.png +0 -0
  10. rust_map_parser-0.1.0/docs/images/splat-composite.png +0 -0
  11. rust_map_parser-0.1.0/docs/images/tunnels-layer.png +0 -0
  12. rust_map_parser-0.1.0/example.py +31 -0
  13. rust_map_parser-0.1.0/pyproject.toml +39 -0
  14. rust_map_parser-0.1.0/refresh_all_data.py +250 -0
  15. rust_map_parser-0.1.0/setup.cfg +4 -0
  16. rust_map_parser-0.1.0/src/rust_map_parser.egg-info/PKG-INFO +538 -0
  17. rust_map_parser-0.1.0/src/rust_map_parser.egg-info/SOURCES.txt +132 -0
  18. rust_map_parser-0.1.0/src/rust_map_parser.egg-info/dependency_links.txt +1 -0
  19. rust_map_parser-0.1.0/src/rust_map_parser.egg-info/requires.txt +7 -0
  20. rust_map_parser-0.1.0/src/rust_map_parser.egg-info/top_level.txt +1 -0
  21. rust_map_parser-0.1.0/src/rustmap/__init__.py +44 -0
  22. rust_map_parser-0.1.0/src/rustmap/config.py +236 -0
  23. rust_map_parser-0.1.0/src/rustmap/data/__init__.py +1 -0
  24. rust_map_parser-0.1.0/src/rustmap/data/monument_metadata.json +954 -0
  25. rust_map_parser-0.1.0/src/rustmap/data/no_build_zones.json +9543 -0
  26. rust_map_parser-0.1.0/src/rustmap/data/prefab_manifest.json +242637 -0
  27. rust_map_parser-0.1.0/src/rustmap/data/spawn_rules.json +3308 -0
  28. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/__init__.py +1 -0
  29. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_000__curve-nw-0.png +0 -0
  30. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_001__curve-nw-1.png +0 -0
  31. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_002__intersection-bb1-n.png +0 -0
  32. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_003__intersection-bb4-n.png +0 -0
  33. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_004__intersection-b5-n.png +0 -0
  34. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_005__intersection-b3-n.png +0 -0
  35. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_006__intersection-b2-n.png +0 -0
  36. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_007__intersection-bb2-n.png +0 -0
  37. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_008__intersection-b6-n.png +0 -0
  38. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_009__intersection-bb3-n.png +0 -0
  39. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_010__intersection-b4-n.png +0 -0
  40. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_011__intersection-bb6-n.png +0 -0
  41. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_012__intersection-n.png +0 -0
  42. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_013__intersection-b1-n.png +0 -0
  43. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_014__intersection-bb5-n.png +0 -0
  44. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_015__station-sn-0.png +0 -0
  45. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_016__straight-sn-3.png +0 -0
  46. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_017__straight-sn-2.png +0 -0
  47. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_018__station-sn-1.png +0 -0
  48. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_019__straight-sn-0.png +0 -0
  49. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_020__straight-sn-1.png +0 -0
  50. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_021__station-sn-3.png +0 -0
  51. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_022__straight-sn-4.png +0 -0
  52. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_023__station-sn-2.png +0 -0
  53. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_024__straight-sn-5.png +0 -0
  54. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_025__curve-ne-1.png +0 -0
  55. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_026__curve-ne-0.png +0 -0
  56. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_027__intersection.png +0 -0
  57. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_028__intersection-bb2-s.png +0 -0
  58. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_029__intersection-b2-s.png +0 -0
  59. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_030__intersection-b6-s.png +0 -0
  60. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_031__intersection-bb5-s.png +0 -0
  61. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_032__intersection-bb1-s.png +0 -0
  62. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_033__intersection-s.png +0 -0
  63. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_034__intersection-b1-s.png +0 -0
  64. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_035__intersection-bb6-s.png +0 -0
  65. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_036__intersection-b5-s.png +0 -0
  66. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_037__intersection-bb4-s.png +0 -0
  67. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_038__intersection-b3-s.png +0 -0
  68. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_039__intersection-bb3-s.png +0 -0
  69. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_040__intersection-b4-s.png +0 -0
  70. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_041__straight-we-3.png +0 -0
  71. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_042__station-we-1.png +0 -0
  72. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_043__straight-we-5.png +0 -0
  73. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_044__straight-we-4.png +0 -0
  74. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_045__station-we-2.png +0 -0
  75. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_046__station-we-3.png +0 -0
  76. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_047__station-we-0.png +0 -0
  77. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_048__straight-we-2.png +0 -0
  78. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_049__straight-we-1.png +0 -0
  79. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_050__straight-we-0.png +0 -0
  80. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_051__curve-se-0.png +0 -0
  81. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_052__curve-se-1.png +0 -0
  82. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_053__intersection-bb3-w.png +0 -0
  83. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_054__intersection-w.png +0 -0
  84. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_055__intersection-bb5-w.png +0 -0
  85. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_056__intersection-b5-w.png +0 -0
  86. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_057__intersection-b1-w.png +0 -0
  87. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_058__intersection-bb1-w.png +0 -0
  88. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_059__intersection-bb4-w.png +0 -0
  89. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_060__intersection-b4-w.png +0 -0
  90. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_061__intersection-b6-w.png +0 -0
  91. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_062__intersection-bb2-w.png +0 -0
  92. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_063__intersection-b3-w.png +0 -0
  93. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_064__intersection-b2-w.png +0 -0
  94. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_065__intersection-bb6-w.png +0 -0
  95. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_066__curve-sw-0.png +0 -0
  96. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_067__curve-sw-1.png +0 -0
  97. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_068__intersection-bb3-e.png +0 -0
  98. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_069__intersection-bb6-e.png +0 -0
  99. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_070__intersection-b4-e.png +0 -0
  100. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_071__intersection-b6-e.png +0 -0
  101. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_072__intersection-b5-e.png +0 -0
  102. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_073__intersection-b1-e.png +0 -0
  103. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_074__intersection-b3-e.png +0 -0
  104. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_075__intersection-bb2-e.png +0 -0
  105. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_076__intersection-e.png +0 -0
  106. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_077__intersection-bb1-e.png +0 -0
  107. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_078__intersection-bb4-e.png +0 -0
  108. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_079__intersection-b2-e.png +0 -0
  109. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/template_080__intersection-bb5-e.png +0 -0
  110. rust_map_parser-0.1.0/src/rustmap/data/tunnel_tiles/tiles.json +997 -0
  111. rust_map_parser-0.1.0/src/rustmap/exporter.py +409 -0
  112. rust_map_parser-0.1.0/src/rustmap/layers.py +323 -0
  113. rust_map_parser-0.1.0/src/rustmap/monument_assets.py +145 -0
  114. rust_map_parser-0.1.0/src/rustmap/monuments.py +226 -0
  115. rust_map_parser-0.1.0/src/rustmap/no_build.py +288 -0
  116. rust_map_parser-0.1.0/src/rustmap/no_build_assets.py +303 -0
  117. rust_map_parser-0.1.0/src/rustmap/parser.py +297 -0
  118. rust_map_parser-0.1.0/src/rustmap/png.py +18 -0
  119. rust_map_parser-0.1.0/src/rustmap/populations.py +361 -0
  120. rust_map_parser-0.1.0/src/rustmap/prefabs.py +281 -0
  121. rust_map_parser-0.1.0/src/rustmap/renderer.py +460 -0
  122. rust_map_parser-0.1.0/src/rustmap/resources.py +26 -0
  123. rust_map_parser-0.1.0/src/rustmap/tunnel_assets.py +398 -0
  124. rust_map_parser-0.1.0/src/rustmap/tunnels.py +417 -0
  125. rust_map_parser-0.1.0/src/rustmap/validation.py +106 -0
  126. rust_map_parser-0.1.0/tests/test_exporter.py +124 -0
  127. rust_map_parser-0.1.0/tests/test_map_tiles.py +85 -0
  128. rust_map_parser-0.1.0/tests/test_monuments.py +54 -0
  129. rust_map_parser-0.1.0/tests/test_no_build.py +170 -0
  130. rust_map_parser-0.1.0/tests/test_png_metadata.py +18 -0
  131. rust_map_parser-0.1.0/tests/test_populations.py +46 -0
  132. rust_map_parser-0.1.0/tests/test_renderer_water.py +55 -0
  133. rust_map_parser-0.1.0/tests/test_tunnels.py +166 -0
  134. rust_map_parser-0.1.0/tests/test_validation.py +35 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 rust-map-parser contributors
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,8 @@
1
+ include LICENSE
2
+ include README.md
3
+ include example.py
4
+ include refresh_all_data.py
5
+ recursive-include docs *.md *.png *.jpg
6
+ recursive-include tests *.py
7
+ recursive-include src/rustmap/data *.json
8
+ recursive-include src/rustmap/data/tunnel_tiles *.json *.png
@@ -0,0 +1,538 @@
1
+ Metadata-Version: 2.4
2
+ Name: rust-map-parser
3
+ Version: 0.1.0
4
+ Summary: Standalone Python parser, heatmap generator, and renderer for Facepunch Rust map files
5
+ Author: rust-map-parser contributors
6
+ License-Expression: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Requires-Python: >=3.11
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: lz4==4.4.4
16
+ Requires-Dist: numpy<3,>=2.0
17
+ Requires-Dist: Pillow<13,>=11
18
+ Requires-Dist: numba<1,>=0.61
19
+ Provides-Extra: assets
20
+ Requires-Dist: UnityPy==1.24.2; extra == "assets"
21
+ Dynamic: license-file
22
+
23
+ <div align="center">
24
+
25
+ # Rust Map Parser
26
+
27
+ ### Decode, analyze, and render Rust `.map` files entirely from Python.
28
+
29
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-3776AB?logo=python&logoColor=white)](https://www.python.org/)
30
+ [![License: MIT](https://img.shields.io/badge/license-MIT-2ea44f)](LICENSE)
31
+ [![Library First](https://img.shields.io/badge/interface-Python%20API-6f42c1)](#quick-start)
32
+ [![UnityPy Optional](https://img.shields.io/badge/UnityPy-maintainers%20only-orange)](#refreshing-rust-data)
33
+
34
+ **A standalone, library-first toolkit for heatmaps, server-style terrain
35
+ renders, monuments, train tunnels, no-build zones, diagnostics, and map tiles.**
36
+
37
+ ![Server-style map render](docs/images/map-render.jpg)
38
+
39
+ </div>
40
+
41
+ > [!NOTE]
42
+ > This is an independent community project. It is not affiliated with or
43
+ > endorsed by Facepunch Studios. Rust and its game assets belong to Facepunch.
44
+
45
+ ## Why this project?
46
+
47
+ Rust `.map` files contain much more than a terrain image. They serialize height,
48
+ water, splat, biome, topology, path, and prefab data. Rust Map Parser turns those
49
+ layers into typed Python objects and useful artifacts without starting a game
50
+ server.
51
+
52
+ - **Choose exactly what runs** -- map only, heatmaps only, gameplay layers, or
53
+ any custom combination.
54
+ - **Pure Python consumer API** -- no CLI, Unity editor, or running Rust server.
55
+ - **Exact heatmap data** -- named `uint8` arrays in compressed NPZ files.
56
+ - **Server-style rendering** -- a close Python port of Rust's map renderer.
57
+ - **Gameplay-aware exports** -- monuments, recyclers, cards, puzzles, loot tiers,
58
+ tunnels, and no-build zones.
59
+ - **Asset-free normal use** -- sanitized, versioned runtime data ships with the
60
+ package.
61
+
62
+ ## Output gallery
63
+
64
+ These images were generated from a real size-4500 Rust map.
65
+
66
+ <table>
67
+ <tr>
68
+ <td width="33%" align="center">
69
+ <img src="docs/images/tunnels-layer.png" alt="Underground train tunnel layer" />
70
+ <br /><strong>Underground train tunnels</strong>
71
+ </td>
72
+ <td width="33%" align="center">
73
+ <img src="docs/images/no-build-zones.png" alt="No-build zones" />
74
+ <br /><strong>Building-block exclusion zones</strong>
75
+ </td>
76
+ <td width="33%" align="center">
77
+ <img src="docs/images/heatmap-ores.png" alt="Ore heatmap" />
78
+ <br /><strong>Ore population heatmap</strong>
79
+ </td>
80
+ </tr>
81
+ </table>
82
+
83
+ ## Features
84
+
85
+ - Rust header, legacy LZ4 stream, and protobuf world decoding
86
+ - Height, terrain, water, alpha, splat, biome, and topology layers
87
+ - Paths, monuments, and placed prefab transforms
88
+ - 25 procedural population heatmaps at configurable resolution
89
+ - Raw grayscale previews and native diagnostic layers
90
+ - Half-scale and world-size server-style terrain rendering
91
+ - Optional bottom-left-indexed padded map tiles
92
+ - Exact pre-rasterized underground train-tunnel layer
93
+ - Compact monument JSON with extracted gameplay metadata
94
+ - Compact circle/rectangle no-build zone JSON and overlays
95
+ - Detailed timings, source identities, counts, and validation metadata
96
+ - Correct orientation: exported images reverse rows only and never mirror X
97
+
98
+ ## Requirements and installation
99
+
100
+ - Python 3.11 or newer
101
+ - A Rust `.map` file
102
+
103
+ Install from a checkout:
104
+
105
+ ```powershell
106
+ python -m pip install .
107
+ ```
108
+
109
+ Editable development install:
110
+
111
+ ```powershell
112
+ python -m pip install -e .
113
+ ```
114
+
115
+ UnityPy and a local Rust install are not required for normal use. Maintainers
116
+ refreshing packaged game data can install the optional dependency:
117
+
118
+ ```powershell
119
+ python -m pip install -e ".[assets]"
120
+ ```
121
+
122
+ ## Quick start
123
+
124
+ There is intentionally no command-line interface. Pick an output preset or
125
+ compose output sections in Python.
126
+
127
+ ### Render only the map
128
+
129
+ ```python
130
+ from pathlib import Path
131
+
132
+ from rustmap import ExportConfig, ExportOptions, RustMapExporter
133
+
134
+
135
+ config = ExportConfig(
136
+ map_path=Path(r"C:\path\to\procedural.map"),
137
+ output_dir=Path("output/map-only"),
138
+ exports=ExportOptions.map_only(tiles=True),
139
+ )
140
+
141
+ result = RustMapExporter(config).run()
142
+ print(result.full_map_image)
143
+ print(result.map_tiles_dir)
144
+ ```
145
+
146
+ This run does not load spawn rules or generate heatmaps, diagnostics, monuments,
147
+ tunnels, or no-build zones.
148
+
149
+ ### Generate only heatmaps
150
+
151
+ ```python
152
+ config = ExportConfig(
153
+ map_path=Path(r"C:\path\to\procedural.map"),
154
+ output_dir=Path("output/heatmaps-only"),
155
+ exports=ExportOptions.heatmaps_only(
156
+ resolution=2048,
157
+ previews=False,
158
+ ),
159
+ )
160
+ ```
161
+
162
+ ### Export everything
163
+
164
+ The default remains a complete export:
165
+
166
+ ```python
167
+ config = ExportConfig(
168
+ map_path=Path(r"C:\path\to\procedural.map"),
169
+ output_dir=Path("output/full"),
170
+ )
171
+
172
+ # Equivalent explicit form:
173
+ config = ExportConfig(
174
+ map_path=Path(r"C:\path\to\procedural.map"),
175
+ output_dir=Path("output/full"),
176
+ exports=ExportOptions.all(),
177
+ )
178
+ ```
179
+
180
+ ### Mix exactly what you need
181
+
182
+ ```python
183
+ from rustmap import (
184
+ ExportConfig,
185
+ ExportOptions,
186
+ NoBuildZoneOptions,
187
+ RustMapExporter,
188
+ TerrainOptions,
189
+ TileOptions,
190
+ )
191
+
192
+
193
+ config = ExportConfig(
194
+ map_path=Path(r"C:\path\to\procedural.map"),
195
+ output_dir=Path("output/custom"),
196
+ exports=ExportOptions(
197
+ monuments=True,
198
+ terrain=TerrainOptions(
199
+ scale=0.5,
200
+ formats=("png",),
201
+ full_size=True,
202
+ tiles=TileOptions(size=512),
203
+ ),
204
+ no_build_zones=NoBuildZoneOptions(
205
+ outline_width=4,
206
+ ),
207
+ ),
208
+ timing_debug=True,
209
+ )
210
+
211
+ result = RustMapExporter(config).run()
212
+ ```
213
+
214
+ Only monuments, terrain, tiles, and no-build zones run in this example.
215
+ `None` disables configurable stages; `False` disables simple stages.
216
+
217
+ Use [`example.py`](example.py) for a complete editable example after installing
218
+ the package with `python -m pip install -e .`.
219
+
220
+ ## The configuration model
221
+
222
+ For the complete option-by-option guide, see
223
+ **[ExportConfig in depth](docs/export-config.md)**. For every return field,
224
+ status, path guarantee, and consumption pattern, see
225
+ **[ExportResult in depth](docs/export-result.md)**.
226
+
227
+ The API separates three concerns:
228
+
229
+ ```text
230
+ ExportConfig
231
+ |-- map_path / output_dir Where data comes from and goes
232
+ |-- exports: ExportOptions Which stages run and their settings
233
+ |-- data: DataOptions Optional packaged-data overrides
234
+ `-- timing_debug Console timing report
235
+ ```
236
+
237
+ `ExportOptions()` starts with every stage disabled, which makes explicit custom
238
+ combinations predictable. `ExportConfig` itself uses `ExportOptions.all()` as
239
+ its default for convenient complete exports.
240
+
241
+ ### Output sections
242
+
243
+ | Section | Type | Disabled value | Purpose |
244
+ |---|---|---|---|
245
+ | `heatmaps` | `HeatmapOptions` | `None` | NPZ arrays and optional previews |
246
+ | `diagnostics` | `bool` | `False` | Native decoded layer images |
247
+ | `monuments` | `bool` | `False` | Enriched gameplay monument JSON |
248
+ | `terrain` | `TerrainOptions` | `None` | Scaled/full terrain renders and tiles |
249
+ | `tunnels` | `TunnelOptions` | `None` | Train-tunnel layer and optional overlay |
250
+ | `no_build_zones` | `NoBuildZoneOptions` | `None` | Building exclusion layer, JSON, and overlay |
251
+
252
+ ### Option types
253
+
254
+ `HeatmapOptions`:
255
+
256
+ | Field | Default | Meaning |
257
+ |---|---:|---|
258
+ | `resolution` | `2048` | Array width/height; `None` uses the world size |
259
+ | `previews` | `True` | Write exact grayscale PNG previews |
260
+
261
+ `TerrainOptions`:
262
+
263
+ | Field | Default | Meaning |
264
+ |---|---:|---|
265
+ | `scale` | `0.5` | Scaled map render size |
266
+ | `ocean_margin` | `0` | Extra ocean pixels around scaled output |
267
+ | `formats` | `("png", "jpg")` | Scaled formats used when `full_size=False` |
268
+ | `full_size` | `True` | Write only the one-pixel-per-metre render |
269
+ | `tiles` | `None` | Optional `TileOptions` |
270
+ | `debug` | `False` | Renderer debug mode |
271
+
272
+ `TunnelOptions` controls resolution, tunnel opacity, the purple-blue terrain
273
+ tint, and whether to write the transparent layer, terrain overlay, or both.
274
+ `NoBuildZoneOptions` controls
275
+ resolution, RGBA colors, outline width, and independent image/JSON output.
276
+ A `None` resolution uses the map's world size.
277
+
278
+ `DataOptions` can override the bundled version-matched resources:
279
+
280
+ ```python
281
+ from rustmap import DataOptions
282
+
283
+ config = ExportConfig(
284
+ map_path=map_path,
285
+ output_dir=output_dir,
286
+ exports=ExportOptions.heatmaps_only(),
287
+ data=DataOptions(
288
+ spawn_rules_path=Path("custom/spawn_rules.json"),
289
+ prefab_manifest_path=Path("custom/prefab_manifest.json"),
290
+ ),
291
+ )
292
+ ```
293
+
294
+ Only resources needed by selected stages are loaded. A map-only export never
295
+ opens the prefab manifest or spawn-rule database.
296
+
297
+ ## Results and metadata
298
+
299
+ `RustMapExporter.run()` returns an `ExportResult`. Paths for disabled stages are
300
+ `None`, counts are zero, and status strings are `"disabled"`.
301
+
302
+ Common fields:
303
+
304
+ ```python
305
+ result.world_size
306
+ result.elapsed_seconds
307
+ result.metadata_file # export_metadata.json
308
+ result.heatmaps_file
309
+ result.heatmap_categories
310
+ result.full_map_image
311
+ result.map_tiles_dir
312
+ result.map_tile_count
313
+ result.monuments_file
314
+ result.monument_count
315
+ result.tunnels_image
316
+ result.tunnel_render_status
317
+ result.no_build_zones_file
318
+ result.no_build_zone_count
319
+ ```
320
+
321
+ Every run writes `export_metadata.json`, a stage-neutral summary containing the
322
+ enabled-output selection, world metadata, artifacts, timings, validation data,
323
+ and per-stage results. This replaces the old heatmap-named top-level metadata
324
+ file, which was confusing for partial exports.
325
+
326
+ Every exported PNG also contains a non-visible `Source` text metadata field
327
+ linking to `https://github.com/Cooperkit/Rustmap-Parser`. The tag does not alter
328
+ the image pixels and can be inspected with Pillow through `image.info["Source"]`.
329
+
330
+ ## Output layout
331
+
332
+ A complete export resembles:
333
+
334
+ ```text
335
+ output/full/
336
+ |-- export_metadata.json
337
+ |-- heatmaps_2048.npz
338
+ |-- Heatmap-previews/
339
+ |-- diagnostics/
340
+ |-- monuments.json
341
+ |-- map_render_full.png
342
+ |-- map_render_metadata.json
343
+ |-- map_render_tiles/ # only when TerrainOptions.tiles is enabled
344
+ | |-- x_0_y_0.png
345
+ | `-- tiles.json
346
+ |-- tunnels.png
347
+ |-- tunnels_on_map.png
348
+ |-- tunnels_metadata.json
349
+ |-- no_build_zones.png
350
+ |-- no_build_zones_on_map.png
351
+ `-- no_build_zones.json
352
+ ```
353
+
354
+ Use a fresh output directory for logically different selections. The exporter
355
+ does not delete artifacts from a previous run merely because their stage is now
356
+ disabled.
357
+
358
+ ## Heatmaps
359
+
360
+ Heatmaps are named `uint8[resolution, resolution]` arrays stored in one
361
+ compressed NPZ archive. Values range from 0 to 255.
362
+
363
+ ```text
364
+ bear, berries, boar, chicken, corn, crocodile, flowers, hab, hemp,
365
+ horse, junkpiles, logs, modularcar, mushroom, ores, pedalbike,
366
+ potato, pumpkin, rowboat, snake, stag, tiger, wheat, wolf, wood
367
+ ```
368
+
369
+ ```python
370
+ import numpy as np
371
+
372
+ with np.load(result.heatmaps_file) as heatmaps:
373
+ ores = heatmaps["ores"]
374
+ print(ores.dtype, ores.shape, ores.max())
375
+ ```
376
+
377
+ Repeated population rules and splat samples are cached without changing final
378
+ NPZ bytes.
379
+
380
+ ## Server-style terrain rendering
381
+
382
+ The renderer ports Rust's ordered splat blends, height normals, lighting,
383
+ shoreline distance field, topology lookup, water depth, contrast, brightness,
384
+ and offshore color.
385
+
386
+ - With `full_size=False`, `map_render.png` / `.jpg` are convenient scaled renders.
387
+ - With `full_size=True`, only the native one-pixel-per-metre
388
+ `map_render_full.png` is rendered; scaled outputs are skipped.
389
+ - Optional tiles crop from the in-memory full render and do not change its bytes.
390
+
391
+ Tiles use a bottom-left origin, fixed RGBA dimensions, and transparent padding
392
+ on partial top/right edges. A size-4250 map produces 81 512px tiles; size-6000
393
+ produces 144.
394
+
395
+ ## Monuments
396
+
397
+ `monuments.json` includes deterministic gameplay monuments with:
398
+
399
+ - Resolved prefab path and world position
400
+ - Bottom-left `map_position` in metres
401
+ - Heading, display name, family, kind, environment, spawn group, and size class
402
+ - Safe-zone status, recycler count, keycards, puzzle type, and detected loot tier
403
+ - Searchable tags
404
+
405
+ Gameplay facts come from a sanitized, build-versioned component database
406
+ bundled with the package.
407
+
408
+ ## Underground train tunnels
409
+
410
+ The package ships 81 sanitized final-LOD tunnel puzzle-piece PNGs rasterized at
411
+ 8 pixels per metre. Normal exports need neither UnityPy nor installed Rust
412
+ assets.
413
+
414
+ - `tunnels.png` is the authoritative transparent layer.
415
+ - `tunnels_on_map.png` is written when a full terrain render is selected.
416
+ - Set `export_layer=False, export_overlay=True` for overlay-only output.
417
+ - Without terrain, tunnel export still succeeds and omits only the overlay.
418
+ - Build mismatches and missing templates are recorded instead of hidden.
419
+
420
+ Original Facepunch meshes are never shipped.
421
+
422
+ ## No-build zones
423
+
424
+ No-build export represents restrictions affecting building blocks. It keeps
425
+ useful circles and rotated rectangles, removes shapes fully contained by a
426
+ larger primitive from the same owner, and excludes deployable-only volumes,
427
+ capsules, mesh blockers, and redundant internals.
428
+
429
+ The transparent layer and JSON work without terrain. The composited preview is
430
+ added only when the selected terrain section includes a full-size render. Set
431
+ `export_images=False, export_json=True` for a fast JSON-only export that skips
432
+ rasterization and PNG encoding.
433
+
434
+ ## Low-level parsing
435
+
436
+ Skip the exporter entirely when you only need decoded data:
437
+
438
+ ```python
439
+ from rustmap import load_map
440
+ from rustmap.layers import biome_grid, splat_grid, topology_grid, world_height_grid
441
+
442
+ world = load_map("procedural.map")
443
+ height = world_height_grid(world)
444
+ water = world_height_grid(world, "water")
445
+ splat = splat_grid(world)
446
+ biome = biome_grid(world)
447
+ topology = topology_grid(world)
448
+ ```
449
+
450
+ ## Coordinates and orientation
451
+
452
+ | Space | Origin | Axes |
453
+ |---|---|---|
454
+ | Unity world | Map center | X right, Y elevation, Z up/north |
455
+ | Analytical arrays | Native `[z, x]` | No image transformation |
456
+ | Exported images | Top-left | X preserved; Z rows reversed |
457
+ | `map_position` | Bottom-left | X right, Y up, metres |
458
+ | Tile coordinates | Bottom-left | X right, Y up |
459
+
460
+ ```text
461
+ map_x = world_x + world_size / 2
462
+ map_y = world_z + world_size / 2
463
+ ```
464
+
465
+ For a world-size image, one metre equals one pixel. Convert bottom-left map Y
466
+ to an image row with `image_y = world_size - map_y`.
467
+
468
+ ## Performance
469
+
470
+ The exporter reuses decoded layers, splat samples, shoreline data, height
471
+ normals, and rule evaluations. Independent work uses bounded threads without
472
+ duplicating the full terrain state.
473
+
474
+ Recorded size-4500 timings on the development machine:
475
+
476
+ | Selection | Wall time |
477
+ |---|---:|
478
+ | Heatmaps only, no previews | 4.49 s |
479
+ | Gameplay layers only | 2.05 s |
480
+ | Map only | 18.95 s |
481
+ | Complete export | about 27 s |
482
+
483
+ Times depend on hardware, map size, enabled outputs, and PNG compressibility.
484
+
485
+ ## Refreshing Rust data
486
+
487
+ Maintainers refresh every packaged runtime resource with one script:
488
+
489
+ ```powershell
490
+ python -m pip install -e ".[assets]"
491
+ python refresh_all_data.py
492
+ ```
493
+
494
+ [`refresh_all_data.py`](refresh_all_data.py) stages and validates the prefab
495
+ manifest, spawn rules, monument gameplay metadata, no-build geometry, and 81
496
+ tunnel tiles. It checks Rust build IDs, bundle identities, counts, and machine
497
+ path leaks before replacing `src/rustmap/data`.
498
+
499
+ ## Development
500
+
501
+ ```powershell
502
+ $env:PYTHONPATH = "src"
503
+ python -m pytest -q
504
+ python -m build
505
+ ```
506
+
507
+ The suite covers configuration, selection presets, orientation, water behavior,
508
+ no-build containment, tunnel templates, map tiles, resources, and exact output
509
+ comparisons.
510
+
511
+ ## Project layout
512
+
513
+ ```text
514
+ rust-map-parser/
515
+ |-- src/rustmap/ # Package and implementation
516
+ | `-- data/ # Versioned sanitized runtime resources
517
+ |-- tests/ # Unit and regression tests
518
+ |-- docs/images/ # README showcase outputs
519
+ |-- example.py # Complete editable installed-package example
520
+ |-- refresh_all_data.py # Unified maintainer refresh
521
+ |-- pyproject.toml
522
+ `-- LICENSE
523
+ ```
524
+
525
+ ## Limitations
526
+
527
+ - Runtime resources are tied to their recorded Rust build.
528
+ - Unknown custom-map prefabs are reported and skipped rather than guessed.
529
+ - Runtime-only water volumes or engine floating-point details can differ from a
530
+ live server render.
531
+ - This repository does not include `.map` files, Facepunch source assets, or
532
+ original extracted meshes.
533
+
534
+ ## License
535
+
536
+ Original Python code is licensed under the [MIT License](LICENSE). Rust, its
537
+ formats, assets, names, and related intellectual property remain the property
538
+ of Facepunch Studios.