--- name: vvvv-node-libraries description: Helps set up C# library projects that provide nodes to vvvv gamma — project directory structure, Initialization.cs with AssemblyInitializer, service registration via RegisterService, IResourceProvider factories, ImportAsIs namespace/category configuration, .csproj setup, and dynamic node factories via RegisterNodeFactory. Use when creating a new library project, registering services or node factories, configuring ImportAsIs parameters, or setting up .csproj. license: CC-BY-SA-4.0 compatibility: Designed for coding AI agents assisting with vvvv gamma development metadata: author: Tebjan Halm version: "1.0" --- # Creating vvvv gamma Node Libraries A node library is a project that provides multiple nodes to vvvv gamma as a distributable package. This skill covers the project-level concerns: directory structure, naming conventions, category organization, service registration, and node factories. For writing individual node classes (ProcessNode, Update, pins, change detection), see vvvv-custom-nodes. For consuming services inside node constructors (IFrameClock, Game, logging), see vvvv-custom-nodes/services.md. ## Library Recognition Pattern vvvv recognizes a directory as a library when the **folder name, .vl file, and .nuspec all share the same name**: ``` VL.MyLibrary/ # Folder name = package name ├── VL.MyLibrary.vl # .vl document — MUST match folder name ├── VL.MyLibrary.nuspec # NuGet spec — MUST match folder name ├── lib/ │ └── net8.0/ # Compiled DLLs go here │ └── VL.MyLibrary.dll ├── src/ │ ├── Initialization.cs # [assembly:] attributes + AssemblyInitializer │ ├── Nodes/ │ │ ├── MyProcessNode.cs # [ProcessNode] classes │ │ └── MyOperations.cs # Static methods (stateless nodes) │ ├── Services/ │ │ └── MyService.cs # Per-app singletons │ └── VL.MyLibrary.csproj ├── shaders/ # Optional: SDSL shaders (auto-discovered) │ └── MyEffect_TextureFX.sdsl └── help/ # Optional: .vl help patches └── HowTo Use MyNode.vl ``` **Critical conventions**: - Folder name, `.vl` file, and `.nuspec` must be identical (e.g., all `VL.MyLibrary`) - The `.csproj` must output DLLs to `lib/net8.0/` relative to the package root - No `.vl` file within a package should reference a `.csproj` — this forces the package into editable mode - The library directory must be in a configured **package-repository** directory for vvvv to find it ### .csproj Output Path The `.csproj` must compile into the library's `lib/net8.0/` folder: ```xml net8.0 ..\..\lib\net8.0\ false ``` ## Initialization.cs — The Entry Point Every node library needs assembly-level attributes. Combine in one file: ```csharp using VL.Core; using VL.Core.CompilerServices; using VL.Core.Import; // Required: tells vvvv to scan this assembly for nodes [assembly: ImportAsIs(Namespace = "MyCompany.MyLibrary", Category = "MyLibrary")] // Optional: register services before any node runs [assembly: AssemblyInitializer(typeof(MyCompany.MyLibrary.Initialization))] namespace MyCompany.MyLibrary; public sealed class Initialization : AssemblyInitializer { public override void Configure(AppHost appHost) { var services = appHost.Services; // Register per-app singletons (created lazily on first access) services.RegisterService(serviceProvider => { return new MyService(serviceProvider); }); } } ``` ### ImportAsIs and Category Naming | Parameter | Purpose | Example | |---|---|---| | `Namespace` | C# namespace to scan for public types | `"VL.MyLibrary"` | | `Category` | Node browser category for all discovered nodes | `"MyLibrary"` | Without parameters (`[assembly: ImportAsIs]`), vvvv scans all namespaces and strips one namespace level to derive the category. The parameterized form gives explicit control over library organization. **Namespace → Category mapping**: C# namespaces map to dot-separated node browser categories. Classes in `VL.MyLibrary.Particles` appear under `MyLibrary.Particles` in the node browser (the root namespace specified in `ImportAsIs` is stripped). ### Per-Type Category Override Override the category for individual types: ```csharp [assembly: ImportType(typeof(SpecialNode), Category = "MyLibrary.Advanced")] ``` Use this to place specific nodes into different categories than their namespace would suggest. ## Service Registration Patterns Services are registered in `Configure(AppHost)` and consumed by nodes via `NodeContext`. This section covers registration only — for consumption patterns, see vvvv-custom-nodes/services.md. ### Direct Singleton (Recommended) ```csharp services.RegisterService(serviceProvider => { // Created lazily on first GetService() call return new MyService(serviceProvider); }); ``` ### IResourceProvider Pattern (For Managed Lifecycle) When the service wraps a resource that needs explicit disposal: ```csharp services.RegisterService>(serviceProvider => { var gameProvider = serviceProvider.GetService>(); return gameProvider.Bind(game => { var service = MyGPUService.Create(game); return ResourceProvider.Return(service, disposeAction: s => s?.Dispose()); }); }); ``` ## Dynamic Node Factories Register programmatic node generation for dynamic node sets: ```csharp public override void Configure(AppHost appHost) { // Dynamic node factory from shader files or other sources appHost.RegisterNodeFactory("VL.MyLibrary.ShaderNodes", init: MyShaderNodeFactory.Init); } ``` Use node factories when nodes are generated from external files (shaders, configs) rather than written as C# classes. For details, see the [vvvv Node Factories docs](https://thegraybook.vvvv.org/reference/extending/node-factories.html). ## Extension Methods for Service Access Provide typed accessors for your services: ```csharp public static class MyLibraryExtensions { public static MyService? GetMyService(this ServiceRegistry services) => services.GetService(typeof(MyService)) as MyService; public static MyService? GetMyService(this IServiceProvider services) => services.GetService(typeof(MyService)) as MyService; } ``` ## .csproj Essentials Full library `.csproj` with output to `lib/net8.0/`: ```xml net8.0 enable ..\..\lib\net8.0\ false ``` Match VL package versions to your vvvv installation version. The `OutputPath` places compiled DLLs in the library's `lib/net8.0/` folder where vvvv expects to find them. ## Real-World Example: Custom Rendering Library Library initialization with service registration and node factory: ```csharp [assembly: AssemblyInitializer(typeof(Initialization))] [assembly: ImportAsIs(Namespace = "VL.MyRendering", Category = "MyRendering")] public sealed class Initialization : AssemblyInitializer { public override void Configure(AppHost appHost) { appHost.Services.RegisterService(sp => { var vlGame = sp.GetService(); if (vlGame == null) return null!; var customGame = CustomGameSystem.Create(vlGame, sp); vlGame.GameSystems.Add(customGame); return customGame; }); // Dynamic node factory from shader files appHost.RegisterNodeFactory("VL.MyRendering.ShaderNodes", init: ShaderNodeFactory.Init); } } ``` For naming conventions, pin rules, aspects, and standard types, see [design-guidelines.md](design-guidelines.md). For publishing NuGets, help patches, and library structure, see [publishing.md](publishing.md). For complete real-world examples (VL.IO.MQTT, VL.Audio), see [examples.md](examples.md).