Editor/SCFU.cs
using Editor;
using System;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
namespace Sandbox;
public static class SCFU
{
const string BaseUrl = "https://f4pl0.com/scfu/";
static readonly string[] BinaryFiles =
{
"scfu.exe",
"scfu.dll",
"scfu.runtimeconfig.json",
"Microsoft.CodeAnalysis.CSharp.dll",
"Microsoft.CodeAnalysis.dll",
};
static string CacheDir => Path.Combine( Environment.GetFolderPath( Environment.SpecialFolder.LocalApplicationData ), "scfu" );
static string ExePath => Path.Combine( CacheDir, "scfu.exe" );
[Menu( "Editor", "SCFU/Fuck", "lock" )]
public static void Fuck() => _ = RunCommand( "fuck" );
[Menu( "Editor", "SCFU/Unfuck", "lock_open" )]
public static void Unfuck() => _ = RunCommand( "unfuck" );
[Menu( "Editor", "SCFU/Configure…", "tune" )]
public static void Configure()
{
var projectRoot = Project.Current?.GetRootPath();
if ( string.IsNullOrEmpty( projectRoot ) )
{
EditorUtility.DisplayDialog( "SCFU", "No active project.", icon: "🙃" );
return;
}
var window = new ScfuConfigWindow( projectRoot );
window.Show();
}
static async Task RunCommand( string command )
{
try
{
var projectRoot = Project.Current?.GetRootPath();
if ( string.IsNullOrEmpty( projectRoot ) )
{
EditorUtility.DisplayDialog( "SCFU", "No active project.", icon: "🙃" );
return;
}
await Download();
int exitCode;
try
{
exitCode = await RunProcess( command, projectRoot );
}
finally
{
Cleanup();
}
if ( exitCode == 0 )
{
if (command == "fuck")
EditorUtility.DisplayDialog( "SCFU", $"Fucking succeeded. RESTART YOUR EDITOR!", icon:"🤫" );
else if (command == "unfuck")
EditorUtility.DisplayDialog( "SCFU", $"Unfucking succeeded. RESTART YOUR EDITOR!", icon:"🤫" );
else
EditorUtility.DisplayDialog( "SCFU", $"`{command}` succeeded.", icon:"🤫" );
}
else
EditorUtility.DisplayDialog( "SCFU", $"`{command}` failed (exit {exitCode}). See console.", icon: "😬" );
}
catch ( Exception ex )
{
Log.Warning( $"[SCFU] {ex}" );
Cleanup();
EditorUtility.DisplayDialog( "SCFU error", ex.Message, icon: "😬" );
}
}
static void Cleanup()
{
try
{
if ( Directory.Exists( CacheDir ) )
Directory.Delete( CacheDir, recursive: true );
}
catch ( Exception ex )
{
Log.Warning( $"[SCFU] Cleanup failed: {ex.Message}" );
}
}
static async Task Download()
{
Cleanup();
Directory.CreateDirectory( CacheDir );
Log.Info( $"[SCFU] Downloading binary to {CacheDir}" );
using var http = new HttpClient();
foreach ( var file in BinaryFiles )
{
var url = BaseUrl + file;
var dest = Path.Combine( CacheDir, file );
try
{
using var resp = await http.GetAsync( url, HttpCompletionOption.ResponseHeadersRead );
resp.EnsureSuccessStatusCode();
await using var src = await resp.Content.ReadAsStreamAsync();
await using var fs = File.Create( dest );
await src.CopyToAsync( fs );
Log.Info( $"[SCFU] Fetched {file}" );
}
catch ( Exception ex )
{
throw new Exception( $"Failed to download {file}: {ex.Message}", ex );
}
}
}
static async Task<int> RunProcess( string command, string projectRoot )
{
var startInfo = new ProcessStartInfo
{
FileName = ExePath,
WorkingDirectory = CacheDir,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
};
startInfo.ArgumentList.Add( command );
startInfo.ArgumentList.Add( projectRoot );
Log.Info( $"[SCFU] Running: scfu {command} \"{projectRoot}\"" );
using var proc = new Process { StartInfo = startInfo };
proc.OutputDataReceived += ( _, e ) => { if ( e.Data != null ) Log.Info( $"[SCFU] {e.Data}" ); };
proc.ErrorDataReceived += ( _, e ) => { if ( e.Data != null ) Log.Warning( $"[SCFU] {e.Data}" ); };
proc.Start();
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
await proc.WaitForExitAsync();
return proc.ExitCode;
}
}