Skip to content

About

A location logging app for NMS. Reads save files every so often based on configuration, records location details and adds them to a log file.

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

NMS Location Logger

A small Windows console app that watches your No Man's Sky save folder, decodes the most recently modified save, and appends your galaxy and galactic coordinates to a log file every X minutes.

Files

File Purpose
nms_location_logger.py The application (pure standard library, no dependencies)
config.ini All user-configurable settings
build_exe.bat Builds dist\NMSLocationLogger.exe with PyInstaller
galaxies.txt (optional) Your own galaxy-name list, one per line, in order

Quick start

Run straight from Python:

python nms_location_logger.py

Or build a standalone exe (Python only needed on the build machine):

build_exe.bat

Then edit dist\config.ini and run dist\NMSLocationLogger.exe. A different config path can be passed as the first argument:

NMSLocationLogger.exe "D:\configs\my_nms.ini"

Press Ctrl+C to stop. To run it automatically, create a Task Scheduler task ("At log on", action = the exe, "Start in" = the exe's folder).

Configuration

Everything lives in config.ini. %APPDATA% and %USERPROFILE% are expanded.

  • save_directory – defaults to %APPDATA%\HelloGames\NMS. The real saves sit in a st_<steamid> subfolder, which is why search_subfolders = true.
  • save_file_pattern – save*.hg. Files beginning with mf_ are always skipped; they are metadata companions, not saves.
  • output_file – log file to append to (created if missing).
  • interval_minutes – how often to check.
  • lock_retry_seconds – how long to keep retrying while the file is locked.

Sample output

2026-09-17 16:30:26 | save2.hg | Eissentam (10) | 0796:0083:0D09:0155 | 315585D0B798 | SSI 341 | Planet 3 | Voxel -105,4,1290

Columns: timestamp, save file, galaxy, signal-booster coordinates, portal glyph address, solar system index, planet index, and the raw voxel values.

How the locking is handled

The game keeps the save open while writing it, so each read does three things:

  1. Lock probe – CreateFileW with dwShareMode = 0 and GENERIC_READ. If Windows returns ERROR_SHARING_VIOLATION, another process has it open. Nothing is ever opened for writing.
  2. Stat sandwich – size and modification time are compared before and after the read, so a file that changed mid-read is rejected.
  3. Structural validation – the chunk headers must be intact and the decompressed payload must parse as complete JSON.

Any failure triggers an exponential back-off (0.5s, 1s, 2s, 4s...) for up to lock_retry_seconds (default 15). If it still can't be read, that cycle is skipped and the next one runs at the normal interval.

How the save is decoded

Modern .hg saves are a sequence of chunks, each with a 16-byte header (magic 0xFEEDA1E5, compressed size, decompressed size, padding) followed by an LZ4 block-format payload. The app concatenates the decompressed chunks to get the JSON. An LZ4 decoder is included in pure Python; if the lz4 package happens to be installed it will be used instead for speed. Older uncompressed saves are also handled.

About the obfuscated keys

Hello Games obfuscates the JSON key names (PlayerStateData becomes 6f=, and so on) and these mappings change between game versions. Rather than hard-coding a mapping that breaks on every update, the app finds the player's address structurally: it looks for the dictionary shaped like {RealityIndex: int, GalacticAddress: {5 ints}} sitting one level inside the largest top-level section — which is PlayerStateData. If the save is not obfuscated, the real key names are used directly. Values are range-checked and a warning is printed if they look wrong.

Coordinate conventions

From the voxel values in the save:

Galactic (signal booster)      Portal glyph address
X = (VoxelX + 2047) & 0xFFF    X = (VoxelX + 2049) & 0xFFF
Y = (VoxelY +  127) & 0xFF     Y = (VoxelY +  129) & 0xFF
Z = (VoxelZ + 2047) & 0xFFF    Z = (VoxelZ + 2049) & 0xFFF
S = SolarSystemIndex           format: P SSS YY ZZZ XXX

A voxel of 0,0,0 therefore gives the portal address ...81 801 801, which is the familiar pattern for the galactic centre. Worth confirming once against an in-game signal booster reading; if your build ever disagrees, the four offset constants are at the top of nms_location_logger.py.

Fleet tracker (optional)

Set updateurl = Y in the [fleet] section of config.ini and every time a location is logged, the app also sends it to a fleet-tracking URL:

https://fleet.nms-agt.com/?travellerName=...&password=...&coordinate=...&galaxyNumber=...&fleetName=...&fleetType=...&status=...&notes=...

Field rules, enforced when the config is loaded (invalid values are logged as a warning and replaced with the default shown):

Field Rule If missing/invalid
travellername up to 42 characters updateurl is forced to N
password exactly 5 alphanumeric characters updateurl is forced to N
fleetname up to 42 characters "Null"
fleettype one of Explorer, Shuttle, Fighter, Hauler, Exotic, Living Ship, Solar, Sentinel, Freighter, Corvette (case-insensitive) "Null"
status up to 25 characters "Null"
updateurl Y or N (case-insensitive) N

notes is not a config setting — it's read automatically from the save file's own save-summary text (see below) each time a location is logged, up to 50 characters, defaulting to "Null" if it can't be read.

The URL is sent with a 10-second timeout (configurable as url_timeout_seconds under [advanced]). If the send fails - no internet, server down, timeout - it's logged as a warning and the app carries on normally; it never stops the location log from being written.

Fleet tracker request log

Every send attempt - success or failure - is also recorded in FleetTrackerSendRequest.log, created next to your main output_file. Each line has the timestamp, the exact URL that was sent, and the outcome:

2026-09-24 15:57:20 | https://fleet.nms-agt.com/?travellerName=... | HTTP 200
2026-09-24 15:58:20 | https://fleet.nms-agt.com/?travellerName=... | HTTP 500 (error response)
2026-09-24 15:59:20 | https://fleet.nms-agt.com/?travellerName=... | No response received (<urlopen error ...>)

"HTTP 200" etc. is the status code the server actually returned. "No response received" means the request never got a reply at all - no internet, DNS failure, connection refused, or timeout - so you can tell that apart from the server replying with an error.

About the "notes" value

This is the game's own save-summary text: the line shown on the file-select/ loading screen. It's read directly from the save file, not typed into config.ini. Note that this text is whatever the game currently has set for that context - in practice this is often the name of the base, freighter, or ship the player is at/aboard, not necessarily the star system's name. It's included here verbatim because that's what the field actually is; treat it as informational context, not a system name.

The obfuscated key for this field is set in config.ini under [advanced] save_summary_key (default vxi, correct as of the game version this was built against). Hello Games changes this obfuscation on some updates; if notes always comes back "Null", that key likely needs updating - open a save file with a JSON viewer and look for a short, human-readable string directly inside PlayerStateData.

Galaxy names

The first 32 galaxy names are built in; anything beyond that logs as Galaxy #N. To get full names, drop a galaxies.txt next to the exe with one name per line in galaxy order (Euclid first) and it will be used instead.

About

A location logging app for NMS. Reads save files every so often based on configuration, records location details and adds them to a log file.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages