mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-02 03:18:23 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
284 lines
7.2 KiB
C#
284 lines
7.2 KiB
C#
using System.ComponentModel;
|
|
using System.Runtime.CompilerServices;
|
|
using Sandbox.Network;
|
|
|
|
namespace Sandbox;
|
|
|
|
public abstract partial class GameObjectSystem : IDeltaSnapshot
|
|
{
|
|
private readonly Dictionary<string, IInterpolatedSyncVar> InterpolatedVars = new();
|
|
private NetworkTable dataTable = new();
|
|
|
|
private Guid _id;
|
|
public Guid Id
|
|
{
|
|
get => _id;
|
|
internal set
|
|
{
|
|
if ( _id == value ) return;
|
|
|
|
var oldId = _id;
|
|
_id = value;
|
|
|
|
Scene?.Directory?.Add( this, oldId );
|
|
CreateDataTable();
|
|
}
|
|
}
|
|
|
|
bool IDeltaSnapshot.IsProxy => !Networking.IsHost;
|
|
bool IDeltaSnapshot.ShouldTransmit( Connection target ) => true;
|
|
bool IDeltaSnapshot.UpdateTransmitState( Connection[] targets ) => true;
|
|
ushort IDeltaSnapshot.SnapshotVersion => 0;
|
|
|
|
/// <summary>
|
|
/// Should only be called by <see cref="GameObjectDirectory.Add( GameObjectSystem )"/>.
|
|
/// </summary>
|
|
internal void ForceChangeId( Guid guid )
|
|
{
|
|
_id = guid;
|
|
}
|
|
|
|
[EditorBrowsable( EditorBrowsableState.Never )]
|
|
[MethodImpl( MethodImplOptions.AggressiveInlining )]
|
|
protected void __rpc_Wrapper( in WrappedMethod m, params object[] argumentList )
|
|
{
|
|
Rpc.OnCallInstanceRpc( this, m, argumentList );
|
|
}
|
|
|
|
[EditorBrowsable( EditorBrowsableState.Never )]
|
|
protected void __sync_SetValue<T>( in WrappedPropertySet<T> p )
|
|
{
|
|
try
|
|
{
|
|
// If we aren't valid then just set the property value anyway.
|
|
if ( !Scene.IsValid() )
|
|
{
|
|
p.Setter?.Invoke( p.Value );
|
|
return;
|
|
}
|
|
|
|
var slot = NetworkObject.GetPropertySlot( p.MemberIdent, Id );
|
|
|
|
if ( !dataTable.IsRegistered( slot ) )
|
|
{
|
|
p.Setter?.Invoke( p.Value );
|
|
return;
|
|
}
|
|
|
|
if ( !dataTable.HasControl( slot ) )
|
|
{
|
|
if ( !NetworkTable.IsReadingChanges )
|
|
return;
|
|
|
|
var attribute = p.GetAttribute<SyncAttribute>();
|
|
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;
|
|
}
|
|
|
|
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<T>( WrappedPropertyGet<T> p )
|
|
{
|
|
var attribute = p.GetAttribute<SyncAttribute>();
|
|
var interpolate = attribute?.Flags.HasFlag( SyncFlags.Interpolate ) ?? false;
|
|
if ( !interpolate ) return p.Value;
|
|
|
|
var slot = NetworkObject.GetPropertySlot( p.MemberIdent, Id );
|
|
|
|
if ( !dataTable.IsRegistered( slot ) || dataTable.HasControl( slot ) )
|
|
return p.Value;
|
|
|
|
if ( InterpolatedVars.TryGetValue( p.PropertyName, out var i ) )
|
|
return (T)i.Query( Time.Now );
|
|
|
|
return p.Value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
private InterpolatedSyncVar<T> GetOrCreateInterpolatedVar<T>( T value, string propertyName )
|
|
{
|
|
if ( InterpolatedVars.TryGetValue( propertyName, out var i ) )
|
|
return (InterpolatedSyncVar<T>)i;
|
|
|
|
var interpolator = IInterpolatedSyncVar.Create( value );
|
|
|
|
if ( interpolator is null )
|
|
return null;
|
|
|
|
var interpolated = new InterpolatedSyncVar<T>( interpolator );
|
|
InterpolatedVars[propertyName] = interpolated;
|
|
|
|
return interpolated;
|
|
}
|
|
|
|
private void RegisterSyncVarProperties()
|
|
{
|
|
var type = GetType();
|
|
|
|
// Register all our Sync properties with the data table.
|
|
foreach ( var propertyAndAttribute in ReflectionQueryCache.SyncProperties( type ) )
|
|
{
|
|
var isQuery = propertyAndAttribute.Attribute.Flags.HasFlag( SyncFlags.Query );
|
|
|
|
try
|
|
{
|
|
var originType = propertyAndAttribute.Property.DeclaringType ?? type;
|
|
var identity = NetworkObject.GetPropertySlot( $"{originType.FullName}.{propertyAndAttribute.Property.Name}".FastHash(), Id );
|
|
|
|
var entry = new NetworkTable.Entry
|
|
{
|
|
TargetType = propertyAndAttribute.Property.PropertyType,
|
|
ControlCondition = c => c.IsHost,
|
|
GetValue = () => propertyAndAttribute.Property.GetValue( this ),
|
|
SetValue = v => propertyAndAttribute.Property?.SetValue( this, v ),
|
|
NeedsQuery = isQuery,
|
|
DebugName = $"{originType.Name}.{propertyAndAttribute.Property.Name}"
|
|
};
|
|
|
|
dataTable.Register( identity, entry );
|
|
}
|
|
catch ( Exception e )
|
|
{
|
|
Log.Warning( e, $"Got exception when creating network table (reading {this}.{propertyAndAttribute.Property.Name}) - {e.Message}" );
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CreateDataTable()
|
|
{
|
|
dataTable?.Dispose();
|
|
dataTable = new();
|
|
|
|
RegisterSyncVarProperties();
|
|
}
|
|
|
|
internal readonly LocalSnapshotState LocalSnapshotState = new();
|
|
|
|
void IDeltaSnapshot.OnSnapshotAck( Connection source, DeltaSnapshot snapshot, RemoteSnapshotState state )
|
|
{
|
|
var hasFullSnapshotState = true;
|
|
|
|
foreach ( var entry in LocalSnapshotState.Entries )
|
|
{
|
|
if ( state.IsValueHashEqual( entry.Slot, entry.Hash, snapshot.SnapshotId ) )
|
|
{
|
|
entry.Connections.Add( source.Id );
|
|
}
|
|
else
|
|
{
|
|
entry.Connections.Remove( source.Id );
|
|
hasFullSnapshotState = false;
|
|
}
|
|
}
|
|
|
|
if ( hasFullSnapshotState )
|
|
LocalSnapshotState.UpdatedConnections.Add( source.Id );
|
|
else
|
|
LocalSnapshotState.UpdatedConnections.Remove( source.Id );
|
|
}
|
|
|
|
LocalSnapshotState IDeltaSnapshot.WriteSnapshotState()
|
|
{
|
|
var system = SceneNetworkSystem.Instance;
|
|
if ( system is null ) return null;
|
|
|
|
LocalSnapshotState.SnapshotId = system.DeltaSnapshots.CreateSnapshotId( Id );
|
|
LocalSnapshotState.ObjectId = Id;
|
|
|
|
dataTable.QueryValues();
|
|
dataTable.WriteSnapshotState( LocalSnapshotState );
|
|
|
|
return LocalSnapshotState;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Try to send a network update or do nothing if no update is required. This is most
|
|
/// likely called after WriteDeltaSnapshot.
|
|
/// </summary>
|
|
void IDeltaSnapshot.SendNetworkUpdate( bool queryValues )
|
|
{
|
|
if ( queryValues )
|
|
dataTable.QueryValues( true );
|
|
|
|
if ( !dataTable.HasReliableChanges() )
|
|
return;
|
|
|
|
var msg = new SceneNetworkTableMsg { Guid = Id };
|
|
var data = ByteStream.Create( 4096 );
|
|
|
|
dataTable.WriteReliableChanged( ref data );
|
|
msg.TableData = data.ToArray();
|
|
|
|
data.Dispose();
|
|
|
|
SceneNetworkSystem.Instance.Broadcast( msg );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write all pending data table changes.
|
|
/// </summary>
|
|
internal byte[] WriteDataTable( bool full )
|
|
{
|
|
if ( !dataTable.HasAnyChanges && !full )
|
|
return null;
|
|
|
|
var data = ByteStream.Create( 32 );
|
|
|
|
if ( full )
|
|
dataTable.WriteAll( ref data );
|
|
else
|
|
dataTable.WriteChanged( ref data );
|
|
|
|
var bytes = data.ToArray();
|
|
data.Dispose();
|
|
|
|
return bytes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read the network table data.
|
|
/// </summary>
|
|
internal void ReadDataTable( byte[] data, NetworkTable.ReadFilter filter = null )
|
|
{
|
|
if ( data is null ) return;
|
|
|
|
var reader = ByteStream.CreateReader( data );
|
|
dataTable.Read( ref reader, filter );
|
|
reader.Dispose();
|
|
}
|
|
|
|
bool IDeltaSnapshot.OnSnapshot( Connection source, DeltaSnapshot snapshot )
|
|
{
|
|
// Don't process this if the source connection is not the host.
|
|
if ( !source.IsHost )
|
|
return false;
|
|
|
|
dataTable.ReadSnapshot( source, snapshot );
|
|
return true;
|
|
}
|
|
|
|
internal void OnHotload()
|
|
{
|
|
CreateDataTable();
|
|
}
|
|
}
|