mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-20 04:10:00 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
304 lines
7.6 KiB
C#
304 lines
7.6 KiB
C#
using Sandbox.Resources;
|
|
using System;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Nodes;
|
|
|
|
namespace Editor;
|
|
|
|
unsafe class ResourceCompileContextImp : ResourceCompileContext, IDisposable
|
|
{
|
|
IResourceCompilerContext _context;
|
|
|
|
public ResourceCompileContextImp( IResourceCompilerContext context )
|
|
{
|
|
_context = context;
|
|
|
|
Data = new DataStreamImp { _stream = _context.Data() };
|
|
StreamingData = new DataStreamImp { _stream = _context.StreamingData() };
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_context = default;
|
|
|
|
if ( Data is DataStreamImp a ) a.Dispose();
|
|
if ( StreamingData is DataStreamImp b ) b.Dispose();
|
|
|
|
Data = default;
|
|
StreamingData = default;
|
|
}
|
|
|
|
public override string AbsolutePath => _context.FullPath();
|
|
public override string RelativePath => _context.RelativePath();
|
|
|
|
int _resourceVersion;
|
|
|
|
/// <summary>
|
|
/// The resource version can be important
|
|
/// </summary>
|
|
public override int ResourceVersion
|
|
{
|
|
get => _resourceVersion;
|
|
set
|
|
{
|
|
_resourceVersion = value;
|
|
_context.SpecifyResourceVersion( value );
|
|
}
|
|
}
|
|
|
|
internal override int WriteBlock( string blockName, IntPtr data, int count )
|
|
{
|
|
return _context.WriteBlock( blockName, data, count );
|
|
}
|
|
|
|
public override void AddRuntimeReference( string path )
|
|
{
|
|
_context.RegisterReference( path );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a reference that is needed to compile this resource, but isn't actually needed once compiled.
|
|
/// </summary>
|
|
public override void AddCompileReference( string path )
|
|
{
|
|
_context.RegisterInputFileDependency( path, 0 );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a child resource
|
|
/// </summary>
|
|
public override Child CreateChild( string path )
|
|
{
|
|
var childContext = _context.CreateChildContext( path );
|
|
|
|
return new ChildImplementation( childContext );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read the source, either from in memory, or from disk
|
|
/// </summary>
|
|
public override byte[] ReadSource()
|
|
{
|
|
// read from in memory, if we have an override
|
|
var buffer = _context.GetOverrideData();
|
|
if ( buffer.IsValid )
|
|
{
|
|
IntPtr basePtr = buffer.Base();
|
|
int length = buffer.TellMaxPut();
|
|
byte[] data = new byte[length];
|
|
System.Runtime.InteropServices.Marshal.Copy( basePtr, data, 0, length );
|
|
return data;
|
|
}
|
|
|
|
for ( int i = 0; i < 10; i++ )
|
|
{
|
|
try
|
|
{
|
|
var filename = AbsolutePath;
|
|
return System.IO.File.ReadAllBytes( filename );
|
|
}
|
|
catch ( System.IO.DirectoryNotFoundException )
|
|
{
|
|
Log.Warning( $"Couldn't find source file: [{AbsolutePath}] (directory not found)" );
|
|
return null;
|
|
}
|
|
catch ( System.IO.FileNotFoundException )
|
|
{
|
|
Log.Warning( $"Couldn't find source file: [{AbsolutePath}] (file not found)" );
|
|
return null;
|
|
}
|
|
catch { }
|
|
|
|
// retry
|
|
System.Threading.Thread.Sleep( 10 );
|
|
}
|
|
|
|
Log.Warning( $"Couldn't read source file: [{AbsolutePath}]" );
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load the json and scan it for paths or any embedded resources.
|
|
/// Returns modified Json, which your compiler should use instead.
|
|
/// </summary>
|
|
public override string ScanJson( string jsonString )
|
|
{
|
|
var docOptions = new JsonDocumentOptions();
|
|
docOptions.MaxDepth = 512;
|
|
docOptions.CommentHandling = JsonCommentHandling.Skip;
|
|
|
|
var serializeOptions = new JsonSerializerOptions( JsonSerializerOptions.Default );
|
|
serializeOptions.MaxDepth = 512;
|
|
|
|
JsonNode node;
|
|
|
|
try
|
|
{
|
|
node = JsonNode.Parse( jsonString, default, docOptions );
|
|
}
|
|
catch ( System.Exception e )
|
|
{
|
|
Log.Warning( e, $"Couldn't s json [{AbsolutePath}]" );
|
|
return jsonString;
|
|
}
|
|
|
|
RecurseJsonNodes( node );
|
|
|
|
return node.ToJsonString( serializeOptions );
|
|
}
|
|
|
|
private void RecurseJsonNodes( JsonNode element )
|
|
{
|
|
if ( element is null )
|
|
return;
|
|
|
|
if ( element is JsonArray array )
|
|
{
|
|
// This is a copy cause it's modified during enumuration, specifically for embedded resources below
|
|
foreach ( var e in array.ToList() )
|
|
{
|
|
RecurseJsonNodes( e );
|
|
}
|
|
}
|
|
|
|
if ( element is JsonObject jsonobj )
|
|
{
|
|
foreach ( var e in jsonobj )
|
|
{
|
|
//
|
|
// An object with a $compiler has been found.
|
|
// This is an embedded resource definition.
|
|
// Process it via ResourceCompiler.
|
|
//
|
|
if ( e.Key == "$compiler" )
|
|
{
|
|
try
|
|
{
|
|
EmbeddedResource serialized = JsonSerializer.Deserialize<EmbeddedResource>( jsonobj );
|
|
if ( string.IsNullOrWhiteSpace( serialized.ResourceCompiler ) ) continue;
|
|
|
|
var allCompilers = EditorTypeLibrary.GetTypes<ResourceCompiler>();
|
|
|
|
var compilerType = allCompilers
|
|
.Where( x => x.Attributes
|
|
.OfType<ResourceCompiler.ResourceIdentityAttribute>()
|
|
.Any( y => y.Name == serialized.ResourceCompiler ) )
|
|
.FirstOrDefault();
|
|
|
|
if ( compilerType is null ) continue;
|
|
|
|
var compiler = compilerType.Create<ResourceCompiler>();
|
|
if ( compiler is not null )
|
|
{
|
|
compiler.SetContext( this );
|
|
|
|
if ( compiler.CompileEmbeddedInternal( ref serialized ) )
|
|
{
|
|
jsonobj.ReplaceWith( JsonSerializer.SerializeToElement( serialized ) );
|
|
}
|
|
}
|
|
|
|
}
|
|
catch ( System.Exception exception )
|
|
{
|
|
Log.Warning( exception, "Couldn't deserialize embedded resource {}" );
|
|
}
|
|
}
|
|
|
|
RecurseJsonNodes( e.Value );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this is a string, it might be a filename
|
|
//
|
|
if ( element.GetValueKind() == JsonValueKind.String )
|
|
{
|
|
var str = element.ToString()?.Trim( '"' );
|
|
|
|
if ( string.IsNullOrWhiteSpace( str ) )
|
|
return;
|
|
|
|
var extension = System.IO.Path.GetExtension( str );
|
|
if ( string.IsNullOrWhiteSpace( extension ) )
|
|
return;
|
|
|
|
if ( AssetSystem.FindByPath( str ) is { } asset )
|
|
{
|
|
// If the file type doesn't compile to a file (like images don't) then
|
|
// we should just add it as a compile reference.
|
|
// If we use AddRuntimeReference it's going to try to load it when this
|
|
// resource loads. But if it's an image etc, it won'#t be able to, you'll end
|
|
// up with loads of Error loading resource file "textures/sprites/zombie_elite/zombie_elite_00.jpg_c"
|
|
if ( asset.AssetType == AssetType.ImageFile )
|
|
{
|
|
AddCompileReference( str );
|
|
Log.Trace( $"AddCompileReference: {str} {asset.Path}" );
|
|
}
|
|
else
|
|
{
|
|
AddRuntimeReference( asset.Path );
|
|
Log.Trace( $"AddRuntimeReference: {str} {asset.Path}" );
|
|
}
|
|
|
|
|
|
}
|
|
else if ( AssetType.FromExtension( extension ) is { } type )
|
|
{
|
|
// the c++ asset system supports refs to currently unrecognized assets where, if/when that asset is registered,
|
|
// our dependency tree will be rebuilt, and include proper refs to those assets - so lets make sure we use that, otherwise this is all order dependent
|
|
// (we're really just guessing this is an asset path, is there a better way to do this?)
|
|
AddRuntimeReference( str );
|
|
Log.Trace( $"RegisterUnrecognizedReference: {str} (Type: {type.FriendlyName})" );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class ChildImplementation : ResourceCompileContext.Child
|
|
{
|
|
private IResourceCompilerContextChild _context;
|
|
|
|
public ChildImplementation( IResourceCompilerContextChild childContext )
|
|
{
|
|
_context = childContext;
|
|
}
|
|
|
|
public override bool Compile()
|
|
{
|
|
return _context.CompileImmediately();
|
|
}
|
|
|
|
public override void SetInputData( string data )
|
|
{
|
|
_context.SetOverrideInputData( data );
|
|
}
|
|
}
|
|
|
|
public class DataStreamImp : DataStream
|
|
{
|
|
internal CResourceStream _stream;
|
|
|
|
public void Dispose()
|
|
{
|
|
_stream = default;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write data to the resource
|
|
/// </summary>
|
|
public override void Write( byte[] bytes )
|
|
{
|
|
fixed ( void* ptr = bytes )
|
|
{
|
|
_stream.WriteBytes( (IntPtr)ptr, bytes.Length );
|
|
}
|
|
}
|
|
|
|
//public override void SetAlignment( int bytes )
|
|
//{
|
|
// _stream.Align( bytes, 0 );
|
|
//}
|
|
}
|
|
}
|