Romania XPlayZM.CSBlackDevil.COM # Zombie Plague UltimateX [ Respawn + FDL + VIP 22-12PM + POINTS] ® 25/32
Unknown ❤ RESPAWN.ZAFEST.RO | VIP FREE | FREE FOR ALL ❤ 0/32
France EMIRATES.KiNGS 亗 GAMEAREA ||͇̿P͇̿U͇̿B͇̿L͇̿I͇̿C͇̿| ✪ 32/32
Promover servidor
C counter-strike-boost.com 6afd8e0bd6 Add fix-nvault-corrupted/en.md
7 min read 21 visualizações Markdown Source Rendered Raw

Fix: nVault Corrupted or Reset (AMX Mod X Vault Data Lost)

nVault is the AMX Mod X module behind most plugins that save XP, money or settings between maps. Each vault is one binary file of key/value pairs, usually keyed by SteamID. The format has no checksum, and the module never reports a file that failed to load: it starts with whatever it managed to read and writes that back over the file at the end of the map. That is why a damaged vault so often becomes permanent data loss, and why step one of any recovery is to copy the file before the map changes.

1. Where vault files live and when they are written

<code>cstrike/addons/amxmodx/data/vault/<name>.vault
cstrike/addons/amxmodx/data/vault/<name>.journal</code>

<name> is the string the plugin passes to nvault_open(); search the plugin's .sma for it if the file name is not obvious. If you changed amxx_datadir in configs/core.ini, the folder follows it. Windows uses the same layout. The lifecycle, from the module source:

  • Open (normally at map start): the whole .vault is read into memory, any leftover .journal is replayed on top, then the journal is deleted and started fresh.
  • During the map: every set or remove is appended to the .journal. The .vault is not touched.
  • Close (map change, nvault_close(), clean shutdown): the .vault is truncated and rewritten in place from memory, then the journal is deleted. No temporary file, no rename.

So a crash in the middle of a map is usually survivable: the journal is replayed at the next start, minus the last few writes still sitting in a buffer. The dangerous moment is the rewrite at map end.

2. Match the symptom

  • Everyone's data reset at once, no error anywhere: the file header was unreadable, so the vault opened empty. The next map change writes that over the old file.
  • Some players kept their data, others lost it: the file is truncated. nVault loads the entries before the break and silently drops the rest.
  • Progress saves during a map, then reverts next map: the HLDS user cannot write the file, typically because the server was once started as root. The rewrite fails and nothing is logged.
  • Invalid vault id: -1 with run time error 10 on nvault_get or nvault_set: nvault_open() failed because the vault folder or file could not be created. Any other number means the handle was closed under the plugin. See run time error 10.
  • Map changes take seconds and the file is many megabytes: the vault was never pruned, and every map change reads and rewrites all of it.
  • Crash at map change on AMX Mod X 1.8.2: 1.8.2 has two nVault crash bugs fixed in later versions, triggered by an empty key or value in the vault and by a journal it cannot write. Upgrade.

3. Check the file

A healthy vault starts with the four bytes TLVn. A zero-byte file is normal only for a vault created during the current map.

<code>head -c 4 xpmod.vault; echo</code>

For a full check, save this as nvcheck.py and run python3 nvcheck.py xpmod.vault against a copy. Any machine with Python 3 will do, including a Windows PC.

<code>import struct, sys
data = open(sys.argv[1], "rb").read()
if len(data) < 10 or data[:4] != b"TLVn":
    sys.exit("BAD: no nVault header (file must start with TLVn)")
version, entries = struct.unpack_from("<Hi", data, 4)
pos, good = 10, 0
while good < entries and pos + 7 <= len(data):
    stamp, klen, vlen = struct.unpack_from("<iBH", data, pos)
    if pos + 7 + klen + vlen > len(data):
        break
    pos += 7 + klen + vlen
    good += 1
print("header says %d entries, %d readable" % (entries, good))
if version != 0x0200 or good != entries or pos != len(data):
    sys.exit("BAD: damaged after entry %d" % good)
print("OK")</code>

It walks the file the way the module does. Anything but OK is a damaged file. OK with everyone reset means the keys changed, not the file: a new Reunion salt re-rolls every non-Steam SteamID.

4. Recover

  1. Copy the vault and journal now, before the map changes. Mid-map the .vault is not being written, so the copy is safe. At the next map change or clean shutdown, the module overwrites it with whatever it holds in memory.
    <code>mkdir -p ~/vault-rescue
    cd /home/steam/hlds/cstrike/addons/amxmodx/data/vault
    cp -p xpmod.* ~/vault-rescue/</code>
  2. Stop the server. Files restored while it runs are overwritten at the next map change.
  3. Restore from a backup if you have one. Copy the backup's .vault into place and delete the current .journal. That journal holds this session's writes, including the zeroed values the plugin saved for players after the reset; replaying it onto the backup would wipe them again.
  4. Without a backup, salvage or accept the loss. If the checker shows most entries readable (a truncated file), start the server with it: the module keeps every complete entry and writes a clean file at map end. If the header is gone, rename both files to .bad and let the plugin start a fresh vault. Viewers listed on the AlliedModders wiki (nVault Reader, nVault Editor) can show a readable file's contents; nothing rebuilds a missing header.
  5. Fix the cause from the list below before starting again, then watch one full map change.

Common causes

  • Server killed during the map-end save: kill -9, the out-of-memory killer, a reboot or a panel's force stop. The bigger the vault, the wider the window. Stop servers with quit, not by killing the process.
  • Disk full: the save truncates the file, fails partway, and the journal is deleted right after. This is the worst case; check df -h whenever a vault resets.
  • Wrong owner: fix it with chown -R steam:steam /home/steam/hlds/cstrike/addons/amxmodx/data and never start the server as root.
  • Two servers, one vault: several instances started from the same directory, or copies made with cp -al as in multiple servers on one VPS. A vault that existed when you made the copy stays hardlinked, because nVault rewrites it in place. Each instance saves over the other's data and deletes the other's journal. Find shared files with find /home/steam -path '*data/vault/*' -links +1, then, with the servers stopped, give each instance its own file: cp -p xpmod.vault xpmod.tmp && mv xpmod.tmp xpmod.vault.
  • Two plugins, one vault name: on one server they share a single handle and key space, so one plugin's SteamID key overwrites the other's. If one closes the vault in plugin_end first, the other's final saves fail with Invalid vault id. Give every plugin its own vault name.
  • Careless copies: a copy taken during a map change can catch a half-written file, and one without the .journal misses the current map. FTP in ASCII mode or a text editor mangles the binary file.
  • Oversized keys: the format stores a key's length in one byte (255 max) and a value's in two (65535 max), and the module does not check. A plugin that builds longer keys writes a record that breaks every entry after it. Fix the plugin's key format.

5. Prevention

Back up the vault folder every few hours with dated archives, journals included, and keep many generations so one copy taken mid-save does not matter:

<code>#!/bin/bash
SRC=/home/steam/hlds/cstrike/addons/amxmodx/data/vault
DST=/home/steam/backups/vault
mkdir -p "$DST"
tar czf "$DST/vault-$(date +%F_%H%M).tar.gz" -C "$SRC" .
ls -1t "$DST"/vault-*.tar.gz | tail -n +57 | xargs -r rm --</code>

Run it from the steam user's crontab with 0 */6 * * *; 56 archives is two weeks. Getting copies off the box is covered in backing up configs, bans and stats.

Prune in code if you maintain the plugin. Entries saved with nvault_set() carry a timestamp, so dropping players not seen for 30 days is one call after opening:

<code>public plugin_cfg()
{
    g_vault = nvault_open("xpmod")
    if (g_vault == INVALID_HANDLE)
        set_fail_state("Cannot open vault xpmod")

    nvault_prune(g_vault, 0, get_systime() - 30 * 86400)
}</code>

Entries written with nvault_pset() have no timestamp and are never pruned, so do not use it for per-player data. The set_fail_state() line turns a silent failure into a visible one.

Move structured data to SQLite. Anything with several fields per player, or that you want to sort into a top 15, belongs in a real database: see the SQLite module. nVault has no native that lists its keys, so migrate lazily: when a player connects and is missing from SQLite, read their old value with nvault_lookup(), insert it, then nvault_remove() the key.

Verification checklist

  • nvcheck.py reports OK on the live vault after a map change.
  • The vault and journal are owned by the HLDS user, and the find ... -links +1 check returns nothing.
  • A value changed in-game survives a map change and a full restart.
  • The error log has no Invalid vault id lines.
  • The latest backup archive exists and its .vault passes the checker.
Compartilhar: