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 optEConstruct Type System
The construct field (type EConstruct) defines the structural role and determines snapping behavior, support requirements, and placement constraints:
| Value | Snaps To | Valid On | Notes |
|---|---|---|---|
FLOOR | Pillars, walls | Terrain, foundation | Must be above terrain (terrainTestHeight) |
WALL | Floor, pillar, roof | Floor edge | Vertical surface |
PILLAR | Floor, wall | Floor corner | Vertical support column |
ROOF | Wall, pillar | Wall top | Top surface |
STAIRS | Floor, wall | Floor edge | Vertical transition |
WINDOW | Wall opening | Wall cutout | Opening with bars |
DOOR | Wall opening | Wall cutout | Passage with hinge |
POST | Floor | Floor | Decorative column |
ARCH | Wall | Wall | Arched opening |
HATCH | Floor, pillar | Floor | Ceiling 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
| Field | Type | .dat Key | Default | Description |
|---|---|---|---|---|
_health | ushort | Health | — | Maximum hit points |
_range | float | Range | — | Placement range in meters |
requiresPillars | bool | Requires_Pillars | true | Needs pillar support beneath |
foliageCutRadius | float | Foliage_Cut_Radius | 6.0 | Radius to clear foliage on placement |
terrainTestHeight | float | Terrain_Test_Height | 10.0 | Max meters above terrain for floor placement |
armorTier | EArmorTier | Armor_Tier | LOW/HIGH by name | Damage resistance |
canBeDamaged | bool | Can_Be_Damaged | true | Damage toggle |
eligibleForPooling | bool | Eligible_For_Pooling | true | Object pool eligibility |
isVulnerable | bool | Vulnerable | false | Can be damaged (flag) |
isRepairable | bool | Unrepairable (inverted) | true | Can be repaired |
proofExplosion | bool | Proof_Explosion | false | Explosion immunity |
isUnpickupable | bool | Unpickupable | false | Cannot be salvaged |
isSalvageable | bool | Unsalvageable (inverted) | true | Can be salvaged |
salvageDurationMultiplier | float | Salvage_Duration_Multiplier | 1.0 | Salvage time multiplier |
isSaveable | bool | Unsaveable (inverted) | true | Persists 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;
}| Condition | Armor Tier |
|---|---|
.dat sets Armor_Tier | Uses specified value |
Name contains "Metal" or "Brick" | HIGH |
| Otherwise | LOW |
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.
| Value | Effect |
|---|---|
10.0 (default) | Floor must be within 10m of terrain |
0.0 | Floor can be placed anywhere (floating sky bases) |
50.0 | Floor 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");
}| Condition | Audio |
|---|---|
| Name contains "Metal" | SmallMetal.asset |
| Grid size ≤ 1 in either dim | LightMetalEquipment.asset |
| Grid size ≤ 2 in either dim | MediumMetalEquipment.asset |
| Larger | HeavyMetalEquipment.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
| Feature | ItemStructureAsset | ItemBarricadeAsset |
|---|---|---|
| Discriminant | EConstruct | EBuild |
| State size | 0 bytes (most types) | Varies (0–73 bytes) |
| Snapping/nav | Nav GameObject | Nav GameObject |
| Server Clip prefab | Yes | Yes |
| Snap collider removal | Yes ("Climb", "Hatch", "Slot", etc.) | No |
| LOD culling disable | Yes (client) | No |
| Foliage clearance | Yes (default 6.0m) | No |
| Terrain test | Yes (default 10.0m) | No |
| Pillar requirement | Yes (default true) | No |
| Key locking | No | Yes (Locked flag) |
| Key locking | No | Yes |
.dat File Reference — Structure-Specific
| .dat Key | Type | Default | Notes |
|---|---|---|---|
Construct | string | Required | EConstruct enum value |
Health | ushort | Required | Hit points |
Range | float | Required | Placement range |
Requires_Pillars | bool | true | Need pillar support |
Foliage_Cut_Radius | float | 6.0 | Foliage clearance radius |
Terrain_Test_Height | float | 10.0 | Max floor height above terrain |
Armor_Tier | string | Name-based | Low or High |
Explosion | GUID/ID | — | Destruction effect |
Can_Be_Damaged | bool | true | Damage toggle |
Eligible_For_Pooling | bool | true | Object pool |
Vulnerable | flag | — | Can be damaged (flag) |
Unrepairable | flag | — | Cannot repair |
Proof_Explosion | flag | — | Explosion immune |
Unpickupable | flag | — | Cannot salvage |
Unsalvageable | flag | — | Cannot salvage |
Salvage_Duration_Multiplier | float | 1.0 | Salvage time |
Unsaveable | flag | — | Does not persist |
Has_Clip_Prefab | bool | true | Server Clip prefab exists |
PlacementPreviewPrefab | MasterBundleRef | — | Client 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.0Modding Example — Reinforced Wall .dat
ini
GUID f1e2d3c4b5a69788796a5b4c3d2e1f0a
Type Structure
Construct Wall
Health 1100
Range 6
Armor_Tier High
Requires_Pillars true
Proof_ExplosionCommon Issues
Floating structures: Set
requiresPillars falseintentionally. Unintentional floating occurs when pillar support isn't found below — check the support radius.Structure placement blocked:
terrainTestHeightlimits how far above terrain a floor can be. Increase the value for elevated platforms.Trees clipping through base: Increase
foliageCutRadiusto clear a larger area on placement. Default 6.0m may not be enough for large structures.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.
Server memory from snap colliders: The
RemoveClientComponentsmethod 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
| Version | Date | Author | Notes |
|---|---|---|---|
| 1.0 | 2026-07-28 | 57 Studios | Initial publication. Full structure asset documentation including EConstruct system, prefab loading, server optimization, foliage clearance, and LOD culling. |
