mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-16 02:09:20 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
215 lines
5.1 KiB
C#
215 lines
5.1 KiB
C#
using Sandbox.Internal;
|
|
using Sandbox.Network;
|
|
using System.Threading;
|
|
|
|
namespace Sandbox;
|
|
|
|
internal class LargeNetworkFiles
|
|
{
|
|
public StringTable StringTable { get; init; }
|
|
|
|
record struct LargeFileInfo( long Size, ulong CRC );
|
|
|
|
RedirectFileSystem RedirectFileSystem { get; set; }
|
|
|
|
HashSet<string> downloadQueue = new();
|
|
|
|
public LargeNetworkFiles( string name )
|
|
{
|
|
StringTable = new( name, true );
|
|
StringTable.OnChangeOrAdd += OnTableEntryUpdated;
|
|
StringTable.OnRemoved += OnTableEntryRemoved;
|
|
StringTable.OnSnapshot += OnTableSnapshot;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reset the string table.
|
|
/// </summary>
|
|
public void Reset()
|
|
{
|
|
StringTable.Reset();
|
|
|
|
RedirectFileSystem?.Dispose();
|
|
RedirectFileSystem = AssetDownloadCache.CreateRedirectFileSystem();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add all files from the network.
|
|
/// </summary>
|
|
public void Refresh()
|
|
{
|
|
foreach ( var (_, entry) in StringTable.Entries )
|
|
{
|
|
AddFileToFileSystem( entry.Name, entry.Read<LargeFileInfo>() );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a file to be networked.
|
|
/// </summary>
|
|
public bool AddFile( string fileName )
|
|
{
|
|
if ( !EngineFileSystem.Mounted.FileExists( fileName ) )
|
|
return false;
|
|
|
|
var crc = EngineFileSystem.Mounted.GetCrc( fileName );
|
|
var size = EngineFileSystem.Mounted.FileSize( fileName );
|
|
var normalizedFileName = NormalizeFileName( fileName );
|
|
StringTable.Set( normalizedFileName, new LargeFileInfo( size, crc ) );
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove a networked file.
|
|
/// </summary>
|
|
public void RemoveFile( string fileName )
|
|
{
|
|
var normalizedFileName = NormalizeFileName( fileName );
|
|
StringTable.Remove( normalizedFileName );
|
|
}
|
|
|
|
string NormalizeFileName( string fileName )
|
|
{
|
|
return BaseFileSystem.NormalizeFilename( fileName ).TrimStart( '/' );
|
|
}
|
|
|
|
void OnTableEntryUpdated( StringTable.Entry entry )
|
|
{
|
|
AddFileToFileSystem( entry.Name, entry.Read<LargeFileInfo>() );
|
|
}
|
|
|
|
void OnTableEntryRemoved( StringTable.Entry entry )
|
|
{
|
|
|
|
}
|
|
|
|
void OnTableSnapshot()
|
|
{
|
|
Log.Info( "Checking for network files.." );
|
|
var sw = System.Diagnostics.Stopwatch.StartNew();
|
|
|
|
Refresh();
|
|
|
|
Log.Info( $"..done in {sw.Elapsed.TotalSeconds:0.00}s" );
|
|
}
|
|
|
|
void AddFileToFileSystem( string fileName, LargeFileInfo contents )
|
|
{
|
|
// Can we find this file somewhere, or do we need to download it?
|
|
|
|
if ( EngineFileSystem.Mounted.FileExists( fileName ) )
|
|
{
|
|
var size = EngineFileSystem.Mounted.FileSize( fileName );
|
|
if ( size == contents.Size )
|
|
{
|
|
var crc = EngineFileSystem.Mounted.GetCrc( fileName );
|
|
if ( crc == contents.CRC )
|
|
{
|
|
if ( AssetDownloadCache.DebugNetworkFiles )
|
|
{
|
|
Log.Info( $"Skipping downloading {fileName} - we already have it" );
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !AssetDownloadCache.IsLegalDownload( fileName ) )
|
|
return;
|
|
|
|
if ( AssetDownloadCache.TryMount( RedirectFileSystem, fileName, contents.CRC ) )
|
|
return;
|
|
|
|
if ( AssetDownloadCache.DebugNetworkFiles )
|
|
{
|
|
Log.Info( $"Queued Network File: {fileName} / {contents.Size} / {contents.CRC}" );
|
|
}
|
|
downloadQueue.Add( fileName );
|
|
}
|
|
|
|
public async Task RunDownloadQueue( NetworkSystem system, CancellationToken token )
|
|
{
|
|
if ( RedirectFileSystem is null )
|
|
return;
|
|
|
|
Assert.NotNull( Connection.Host );
|
|
|
|
Log.Info( $"Downloading {downloadQueue.Count} files.." );
|
|
var currentCount = 0;
|
|
var sw = System.Diagnostics.Stopwatch.StartNew();
|
|
|
|
foreach ( var file in downloadQueue )
|
|
{
|
|
if ( !StringTable.Entries.TryGetValue( file, out var entry ) )
|
|
continue;
|
|
|
|
var info = entry.Read<LargeFileInfo>();
|
|
|
|
if ( AssetDownloadCache.DebugNetworkFiles )
|
|
{
|
|
Log.Info( $"Download file {file}" );
|
|
}
|
|
|
|
system.UpdateLoading( $"Download file ({currentCount + 1}/{downloadQueue.Count}) {file}" );
|
|
|
|
if ( RedirectFileSystem.FileExists( file.NormalizeFilename( true ) ) )
|
|
{
|
|
currentCount++;
|
|
continue;
|
|
}
|
|
|
|
token.ThrowIfCancellationRequested();
|
|
|
|
if ( Connection.Host is null )
|
|
{
|
|
throw new TaskCanceledException( "Connection became null" );
|
|
}
|
|
|
|
// download the file
|
|
var response = await Connection.Host.SendRequest( new RequestFile { filename = file } );
|
|
|
|
token.ThrowIfCancellationRequested();
|
|
|
|
if ( response is not byte[] data )
|
|
{
|
|
Log.Warning( $"Unknown response {response}!" );
|
|
currentCount++;
|
|
continue;
|
|
}
|
|
|
|
var fn = AssetDownloadCache.StoreFile( file, info.CRC, data );
|
|
if ( fn is not null )
|
|
{
|
|
RedirectFileSystem.AddAbsFile( file, fn );
|
|
}
|
|
|
|
currentCount++;
|
|
}
|
|
|
|
downloadQueue.Clear();
|
|
|
|
Log.Info( $"Download Complete ({downloadQueue.Count()} files total) ({sw.Elapsed.TotalSeconds:0.00}s)" );
|
|
}
|
|
|
|
internal void NetworkInitialize( GameNetworkSystem instance )
|
|
{
|
|
instance.AddHandler<RequestFile>( OnRequestNetworkFile );
|
|
}
|
|
|
|
async Task OnRequestNetworkFile( RequestFile file, Connection connection, Guid msgGuid )
|
|
{
|
|
Log.Info( $"Client requested file {file.filename} ({msgGuid})" );
|
|
await Task.Delay( 100 );
|
|
|
|
var contents = await EngineFileSystem.Mounted.ReadAllBytesAsync( file.filename );
|
|
Log.Info( $"Read {contents.Length.FormatBytes()}" );
|
|
|
|
connection.SendResponse( msgGuid, contents );
|
|
}
|
|
|
|
[Expose]
|
|
public record struct RequestFile( string filename );
|
|
|
|
}
|