Skip to content

Custom models

A Neon custom model has two identities: a stable ID used by the server and network, and a physical GTA slot chosen locally by each client. This page covers that model registry only; city archives and native world packs have their own ownership and lifecycle.

The registry covers objects, vehicles, peds, players, buildings, pickups, spawn packets, RPCs, names, enumeration, type queries, capacity queries, and lifecycle fallback. The initial object and vehicle work is tracked by a7a20d32b, and the completed cross-element registry by dc25a615c.

server logical ID (42341–65534) ──network identity──> client definition
├─ native parent fallback
└─ client-local GTA runtime slot

In practice:

  • logical IDs run from 42,341 through 65,534 and are not reused during the server process; 65,535 remains the invalid-model sentinel;
  • each definition has a resource owner, type, native parent, and optional qualified name;
  • each client may allocate a different runtime slot for the same logical ID;
  • clients without an active runtime slot use the native parent;
  • freeing a definition remaps surviving elements before runtime slots disappear;
  • resource shutdown performs the same cleanup automatically;
  • native ped and player render entities are prepared before slot release to avoid stale CBaseModelInfo references.

The model API group contains allocation, freeing, metadata, naming, enumeration, capacity, and forward/reverse mapping functions. Commit ac3a54f57 moved the server range above Neon’s complete FileID layout and made the standard client model consumers resolve logical IDs before touching GTA slots.

Server element APIs use the stable logical ID:

local logical = assert(engineRequestModel("vehicle", 411, "mission_car"))
local vehicle = createVehicle(logical, 0, 0, 3)

Client replacement APIs operate on GTA slots, so resolve the local runtime ID first:

local runtime = engineGetModelRuntimeID(logical)
if runtime then
engineReplaceModel(dff, runtime)
end

Do not save or synchronize a runtime ID as the model identity. That number only makes sense on one client for the lifetime of its current allocation.

These systems can use the same low-level GTA model and streaming primitives, but they do not share ownership:

SystemWhat it ownsLifecycle
Custom model registryStable server IDs, native parents, network definitions, and client runtime-slot mappingsResource and connection
Resident IMG citiesRuntime IMG archives, bounded DFF/TXD pools, preload barriers, and city switchingWhile GTA is running
Native world packsAudited IDE/IMG/COL/IPL payloads, immutable cache objects, and startup authorizationStartup and process lifetime

Allocating a server model does not register a city archive or authorize a native world pack. Resident IMG and Native World deliberately remain separate paths.

server-model-registry-test covers:

  • allocation, names, types, enumeration, and quotas;
  • object, vehicle, ped, building, pickup, and spawn paths;
  • client runtime and reverse mappings;
  • replacement and LOD APIs;
  • release while elements survive;
  • resource cleanup and parent fallback.

Runtime checks cover spawn and respawn, model replacement, safe freeing, and the post-free crash regression.

This evidence applies to the registry and its cleanup paths. It does not validate a resident IMG city or authorize a native world pack; those systems have separate lifecycles and tests.