Valheim
Install with App

Details

Latest version
0.1.3
Last Updated
First Uploaded
Downloads
28
Likes
1
Size
52KB
Dependants

Categories

ADDatHost Valheim hosting
30% off

InventoryAnticheat

InventoryAnticheat keeps a server-owned inventory snapshot for each enrolled Valheim character. The installed plugin file remains CatosServerInventory.dll. At login, the server compares the client inventory report with the last accepted snapshot. During play, the server asks the client for a fresh report at the configured interval and writes the result to the server authority store.

The default setup is for one Valheim server. A trusted multiserver setup is available when all server instances can read and write the same authority directory.

Character requirement

Players must join a protected server with a new Valheim character created for that server. The first join establishes the character's clean inventory baseline. Do not bring an existing character into the protected setup. The mod will reject it because it is designed to keep that character's inventory original to its designated server.

For a trusted multiserver setup, create the character once as a new character for that server group. The same enrolled character can then use the servers that share the authority directory. If each server must keep separate characters and inventories, create a separate new character for each server and use Standalone mode.

What administrators need to install

Install the same CatosServerInventory.dll in BepInEx/plugins on:

  • the dedicated Valheim server;
  • every client that joins that server.

The server stores the authority records. The client component supplies the current inventory report when the server requests it. Valheim's dedicated server does not expose the live remote Player inventory object to this plugin, so the client component is required for checkpoint capture and login validation.

Use the same mod version on every server and client. Do not test with an older DLL left in another Gale or r2modman profile.

First setup: one server

Standalone mode is the default and is the right choice when the mod protects one server or when each server should have its own inventory authority.

  1. Install the DLL on the server and clients.

  2. Start the server once. BepInEx creates:

    BepInEx/config/com.catosvalheim.serverinventory.cfg
    
  3. Keep the default deployment settings:

    [Deployment]
    Mode = Standalone
    SharedAuthorityPath =
    ClusterId =
    NodeId =
    
  4. Start with EnforcementMode = AuditOnly.

  5. Join with a new character. A fresh Valheim character must have the configured starter items, which default to a Rag Tunic and Torch.

  6. Pick up a few ordinary items, wait for at least one snapshot interval, then reconnect.

  7. Check the server log and the local authority directory before switching to enforcement.

In Standalone mode, a blank PlayerDataDirectory stores records under the server's BepInEx config directory:

BepInEx/config/CatosServerInventory/players
BepInEx/config/CatosServerInventory/incidents

Each server has its own records in this mode. Do not use Standalone mode if a character must move between trusted Valheim servers.

Trusted multiserver setup

Shared mode is intended for trusted servers on the same machine, or for servers that can access one shared filesystem. Both Valheim processes must see the same directory and the same files. Giving both servers the same text path is not enough if a container, sandbox, namespace, VM, or hosting panel gives each process a private filesystem view.

Each server reads and writes the same character record when the player connects. The shared filesystem is the handoff. Players can choose which server to join through any method available in their setup.

Use different node IDs for every server. Use the same cluster ID and shared authority path on every server.

1. Choose shared storage

Choose an absolute directory that both server processes can read, write, lock, rename, and create subdirectories in. A local directory shared by two processes on one host is the simplest option. A network filesystem can work, but verify that it supports reliable file locking and atomic rename operations before trusting it with live character records.

The plugin creates these subdirectories inside the authority root:

players
incidents
nodes/<NodeId>

Create the root using the operating system's equivalent of an administrator-created directory. On Linux, the server account needs directory read, write, and execute permission. On Windows, grant the account or service identity running both Valheim servers Modify permission on the directory and its children.

Linux example:

sudo install -d -o <server-user> -g <server-group> -m 750 \
  /srv/valheim/shared/CatosServerInventory

Verify the write path as the actual server account before starting either server:

sudo -u <server-user> sh -c '
  set -eu
  d=/srv/valheim/shared/CatosServerInventory
  test -x "$d"
  probe="$d/.catos-write-test-$$"
  printf x > "$probe"
  rm -f "$probe"
'

2. Make the path visible to both processes

Check the isolation layer provided by the host, container runtime, service manager, or game panel. Disable per-server filesystem isolation, or mount the same host volume at the same path inside both server environments. The result must be one shared filesystem view, not two empty directories with the same name.

Verify this from both server environments:

  1. Create a temporary file through server A's view of the authority root.
  2. Confirm that server B can see and read it.
  3. Delete the file through server B's view.
  4. Confirm that server A no longer sees it.
  5. Confirm that both server processes can create and remove a lock file in players.

On Linux, compare the device and inode for the root with stat -c '%d:%i %n'. If the processes use separate mount namespaces, compare /proc/<pid>/ns/mnt for both processes. Different namespace values do not always mean the setup is impossible, but they require an explicit shared bind mount and a file visibility test.

3. Configure every server

Each server generates its own config at:

BepInEx/config/com.catosvalheim.serverinventory.cfg

Set the same shared path and cluster ID on every server. Change only NodeId between servers.

Server A:

[Deployment]
Mode = SharedCluster
SharedAuthorityPath = /srv/valheim/shared/CatosServerInventory
ClusterId = MyValheimCluster
NodeId = server-a

Server B:

[Deployment]
Mode = SharedCluster
SharedAuthorityPath = /srv/valheim/shared/CatosServerInventory
ClusterId = MyValheimCluster
NodeId = server-b

Use the real absolute path for your host. ClusterId is the logical name of the trusted group and must match exactly, including case. NodeId identifies the individual server and must be unique. Do not copy one server's nodes/<NodeId>/node-identity.json into another node's directory.

4. Restart and verify startup

Restart all server instances after changing the deployment settings. Each log should contain a line showing:

Mode=...; deployment=SharedCluster; ... storage=<your-shared-path>

The log must show the correct node configuration. If the plugin cannot write the authority directory, it stops shared-mode initialization with an explicit directory permission error.

5. Install the same DLL on every client

Every player who joins either server needs the same client DLL. The server and client must agree on the handshake protocol. A client with no CatosServerInventory plugin cannot complete the required inventory handshake when RequireClientHandshake = true.

6. Verify multiple servers

Use AuditOnly for the first multiserver test:

  1. Join server A with an enrolled character.
  2. Pick up an item and wait at least one snapshot interval, which is 15 seconds by default.
  3. Confirm that the shared player record's revision, timestamp, and NodeId advance.
  4. Stop or leave server A using the normal method for your setup.
  5. Join server B with the same character through the normal server-selection or connection method.
  6. Confirm that server B reads the existing record and accepts the inventory.
  7. Confirm that server B's next checkpoint writes the same character record with server B's NodeId.
  8. Review both logs for Accepted inventory and unexpected AuditOnly would reject messages.

Cross-server portal mods are optional. If you use one, it can move the player between these already-connected server instances, but CatosServerInventory does not require a portal mod and does not depend on a portal callback.

Once the route works cleanly, change this setting on every server:

[General]
EnforcementMode = Enforce

Restart all servers after changing enforcement settings.

Hosting-panel example

If a hosting panel provides per-server unshare, container, or sandbox isolation, disable that isolation for the participating servers or configure one shared volume inside every server environment. The setting name differs by panel. The important test is whether a file created through one server's path appears through the other server's path. Do not assume that matching absolute paths are enough.

Important configuration

The plugin creates the complete config file after its first startup. The main settings are:

Section and setting Default Purpose
General.Enabled true Enables the plugin.
General.EnforcementMode AuditOnly Logs mismatches and allows them in AuditOnly; disconnects mismatches in Enforce.
General.SnapshotIntervalSeconds 15 Seconds between client checkpoint requests.
General.RequireClientHandshake true Requires the client plugin to report an inventory at login.
General.HandshakeTimeoutSeconds 60 Login and checkpoint response timeout.
General.CheckpointQueuePerTick 1 Maximum number of server-side checkpoint jobs processed per Unity tick.
StarterPolicy.AllowNewCharacters true Allows enrollment when the starter inventory matches the policy.
StarterPolicy.AllowEmptyStarterInventory false Rejects empty new-character inventories by default.
StarterPolicy.StarterItems $item_chest_rags,$item_torch Exact fresh-character items allowed for enrollment.
Storage.PlayerDataDirectory blank Standalone authority path. Shared mode uses SharedAuthorityPath instead.
Storage.BackupCount 3 Previous snapshots retained per character.
Deployment.RestorePolicy HoldForReview Controls the optional native restore path after a mismatch.
Deployment.AuthorityLockTimeoutSeconds 5 Maximum wait for a per-character authority lock.
Discord.Enabled false Enables rejection and incident notifications.

The Discord webhook URL is server-only configuration. Do not put it in a client profile or publish it in a package.

Discord webhook notifications

InventoryAnticheat can send useful admission events to a Discord channel through a standard Discord incoming webhook. This gives server staff a record of suspicious joins without requiring them to watch the Valheim console.

The notification includes:

  • the decision, such as Rejected, AuditOnlyWouldReject, or Restored;
  • the player name and incident ID;
  • the rejection reason;
  • readable names and quantities for items that were not present in the server snapshot.

Enable it in the server's BepInEx config:

[Discord]
Enabled = true
WebhookUrl = https://discord.com/api/webhooks/your-webhook-url
IncludePlayerId = false

Set IncludePlayerId = true only when your staff needs the Valheim player ID in the alert. Keep the webhook URL on the server. Do not copy it into a client profile, commit it to source control, or include it in a Thunderstore package. In a multiserver setup, configure the webhook separately for each server that should report events. You can use one staff channel for the whole group or give each server its own channel.

Use AuditOnly while testing. Discord will report what the mod would reject while still allowing the player to continue, so staff can verify the alert details before switching to Enforce.

How the authority record works

The server keeps one JSON record per character in the authority directory. Each successful checkpoint increments Revision, records the inventory content hash, and writes the file atomically. The previous records remain as .bak1.json, .bak2.json, and .bak3.json by default.

The client does not hold the authority record. It reports its current inventory to the server. The server checks the character identity, loads the stored record, assigns the next revision, and writes the snapshot. At login, the server compares the new client report with the stored content hash before allowing or rejecting the connection.

This design protects the shared server record and makes multiserver validation possible. It is not a cryptographic proof against a modified client. Use trusted client modpacks and server-side anti-cheat controls alongside this mod when that distinction matters.

Troubleshooting

The config file is missing

Start the server once with the DLL installed in BepInEx/plugins. BepInEx creates com.catosvalheim.serverinventory.cfg under that server's BepInEx/config directory. The mod does not read template.json as its runtime config.

The server says no snapshot was recorded

Check that the character completed enrollment, that the server can write the authority directory, and that all servers use the same shared path and cluster ID. In shared mode, check that the hosting layer did not put the server processes in separate private filesystem views.

The snapshot revision does not advance

Check that the client has the same DLL, that RequireClientHandshake is not being blocked by a client load error, and that the server log shows the plugin requesting and receiving checkpoints. A dedicated Valheim server cannot capture a remote inventory without the client component.

A server connection is rejected

Start with AuditOnly and read the log entry and incident file. The webhook evidence lists extra inventory quantities in readable item names. Confirm that the destination server can read the same current authority record before changing to Enforce.

Shared mode refuses to start

Confirm that Mode is exactly SharedCluster, SharedAuthorityPath is absolute, ClusterId is not blank, and NodeId is unique and not blank. Then run the Linux write probe as the same user that runs Valheim.

Build from source

dotnet build src/CatosServerInventory/CatosServerInventory.csproj -c Release

The Release DLL is written to:

src/CatosServerInventory/bin/Release/net48/net48/CatosServerInventory.dll

Copy that DLL into BepInEx/plugins on every server and client before testing the build.

Package contents

The Thunderstore upload ZIP contains manifest.json, README.md, CHANGELOG.md, icon.png, and the compiled CatosServerInventory.dll at the package root. Upload the ZIP itself, not the source repository archive.

Thunderstore development is made possible with ads. Please consider making an exception to your adblock.