mirror of
https://github.com/Facepunch/sbox-public.git
synced 2025-12-28 00:48:18 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
158 lines
5.1 KiB
C#
158 lines
5.1 KiB
C#
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using System.Text.Json;
|
|
|
|
namespace Sandbox;
|
|
|
|
public class CodeArchive
|
|
{
|
|
/// <summary>
|
|
/// The name of the compiler
|
|
/// </summary>
|
|
public string CompilerName { get; set; }
|
|
|
|
/// <summary>
|
|
/// The compiler's configuration settings
|
|
/// </summary>
|
|
public Compiler.Configuration Configuration { get; set; }
|
|
|
|
/// <summary>
|
|
/// The syntax trees that should be compiled
|
|
/// </summary>
|
|
public List<SyntaxTree> SyntaxTrees { get; } = new();
|
|
|
|
/// <summary>
|
|
/// Represents a file to send to the compiler along with all the code. This is usually
|
|
/// something that the generator turns into code, such as a Razor file.
|
|
/// </summary>
|
|
public record AdditionalFile( string Text, string LocalPath );
|
|
|
|
/// <summary>
|
|
/// Additional files that the compiler/generator needs. This is going to be .razor files.
|
|
/// </summary>
|
|
public List<AdditionalFile> AdditionalFiles { get; } = new();
|
|
|
|
/// <summary>
|
|
/// Converts the syntax tree paths from physical paths to project local paths
|
|
/// </summary>
|
|
public Dictionary<string, string> FileMap { get; } = new( StringComparer.OrdinalIgnoreCase );
|
|
|
|
/// <summary>
|
|
/// References that this compiler/generator needs to compile the code
|
|
/// </summary>
|
|
public HashSet<string> References { get; } = new HashSet<string>( StringComparer.OrdinalIgnoreCase );
|
|
|
|
/// <summary>
|
|
/// The version of the code archive
|
|
/// 1005 - Inital version
|
|
/// 1006 - Razor updates. Add razor namespaces on older versions.
|
|
/// 1007 - Razor changed to our own Microsoft.AspNetCore.Components assembly
|
|
/// </summary>
|
|
public long Version { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
/// If true then we shouldn't automatically add @namespace to razor output, we shouldn't
|
|
/// use the folder namespace structure.
|
|
/// </summary>
|
|
internal bool Version_UsesOldRazorNamespaces => Version < 1007;
|
|
|
|
public CodeArchive()
|
|
{
|
|
Version = 1007;
|
|
}
|
|
|
|
public CodeArchive( byte[] data )
|
|
{
|
|
Deserialize( data );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serialize to a byte array
|
|
/// </summary>
|
|
public byte[] Serialize()
|
|
{
|
|
using ByteStream bs = ByteStream.Create( 128 );
|
|
|
|
bs.Write( "GMCA" ); // gmod code archive
|
|
bs.Write( Version ); // version
|
|
|
|
bs.Write( CompilerName );
|
|
bs.Write( JsonSerializer.Serialize( Configuration ) );
|
|
bs.Write( JsonSerializer.Serialize( References ) );
|
|
bs.Write( JsonSerializer.Serialize( SyntaxTrees.Select( x => new AdditionalFile( x.GetText().ToString(), x.FilePath ) ) ) );
|
|
bs.Write( JsonSerializer.Serialize( AdditionalFiles ) );
|
|
bs.Write( JsonSerializer.Serialize( FileMap ) );
|
|
|
|
return bs.Compress().ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deserialize from a byte array
|
|
/// </summary>
|
|
internal void Deserialize( byte[] data )
|
|
{
|
|
using var compressed = ByteStream.CreateReader( data );
|
|
using var bs = compressed.Decompress();
|
|
|
|
var ident = bs.Read<string>();
|
|
if ( ident != "GMCA" ) throw new Exception( "Invalid archive" );
|
|
|
|
Version = bs.Read<long>();
|
|
if ( Version < 1005 ) throw new Exception( "Invalid archive version" );
|
|
if ( Version > 1007 ) throw new Exception( "Invalid archive version" );
|
|
|
|
CompilerName = bs.Read<string>();
|
|
Configuration = JsonSerializer.Deserialize<Compiler.Configuration>( bs.Read<string>() );
|
|
|
|
// parse the syntaxtree's using the same options as the creator
|
|
var parseOptions = Configuration.GetParseOptions();
|
|
|
|
References.Clear();
|
|
var references = JsonSerializer.Deserialize<List<string>>( bs.Read<string>() );
|
|
foreach ( string reference in references )
|
|
{
|
|
// 7th June 2025 - We removed Sandbox.Game, which held the scene and ui systems. They're in engine now.
|
|
if ( reference.Equals( "Sandbox.Game", StringComparison.OrdinalIgnoreCase ) ) continue;
|
|
|
|
References.Add( reference );
|
|
}
|
|
|
|
// 14th Nov 2025 - Older archives didn't reference Microsoft.AspNetCore.Components, but they should now.
|
|
if ( Version < 1007 )
|
|
{
|
|
References.Add( "Microsoft.AspNetCore.Components" );
|
|
}
|
|
|
|
SyntaxTrees.Clear();
|
|
var syntaxTrees = JsonSerializer.Deserialize<List<AdditionalFile>>( bs.Read<string>() );
|
|
foreach ( var file in syntaxTrees )
|
|
{
|
|
var snytaxTree = CSharpSyntaxTree.ParseText( file.Text, path: file.LocalPath, encoding: System.Text.Encoding.UTF8, options: parseOptions );
|
|
SyntaxTrees.Add( snytaxTree );
|
|
}
|
|
|
|
AdditionalFiles.Clear();
|
|
var additionalFiles = JsonSerializer.Deserialize<List<AdditionalFile>>( bs.Read<string>() );
|
|
foreach ( var file in additionalFiles )
|
|
{
|
|
AdditionalFiles.Add( file );
|
|
}
|
|
|
|
var fileMap = JsonSerializer.Deserialize<Dictionary<string, string>>( bs.Read<string>() );
|
|
FileMap.Clear();
|
|
foreach ( (string k, string v) in fileMap )
|
|
{
|
|
FileMap[k] = v;
|
|
}
|
|
|
|
if ( Version < 1006 )
|
|
{
|
|
// In archives previous to 1006 we didn't define a global using for global using Microsoft.AspNetCore.Components
|
|
// and after that we moved the razor stuff into that namespace to be compatible with visual studio.
|
|
var tree = CSharpSyntaxTree.ParseText( "global using Microsoft.AspNetCore.Components; \nglobal using Microsoft.AspNetCore.Components.Rendering;\n", path: "__gen_RazorNamespace.cs", encoding: System.Text.Encoding.UTF8, options: parseOptions );
|
|
SyntaxTrees.Add( tree );
|
|
}
|
|
}
|
|
}
|