using System.ComponentModel; using System.Runtime.CompilerServices; using Sandbox.Network; namespace Sandbox; public abstract partial class Component { [ActionGraphInclude, Icon( "wifi" )] public GameObject.NetworkAccessor Network => GameObject.Network; private readonly Dictionary InterpolatedVars = new(); /// /// True if this is a networked object and is owned by another client. This means that we're /// not controlling this object, so shouldn't try to move it or anything. /// public bool IsProxy => GameObject.IsProxy; [EditorBrowsable( EditorBrowsableState.Never )] protected void __sync_SetValue( in WrappedPropertySet p ) { try { // If we aren't valid then just set the property value anyway. if ( !IsValid ) { p.Setter?.Invoke( p.Value ); return; } var root = GameObject.FindNetworkRoot(); var slot = NetworkObject.GetPropertySlot( p.MemberIdent, Id ); if ( root is null ) { p.Setter?.Invoke( p.Value ); return; } var net = root._net; if ( !net.dataTable.IsRegistered( slot ) ) { p.Setter?.Invoke( p.Value ); return; } if ( !net.dataTable.HasControl( slot ) ) { if ( !NetworkTable.IsReadingChanges ) return; var attribute = p.GetAttribute(); var interpolate = attribute?.Flags.HasFlag( SyncFlags.Interpolate ) ?? false; if ( interpolate && p.Value is not null ) { var interpolated = GetOrCreateInterpolatedVar( p.Value, p.PropertyName ); interpolated?.Update( p.Value ); } p.Setter?.Invoke( p.Value ); return; } net.dataTable.UpdateSlotHash( slot, p.Value ); p.Setter?.Invoke( p.Value ); } catch ( Exception e ) { Log.Error( e, $"Exception when setting {p.TypeName}.{p.PropertyName} - {e.Message}" ); } } [EditorBrowsable( EditorBrowsableState.Never )] protected T __sync_GetValue( WrappedPropertyGet p ) { var attribute = p.GetAttribute(); var interpolate = attribute?.Flags.HasFlag( SyncFlags.Interpolate ) ?? false; if ( !interpolate ) return p.Value; var root = GameObject.FindNetworkRoot(); if ( root is null ) return p.Value; var slot = NetworkObject.GetPropertySlot( p.MemberIdent, Id ); var net = root._net; if ( !net.dataTable.IsRegistered( slot ) || net.dataTable.HasControl( slot ) ) return p.Value; if ( InterpolatedVars.TryGetValue( p.PropertyName, out var i ) ) return (T)i.Query( Time.Now ); return p.Value; } [EditorBrowsable( EditorBrowsableState.Never )] [MethodImpl( MethodImplOptions.AggressiveInlining )] protected void __rpc_Wrapper( in WrappedMethod m, params object[] argumentList ) { Rpc.OnCallInstanceRpc( GameObject, this, m, argumentList ); } /// /// Get or create a new interpolated variable. This will set the current interpolated value to the /// provided one if it hasn't been created yet. /// private InterpolatedSyncVar GetOrCreateInterpolatedVar( T value, string propertyName ) { if ( InterpolatedVars.TryGetValue( propertyName, out var i ) ) return (InterpolatedSyncVar)i; var interpolator = IInterpolatedSyncVar.Create( value ); if ( interpolator is null ) return null; var interpolated = new InterpolatedSyncVar( interpolator ); InterpolatedVars[propertyName] = interpolated; return interpolated; } }