using System.Collections.Generic; using Unity.Netcode; using UnityEngine; /// /// Replicates live mesh painting across the network as brush-stamp commands (not pixels). The owner /// captures every committed stamp from its , batches the frame's stamps and /// forwards them; remote avatars replay each stamp through their own painter onto the identical /// mesh/UVs. Position/animation come from the other sync components; this only carries the tiny paint /// commands (~tens of bytes each), so two players see each other paint live without streaming the /// 12 MB of paint maps. /// /// Live-only milestone: a client that joins AFTER painting happened does not receive the prior strokes /// (no snapshot baseline yet) - it starts seeing paint from the moment it connects. /// [DisallowMultipleComponent] public class PlayerPaintSync : NetworkBehaviour { [Tooltip("Max brush stamps forwarded to the network per frame. Pathological fast strokes (hundreds " + "of interpolated sub-stamps in one frame) are decimated to this to bound the RPC size; local " + "painting stays full fidelity.")] [SerializeField] private int maxStampsPerFrame = 64; private MeshPainter _painter; private readonly List _pending = new List(); private void Awake() => _painter = GetComponentInChildren(true); public override void OnNetworkSpawn() { if (!_painter) return; if (IsOwner) { _painter.OnStampCommitted += OnStampCommitted; } else { // Remote replica: never paint from this machine's mouse; paint only from replicated stamps. _painter.PaintingEnabled = false; } } public override void OnNetworkDespawn() { if (_painter && IsOwner) _painter.OnStampCommitted -= OnStampCommitted; } private void OnStampCommitted(PaintStampNet stamp) => _pending.Add(stamp); private void LateUpdate() { if (!IsOwner || _pending.Count == 0) return; SubmitStampsServerRpc(BuildBatch()); _pending.Clear(); } /// Returns this frame's stamps, uniformly decimated to maxStampsPerFrame if a fast stroke overran it. private PaintStampNet[] BuildBatch() { int count = _pending.Count; if (count <= maxStampsPerFrame) return _pending.ToArray(); var outp = new PaintStampNet[maxStampsPerFrame]; for (int i = 0; i < maxStampsPerFrame; i++) outp[i] = _pending[(int)((long)i * count / maxStampsPerFrame)]; return outp; } [ServerRpc] private void SubmitStampsServerRpc(PaintStampNet[] stamps) => ApplyStampsClientRpc(stamps); [ClientRpc] private void ApplyStampsClientRpc(PaintStampNet[] stamps) { // The owner already painted these locally; only remotes replay them. if (IsOwner || !_painter) return; for (int i = 0; i < stamps.Length; i++) _painter.ApplyNetworkStamp(stamps[i]); } }