mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-05-08 15:14:39 -04:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
366 lines
10 KiB
C#
366 lines
10 KiB
C#
using Sandbox.SolutionGenerator;
|
|
using System.IO;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace Sandbox;
|
|
|
|
/// <summary>
|
|
/// Represents an on-disk project.
|
|
/// </summary>
|
|
public sealed partial class Project
|
|
{
|
|
bool AddonTypeUsesCode()
|
|
{
|
|
if ( Config.Type == "game" ) return true;
|
|
if ( Config.Type == "tool" ) return true;
|
|
if ( Config.Type == "library" ) return true;
|
|
if ( Config.Type == "addon" ) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
internal async Task GenerateProject( Sandbox.SolutionGenerator.Generator generator )
|
|
{
|
|
// justify the async
|
|
await Task.Yield();
|
|
|
|
if ( !AddonTypeUsesCode() )
|
|
return;
|
|
|
|
if ( Config.Directory is null )
|
|
return;
|
|
|
|
var projectName = Config.GetMetaOrDefault( "CsProjName", Config.Ident );
|
|
if ( !Regex.IsMatch( projectName, @"^[a-zA-Z\.\ \-_0-9]{1,32}$" ) )
|
|
{
|
|
if ( !string.IsNullOrWhiteSpace( projectName ) )
|
|
{
|
|
Log.Warning( $"Project name '{projectName}' is invalid - reverting to ident" );
|
|
}
|
|
|
|
projectName = Config.Ident;
|
|
}
|
|
|
|
string projectFolder = Config.Type == "library" ? "Libraries" : $"{Config.Type.ToTitleCase()}s";
|
|
if ( projectFolder == "Games" ) projectFolder = null;
|
|
|
|
ProjectInfo project = null;
|
|
|
|
if ( HasCodePath() )
|
|
{
|
|
//
|
|
// Code project
|
|
//
|
|
var compilerSettings = Config.GetCompileSettings();
|
|
|
|
if ( Config.Type == "game" )
|
|
{
|
|
compilerSettings.IgnoreFolders.Add( "editor" );
|
|
compilerSettings.IgnoreFolders.Add( "unittest" );
|
|
}
|
|
|
|
project = generator.AddProject( Config.Type, Config.FullIdent, projectName, GetCodePath(), compilerSettings );
|
|
project.Folder = projectFolder;
|
|
project.SandboxProjectFilePath = ConfigFilePath;
|
|
|
|
//
|
|
// Add each reference to the project
|
|
//
|
|
foreach ( var reference in compilerSettings.DistinctAssemblyReferences )
|
|
{
|
|
project.References.Add( $"{reference}.dll" );
|
|
}
|
|
|
|
//
|
|
// Server projects
|
|
//
|
|
if ( Config.Type == "game" || Config.Type == "library" )
|
|
{
|
|
var serverProject = AddServerProjectFrom( projectName, generator );
|
|
if ( serverProject is not null )
|
|
{
|
|
serverProject.Folder = projectFolder;
|
|
|
|
//
|
|
// Hide server files from the main project
|
|
//
|
|
project.IgnoreFiles.Add( "**/*.Server.cs" );
|
|
}
|
|
}
|
|
|
|
if ( Config.Type == "tool" )
|
|
{
|
|
project.References.Add( "Sandbox.Tools.dll" );
|
|
project.References.Add( "Sandbox.Compiling.dll" );
|
|
project.References.Add( "Microsoft.CodeAnalysis.dll" );
|
|
project.References.Add( "Microsoft.CodeAnalysis.CSharp.dll" );
|
|
project.References.Add( "Sandbox.Bind.dll" );
|
|
project.References.Add( "Facepunch.ActionGraphs.dll" );
|
|
project.References.Add( "SkiaSharp.dll" );
|
|
project.GlobalStatic.Add( "Sandbox.Internal.GlobalToolsNamespace" );
|
|
project.GlobalStatic.Add( "Sandbox.Internal.GlobalGameNamespace" );
|
|
|
|
if ( Config.Ident != "toolbase" )
|
|
project.PackageReferences.Add( "local.toolbase" );
|
|
}
|
|
else if ( Config.Type == "game" || Config.Type == "library" )
|
|
{
|
|
project.GlobalUsing.Add( "Microsoft.AspNetCore.Components" );
|
|
project.GlobalUsing.Add( "Microsoft.AspNetCore.Components.Rendering" );
|
|
project.GlobalStatic.Add( "Sandbox.Internal.GlobalGameNamespace" );
|
|
|
|
if ( !project.PackageReferences.Contains( "local.base" ) )
|
|
{
|
|
project.PackageReferences.Add( "local.base" );
|
|
}
|
|
}
|
|
|
|
if ( Config.Type == "game" )
|
|
{
|
|
AddLibrariesToProject( project );
|
|
|
|
}
|
|
}
|
|
|
|
if ( Config.Type == "game" || Config.Type == "library" )
|
|
{
|
|
//
|
|
// Editor project
|
|
//
|
|
var editorProject = AddEditorProjectFrom( projectName, generator );
|
|
if ( editorProject is not null )
|
|
{
|
|
editorProject.Folder = projectFolder;
|
|
editorProject.SandboxProjectFilePath = ConfigFilePath;
|
|
|
|
if ( project is not null )
|
|
editorProject.PackageReferences.Add( project.Name );
|
|
}
|
|
|
|
//
|
|
// Unit test project
|
|
//
|
|
var testProject = AddUnitTestProjectFrom( projectName, generator );
|
|
if ( testProject is not null )
|
|
{
|
|
testProject.Folder = projectFolder;
|
|
|
|
if ( project is not null )
|
|
testProject.PackageReferences.Add( project.Name );
|
|
|
|
if ( editorProject is not null )
|
|
testProject.PackageReferences.Add( editorProject.Name );
|
|
}
|
|
}
|
|
}
|
|
|
|
private void AddLibrariesToProject( ProjectInfo project )
|
|
{
|
|
foreach ( var library in Project.Libraries.Where( x => x.HasCodePath() ) )
|
|
{
|
|
project.PackageReferences.Add( library.Package.GetIdent( false, false ) );
|
|
}
|
|
}
|
|
|
|
private bool HasServersideCode()
|
|
{
|
|
if ( !HasCodePath() )
|
|
return false;
|
|
|
|
var codePath = GetCodePath().Replace( '\\', '/' );
|
|
|
|
//
|
|
// Check for any .Server.cs files recursively
|
|
//
|
|
try
|
|
{
|
|
return Directory.EnumerateFiles( codePath, "*.Server.cs", SearchOption.AllDirectories ).Any();
|
|
}
|
|
catch ( Exception e )
|
|
{
|
|
Log.Warning( $"Failed to check for server files in {codePath}: {e.Message}" );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ProjectInfo AddServerProjectFrom( string projectName, Sandbox.SolutionGenerator.Generator generator )
|
|
{
|
|
if ( !HasCodePath() )
|
|
return default;
|
|
|
|
if ( !HasServersideCode() )
|
|
return default;
|
|
|
|
var serverSettings = Config.GetCompileSettings();
|
|
serverSettings.DefineConstants += ";SERVER";
|
|
|
|
var project = generator.AddProject( Config.Type,
|
|
$"{Config.FullIdent}.server",
|
|
$"{projectName}.server",
|
|
GetCodePath(),
|
|
serverSettings );
|
|
|
|
foreach ( var reference in serverSettings.DistinctAssemblyReferences )
|
|
{
|
|
project.References.Add( $"{reference}.dll" );
|
|
}
|
|
|
|
// Add standard references
|
|
if ( Config.Type == "game" || Config.Type == "library" )
|
|
{
|
|
project.GlobalUsing.Add( "Microsoft.AspNetCore.Components" );
|
|
project.GlobalUsing.Add( "Microsoft.AspNetCore.Components.Rendering" );
|
|
project.GlobalStatic.Add( "Sandbox.Internal.GlobalGameNamespace" );
|
|
|
|
if ( !project.PackageReferences.Contains( "local.base" ) )
|
|
{
|
|
project.PackageReferences.Add( "local.base" );
|
|
}
|
|
}
|
|
|
|
if ( Config.Type == "game" )
|
|
{
|
|
AddLibrariesToProject( project );
|
|
}
|
|
|
|
return project;
|
|
}
|
|
|
|
ProjectInfo AddEditorProjectFrom( string projectName, Sandbox.SolutionGenerator.Generator generator )
|
|
{
|
|
if ( !HasEditorPath() )
|
|
return default;
|
|
|
|
var compilerSettings = Config.GetCompileSettings();
|
|
var project = generator.AddProject( "tool", $"{Config.FullIdent}.editor", $"{projectName}.editor", GetEditorPath(), compilerSettings );
|
|
project.IsEditorProject = true;
|
|
|
|
//
|
|
// Add each reference to the project
|
|
//
|
|
foreach ( var reference in compilerSettings.DistinctAssemblyReferences )
|
|
{
|
|
project.References.Add( $"{reference}.dll" );
|
|
}
|
|
|
|
if ( Config.Type == "game" )
|
|
{
|
|
// editor libraries
|
|
foreach ( var library in Libraries.Where( x => x.HasEditorPath() ) )
|
|
{
|
|
project.PackageReferences.Add( $"{library.Package.GetIdent( false, false )}.editor" );
|
|
}
|
|
}
|
|
|
|
// tool includes
|
|
project.References.Add( "Sandbox.Tools.dll" );
|
|
project.References.Add( "Sandbox.Compiling.dll" );
|
|
project.References.Add( "Microsoft.CodeAnalysis.dll" );
|
|
project.References.Add( "Microsoft.CodeAnalysis.CSharp.dll" );
|
|
project.References.Add( "Sandbox.Bind.dll" );
|
|
project.References.Add( "Facepunch.ActionGraphs.dll" );
|
|
project.References.Add( "SkiaSharp.dll" );
|
|
project.GlobalStatic.Add( "Sandbox.Internal.GlobalToolsNamespace" );
|
|
project.GlobalStatic.Add( "Sandbox.Internal.GlobalGameNamespace" );
|
|
project.PackageReferences.Add( "local.toolbase" );
|
|
project.PackageReferences.Add( "actiongraph" );
|
|
project.PackageReferences.Add( "shadergraph" );
|
|
project.PackageReferences.Add( "hammer" );
|
|
return project;
|
|
}
|
|
|
|
ProjectInfo AddUnitTestProjectFrom( string projectName, Sandbox.SolutionGenerator.Generator generator )
|
|
{
|
|
var dirinfo = new DirectoryInfo( System.IO.Path.Combine( GetRootPath(), "UnitTests" ) );
|
|
if ( !dirinfo.Exists )
|
|
return default;
|
|
|
|
var compilerSettings = Config.GetCompileSettings();
|
|
var project = generator.AddProject( "unittest", $"{Config.FullIdent}.unittest", $"{projectName}.unittest", dirinfo.FullName, compilerSettings );
|
|
project.IsUnitTestProject = true;
|
|
|
|
//
|
|
// Add each reference to the project
|
|
//
|
|
foreach ( var reference in compilerSettings.DistinctAssemblyReferences )
|
|
{
|
|
project.References.Add( $"{reference}.dll" );
|
|
}
|
|
|
|
// tool includes
|
|
project.References.Add( "Sandbox.Tools.dll" );
|
|
project.References.Add( "Sandbox.Compiling.dll" );
|
|
project.References.Add( "Microsoft.CodeAnalysis.dll" );
|
|
project.References.Add( "Microsoft.CodeAnalysis.CSharp.dll" );
|
|
project.References.Add( "Sandbox.Bind.dll" );
|
|
project.GlobalStatic.Add( "Sandbox.Internal.GlobalToolsNamespace" );
|
|
project.GlobalStatic.Add( "Sandbox.Internal.GlobalGameNamespace" );
|
|
project.PackageReferences.Add( "local.toolbase" );
|
|
|
|
return project;
|
|
}
|
|
|
|
async Task<string[]> FindReferences( string packageIdent )
|
|
{
|
|
//
|
|
// If we have this package locally installed, then use that one
|
|
//
|
|
var localAddon = Project.FindByIdent( packageIdent );
|
|
if ( localAddon != null && localAddon.Active )
|
|
{
|
|
return new[] { $"{packageIdent}" };
|
|
}
|
|
|
|
//
|
|
// Is this a valid package name etc?
|
|
//
|
|
if ( !Package.TryParseIdent( packageIdent, out var parts ) )
|
|
return null;
|
|
|
|
//
|
|
// Find the package with the dll and download it
|
|
//
|
|
var package = await Package.Fetch( packageIdent, false );
|
|
if ( package == null || package.Revision == null ) return null;
|
|
|
|
// download the manifest
|
|
await package.Revision.DownloadManifestAsync( default );
|
|
|
|
EngineFileSystem.Root.CreateDirectory( "/.source2/references" );
|
|
|
|
|
|
// PAINDAY: Remove legacy check
|
|
var dlls = package.Revision.Manifest.Files.Where( x => x.Path == ".assembly" || (x.Path.EndsWith( ".dll" ) && x.Path != ".assembly/dynamic.base.dll") ).ToArray();
|
|
|
|
// This is fine/normal - this package has no assemblies
|
|
if ( !dlls.Any() ) return null;
|
|
|
|
List<string> assemblies = new List<string>();
|
|
|
|
foreach ( var dll in dlls )
|
|
{
|
|
var filename = $"{package.Org.Ident}.{package.Ident}.{package.Revision.VersionId}.{dll.Crc}.dll";
|
|
|
|
string dllPath = $"/.source2/references/{filename}";
|
|
dllPath = EngineFileSystem.Root.GetFullPath( dllPath );
|
|
|
|
//
|
|
// Have we already got this dll? Just use it then dummy
|
|
//
|
|
if ( System.IO.File.Exists( dllPath ) )
|
|
{
|
|
assemblies.Add( dllPath );
|
|
continue;
|
|
}
|
|
|
|
// WTF WTF WHAT THE FUCK
|
|
var gameDll = await Sandbox.Utility.Web.GrabFile( $"{dll.Url}", default );
|
|
Log.Trace( $"Caching Referenced assembly for {packageIdent}.." );
|
|
System.IO.File.WriteAllBytes( dllPath, gameDll );
|
|
assemblies.Add( dllPath );
|
|
}
|
|
|
|
return assemblies.ToArray();
|
|
}
|
|
}
|