Skip to content

VehicleRedirectorAsset — Vehicle Redirectors

Migrating legacy vehicle IDs to consolidated vehicle assets while preserving paint colors requires understanding how VehicleRedirectorAsset intercepts spawn and load requests, resolves to target vehicle assets, and applies LoadPaintColor and SpawnPaintColor overrides. VehicleRedirectorAsset enables seamless transition from legacy vehicle IDs to consolidated assets. When a legacy vehicle ID is loaded or spawned, the redirector intercepts the request and creates a different (consolidated) vehicle instead, optionally preserving paint colors from the original vehicle.

Source code location: Unturned/Bundles/VehicleRedirectorAsset.cs

Inheritance Chain

Asset
  → VehicleRedirectorAsset

Purpose

In earlier versions of Unturned, each color variant of a vehicle had its own item ID (e.g., Off_Roader_Orange = 4, Off_Roader_Blue = 5, etc.). When vehicles were consolidated into single assets with a paint color system, these legacy IDs needed to resolve to the new consolidated asset while preserving the original color.

A VehicleRedirectorAsset is assigned the legacy item ID. When the game attempts to load or spawn that ID:

  1. The redirector intercepts the request.
  2. The TargetVehicle reference resolves to the consolidated vehicle asset.
  3. If LoadPaintColor is set, that color is applied when loading from saves.
  4. If SpawnPaintColor is set, that color is applied when spawning new vehicles.

Class Definition

csharp
public class VehicleRedirectorAsset : Asset
{
    public override EAssetType assetCategory => EAssetType.VEHICLE;

    public AssetReference<VehicleAsset> TargetVehicle { get; protected set; }
    public Color32? LoadPaintColor { get; protected set; }
    public Color32? SpawnPaintColor { get; protected set; }
}

Core Fields

FieldType.dat KeyDescription
TargetVehicleAssetReference<VehicleAsset>TargetVehicleThe consolidated vehicle to redirect to
LoadPaintColorColor32?LoadPaintColorPaint color applied when loading from save files
SpawnPaintColorColor32?SpawnPaintColorPaint color applied when spawning new vehicles

Asset Category

csharp
public override EAssetType assetCategory => EAssetType.VEHICLE;

The redirector advertises itself as EAssetType.VEHICLE so that legacy vehicle IDs resolve through the redirector in the asset registry. When the game looks up a vehicle by ID, it finds the redirector instead of the original vehicle — the redirector then resolves to the consolidated asset.

Load Paint Color

When a save file references the legacy vehicle ID, the redirector's LoadPaintColor preserves the original vehicle's appearance in the save. Without this, saved vehicles would appear with the consolidated vehicle's default random color, which might not match the original.

Spawn Paint Color

When a spawn table or world spawn references the legacy vehicle ID, SpawnPaintColor ensures newly spawned vehicles match the original color. This preserves color diversity in spawn tables — without it, all spawns of a given legacy ID would get the same random color distribution as the consolidated asset.

PopulateAsset

csharp
public override void PopulateAsset(in PopulateAssetParameters p)
{
    base.PopulateAsset(in p);

    TargetVehicle = p.data.readAssetReference<VehicleAsset>("TargetVehicle");

    if (p.data.TryParseColor32RGB("LoadPaintColor", out Color32 loadPaintColor))
        LoadPaintColor = loadPaintColor;

    if (p.data.TryParseColor32RGB("SpawnPaintColor", out Color32 spawnPaintColor))
        SpawnPaintColor = spawnPaintColor;
}

The TargetVehicle reference is read via readAssetReference<VehicleAsset> which resolves the referenced asset by GUID or legacy ID. Both paint colors are optional — parsed via TryParseColor32RGB which only sets the value if the key exists and contains valid color data. If absent, the colors remain null and the consolidated vehicle's default paint system handles color assignment.

Redirection Flow

Vehicle Loading (from Save File)

  1. Save file contains a legacy vehicle ID (e.g., 4 for Off_Roader_Orange).
  2. Asset registry resolves ID 4 to the VehicleRedirectorAsset.
  3. Redirector provides TargetVehicle — the consolidated Off_Roader.
  4. If LoadPaintColor is set, that color replaces the saved color.
  5. The consolidated vehicle is loaded with the correct paint.

Vehicle Spawning (Spawn Table / World)

  1. Spawn table entry references legacy vehicle ID 4.
  2. Asset registry resolves to the redirector.
  3. Redirector provides the consolidated vehicle.
  4. If SpawnPaintColor is set, it overrides the consolidated vehicle's default random paint.
  5. The vehicle spawns with the preserved color.

Use Cases

Color Variant Consolidation

The primary use case: Off_Roader_Orange, Off_Roader_Blue, etc. become a single Off_Roader vehicle with paint color support. Each old ID gets a redirector pointing to the consolidated asset with the appropriate paint color.

Content Updates

When a vehicle model is updated but old saves reference the previous asset: a redirector can map the old asset to the new one while preserving appearance.

Seasonal / Event Variants

Seasonal vehicle variants (e.g., Halloween Off_Roader) can be redirected to the standard vehicle post-event, with or without preserved special colors.

Comparison to AssetConsolidation

VehicleRedirectorAsset is vehicle-specific. The more general AssetConsolidation system (in Assets.cs) handles similar mappings for generic assets through the consolidation table. The vehicle redirector exists specifically because vehicles need paint color preservation, which the generic system does not support.

FeatureVehicleRedirectorAssetAssetConsolidation
ScopeVehicles onlyAll asset types
Paint color preservationYes (two modes)No
Category registrationEAssetType.VEHICLEConsolidation table
Runtime resolutionThrough asset lookupThrough consolidation path

Common Issues

  1. Missing TargetVehicle — If TargetVehicle is null or references a missing asset, the redirector resolves to nothing. The spawn or load silently fails with no vehicle appearing.
  2. Paint color not set — If neither LoadPaintColor nor SpawnPaintColor is set, the consolidated vehicle uses its own default paint system. The legacy vehicle's original color is lost.
  3. Both paint colors set — Setting both LoadPaintColor and SpawnPaintColor is valid but may be redundant if the intent is identical for load and spawn. Setting different colors for each is intentional for distinguishing between "what the vehicle looked like when saved" and "what new vehicles should look like."
  4. Color formatTryParseColor32RGB expects RGB values (3 components, 0-255 range). A fourth component (alpha) is ignored. Colors specified in hex or named formats may fail to parse.
  5. Circular redirects — A redirector whose TargetVehicle is another redirector would create a chain. The game expects direct resolution; circular or long chains may not be validated at asset load time.

Worked Code Example: Redirector Validation

csharp
using SDG.Unturned;
using System.Collections.Generic;

public static class RedirectorValidator
{
    /// <summary>
    /// Validates all VehicleRedirectorAssets in the registry, detecting
    /// circular chains, missing targets, and duplicate mappings that
    /// could cause silent vehicle load failures.
    /// </summary>
    public static List<string> ValidateAllRedirectors()
    {
        List<string> issues = new List<string>();
        Dictionary<System.Guid, VehicleRedirectorAsset> redirectors =
            new Dictionary<System.Guid, VehicleRedirectorAsset>();

        foreach (Asset asset in Assets.find(EAssetType.VEHICLE))
        {
            VehicleRedirectorAsset redirector = asset as VehicleRedirectorAsset;
            if (redirector == null) continue;

            if (redirector.TargetVehicle == null || redirector.TargetVehicle.isNull)
            {
                issues.Add($"Redirector {redirector.GUID}: TargetVehicle is null or missing");
                continue;
            }

            // Check for circular chains
            VehicleRedirectorAsset chainCheck = redirector;
            for (int depth = 0; depth < 10; depth++)
            {
                VehicleAsset target = chainCheck.TargetVehicle.Find();
                if (target is VehicleRedirectorAsset nextRedirector)
                {
                    if (nextRedirector == redirector)
                    {
                        issues.Add($"Redirector {redirector.GUID}: Circular chain detected");
                        break;
                    }
                    chainCheck = nextRedirector;
                }
                else break;
            }
        }
        return issues;
    }
}

/// <summary>
/// Resolves a legacy vehicle ID through any redirector chain to find
/// the final consolidated VehicleAsset, applying paint color overrides
/// from each redirector in the chain.
/// </summary>
public static VehicleAsset ResolveLegacyVehicle(ushort legacyId, out Color32? paintColor)
{
    paintColor = null;
    VehicleRedirectorAsset redirector = Assets.find(EAssetType.VEHICLE, legacyId)
        as VehicleRedirectorAsset;

    while (redirector != null)
    {
        if (redirector.SpawnPaintColor.HasValue)
            paintColor = redirector.SpawnPaintColor;

        VehicleAsset target = redirector.TargetVehicle.Find();
        redirector = target as VehicleRedirectorAsset;

        if (redirector == null)
            return target;
    }

    return null;
}

Mermaid Diagram: Vehicle ID Resolution

Failure Modes and Common Mistakes

  1. Redirector ID conflict — If a redirector occupies the same item ID as a real vehicle asset, the redirector wins (asset registry first-match behavior). The real vehicle becomes inaccessible. Always ensure redirector IDs are unique and do not overlap with active asset IDs.

  2. Redirector chains breaking between updates — If the TargetVehicle GUID changes (e.g., vehicle asset is regenerated with a new GUID), the redirector silently breaks. All legacy vehicle spawns referencing it produce no vehicle.

How This Differs from SDG Docs

  • SDG docs describe redirectors as "deprecated migration code." In the SDK, redirectors are live runtime assets in the VEHICLE category. They are not deprecated — they are the active mechanism for backward compatibility.
  • SDG docs claim redirectors only work on spawn. The documentation focuses on spawn tables. In the SDK, redirectors also handle save-file loads (LoadPaintColor), making them critical for preserving existing player vehicles across updates.

Performance Considerations

One asset lookup per vehicle spawn or load. O(1) hash table access in the asset registry. With 200 redirector assets, a lookup is approximately 0.001ms. Paint color application is a single struct assignment. Redirector chain traversal is limited to loaded assets only, not per-frame iteration.

Deeper FAQ

Q: Can I create a redirector that maps to a custom vehicle mod?

Yes. Set the TargetVehicle to the GUID of the custom mod's VehicleAsset. The redirector bridges vanilla legacy IDs to custom replacement vehicles, useful for server-specific migration projects.

Q: What happens if both LoadPaintColor and SpawnPaintColor are null?

The consolidated vehicle uses its own default paint randomizer. For saved vehicles, the original paint color is lost — the vehicle may respawn with a random color. For spawned vehicles, they get the consolidated asset's default random color distribution.

Q: Can a redirector point to a vehicle in a different asset bundle?

Yes. AssetReference<VehicleAsset> resolves by GUID across all loaded bundles. The redirector does not need to reside in the same bundle as the target vehicle.

Cross-References

Document history