Skip to content

ItemStructureAsset — Placeable Structure Definition

Overview

ItemStructureAsset defines the building blocks of player bases in Unturned: floors, walls, ramps, pillars, roofs, windows, doors, posts, arches, and hatches. It inherits from ItemPlaceableAsset (salvage, destroy drops, crafting tags, armor falloff), which inherits from ItemAsset and implements IArmorFalloff.

Structures are the larger counterpart to barricades. While barricades are smaller placed objects (furniture, storage, lights), structures form the skeleton of player-built buildings. They interact with the building claim system, pillar support requirements, and terrain validation through their EConstruct type.

A key distinction between structures and barricades is the server optimization: on dedicated servers, structures load a lighter "Clip" prefab first, strip invisible snapping colliders, and disable LOD culling on clients to prevent entities inside bases from being seen through LOD transitions.

Source code location: Unturned/Bundles/ItemStructureAsset.cs (348 lines), inheriting from ItemPlaceableAsset.cs (454 lines), and ItemAsset.cs (base).

Inheritance Chain

ItemAsset → IArmorFalloff
  └─ ItemPlaceableAsset — salvage, destroy drops, crafting tags, armor falloff
       └─ ItemStructureAsset — EConstruct system, health, placement, server opt

EConstruct Type System

The construct field (type EConstruct) defines the structural role and determines snapping behavior, support requirements, and placement constraints:

ValueSnaps ToValid OnNotes
FLOORPillars, wallsTerrain, foundationMust be above terrain (terrainTestHeight)
WALLFloor, pillar, roofFloor edgeVertical surface
PILLARFloor, wallFloor cornerVertical support column
ROOFWall, pillarWall topTop surface
STAIRSFloor, wallFloor edgeVertical transition
WINDOWWall openingWall cutoutOpening with bars
DOORWall openingWall cutoutPassage with hinge
POSTFloorFloorDecorative column
ARCHWallWallArched opening
HATCHFloor, pillarFloorCeiling access with ladder

Parsing

csharp
_construct = (EConstruct) System.Enum.Parse(typeof(EConstruct),
    p.data.GetString("Construct"), true);

The true parameter enables case-insensitive parsing.


Core Properties

FieldType.dat KeyDefaultDescription
_healthushortHealthMaximum hit points
_rangefloatRangePlacement range in meters
requiresPillarsboolRequires_PillarstrueNeeds pillar support beneath
foliageCutRadiusfloatFoliage_Cut_Radius6.0Radius to clear foliage on placement
terrainTestHeightfloatTerrain_Test_Height10.0Max meters above terrain for floor placement
armorTierEArmorTierArmor_TierLOW/HIGH by nameDamage resistance
canBeDamagedboolCan_Be_DamagedtrueDamage toggle
eligibleForPoolingboolEligible_For_PoolingtrueObject pool eligibility
isVulnerableboolVulnerablefalseCan be damaged (flag)
isRepairableboolUnrepairable (inverted)trueCan be repaired
proofExplosionboolProof_ExplosionfalseExplosion immunity
isUnpickupableboolUnpickupablefalseCannot be salvaged
isSalvageableboolUnsalvageable (inverted)trueCan be salvaged
salvageDurationMultiplierfloatSalvage_Duration_Multiplier1.0Salvage time multiplier
isSaveableboolUnsaveable (inverted)truePersists in saves

Armor Tier Resolution

csharp
if (p.data.ContainsKey("Armor_Tier"))
{
    armorTier = (EArmorTier) System.Enum.Parse(typeof(EArmorTier),
        p.data.GetString("Armor_Tier"), true);
}
else
{
    if (name.Contains("Metal") || name.Contains("Brick"))
        armorTier = EArmorTier.HIGH;
    else
        armorTier = EArmorTier.LOW;
}
ConditionArmor Tier
.dat sets Armor_TierUses specified value
Name contains "Metal" or "Brick"HIGH
OtherwiseLOW

Structures additionally check for "Brick" in the name (barricades only check "Metal").


Prefab Loading — Clip vs Structure

The structure prefab loading has the same dual-prefab pattern as barricades but with additional server optimization:

csharp
if (Dedicator.IsDedicatedServer && p.data.ParseBool("Has_Clip_Prefab", defaultValue: true))
{
    _structure = p.bundle.load<GameObject>("Clip");
    if (structure == null)
    {
        shouldLoadStructurePrefab = true;
        Assets.ReportError(this, "missing \"Clip\" GameObject, loading \"Structure\" GameObject instead");
    }
    else
    {
        shouldLoadStructurePrefab = false;
        AssetValidation.searchGameObjectForErrors(this, structure);
    }
}
else
{
    shouldLoadStructurePrefab = true;
}

Structure Prefab with Server Optimization

When the full "Structure" prefab is loaded (no Clip available, or on client):

csharp
if (shouldLoadStructurePrefab)
{
    _structure = p.bundle.load<GameObject>("Structure");
    if (structure != null)
    {
        AssetValidation.searchGameObjectForErrors(this, structure);

        if (Dedicator.IsDedicatedServer)
        {
            ServerPrefabUtil.RemoveClientComponents(_structure, this);
            RemoveClientComponents(_structure);
        }
        else
        {
            LODGroup lodGroup = structure.GetComponent<LODGroup>();
            if (lodGroup != null)
            {
                lodGroup.DisableCulling();
            }
        }
    }
}

Server Component Removal

On dedicated servers, invisible snapping colliders are removed:

csharp
private void RemoveClientComponents(GameObject gameObject)
{
    foreach (Transform child in gameObject.transform)
    {
        if (child.name == "Climb" || child.name == "Hatch"
            || child.name == "Slot" || child.name == "Door"
            || child.name == "Gate")
        {
            transformsToDestroy.Add(child);
        }
    }

    foreach (Transform child in transformsToDestroy)
    {
        Object.DestroyImmediate(child.gameObject, /*allowDestroyingAssets*/ true);
    }
    transformsToDestroy.Clear();
}

These children are invisible snapping colliders used by the client for placement preview. They serve no purpose on the server and are removed to save memory.

LOD Culling Disable

On clients, structure LOD culling is disabled:

csharp
LODGroup lodGroup = structure.GetComponent<LODGroup>();
if (lodGroup != null)
{
    lodGroup.DisableCulling();
}

This prevents entities inside bases from being visible through LOD transitions. Without this, a structure at far distance might LOD to a simpler mesh, exposing players or items inside the base.


Foliage Clearance

csharp
foliageCutRadius = p.data.ParseFloat("Foliage_Cut_Radius", defaultValue: 6.0f);

On placement, deployable foliage (trees, rocks, grass) within foliageCutRadius is cleared. This prevents structures from clipping through environmental objects. The default 6.0m ensures a clean building footprint.

Set foliageCutRadius to 0 to disable foliage clearance (useful for structures that co-exist with vegetation, like decorative poles or beams that don't need a clear footprint).


Terrain Test Height

csharp
terrainTestHeight = p.data.ParseFloat("Terrain_Test_Height", defaultValue: 10.0f);

Floors must be within terrainTestHeight meters above the terrain. This prevents sky-floating floors. A downward raycast is performed from the floor's pivot position; if the terrain is not found within terrainTestHeight, placement is blocked.

ValueEffect
10.0 (default)Floor must be within 10m of terrain
0.0Floor can be placed anywhere (floating sky bases)
50.0Floor can be placed up to 50m above terrain

Pillar Support Requirements

csharp
requiresPillars = p.data.ParseBool("Requires_Pillars", defaultValue: true);

When requiresPillars is true, the structure needs a pillar or other supporting structure below it. A downward raycast checks for pillar support. If no pillar is found within the support radius, placement is blocked.

Setting requiresPillars false allows floating structures — commonly used for decorative pieces that don't need structural support. Be aware that floating structures can appear disconnected from the world if not used intentionally.


Explosion Effect

csharp
_explosion = p.data.ParseGuidOrLegacyId("Explosion", out _explosionGuid);

Identical to the barricade explosion system. The FindExplosionEffectAsset() helper resolves the destruction effect from GUID or legacy ID.


Safezone Behavior

csharp
public override bool canBeUsedInSafezone(SafezoneNode safezone, bool byAdmin)
{
    return safezone.CurrentlyAllowsBuilding;
}

Structures (like barricades) check the safezone's building permissions. If the safezone blocks building, structure placement is prevented.


BuildDescription — Inventory Tooltip

csharp
public override void BuildDescription(ItemDescriptionBuilder builder, Item itemInstance)
{
    base.BuildDescription(builder, itemInstance);

    builder.Append(...health..., DescSort_BuildableCommon);

    // Armor tier
    switch (armorTier) { ... }

    // Pickup/salvage restrictions
    if (_isUnpickupable) { ... }
    else if (!_isSalvageable) { ... }

    // Repairable
    if (!isRepairable) { ... }

    // Proof
    if (proofExplosion) { ... }

    // Invulnerable
    if (!_isVulnerable) { ... }
}

Structure descriptions do not show lockable status (unlike barricades — structures don't support key locking).


Inventory Audio

csharp
protected override AudioReference GetDefaultInventoryAudio()
{
    if (name.Contains("Metal", StringComparison.InvariantCultureIgnoreCase))
        return new AudioReference("core.masterbundle", "Sounds/Inventory/SmallMetal.asset");

    if (size_x <= 1 || size_y <= 1)
        return new AudioReference("core.masterbundle", "Sounds/Inventory/LightMetalEquipment.asset");
    else if (size_x <= 2 || size_y <= 2)
        return new AudioReference("core.masterbundle", "Sounds/Inventory/MediumMetalEquipment.asset");
    else
        return new AudioReference("core.masterbundle", "Sounds/Inventory/HeavyMetalEquipment.asset");
}
ConditionAudio
Name contains "Metal"SmallMetal.asset
Grid size ≤ 1 in either dimLightMetalEquipment.asset
Grid size ≤ 2 in either dimMediumMetalEquipment.asset
LargerHeavyMetalEquipment.asset

BuildCargoData — Wiki Export

csharp
CargoDeclaration data = builder.GetOrAddDeclaration("Structure");
data.Append("GUID", GUID);
data.Append("Construct", construct);
data.Append("Health", health);
data.Append("Range", range);
data.Append("Explosion", explosion);
data.Append("Can_Be_Damaged", canBeDamaged);
data.Append("Eligible_For_Pooling", eligibleForPooling);
data.Append("Requires_Pillars", requiresPillars);
data.Append("Vulnerable", isVulnerable);
data.Append("Unrepairable", !isRepairable);
data.Append("Proof_Explosion", proofExplosion);
data.Append("Unpickupable", isUnpickupable);
data.Append("Unsalvageable", !isSalvageable);
data.Append("Salvage_Duration_Multiplier", salvageDurationMultiplier);
data.Append("Unsaveable", !isSaveable);
data.Append("Armor_Tier", armorTier);
data.Append("Foliage_Cut_Radius", foliageCutRadius);
data.Append("Terrain_Test_Height", terrainTestHeight);

Note: Negative flags (Unrepairable, Unsalvageable, Unsaveable) are inverted for the Cargo table to show the original .dat value.


Comparison: Structure vs Barricade

FeatureItemStructureAssetItemBarricadeAsset
DiscriminantEConstructEBuild
State size0 bytes (most types)Varies (0–73 bytes)
Snapping/navNav GameObjectNav GameObject
Server Clip prefabYesYes
Snap collider removalYes ("Climb", "Hatch", "Slot", etc.)No
LOD culling disableYes (client)No
Foliage clearanceYes (default 6.0m)No
Terrain testYes (default 10.0m)No
Pillar requirementYes (default true)No
Key lockingNoYes (Locked flag)
Key lockingNoYes

.dat File Reference — Structure-Specific

.dat KeyTypeDefaultNotes
ConstructstringRequiredEConstruct enum value
HealthushortRequiredHit points
RangefloatRequiredPlacement range
Requires_PillarsbooltrueNeed pillar support
Foliage_Cut_Radiusfloat6.0Foliage clearance radius
Terrain_Test_Heightfloat10.0Max floor height above terrain
Armor_TierstringName-basedLow or High
ExplosionGUID/IDDestruction effect
Can_Be_DamagedbooltrueDamage toggle
Eligible_For_PoolingbooltrueObject pool
VulnerableflagCan be damaged (flag)
UnrepairableflagCannot repair
Proof_ExplosionflagExplosion immune
UnpickupableflagCannot salvage
UnsalvageableflagCannot salvage
Salvage_Duration_Multiplierfloat1.0Salvage time
UnsaveableflagDoes not persist
Has_Clip_PrefabbooltrueServer Clip prefab exists
PlacementPreviewPrefabMasterBundleRefClient preview model

Modding Example — Wooden Floor .dat

ini
GUID a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6
Type Structure
Construct Floor
Health 300
Range 8
Armor_Tier Low
Requires_Pillars true
Foliage_Cut_Radius 6.0
Terrain_Test_Height 10.0

Modding Example — Reinforced Wall .dat

ini
GUID f1e2d3c4b5a69788796a5b4c3d2e1f0a
Type Structure
Construct Wall
Health 1100
Range 6
Armor_Tier High
Requires_Pillars true
Proof_Explosion

Common Issues

  1. Floating structures: Set requiresPillars false intentionally. Unintentional floating occurs when pillar support isn't found below — check the support radius.

  2. Structure placement blocked: terrainTestHeight limits how far above terrain a floor can be. Increase the value for elevated platforms.

  3. Trees clipping through base: Increase foliageCutRadius to clear a larger area on placement. Default 6.0m may not be enough for large structures.

  4. LOD culling exposing base interiors: The client-side LODGroup disable prevents this. If you see base interiors through walls, verify the Structure prefab has an LODGroup component — the disable operation requires it.

  5. Server memory from snap colliders: The RemoveClientComponents method strips "Climb", "Hatch", "Slot", "Door", and "Gate" child transforms. If your mod adds new snap collider types with different names, they won't be removed and will consume server memory.

Document history

VersionDateAuthorNotes
1.02026-07-2857 StudiosInitial publication. Full structure asset documentation including EConstruct system, prefab loading, server optimization, foliage clearance, and LOD culling.