Skip to content

Encrypted resource assets

Drop DFF, TXD, and COL files into the Neon Asset Encrypter, enter the exact resource name, then download the generated ZIP. Neon clients receive authenticated ciphertext in their resource cache instead of reusable model files. Choosing which native or custom model to replace remains a runtime Lua decision.

The encrypter runs entirely in the browser. Files and keys are not uploaded to the wiki or Vercel.

  1. Open the Asset Encrypter.
  2. Enter the resource folder name exactly as it will appear on the server.
  3. Drop one or more .dff, .txd, or .col files. Files with the same base name become one TXD/DFF/COL group.
  4. Select Encrypt files and download ZIP.
  5. Extract the ZIP into the resource and merge the generated snippets.

The ZIP contains:

FilePurpose
models/*.neonassetClient-downloadable authenticated ciphertext
neon-assets.keyServer-only 256-bit content key
meta.xml.snippetPackage descriptor and client-file declarations
neon-assets-client.luaGrouped TXD, DFF, and COL helper without hard-coded model IDs
INSTALL.txtShort setup checklist with a custom-model example

The resource name and every .neonasset path are authenticated. Renaming the resource, moving a container, or changing its bytes makes loading fail instead of silently applying different data.

Merge the generated entries inside the existing <meta> element:

<neon_assets package="00112233445566778899aabbccddeeff" keyfile="neon-assets.key" />
<file src="models/taxi.txd.neonasset" neon_asset="true" />
<file src="models/taxi.dff.neonasset" neon_asset="true" />
<file src="models/taxi.col.neonasset" neon_asset="true" />
<script src="neon-assets-client.lua" type="client" />

Keep neon-assets.key in the server resource directory, but never declare it as a <file>, <script>, or other downloadable item. Neon rejects a package whose key file is client-visible.

Use one generated key and package ID for all protected files in the same package. Generate a new package when rotating the key.

The generated script defines neonReplaceEncryptedAssetGroup. Pass it a native model ID, model name, or custom model ID obtained through your model-allocation flow:

-- Native replacement
neonReplaceEncryptedAssetGroup("taxi", 411)
-- The same helper also accepts a custom model ID made available to this client.
neonReplaceEncryptedAssetGroup("custom-taxi", customModelId)

The helper applies TXD before DFF and COL for each matching filename group. Internally, engineReplaceEncryptedModel authenticates, decrypts, validates, and applies each asset as one native operation. Neither the key nor plaintext is returned to Lua. The returned TXD, DFF, or COL elements belong to the calling resource and are cleaned up when that resource stops.

The optional third argument enables DFF alpha transparency. The fourth controls TXD filtering:

engineReplaceEncryptedModel(path, modelId, true, false)

Version 1 supports DFF, TXD, and COL files up to 64 MiB each. Clothing-model targets are rejected because their RenderWare path retains source buffers longer than ordinary model replacements.

This prevents trivial extraction of usable models from the downloaded resource cache. It cannot make a client-rendered asset impossible to capture: a determined user can inspect plaintext or key material while the game is actively using it. This is the practical limit of client-side protection, including systems such as pcrypt.

Encrypted packages require matching Neon client and server builds on the network epoch that introduced the asset transport. They do not load on an ordinary MTA client.

The authenticated container, server metadata, resource-scoped key transport, native loader, browser-compatible packer format, and memory cleanup were introduced in 6b7965afb.

The implementation was built as Client Deathmatch Release Win32 and Deathmatch Release x64. The focused test-resources/neon-encrypted-assets checkpoint was checked in game with one connected client using a real WOSA TXD/DFF/COL triplet. The client authenticated and replaced all three files; the server loaded 316 resources with no resource failures. This was a single-client check, not a multiplayer validation.