Code/TerrainSculptNet.cs
// Copyright (c) 2026 SubZero Studios LLC. All rights reserved.
using System;
using Sandbox;
namespace SubZero.SubTerrain;
/// <summary>
/// Optional host-authoritative play-sculpt net. Attach to a NetworkMode.Object
/// GameObject. Host writes height; peers apply the same op locally.
/// </summary>
[Title( "Terrain Sculpt Net" )]
[Category( "SubTerrain" )]
[Icon( "share" )]
public sealed class TerrainSculptNet : Component
{
public bool Submit( TerrainEditRequest request )
{
if ( request.Seed == 0 )
request.Seed = Game.Random.Int( 1, 1_000_000_000 );
if ( !Networking.IsActive || Networking.IsHost )
{
ApplyLocal( request );
if ( Networking.IsActive )
BroadcastStamp( (int)request.Kind, request.WorldPos, request.Radius, request.Depth, request.Seed, request.HasTargetZ, request.TargetZ );
return true;
}
RequestHost( (int)request.Kind, request.WorldPos, request.Radius, request.Depth, request.Seed, request.HasTargetZ, request.TargetZ );
return true;
}
[Rpc.Host]
public void RequestHost( int kind, Vector3 pos, float radius, float depth, int seed, bool hasTargetZ, float targetZ )
{
var request = Make( kind, pos, radius, depth, seed, hasTargetZ, targetZ );
ApplyLocal( request );
BroadcastStamp( kind, pos, radius, depth, seed, hasTargetZ, targetZ );
}
[Rpc.Broadcast]
public void BroadcastStamp( int kind, Vector3 pos, float radius, float depth, int seed, bool hasTargetZ, float targetZ )
{
if ( Networking.IsHost )
return;
ApplyLocal( Make( kind, pos, radius, depth, seed, hasTargetZ, targetZ ) );
}
static TerrainEditRequest Make( int kind, Vector3 pos, float radius, float depth, int seed, bool hasTargetZ, float targetZ )
{
return new TerrainEditRequest
{
Kind = (TerrainEditKind)kind,
WorldPos = pos,
Radius = radius,
Depth = depth,
Seed = seed,
HasTargetZ = hasTargetZ,
TargetZ = targetZ
};
}
void ApplyLocal( TerrainEditRequest request )
{
TerrainSculpt.TryApply( Scene, request );
}
}