Skip to content

ResourceManager — Harvestable Resources

The ResourceManager (837 lines in Unturned/Managers/ResourceManager.cs) manages the harvestable resource layer of an Unturned level: trees, rocks, bushes, and forageable plants. Unlike the building managers, resources are not player-placed — they are part of the level's terrain decoration system, controlled by LevelGround and instantiated as ResourceSpawnpoint instances at load time.

Resources occupy a unique position in the manager hierarchy. They are owned by LevelGround (which stores them in a RegionDictionaryResourceSpawnpoint), queried through ResourceManager for region-based spatial lookups, damaged through ResourceManager.damage or ResourceManager.forage, and their death/alive state is synchronized across the network. The respawn timer is managed per-resource by the checkCanReset method on ResourceSpawnpoint, driven by the asset's reset field.

Source file: Unturned/Managers/ResourceManager.cs (837 lines). Supporting type: Unturned/Level/ResourceSpawnpoint.cs (603 lines), Unturned/Level/LevelGround.cs (1601 lines).

Who this article is for

This article is for plugin developers who need to damage, harvest, or track the state of resource objects. It assumes familiarity with the Regions coordinate system and the ResourceAsset / ResourceSpawnpoint types.

What you'll learn

  • The region-based resource storage model and spatial queries
  • The resource damage pipeline: damage application, tree felling debris physics, reward drops
  • The forage system for instant-gather plants
  • The respawn timer and reset multiplier system
  • The explosion damage integration via IExplosionDamageable
  • Network synchronization of alive/dead state per region

Region-based storage

Resources are stored in a RegionDictionaryResourceSpawnpoint managed by LevelGround:

csharp
private static RegionDictionaryResourceSpawnpoint _regionTrees;

This dictionary maps region coordinates (Vector2Int) to lists of ResourceSpawnpoint instances. The RESOURCE_REGIONS constant (3 by default, 16 in the BEAUTIFUL build) controls the region overlap for spatial queries.

The legacy _trees array (ListResourceSpawnpoint[,]) is maintained alongside the dictionary for backward compatibility but is marked obsolete.

Spatial queries

csharp
public static void getResourcesInRadius(Vector3 center, float sqrRadius,
    List<RegionCoordinate> search, List<Transform> result)

Iterates each region in the search list, gets the tree list from LevelGround.GetTreesOrNullInRegion, and collects alive, damageable transforms within the squared radius.

Full tree enumeration

csharp
public static void GatherAllTrees(ListResourceSpawnpoint results)

Appends every tree in every region to the results list. Used for global operations (world reset, arena round cleanup).

Resource damage pipeline

csharp
public static void damage(Transform resource, Vector3 direction, float damage,
    float times, float drop, out EPlayerKill kill, out uint xp,
    CSteamID instigatorSteamID, EDamageOrigin damageOrigin, bool trackKill)

The damage method is the most complex in ResourceManager. Its full pipeline:

  1. Calculate totalDamage = (ushort)(damage * times).
  2. Fire onDamageResourceRequested for plugin interception.
  3. If cancelled or totalDamage < 1, return.
  4. Resolve region coordinates from the resource position.
  5. Linear scan the region's tree list to find the matching ResourceSpawnpoint.
  6. Check isDead and canBeDamaged. If valid, call region[index].askDamage(totalDamage).
  7. If the resource dies after damage, execute the death sequence:

Death sequence

When region[index].isDead becomes true:

  1. Set kill = EPlayerKill.RESOURCE.
  2. If the asset has an explosion effect, trigger it at the effect spawn position (EffectManager.MEDIUM distance, reliable).
  3. If the asset is NOT a forageable, process drops:

Reward table path (asset.rewardID != 0)

  • Calculate drop multiplier from config Objects.Resource_Drops_Multiplier.
  • Calculate drop direction: resource.InverseTransformDirection(direction) flattened to XZ, normalized, then transformed back to world space.
  • Reward count: CeilToInt(Random.Range(asset.rewardMin, asset.rewardMax + 1) * dropMultiplier), clamped to [0, 100].
  • For debris-having assets: drops are positioned along the drop direction with spacing.
  • For non-debris assets: drops are scattered within a 2-meter radius.

Legacy path (asset.log != 0 or asset.stick != 0)

  • Logs: CeilToInt(Random.Range(3, 7) * dropMultiplier), dropped along the damage direction.
  • Sticks: CeilToInt(Random.Range(2, 5) * dropMultiplier), dropped in a circular scatter pattern.
  1. Set xp = asset.rewardXP.
  2. Track the tree kill for nearby players (within 300 meters).
  3. Call ServerSetResourceDead(x, y, index, direction * totalDamage).

Forage system

The forage system handles one-hit-harvest resources like berry bushes:

csharp
public static void forage(Transform resource)

Client-side, this sends a SendForageRequest with the region coordinates and resource index. The server validates:

  1. Region coordinates are safe.
  2. The requesting player is alive.
  3. The resource index is valid and not dead.
  4. The resource is within 20 meters of the player.
  5. The asset has isForage = true.

On valid forage:

  1. Apply 1 point of damage (via askDamage(1)), which kills the resource.
  2. Trigger explosion effect (if configured).
  3. Resolve the reward item: from asset.rewardID spawn table, or from asset.log.
  4. Add the item directly to the player's inventory (skip ground drop).
  5. Agriculture skill mastery doubles the harvest (50% chance).
  6. Send stat update (EPlayerStat.FOUND_PLANTS).
  7. Pay forage XP via player.skills.askPay(asset.forageRewardExperience).
  8. Call ServerSetResourceDead.

Respawn system

checkCanReset

csharp
public bool checkCanReset(float multiplier)
{
    return isDead && asset != null && asset.reset > 1
        && Time.realtimeSinceStartup - lastDead > asset.reset * multiplier;
}

The reset field from the ResourceAsset controls the respawn delay in seconds. The multiplier parameter allows external scaling (e.g., from game mode config). The resource must have been dead for longer than asset.reset * multiplier seconds.

Global reset

csharp
public static void askClearAllResources()

Iterates every region and revives all trees. Used between arena rounds. The per-region askClearRegionResources sends SendClearRegionResources to revive trees in a single region.

ServerSetResourceDead

csharp
private static void ServerSetResourceDead(byte x, byte y, ushort index, Vector3 ragdoll)

Broadcasts SendResourceDead to all clients. The client-side ReceiveResourceDead calls regionTrees[index].kill(ragdoll) to trigger the visual death effect (debris spawning, model/stump swap).

ServerSetResourceAlive

SendResourceAlive revives a dead resource on the client side, calling regionTrees[index].revive().

ResourceSpawnpoint internals

The ResourceSpawnpoint class (603 lines) is the most feature-rich spawnpoint type in the SDK. Key fields:

FieldTypePurpose
guidSystem.GuidResource asset GUID
pointVector3World position
angleQuaternionRotation
scaleVector3Scale
healthushortCurrent health
isDeadbool (computed)health == 0
lastDeadfloatTime.realtimeSinceStartup at death
assetResourceAssetResolved asset reference
modelTransformThe live tree model (or null if dead)
stumpTransformThe stump model (or null)
skyboxTransformThe distant LOD model
canBeDamagedboolFalse if holiday-restricted and holiday not active
isGeneratedboolWhether this spawn was procedurally generated

Tree felling physics

When a tree dies, the kill(Vector3 ragdoll) method on ResourceSpawnpoint handles the visual:

  1. If the asset has debris and debris graphics are enabled:

    • Compute a randomized ragdoll force: add 8 to Y, scatter X/Z by ±16, multiply by flight-boost modifier (4x if flying, otherwise 2x).
    • Instantiate a debris copy of the tree model (or the dedicated debris prefab) at the model position + vertical offset.
    • Add a Rigidbody with interpolation, discrete collision detection, drag 1, angular drag 1.
    • Apply the ragdoll force.
    • Destroy the debris after 8 seconds.
    • If a stump exists and the asset should ignore stump-debris collision, configure Physics.IgnoreCollision between them.
  2. For forageable assets: deactivate the "Forage" child transform.

Active region management

Trees have region-based visibility activation:

csharp
internal void SetIsActiveInRegion(bool isActive)
internal void SetIsSkyboxActiveInRegion(bool isActive)

The UpdateActive() method determines whether the model, stump, and skybox should be visible based on:

  • Is the region active or is cinematic mode active?
  • Is the tree alive (show model) or dead (show stump)?
  • Are the asset's holiday conditions met?
  • Is this a dedicated server (skip visual activation)?

Explosion damage integration

ResourceSpawnpoint implements IExplosionDamageable through its TreeRefComponent:

csharp
internal class TreeRefComponent : MonoBehaviour, IExplosionDamageable, ICraftingTagProvider

The ApplyExplosionDamage method:

  1. Checks damageParameters.shouldAffectTrees.
  2. Calculates damage falloff by distance from explosion center.
  3. Performs a line-of-sight test (blocked by terrain or objects between explosion and tree).
  4. Calls ResourceManager.damage with the calculated damage.
  5. Reports kills and XP back through damageParameters.

This allows explosions to fell trees with proper line-of-sight occlusion and distance-based damage falloff.

Plugin integration

ResourceManager delegates

DelegateSignatureFires
onDamageResourceRequested(CSteamID, Transform, ref ushort, ref bool, EDamageOrigin)Before any resource damage

Use cases

  • Custom damage handling: Intercept onDamageResourceRequested to modify damage values or add custom harvest effects.
  • Forage augmentation: Wrap forage to add custom logic before the resource is consumed.
  • Respawn scaling: Hook into the game mode config to adjust the multiplier passed to checkCanReset.
  • Resource tracking: Monitor ServerSetResourceDead and ServerSetResourceAlive broadcasts to track resource state.

Serialization

Resources are serialized as part of LevelGround save data. The LevelGround save includes:

  • SAVEDATA_TREES_VERSION tree data format (version 8 adds rotation and scale)
  • Per-resource: GUID, position, rotation, scale, generated flag
  • Legacy support for ID-based loading with automatic GUID migration

The treesHash property provides a hash of the Trees.dat file for integrity checking.

ResourceSpawnpoint — Full lifecycle

Construction and tree instantiation

The ResourceSpawnpoint constructor (the full GUID-taking overload at line 428 of ResourceSpawnpoint.cs) performs the complete tree instantiation:

  1. GUID resolution: If a GUID is provided, resolve the ResourceAsset via Assets.find(guid). If only a legacy ID is available, resolve via Assets.find(EAssetType.RESOURCE, id) and auto-upgrade the GUID.

  2. Asset integrity: If running on a non-dedicated server, queue a ClientAssetIntegrity request for the tree's GUID. If missing, register with ServerAddKnownMissingAsset to prevent client kicks.

  3. Model instantiation: If the asset has a modelGameObject, instantiate it at point + (Vector3.up * scale.y * asset.verticalOffset) with the configured rotation and scale. The model receives a TreeRefComponent for explosion damage integration.

  4. Forageable setup: If the asset is a forageable and not in the editor, add InteractableForage to the "Forage" child transform.

  5. Skybox instantiation: If the asset has a skyboxGameObject, create the skybox LOD copy with a rotated orientation and the asset's skybox material.

  6. Stump instantiation: If the asset has a stumpGameObject, create the stump at the same position.

  7. Holiday restrictions: If the asset has a holiday restriction (asset.holidayRestriction != ENPCHoliday.NONE), set areConditionsMet based on HolidayUtil.isHolidayActive.

  8. Initial active state: Call UpdateActive() which determines whether model, stump, skybox should be visible.

Respawn timer

csharp
public bool checkCanReset(float multiplier)
{
    return isDead && asset != null && asset.reset > 1
        && Time.realtimeSinceStartup - lastDead > asset.reset * multiplier;
}

The lastDead timestamp is set when kill() is called. The reset value from the ResourceAsset defines the base respawn delay in seconds. The multiplier allows game-mode-specific scaling (e.g., faster respawn in arena mode).

Tree kill visual effect

When kill() is called, the debris spawning logic is:

  1. Calculate ragdoll force: ragdoll.y += 8; ragdoll.x/z += Random.Range(-16, 16); ragdoll *= (flight boost ? 4 : 2).
  2. Find or create the debris prefab (uses asset.debrisGameObject if configured, otherwise asset.modelGameObject).
  3. Instantiate the debris at model.position + model.up * asset.DebrisVerticalOffset.
  4. Configure the rigidbody: Interpolate, Discrete collision detection, drag/angular drag = 1.
  5. Apply the ragdoll force via AddForce(ragdoll).
  6. Set 8-second auto-destruction via GameObject.Destroy(gib.gameObject, 8f).
  7. If a stump exists and asset.ShouldIgnoreCollisionBetweenStumpAndDebris, call Physics.IgnoreCollision between the debris and stump colliders.

Active state visibility rules

The UpdateActive() method uses a priority-based visibility system:

bool isActiveOrCinematic = isActiveInRegion || GraphicsSettings.WantsCinematicMode;

bool shouldModelBeVisible = isAlive;
bool shouldStumpBeVisible = !isAlive;
if (asset != null && asset.isForage)
{
    shouldModelBeVisible = true;
    model?.Find("Forage")?.gameObject.SetActive(isAlive);
}

bool shouldBeActive = areConditionsMet && (isDedicatedServer || isActiveOrCinematic);

model?.gameObject.SetActive(shouldBeActive && shouldModelBeVisible);
stump?.gameObject.SetActive(shouldBeActive && shouldStumpBeVisible);

For forageable assets, the model is always visible but the "Forage" child (the harvestable part) is only visible when alive.

Skybox LOD visibility

The UpdateSkyboxActive() method controls distant visibility:

csharp
if (skybox != null)
{
    bool isLandmarkQualityMet = GraphicsSettings.landmarkQuality >= EGraphicQuality.MEDIUM
        && !GraphicsSettings.WantsCinematicMode;
    skybox.gameObject.SetActive(!isActiveInRegion && isSkyboxActiveInRegion
        && isLandmarkQualityMet && areConditionsMet && isAlive);
}

Skybox trees are only shown when:

  • The main tree is not in the active region (i.e., it's far enough away for the LOD)
  • The skybox region is active
  • Landmark quality is at least MEDIUM
  • Cinematic mode is off
  • Holiday conditions are met
  • The tree is alive

LevelGround resource loading

Trees.dat loading

LevelGround.loadTrees() reads Terrain/Resources.dat:

csharp
// Version with rotation and scale (SAVEDATA_TREES_VERSION_ROTATION_AND_SCALE = 8)
byte treeCount = river.readByte();
for (int index = 0; index < treeCount; index++)
{
    System.Guid guid = river.readGUID();
    Vector3 position = river.readSingleVector3();
    Quaternion rotation = river.readQuaternion(); // Version >= 8
    Vector3 scale = river.readSingleVector3();     // Version >= 8
    bool isGenerated = river.readBoolean();
    addSpawn(position, rotation, scale, guid, isGenerated);
}

Legacy versions (before version 8) stored only ID, position, and generated flag without rotation or scale.

RegionDictionary[T] tree access

csharp
public static ListResourceSpawnpoint GetTreesOrNullInRegion(Vector2Int coord)
{
    return _regionTrees?.GetListOrNull(coord);
}

The RegionDictionaryResourceSpawnpoint provides region-keyed access with automatic list creation and cleanup:

  • GetOrAddList(coord): Creates a new list if none exists for this region.
  • GetListOrNull(coord): Returns null if no trees are in this region.
  • ReleaseListIfEmpty(coord): Removes the list entry when the last tree is removed.

Tree count tracking

csharp
private static int _total;
public static int total => _total;

The _total field is incremented on addSpawn and must be manually maintained by consumers.

Foliage bake pre-processing

When the landscape foliage system bakes a tile, generated trees within the tile bounds are removed. This prevents double-rendering of procedurally placed trees and foliage:

csharp
if (bakeSettings.bakeResources)
{
    Bounds worldBounds = foliageTile.worldBounds;
    RegionBoundsInt bounds = Regions.GetCoordinateBoundsInt(worldBounds);
    foreach (Vector2Int coord in bounds)
    {
        ListResourceSpawnpoint trees = GetTreesOrNullInRegion(coord);
        // ... remove generated trees within worldBounds
    }
}

ResourceAsset fields used by ResourceManager

The ResourceAsset fields referenced by ResourceManager.damage and ResourceSpawnpoint:

FieldTypePurpose
healthushortStarting health
resetfloatRespawn delay in seconds
isForageboolWhether this resource is one-hit-harvestable
rewardIDushortSpawn table ID for rewards
rewardMin / rewardMaxintReward drop count range
rewardXPuintExperience awarded on kill
logushortItem ID for log drops (legacy)
stickushortItem ID for stick drops (legacy)
hasDebrisboolWhether felling spawns a debris physics object
verticalOffsetfloatVertical offset for model placement
DebrisVerticalOffsetfloatVertical offset for debris spawning
ExplosionEffectFlagsflagsDetermines explosion position/rotation behavior
holidayRestrictionENPCHolidaySeasonal availability
skyboxGameObject / stumpGameObjectGameObjectLOD and stump prefabs

Resource region loading at level start

During LevelGround.load(), resource data is loaded from Terrain/Resources.dat:

  1. The file is opened with River binary reader.
  2. The version byte determines the format:
    • Version >= SAVEDATA_TREES_VERSION_ROTATION_AND_SCALE (8): reads GUID, position, rotation (Quaternion), scale (Vector3), generated flag.
    • Earlier versions: reads legacy ID, position, generated flag (rotation defaults to identity, scale defaults to one).
  3. Each resource is added via addSpawn(pos, rot, scale, guid, isGenerated).
  4. After loading, trees are registered with the region visibility system.

The treesHash is computed from the Resources.dat file content after loading.

Resource damage edge cases

Protected resources

Resources with canBeDamaged = false cannot be damaged by any means:

csharp
if (!region[index].isDead && region[index].canBeDamaged)

The canBeDamaged property returns false when:

  • The asset has a holidayRestriction set to a non-NONE value.
  • The current active holiday does not match the restriction.

For example, a Christmas tree asset with holidayRestriction = CHRISTMAS will have canBeDamaged = false outside of the Christmas holiday period.

Zero-damage prevention

csharp
if (!shouldAllow || totalDamage < 1)
    return;

If the onDamageResourceRequested delegate reduces damage below 1, or if the weapon/tool damage calculation results in zero, the damage is silently ignored. This prevents infinite zero-damage hits.

Direction-based drop spawning

The drop direction for reward items is computed from the damage direction:

csharp
Vector3 localDropDirection = resource.InverseTransformDirection(direction);
localDropDirection.y = 0.0f;
localDropDirection.Normalize();
Vector3 dropDirection = resource.TransformDirection(localDropDirection);

This transforms the world-space damage direction into the resource's local space, flattens it to the horizontal plane, re-normalizes, and transforms back to world space. The result is a horizontal direction that aligns with the striking direction — logs fly in the direction the tree was hit from.

Debris drop positioning

For resources with asset.hasDebris:

csharp
dropPosition = resource.position + (dropDirection * (2 + reward)) + resource.up * 2f;

Each reward item is placed 2 units apart along the drop direction, starting 2 units from the tree center, elevated 2 units above the ground.

For non-debris resources, items are scattered:

csharp
dropPosition = resource.position
    + resource.right * Random.Range(-2.0f, 2.0f)
    + resource.up * 2.0f
    + resource.forward * Random.Range(-2.0f, 2.0f);

Forage request validation

The ReceiveForageRequest handler (rate-limited at 10 Hz) performs a thorough validation chain:

  1. Region coordinates are within safe bounds.
  2. Player is connected and alive.
  3. Resource index is within the region's tree list.
  4. Resource is not already dead.
  5. Player is within 20 meters of the resource (squared magnitude check).
  6. Resource asset exists and isForage == true.

If any check fails, the forage is silently ignored (no refund sent to the player — the item was never consumed).

Resource region repopulation

askClearAllResources — Full world reset

csharp
public static void askClearAllResources()

Iterates all WORLD_SIZE × WORLD_SIZE regions and calls askClearRegionResources(x, y) for each. This sends SendClearRegionResources to clients for every region, triggering receiveClearRegionResources which calls tree.revive() on each tree.

This is typically used between arena rounds or when a map-wide resource respawn is needed.

Per-region clear

csharp
public static void askClearRegionResources(byte x, byte y)

Server-only. Validates the region coordinates, then broadcasts SendClearRegionResources to all clients. The client calls tree.revive() on every tree in the region, restoring all dead resources to full health.

Plugin integration patterns

Custom resource damage

csharp
ResourceManager.onDamageResourceRequested += (instigator, resource,
    ref damage, ref allow, origin) =>
{
    // Double damage for a specific resource type
    ResourceSpawnpoint spawnpoint = LevelGround.FindResourceSpawnpointByTransform(resource);
    if (spawnpoint?.asset?.GUID == mySpecialTreeGuid)
        damage *= 2;
};

Forage augmentation

The forage method is client-to-server. To add custom behavior before the forage resolves, wrap the ItemConsumeable use skill rather than modifying ResourceManager.

Network synchronization

Region-based initial state

When a client connects, the server sends SendResources for each region. The packet contains:

byte x, y
if (!regions[x, y].isNetworked):
    regions[x, y].isNetworked = true
    ushort treeCount
    for each tree:
        ushort assetID
        Vector3 position
        byte angle
        bool isDead

Dead/alive RPCs

RPCTriggerEffect
SendResourceDeadTree killedCalls regionTrees[index].kill(ragdoll)
SendResourceAliveTree respawnedCalls regionTrees[index].revive()
SendClearRegionResourcesArena round resetCalls tree.revive() for all trees in region

The client-only guard if (!Provider.isServer && !regions[x, y].isNetworked) return prevents processing state updates for regions the client has not yet received the initial state for.

ResourceManager tree initialization state flow

When a ResourceSpawnpoint is constructed, the initialization follows this sequence:

1. GUID resolution: read guid -> resolve asset
2. Asset present?
   YES -> set health = asset.health, isAlive = true, areConditionsMet = true
          instantiate model prefab at modelPosition
          instantiate skybox (if not dedicated server)
          instantiate stump
          check holiday restrictions
          UpdateActive()
   NO  -> set health = 0, isAlive = false
          // Tree will not render, will not be damageable
3. Return

Trees without assets (missing GUID, missing mod) are created as dead, invisible placeholders. They are not functional in gameplay but preserve the save data structure.

ResourceManager — Dead tree tracking

When a tree dies, _lastDead = Time.realtimeSinceStartup records the time of death. The checkCanReset method uses this to determine when to respawn. Dead trees remain in the region tree list — they are not removed. This preserves the save structure and allows the respawn system to re-use the existing ResourceSpawnpoint.

ResourceAsset types

Resources are divided into two categories by ResourceAsset.isForage:

CategoryisForageHarvest typeModel behavior
TreefalseMulti-hit damageFalls over, debris physics, stump remains
ForagetrueOne-hit interact"Forage" child toggles visible/invisible

Forageables are further defined by their forageRewardExperience and interaction with the agriculture skill mastery.

Example: Damaging a tree and handling the result

csharp
Transform treeTransform = /* from raycast hit on tree collider */;
Vector3 damageDirection = (hit.point - Player.player.transform.position).normalized;

EPlayerKill kill;
uint xp;
ResourceManager.damage(treeTransform, damageDirection,
    damageAmount, 1.0f, 1.0f, // damage, times, dropMultiplier
    out kill, out xp,
    player.channel.owner.playerID.steamID,
    EDamageOrigin.Gun);

if (kill == EPlayerKill.RESOURCE)
{
    // Tree was felled, xp was awarded
    player.skills.askPay(xp);
}

Example: Forcing a tree respawn

csharp
// Respawn all trees in a specific region
ResourceManager.askClearRegionResources(x, y);

// Or respawn all trees globally (arena round reset)
ResourceManager.askClearAllResources();

Example: Finding a resource by transform

csharp
Transform treeModel = /* transform from raycast */;
ResourceSpawnpoint spawnpoint = LevelGround.FindResourceSpawnpointByTransform(treeModel);
if (spawnpoint != null)
{
    ResourceAsset asset = spawnpoint.asset;
    ushort health = spawnpoint.health;
    float timeUntilRespawn = spawnpoint.lastDead + (asset?.reset ?? 60) - Time.realtimeSinceStartup;
}

Resource health and repair

Resource health is stored as a ushort on each ResourceSpawnpoint. The askDamage method applies damage:

csharp
public void askDamage(ushort amount)
{
    if (amount == 0 || isDead) return;
    if (amount >= health) health = 0;
    else health -= amount;
}

There is no repair mechanism for resources — once dead, they must respawn naturally via the timer or be revived via askClearAllResources.

Resource collision with barricades and structures

Resources use two collider modes:

  1. Full collider: Trees with hasDebris have a full collider for the standing tree. The collider is disabled when the tree is felled (only the stump remains with collision).
  2. Stump collider: The stump has its own collider that persists after felling.

The Physics.IgnoreCollision between debris and stump prevents the falling tree debris from pushing the stump.

Document history