using Sandbox.Utility; namespace Sandbox; /// /// Marks a method as being an RPC that when invoked will be called for all connected clients including the host. /// The state of the object the RPC is called on will be up-to-date including its and any /// properties with the or attributes by the time the method /// is called on remote clients. The only except is any synchronized properties marked with which /// will generally only be received every network tick. /// [CodeGenerator( CodeGeneratorFlags.Instance | CodeGeneratorFlags.WrapMethod, "__rpc_Wrapper" )] [CodeGenerator( CodeGeneratorFlags.Static | CodeGeneratorFlags.WrapMethod, "Sandbox.Rpc.OnCallRpc" )] [Obsolete( "Please use Rpc.Broadcast" )] public sealed class BroadcastAttribute : RpcAttribute { public BroadcastAttribute() : base( RpcMode.Broadcast ) { } NetPermission _permission; public NetPermission Permission { get => _permission; set { _permission = value; if ( _permission == NetPermission.HostOnly ) Flags |= NetFlags.HostOnly; if ( _permission == NetPermission.OwnerOnly ) Flags |= NetFlags.OwnerOnly; } } public BroadcastAttribute( NetPermission permission ) : base( RpcMode.Broadcast ) { Permission = permission; } } /// /// Marks a method as being an RPC specifically targeted to the owner of the , or the host /// if the doesn't have an owner. ///

/// The state of the object the RPC is called on will be up-to-date including its and any /// properties with the or attributes by the time the method /// is called on remote clients. The only except is any synchronized properties marked with which /// will generally only be received every network tick. ///
[CodeGenerator( CodeGeneratorFlags.Instance | CodeGeneratorFlags.WrapMethod, "__rpc_Wrapper" )] [CodeGenerator( CodeGeneratorFlags.Static | CodeGeneratorFlags.WrapMethod, "Sandbox.Rpc.OnCallRpc" )] [Obsolete( "Please use Rpc.Owner" )] public sealed class AuthorityAttribute : RpcAttribute { public AuthorityAttribute() : base( RpcMode.Owner ) { } NetPermission _permission; public NetPermission Permission { get => _permission; set { _permission = value; if ( _permission == NetPermission.HostOnly ) Flags |= NetFlags.HostOnly; if ( _permission == NetPermission.OwnerOnly ) Flags |= NetFlags.OwnerOnly; } } public AuthorityAttribute( NetPermission permission ) : base( RpcMode.Owner ) { Permission = permission; } } /// /// Specifies who can invoke an action over the network. /// [Expose, Obsolete] public enum NetPermission { /// /// Anyone can invoke this. /// Anyone, /// /// Only the host can invoke this. /// HostOnly, /// /// Only the owner can invoke this. If the action is static, this works the same way as . /// OwnerOnly } public static partial class Rpc { /// /// The that is calling this method. /// public static Connection Caller { get; private set; } /// /// The id of the that is calling this method. /// public static Guid CallerId => Caller.Id; /// /// Whether we're currently being called from a remote . /// public static bool Calling { get; private set; } internal static Connection.Filter? Filter { get; private set; } internal static DisposeAction WithCaller( Connection caller ) { var oldCaller = Caller; var oldCalling = Calling; Calling = true; Caller = caller; unsafe { return new DisposeAction( &RestoreCaller, oldCaller, oldCalling ); } } static void RestoreCaller( Connection oldCaller, bool oldCalling ) { Calling = oldCalling; Caller = oldCaller; } /// /// Resume a method from an RPC. If the RPC caller is our local connection then we'll /// first disable any active filter and restore it afterwards. /// /// internal static void Resume( WrappedMethod m ) { try { if ( Caller != Connection.Local ) { m.Resume?.Invoke(); return; } var oldFilter = Filter; Filter = null; try { m.Resume?.Invoke(); } finally { Filter = oldFilter; } } catch ( Exception e ) { Log.Error( e ); } } /// /// Called right before calling an RPC function. /// public static void PreCall() { if ( Calling ) { Calling = false; return; } Caller = Connection.Local; } /// /// Filter the recipients of any Rpc called in this scope to only include the specified set. /// /// Only send the RPC to these connections. public static IDisposable FilterInclude( IEnumerable connections ) { if ( Filter.HasValue ) throw new InvalidOperationException( "An RPC filter is already active" ); Filter = new( Connection.Filter.FilterType.Include, connections ); return DisposeAction.Create( () => { Filter = null; } ); } /// /// Filter the recipients of any Rpc called in this scope to only include a based on a predicate. /// /// Only send the RPC to connections that meet the criteria of the predicate. public static IDisposable FilterInclude( Predicate predicate ) { if ( Filter.HasValue ) throw new InvalidOperationException( "An RPC filter is already active" ); Filter = new( Connection.Filter.FilterType.Include, predicate ); return DisposeAction.Create( () => { Filter = null; } ); } /// /// Filter the recipients of any Rpc called in this scope to only include the specified . /// /// Only send the RPC to this connection. public static IDisposable FilterInclude( Connection connection ) { if ( Filter.HasValue ) throw new InvalidOperationException( "An RPC filter is already active" ); Filter = new( Connection.Filter.FilterType.Include, c => c == connection ); return DisposeAction.Create( () => { Filter = null; } ); } /// /// Filter the recipients of any Rpc called in this scope to exclude a based on a predicate. /// /// Exclude connections that don't meet the criteria of the predicate from receiving the RPC. public static IDisposable FilterExclude( Predicate predicate ) { if ( Filter.HasValue ) throw new InvalidOperationException( "An RPC filter is already active" ); Filter = new( Connection.Filter.FilterType.Exclude, predicate ); return DisposeAction.Create( () => { Filter = null; } ); } /// /// Filter the recipients of any Rpc called in this scope to exclude the specified set. /// /// Exclude these connections from receiving the RPC. public static IDisposable FilterExclude( IEnumerable connections ) { if ( Filter.HasValue ) throw new InvalidOperationException( "An RPC filter is already active" ); Filter = new( Connection.Filter.FilterType.Exclude, connections ); return DisposeAction.Create( () => { Filter = null; } ); } /// /// Filter the recipients of any Rpc called in this scope to exclude the specified . /// /// Exclude this connection from receiving the RPC. public static IDisposable FilterExclude( Connection connection ) { if ( Filter.HasValue ) throw new InvalidOperationException( "An RPC filter is already active" ); Filter = new( Connection.Filter.FilterType.Exclude, c => c == connection ); return DisposeAction.Create( () => { Filter = null; } ); } }