mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-04-19 05:48:07 -04:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
390 lines
8.7 KiB
C#
390 lines
8.7 KiB
C#
namespace Editor;
|
|
|
|
using Sandbox;
|
|
using Sandbox.Utility;
|
|
using System;
|
|
using Editor.Wizards;
|
|
|
|
public class BatchPublisher : BaseWindow
|
|
{
|
|
public static void FromAssets( Asset[] assets )
|
|
{
|
|
var p = new BatchPublisher( assets );
|
|
p.Show();
|
|
}
|
|
|
|
public static void FromAssetsWithEnablePublish( Asset[] assets )
|
|
{
|
|
if ( assets.All( x => x.Publishing.Enabled ) )
|
|
{
|
|
FromAssets( assets );
|
|
return;
|
|
}
|
|
|
|
var popup = new BatchMarkPublishedWidget( assets );
|
|
popup.SetModal( true, true );
|
|
popup.Hide();
|
|
popup.Show();
|
|
}
|
|
|
|
public List<Asset> Assets { get; set; }
|
|
|
|
public ScrollArea AssetList { get; set; }
|
|
|
|
|
|
public Button PublishButton { get; set; }
|
|
|
|
|
|
private BatchPublisher( IEnumerable<Asset> assets ) : base()
|
|
{
|
|
Size = new Vector2( 900, 670 );
|
|
MinimumSize = Size;
|
|
TranslucentBackground = true;
|
|
NoSystemBackground = true;
|
|
|
|
SetWindowIcon( "backup" );
|
|
|
|
Layout = Layout.Column();
|
|
Layout.Margin = 4;
|
|
Layout.Spacing = 4;
|
|
|
|
WindowTitle = $"Batch Publish v1.0";
|
|
|
|
var row = Layout.AddRow();
|
|
|
|
BuildHeader( row );
|
|
|
|
AssetList = new ScrollArea( this );
|
|
AssetList.MinimumWidth = 700;
|
|
row.Add( AssetList, 100 );
|
|
|
|
AssetList.Canvas = new Widget( AssetList );
|
|
AssetList.Canvas.Layout = Layout.Column();
|
|
AssetList.Canvas.Layout.Margin = new Sandbox.UI.Margin( 8, 8, 16, 8 );
|
|
AssetList.Canvas.Layout.Spacing = 1;
|
|
|
|
var controls = Layout.AddRow();
|
|
controls.Margin = 16;
|
|
controls.Spacing = 8;
|
|
|
|
controls.AddStretchCell();
|
|
//controls.Add( new Button( "Refresh" ) { Clicked = () => _ = RefreshList( assets ) } );
|
|
|
|
PublishButton = controls.Add( new Button.Primary( "Publish" ) );
|
|
PublishButton.Enabled = false;
|
|
PublishButton.Clicked = () => _ = Publish();
|
|
|
|
_ = RefreshList( assets );
|
|
}
|
|
|
|
void BuildHeader( Layout layout )
|
|
{
|
|
var header = layout.AddColumn();
|
|
header.Margin = 16;
|
|
header.Spacing = 8;
|
|
|
|
header.Add( new Label.Subtitle( "Batch Publish" ) );
|
|
header.Add( new Label( "If you're editing multiple published assets it can be a pain to publish each one. Here you can publish all changed assets in one go." ) { WordWrap = true } );
|
|
|
|
header.Add( new Label( "You must own the rights to content you're publishing. Don't upload a bunch of content from someone else's game and write fair use like it's all fine - it will get removed." ) { WordWrap = true } );
|
|
|
|
header.Add( new Label( "You should visit <a href=\"https://sbox.game\">sbox.game</a> to fill in additional details, like description, tags and visibility. We won't change any of that here." ) { WordWrap = true } );
|
|
|
|
header.AddStretchCell();
|
|
|
|
header.Add( new Label( "Pay particular attention to the ident (in green) before first publish - you won't be able to edit this afterwards." ) { WordWrap = true } );
|
|
}
|
|
|
|
private async Task RefreshList( IEnumerable<Asset> assets )
|
|
{
|
|
EditorUtility.ClearPackageCache();
|
|
|
|
AssetList.Canvas.Layout.Clear( true );
|
|
|
|
Assets ??= new();
|
|
Assets.Clear();
|
|
|
|
foreach ( var asset in assets )
|
|
{
|
|
if ( !asset.Publishing.Enabled )
|
|
continue;
|
|
|
|
Assets.Add( asset );
|
|
|
|
var row = new AssetRow( this, asset );
|
|
|
|
AssetList.Canvas.Layout.Add( row, 0 );
|
|
}
|
|
|
|
AssetList.Canvas.Layout.AddStretchCell();
|
|
|
|
var t = new List<Task>();
|
|
|
|
foreach ( var a in AssetList.Canvas.Children.OfType<AssetRow>().OrderBy( x => Guid.NewGuid() ) )
|
|
{
|
|
if ( !a.IsValid ) continue;
|
|
|
|
t.Add( a.RefreshStatus() );
|
|
|
|
while ( t.Count > 32 )
|
|
{
|
|
await Task.WhenAny( t );
|
|
t.RemoveAll( x => x.IsCompleted );
|
|
}
|
|
}
|
|
|
|
await Task.WhenAll( t );
|
|
|
|
var changed = AssetList.Canvas.Children.OfType<AssetRow>().Where( x => x.IsValid ).Count( x => x.NeedsUpload );
|
|
if ( changed > 0 )
|
|
{
|
|
PublishButton.Text = $"Publish {changed} Assets";
|
|
PublishButton.Enabled = true;
|
|
}
|
|
else
|
|
{
|
|
PublishButton.Text = $"No Changes Detected";
|
|
PublishButton.Enabled = false;
|
|
}
|
|
}
|
|
|
|
async Task Publish()
|
|
{
|
|
Enabled = false;
|
|
|
|
HideCompleted();
|
|
|
|
var t = new List<Task>();
|
|
|
|
foreach ( var a in AssetList.Canvas.Children.OfType<AssetRow>() )
|
|
{
|
|
if ( !IsValid || !Visible ) return;
|
|
|
|
t.Add( a.Publish() );
|
|
|
|
while ( t.Count > 8 )
|
|
{
|
|
await Task.WhenAny( t );
|
|
t.RemoveAll( x => x.IsCompleted );
|
|
HideCompleted();
|
|
}
|
|
}
|
|
|
|
await Task.WhenAll( t );
|
|
|
|
//await RefreshList();
|
|
Enabled = true;
|
|
}
|
|
|
|
void HideCompleted()
|
|
{
|
|
foreach ( var a in AssetList.Canvas.Children.OfType<AssetRow>() )
|
|
{
|
|
if ( !a.NeedsUpload )
|
|
a.Visible = false;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
file class AssetRow : Widget
|
|
{
|
|
Asset Asset { get; init; }
|
|
|
|
string _status;
|
|
|
|
string Status
|
|
{
|
|
get => _status;
|
|
set
|
|
{
|
|
if ( _status == value ) return;
|
|
_status = value;
|
|
Update();
|
|
}
|
|
}
|
|
|
|
public bool NeedsUpload { get; set; }
|
|
|
|
public AssetRow( Widget parent, Asset asset ) : base( parent )
|
|
{
|
|
Height = 25;
|
|
MinimumHeight = 25;
|
|
|
|
Asset = asset;
|
|
|
|
Layout = Layout.Row();
|
|
Layout.Spacing = 4;
|
|
|
|
RefreshRow();
|
|
}
|
|
|
|
void RefreshRow()
|
|
{
|
|
Layout.Clear( true );
|
|
Layout.AddStretchCell();
|
|
Layout.Add( new ToolButton( "", "drive_file_move", this ) { FixedHeight = 25, FixedWidth = 25, ToolTip = "Inspect Asset", MouseClick = InspectAsset } );
|
|
Layout.Add( new ToolButton( "", "edit", this ) { FixedHeight = 25, FixedWidth = 25, ToolTip = "Asset Properties", MouseClick = EditAsset } );
|
|
}
|
|
|
|
protected override void OnPaint()
|
|
{
|
|
base.OnPaint();
|
|
|
|
Paint.Antialiasing = true;
|
|
Paint.TextAntialiasing = true;
|
|
Paint.ClearPen();
|
|
Paint.SetBrush( Theme.ControlBackground );
|
|
Paint.DrawRect( LocalRect );
|
|
|
|
var fg = Color.White;
|
|
|
|
Paint.SetPen( Theme.Green );
|
|
var rect = LocalRect.Shrink( 8, 0 );
|
|
var r = Paint.DrawText( rect, $"{Asset.Publishing.ProjectConfig.FullIdent}", TextFlag.LeftCenter );
|
|
|
|
Paint.SetPen( fg.WithAlphaMultiplied( 0.5f ) );
|
|
rect.Left = r.Right + 8;
|
|
r = Paint.DrawText( rect, $"{Asset.Path}", TextFlag.LeftCenter );
|
|
|
|
Paint.SetPen( fg.WithAlphaMultiplied( 0.5f ) );
|
|
rect.Left = r.Right + 8;
|
|
rect.Right -= 70;
|
|
//r = Paint.DrawTextBox( rect, $"{Status}", Theme.Black, new Sandbox.UI.Margin( 4, 2 ), 4, TextFlag.RightCenter );
|
|
r = Paint.DrawText( rect, $"{Status}", TextFlag.RightCenter );
|
|
}
|
|
|
|
void InspectAsset()
|
|
{
|
|
EditorUtility.InspectorObject = Asset;
|
|
}
|
|
|
|
void EditAsset()
|
|
{
|
|
var addon = Asset.Publishing.CreateTemporaryProject();
|
|
addon.Config.Org = Project.Current.Config.Org;
|
|
|
|
ProjectInspector.OpenForProject( addon );
|
|
}
|
|
|
|
public async Task RefreshStatus()
|
|
{
|
|
if ( Asset.Publishing.ProjectConfig.Org != Project.Current.Config.Org && Asset.Publishing.ProjectConfig.Org == "local" )
|
|
{
|
|
Log.Info( $"Changing org to {Project.Current.Config.Org}" );
|
|
Asset.Publishing.ProjectConfig.Org = Project.Current.Config.Org;
|
|
Asset.Publishing.Save();
|
|
}
|
|
|
|
SetEffectOpacity( 1.0f );
|
|
NeedsUpload = true;
|
|
|
|
var package = await Package.FetchAsync( Asset.Publishing.ProjectConfig.FullIdent, false );
|
|
if ( package is null )
|
|
{
|
|
Status = "Doesn't Exist";
|
|
return;
|
|
}
|
|
|
|
if ( package.Revision is null )
|
|
{
|
|
Status = "No Revision";
|
|
return;
|
|
}
|
|
|
|
await package.Revision.DownloadManifestAsync();
|
|
|
|
var schema = package.Revision.Manifest;
|
|
if ( schema is null || schema.Files is null )
|
|
{
|
|
Status = "Invalid Revision";
|
|
return;
|
|
}
|
|
|
|
var files = Asset.GetReferences( true );
|
|
files.Add( Asset );
|
|
foreach ( var file in files )
|
|
{
|
|
if ( !ProjectPublisher.CanPublishFile( file ) ) continue;
|
|
|
|
var compileFileAbsPath = file.GetCompiledFile( true );
|
|
var compileFilePath = file.GetCompiledFile( false );
|
|
|
|
var existingFile = schema.Files.FirstOrDefault( x => x.Path == compileFilePath );
|
|
if ( existingFile.Path is null )
|
|
{
|
|
Status = $"Changed";
|
|
return;
|
|
}
|
|
|
|
var fileInfo = new System.IO.FileInfo( compileFileAbsPath );
|
|
|
|
// size the same?
|
|
if ( existingFile.Size != fileInfo.Length )
|
|
{
|
|
Status = $"Changed";
|
|
return;
|
|
}
|
|
|
|
using var reader = fileInfo.OpenRead();
|
|
var crc = await Crc64.FromStreamAsync( reader );
|
|
|
|
// checksum the same?
|
|
if ( existingFile.Crc != crc.ToString( "x" ) )
|
|
{
|
|
Status = $"Changed";
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
Status = "";
|
|
SetEffectOpacity( 0.3f );
|
|
NeedsUpload = false;
|
|
}
|
|
|
|
public async Task Publish()
|
|
{
|
|
if ( !NeedsUpload )
|
|
return;
|
|
|
|
Status = "Scanning";
|
|
|
|
var upload = await ProjectPublisher.FromAsset( Asset );
|
|
|
|
try
|
|
{
|
|
Status = "Checking Files";
|
|
await upload.PrePublish();
|
|
|
|
if ( upload.MissingFileCount > 0 )
|
|
{
|
|
upload.OnProgressChanged = () => Status = $"{(upload.MissingFileSize).FormatBytes()}";
|
|
await upload.UploadFiles();
|
|
upload.OnProgressChanged = null;
|
|
}
|
|
|
|
Status = "Publishing";
|
|
await upload.Publish();
|
|
}
|
|
catch ( System.Exception e )
|
|
{
|
|
Log.Warning( e, $"Error when publising: {e.Message}" );
|
|
await RefreshStatus();
|
|
return;
|
|
}
|
|
|
|
Status = "Video";
|
|
|
|
await PublishWizard.UploadMediaPage.CreateAndUploadVideo( Asset, s => Status = s );
|
|
|
|
Status = "Completed";
|
|
|
|
EditorUtility.ClearPackageCache();
|
|
|
|
await RefreshStatus();
|
|
}
|
|
|
|
}
|