s&box Package Code Search

Search C# source code, UI razor templates, shaders, and configs across s&box packages.

Showing code results for query: * (2 total matches found)
rwtrcsc.librarysystem / Editor/LibraryTools.cs
Editor library
using Editor;
using Editor.Mcp;
using Sandbox;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace LibraryTools;

/// <summary>
/// The Library Manager and the Publish wizard, exposed to the s&box editor's MCP server, so scripts and AI agents
/// can list, install, remove and publish sbox.game libraries the same way the editor's own buttons do.
/// </summary>
[McpToolset( "libraries", "Library Manager from MCP: list versions, install, uninstall and publish sbox.game libraries" )]
public static class LibraryTools
{
	// Installed libraries report idents like "rwtrcsc.lobbysystem#local"; compare the part before the #.
	static bool Matches( LibraryProject lib, string ident ) => (lib.Project?.Package?.FullIdent ?? "").Split( '#' )[0] == ident;

	/// <summary>Published versions of a library on sbox.game (newest first) and the version installed here, if any.</summary>
	/// <param name="ident">Full package ident, e.g. 'rwtrcsc.lobbysystem'.</param>
	[McpTool.ReadOnly( "library_versions" )]
	public static async Task<string> LibraryVersions( string ident )
	{
		var versions = await Package.FetchVersions( ident, default );
		var installed = Installed().FirstOrDefault( x => Matches( x, ident ) );
		var lines = (versions ?? Enumerable.Empty<Package.IRevision>())
			.Select( v => $"{v.VersionId}  {v.Created:yyyy-MM-dd HH:mm}  files={v.FileCount}  {v.Summary}" );
		return $"installed: {(installed is null ? "no" : installed.Version?.ToString() ?? "local")}\n" + string.Join( "\n", lines );
	}

	/// <summary>Install a published library version into Libraries/ (newest when versionId is 0).</summary>
	/// <param name="ident">Full package ident, e.g. 'rwtrcsc.lobbysystem'.</param>
	/// <param name="versionId">A version id from library_versions, or 0 for the newest.</param>
	[McpTool( "library_install" )]
	public static async Task<string> LibraryInstall( string ident, long versionId = 0 )
	{
		var versions = (await Package.FetchVersions( ident, default ))?.ToList();
		var v = versionId == 0 ? versions?.FirstOrDefault() : versions?.FirstOrDefault( x => x.VersionId == versionId );
		if ( v is null ) return $"No published version found for {ident}.";

		await LibrarySystem.Install( ident, v.VersionId, default );
		return $"Installed {ident} version {v.VersionId} (published {v.Created:yyyy-MM-dd}).";
	}

	/// <summary>Remove an installed library and delete its folder, as the Library Manager's uninstall button does.</summary>
	/// <param name="ident">Full package ident, e.g. 'rwtrcsc.lobbysystem'.</param>
	[McpTool( "library_uninstall" )]
	public static string LibraryUninstall( string ident )
	{
		var lib = Installed().FirstOrDefault( x => Matches( x, ident ) );
		if ( lib is null )
			return $"{ident} is not installed. Installed: " + string.Join( ", ", Installed().Select( x => x.Project?.Package?.FullIdent ) );

		// Delete the folder ourselves rather than lib.RemoveAndDelete(), which does the same delete but then pops
		// s&box's own modal "Library Removed" notice. The editor keeps the library loaded until it restarts.
		var path = lib.Project.GetRootPath();
		for ( int i = 0; i < 10 && System.IO.Directory.Exists( path ); i++ )
		{
			try { System.IO.Directory.Delete( path, true ); }
			catch ( System.IO.DirectoryNotFoundException ) { break; }
			catch { System.Threading.Thread.Sleep( 100 ); }   // files briefly locked by the asset system
		}
		if ( System.IO.Directory.Exists( path ) ) return $"Couldn't delete {path}: a file in it is locked. Close anything using it and try again.";
		return $"Removed {ident} and deleted its folder. Restart the editor to finish unloading it.";
	}

	/// <summary>Libraries still on disk. A removed one stays loaded (and listed by s&amp;box) until the editor restarts.</summary>
	public static IEnumerable<LibraryProject> Installed() =>
		LibrarySystem.All.Where( x => x.Project is not null && System.IO.Directory.Exists( x.Project.GetRootPath() ) );

	/// <summary>
	/// Publish a library from Libraries/ as a new revision on sbox.game, running the Publish wizard's own steps:
	/// compile, gather files, upload, publish. Visibility is not set here: a new package starts Hidden (org members only)
	/// and you switch it to Released on its sbox.game Edit page.
	/// </summary>
	/// <param name="ident">Full package ident, e.g. 'rwtrcsc.combatsystem'.</param>
	/// <param name="changeTitle">Revision title shown on the package page.</param>
	[McpTool( "library_publish" )]
	public static async Task<string> LibraryPublish( string ident, string changeTitle = "" )
	{
		var project = Installed().FirstOrDefault( x => Matches( x, ident ) )?.Project;
		if ( project is null ) return $"{ident} is not in Libraries/.";
		if ( project.Config.Org == "local" ) return "Set an organisation in the .sbproj first.";

		var publisher = await ProjectPublisher.FromProject( project );

		// Same as the Publish wizard: compile only when there is game code (editor-only libraries ship as source).
		if ( project.HasCodePath() )
		{
			var output = await EditorUtility.Projects.Compile( project, _ => { } );
			if ( output is null || output.Length == 0 ) return "Compile produced no code.";

			await publisher.AddFile( System.Text.Json.JsonSerializer.Serialize( output.Select( x => x.Compiler.AssemblyName ).ToList() ), ".bin/manifest.json" );
			foreach ( var asm in output )
			{
				await publisher.AddFile( asm.XmlDocumentation, $".bin/{asm.Compiler.AssemblyName}.xml" );
				await publisher.AddFile( asm.Archive.Serialize(), $".bin/{asm.Compiler.AssemblyName}.cll" );
			}
		}
		await publisher.PrePublish();

		foreach ( var file in publisher.Files.Where( x => !x.Skip ).ToList() )
		{
			bool ok = file.Contents is not null
				? await project.Package.UploadFile( file.Contents, file.Name, _ => { }, default )
				: file.AbsolutePath is not null && await project.Package.UploadFile( file.AbsolutePath, file.Name, _ => { }, default );
			if ( ok ) file.Skip = true;
		}
		var failed = publisher.Files.Where( x => !x.Skip ).Select( x => x.Name ).ToList();
		if ( failed.Count > 0 ) return "Upload failed for: " + string.Join( ", ", failed );

		publisher.SetChangeDetails( string.IsNullOrWhiteSpace( changeTitle ) ? $"Changes on {System.DateTime.UtcNow:yyyy-MM-dd}" : changeTitle, "" );
		await publisher.Publish();
		return $"Published {ident} ({publisher.Files.Count()} files). Visibility is unchanged: new packages start Hidden; release them on sbox.game.";
	}
}
rwtrcsc.librarysystem / Editor/LibrarySystemWindow.cs
Editor library
using Editor;
using Sandbox;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace LibraryTools;

/// <summary>
/// Library System as an editor window (Tools > Library System). Same actions as the MCP tools, which it
/// calls directly: list the project's libraries, check published versions, install, remove and publish.
/// </summary>
[EditorApp( "Library System", "library_books", "List, install, remove and publish the project's libraries" )]
public sealed class LibrarySystemWindow : Widget
{
	readonly Widget _list;
	readonly LineEdit _ident;
	readonly Label _output;

	public LibrarySystemWindow() : base( null )
	{
		WindowTitle = "Library System";
		SetWindowIcon( "library_books" );
		WindowFlags = WindowFlags.Dialog | WindowFlags.Customized | WindowFlags.WindowTitle | WindowFlags.CloseButton | WindowFlags.WindowSystemMenuHint;
		DeleteOnClose = true;
		MinimumSize = new Vector2( 620, 420 );

		Layout = Layout.Column();
		Layout.Margin = 12;
		Layout.Spacing = 8;

		Layout.Add( new Label.Subtitle( "Libraries in this project" ) );
		_list = Layout.Add( new Widget( this ) );
		_list.Layout = Layout.Column();
		_list.Layout.Spacing = 4;

		Layout.Add( new Label.Subtitle( "Install from sbox.game" ) );
		var row = Layout.AddRow();
		row.Spacing = 4;
		_ident = row.Add( new LineEdit( this ) { PlaceholderText = "org.package, e.g. rwtrcsc.lobbysystem" }, 1 );
		row.Add( new Button( "Versions", "history", this ) { Clicked = () => Run( "Versions", () => LibraryTools.LibraryVersions( Ident() ) ) } );
		row.Add( new Button.Primary( "Install", "download", this ) { Clicked = () => Run( "Install", async () => { var r = await LibraryTools.LibraryInstall( Ident() ); Refresh(); return r; } ) } );

		Layout.AddStretchCell();
		_output = Layout.Add( new Label( "Ready.", this ) { WordWrap = true, TextSelectable = true } );

		Refresh();
	}

	string Ident() => _ident.Value?.Trim() ?? "";

	void Refresh()
	{
		_list.Layout.Clear( true );
		var libs = LibraryTools.Installed().ToList();
		if ( libs.Count == 0 ) _list.Layout.Add( new Label( "No libraries in Libraries/ yet.", _list ) );

		foreach ( var lib in libs )
		{
			var ident = (lib.Project?.Package?.FullIdent ?? "").Split( '#' )[0];
			var title = lib.Project?.Config?.Title ?? ident;
			var row = _list.Layout.AddRow();
			row.Spacing = 4;
			row.Add( new Label( $"{title}   {ident}   {(lib.Version?.ToString() ?? "local")}", _list ), 1 );
			row.Add( new Button( "Versions", "history", _list ) { Clicked = () => Run( $"Versions of {ident}", () => LibraryTools.LibraryVersions( ident ) ) } );
			row.Add( new Button( "Publish", "publish", _list )
			{
				Clicked = () => Dialog.AskString( change => Run( $"Publish {ident}", () => LibraryTools.LibraryPublish( ident, change ) ),
					$"Publish a new revision of {title}. Revision title:", "Publish", "Cancel", "", "Publish library" )
			} );
			row.Add( new Button( "Remove", "delete", _list )
			{
				Clicked = () => Dialog.AskConfirm( () => { _output.Text = LibraryTools.LibraryUninstall( ident ); Refresh(); },
					$"Remove {title} and delete its folder from Libraries/?", "Remove library", "Remove", "Cancel" )
			} );
		}
	}

	// Runs an action, showing progress and the result (or the error) in the output line.
	async void Run( string what, Func<Task<string>> action )
	{
		_output.Text = $"{what}...";
		try { _output.Text = await action(); }
		catch ( Exception e ) { _output.Text = $"{what} failed: {e.Message}"; }
	}
}
Debug: View Raw JSON Response
{
    "TotalCount": 2,
    "Files": [
        {
            "Ident": "rwtrcsc.librarysystem",
            "Path": "Editor/LibraryTools.cs",
            "FileName": "LibraryTools.cs",
            "PackageType": "library",
            "CodeKind": "Editor",
            "AssetVersionId": 421927,
            "IsPrivate": false,
            "Code": "using Editor;\nusing Editor.Mcp;\nusing Sandbox;\nusing System.Collections.Generic;\r\nusing System.Linq;\nusing System.Threading.Tasks;\n\nnamespace LibraryTools;\n\n/// <summary>\n/// The Library Manager and the Publish wizard, exposed to the s&box editor's MCP server, so scripts and AI agents\n/// can list, install, remove and publish sbox.game libraries the same way the editor's own buttons do.\n/// </summary>\n[McpToolset( \"libraries\", \"Library Manager from MCP: list versions, install, uninstall and publish sbox.game libraries\" )]\npublic static class LibraryTools\n{\n\t// Installed libraries report idents like \"rwtrcsc.lobbysystem#local\"; compare the part before the #.\n\tstatic bool Matches( LibraryProject lib, string ident ) => (lib.Project?.Package?.FullIdent ?? \"\").Split( '#' )[0] == ident;\n\n\t/// <summary>Published versions of a library on sbox.game (newest first) and the version installed here, if any.</summary>\n\t/// <param name=\"ident\">Full package ident, e.g. 'rwtrcsc.lobbysystem'.</param>\n\t[McpTool.ReadOnly( \"library_versions\" )]\n\tpublic static async Task<string> LibraryVersions( string ident )\n\t{\n\t\tvar versions = await Package.FetchVersions( ident, default );\n\t\tvar installed = Installed().FirstOrDefault( x => Matches( x, ident ) );\n\t\tvar lines = (versions ?? Enumerable.Empty<Package.IRevision>())\n\t\t\t.Select( v => $\"{v.VersionId}  {v.Created:yyyy-MM-dd HH:mm}  files={v.FileCount}  {v.Summary}\" );\n\t\treturn $\"installed: {(installed is null ? \"no\" : installed.Version?.ToString() ?? \"local\")}\\n\" + string.Join( \"\\n\", lines );\n\t}\n\n\t/// <summary>Install a published library version into Libraries/ (newest when versionId is 0).</summary>\n\t/// <param name=\"ident\">Full package ident, e.g. 'rwtrcsc.lobbysystem'.</param>\n\t/// <param name=\"versionId\">A version id from library_versions, or 0 for the newest.</param>\n\t[McpTool( \"library_install\" )]\n\tpublic static async Task<string> LibraryInstall( string ident, long versionId = 0 )\n\t{\n\t\tvar versions = (await Package.FetchVersions( ident, default ))?.ToList();\n\t\tvar v = versionId == 0 ? versions?.FirstOrDefault() : versions?.FirstOrDefault( x => x.VersionId == versionId );\n\t\tif ( v is null ) return $\"No published version found for {ident}.\";\n\n\t\tawait LibrarySystem.Install( ident, v.VersionId, default );\n\t\treturn $\"Installed {ident} version {v.VersionId} (published {v.Created:yyyy-MM-dd}).\";\n\t}\n\n\t/// <summary>Remove an installed library and delete its folder, as the Library Manager's uninstall button does.</summary>\n\t/// <param name=\"ident\">Full package ident, e.g. 'rwtrcsc.lobbysystem'.</param>\n\t[McpTool( \"library_uninstall\" )]\n\tpublic static string LibraryUninstall( string ident )\n\t{\n\t\tvar lib = Installed().FirstOrDefault( x => Matches( x, ident ) );\n\t\tif ( lib is null )\n\t\t\treturn $\"{ident} is not installed. Installed: \" + string.Join( \", \", Installed().Select( x => x.Project?.Package?.FullIdent ) );\n\n\t\t// Delete the folder ourselves rather than lib.RemoveAndDelete(), which does the same delete but then pops\n\t\t// s&box's own modal \"Library Removed\" notice. The editor keeps the library loaded until it restarts.\n\t\tvar path = lib.Project.GetRootPath();\n\t\tfor ( int i = 0; i < 10 && System.IO.Directory.Exists( path ); i++ )\n\t\t{\n\t\t\ttry { System.IO.Directory.Delete( path, true ); }\n\t\t\tcatch ( System.IO.DirectoryNotFoundException ) { break; }\n\t\t\tcatch { System.Threading.Thread.Sleep( 100 ); }   // files briefly locked by the asset system\n\t\t}\n\t\tif ( System.IO.Directory.Exists( path ) ) return $\"Couldn't delete {path}: a file in it is locked. Close anything using it and try again.\";\n\t\treturn $\"Removed {ident} and deleted its folder. Restart the editor to finish unloading it.\";\n\t}\n\n\t/// <summary>Libraries still on disk. A removed one stays loaded (and listed by s&amp;box) until the editor restarts.</summary>\n\tpublic static IEnumerable<LibraryProject> Installed() =>\n\t\tLibrarySystem.All.Where( x => x.Project is not null && System.IO.Directory.Exists( x.Project.GetRootPath() ) );\n\n\t/// <summary>\n\t/// Publish a library from Libraries/ as a new revision on sbox.game, running the Publish wizard's own steps:\n\t/// compile, gather files, upload, publish. Visibility is not set here: a new package starts Hidden (org members only)\n\t/// and you switch it to Released on its sbox.game Edit page.\n\t/// </summary>\n\t/// <param name=\"ident\">Full package ident, e.g. 'rwtrcsc.combatsystem'.</param>\n\t/// <param name=\"changeTitle\">Revision title shown on the package page.</param>\n\t[McpTool( \"library_publish\" )]\n\tpublic static async Task<string> LibraryPublish( string ident, string changeTitle = \"\" )\n\t{\n\t\tvar project = Installed().FirstOrDefault( x => Matches( x, ident ) )?.Project;\n\t\tif ( project is null ) return $\"{ident} is not in Libraries/.\";\n\t\tif ( project.Config.Org == \"local\" ) return \"Set an organisation in the .sbproj first.\";\n\n\t\tvar publisher = await ProjectPublisher.FromProject( project );\n\n\t\t// Same as the Publish wizard: compile only when there is game code (editor-only libraries ship as source).\n\t\tif ( project.HasCodePath() )\n\t\t{\n\t\t\tvar output = await EditorUtility.Projects.Compile( project, _ => { } );\n\t\t\tif ( output is null || output.Length == 0 ) return \"Compile produced no code.\";\n\n\t\t\tawait publisher.AddFile( System.Text.Json.JsonSerializer.Serialize( output.Select( x => x.Compiler.AssemblyName ).ToList() ), \".bin/manifest.json\" );\n\t\t\tforeach ( var asm in output )\n\t\t\t{\n\t\t\t\tawait publisher.AddFile( asm.XmlDocumentation, $\".bin/{asm.Compiler.AssemblyName}.xml\" );\n\t\t\t\tawait publisher.AddFile( asm.Archive.Serialize(), $\".bin/{asm.Compiler.AssemblyName}.cll\" );\n\t\t\t}\n\t\t}\n\t\tawait publisher.PrePublish();\n\n\t\tforeach ( var file in publisher.Files.Where( x => !x.Skip ).ToList() )\n\t\t{\n\t\t\tbool ok = file.Contents is not null\n\t\t\t\t? await project.Package.UploadFile( file.Contents, file.Name, _ => { }, default )\n\t\t\t\t: file.AbsolutePath is not null && await project.Package.UploadFile( file.AbsolutePath, file.Name, _ => { }, default );\n\t\t\tif ( ok ) file.Skip = true;\n\t\t}\n\t\tvar failed = publisher.Files.Where( x => !x.Skip ).Select( x => x.Name ).ToList();\n\t\tif ( failed.Count > 0 ) return \"Upload failed for: \" + string.Join( \", \", failed );\n\n\t\tpublisher.SetChangeDetails( string.IsNullOrWhiteSpace( changeTitle ) ? $\"Changes on {System.DateTime.UtcNow:yyyy-MM-dd}\" : changeTitle, \"\" );\n\t\tawait publisher.Publish();\n\t\treturn $\"Published {ident} ({publisher.Files.Count()} files). Visibility is unchanged: new packages start Hidden; release them on sbox.game.\";\n\t}\n}"
        },
        {
            "Ident": "rwtrcsc.librarysystem",
            "Path": "Editor/LibrarySystemWindow.cs",
            "FileName": "LibrarySystemWindow.cs",
            "PackageType": "library",
            "CodeKind": "Editor",
            "AssetVersionId": 421927,
            "IsPrivate": false,
            "Code": "using Editor;\nusing Sandbox;\nusing System;\nusing System.Linq;\nusing System.Threading.Tasks;\n\nnamespace LibraryTools;\n\n/// <summary>\n/// Library System as an editor window (Tools > Library System). Same actions as the MCP tools, which it\n/// calls directly: list the project's libraries, check published versions, install, remove and publish.\n/// </summary>\n[EditorApp( \"Library System\", \"library_books\", \"List, install, remove and publish the project's libraries\" )]\npublic sealed class LibrarySystemWindow : Widget\n{\n\treadonly Widget _list;\n\treadonly LineEdit _ident;\n\treadonly Label _output;\n\n\tpublic LibrarySystemWindow() : base( null )\n\t{\n\t\tWindowTitle = \"Library System\";\n\t\tSetWindowIcon( \"library_books\" );\n\t\tWindowFlags = WindowFlags.Dialog | WindowFlags.Customized | WindowFlags.WindowTitle | WindowFlags.CloseButton | WindowFlags.WindowSystemMenuHint;\n\t\tDeleteOnClose = true;\n\t\tMinimumSize = new Vector2( 620, 420 );\n\n\t\tLayout = Layout.Column();\n\t\tLayout.Margin = 12;\n\t\tLayout.Spacing = 8;\n\n\t\tLayout.Add( new Label.Subtitle( \"Libraries in this project\" ) );\n\t\t_list = Layout.Add( new Widget( this ) );\n\t\t_list.Layout = Layout.Column();\n\t\t_list.Layout.Spacing = 4;\n\n\t\tLayout.Add( new Label.Subtitle( \"Install from sbox.game\" ) );\n\t\tvar row = Layout.AddRow();\n\t\trow.Spacing = 4;\n\t\t_ident = row.Add( new LineEdit( this ) { PlaceholderText = \"org.package, e.g. rwtrcsc.lobbysystem\" }, 1 );\n\t\trow.Add( new Button( \"Versions\", \"history\", this ) { Clicked = () => Run( \"Versions\", () => LibraryTools.LibraryVersions( Ident() ) ) } );\n\t\trow.Add( new Button.Primary( \"Install\", \"download\", this ) { Clicked = () => Run( \"Install\", async () => { var r = await LibraryTools.LibraryInstall( Ident() ); Refresh(); return r; } ) } );\n\n\t\tLayout.AddStretchCell();\n\t\t_output = Layout.Add( new Label( \"Ready.\", this ) { WordWrap = true, TextSelectable = true } );\n\n\t\tRefresh();\n\t}\n\n\tstring Ident() => _ident.Value?.Trim() ?? \"\";\n\n\tvoid Refresh()\n\t{\n\t\t_list.Layout.Clear( true );\n\t\tvar libs = LibraryTools.Installed().ToList();\n\t\tif ( libs.Count == 0 ) _list.Layout.Add( new Label( \"No libraries in Libraries/ yet.\", _list ) );\n\n\t\tforeach ( var lib in libs )\n\t\t{\n\t\t\tvar ident = (lib.Project?.Package?.FullIdent ?? \"\").Split( '#' )[0];\n\t\t\tvar title = lib.Project?.Config?.Title ?? ident;\n\t\t\tvar row = _list.Layout.AddRow();\n\t\t\trow.Spacing = 4;\n\t\t\trow.Add( new Label( $\"{title}   {ident}   {(lib.Version?.ToString() ?? \"local\")}\", _list ), 1 );\n\t\t\trow.Add( new Button( \"Versions\", \"history\", _list ) { Clicked = () => Run( $\"Versions of {ident}\", () => LibraryTools.LibraryVersions( ident ) ) } );\n\t\t\trow.Add( new Button( \"Publish\", \"publish\", _list )\n\t\t\t{\n\t\t\t\tClicked = () => Dialog.AskString( change => Run( $\"Publish {ident}\", () => LibraryTools.LibraryPublish( ident, change ) ),\n\t\t\t\t\t$\"Publish a new revision of {title}. Revision title:\", \"Publish\", \"Cancel\", \"\", \"Publish library\" )\n\t\t\t} );\n\t\t\trow.Add( new Button( \"Remove\", \"delete\", _list )\n\t\t\t{\n\t\t\t\tClicked = () => Dialog.AskConfirm( () => { _output.Text = LibraryTools.LibraryUninstall( ident ); Refresh(); },\n\t\t\t\t\t$\"Remove {title} and delete its folder from Libraries/?\", \"Remove library\", \"Remove\", \"Cancel\" )\n\t\t\t} );\n\t\t}\n\t}\n\n\t// Runs an action, showing progress and the result (or the error) in the output line.\n\tasync void Run( string what, Func<Task<string>> action )\n\t{\n\t\t_output.Text = $\"{what}...\";\n\t\ttry { _output.Text = await action(); }\n\t\tcatch ( Exception e ) { _output.Text = $\"{what} failed: {e.Message}\"; }\n\t}\n}\n"
        }
    ]
}