Skip to content

Cook and render with Nfty.Core

Nfty.Core is the whole engine: reading and writing archives, validation, generation, rendering, Sets, export, sealing and statistics. The code on this page comes from samples/Nfty.Embedding.Sample, which references Nfty.Core and Nfty.Model.

using Nfty.Core.Formats;       // CookBookArchive, LoadedCookBook
using Nfty.Core.Generation;    // Generator, GenerateOptions, GeneratedSet
using Nfty.Core.Publish;       // MenuRenderer
using Nfty.Model;              // Riser, ModelWriter, StackDepth, RiseOptions
using Nfty.Ticket;             // MenuArchive, AssetTicket
using SixLabors.ImageSharp;    // SaveAsPng - arrives through Nfty.Core

Read a book and generate

// Reading decodes every variant PNG, so the book owns images: dispose it.
using LoadedCookBook book = CookBookArchive.Read(args[0]);

// Generate validates the book itself and throws on any problem, so there is nothing to pre-check.
using GeneratedSet set = Generator.Generate(book, new GenerateOptions(Count: 3, Seed: "embed-demo"));

foreach (GeneratedAsset asset in set.Assets)                // the set owns the asset images too
{
    asset.Image.SaveAsPng(Path.Combine(outDir, $"{asset.SetNumber:0000}.png"));
    Console.WriteLine($"#{asset.SetNumber} {asset.RecipeName} {asset.Dna[..12]}");
}

You own the images. Reading a .cbk decodes every variant PNG, so LoadedCookBook (and the Recipes and Ingredients inside it) is IDisposable, and using var frees the whole tree. GeneratedSet and GeneratedAsset are disposable too; disposing the set disposes every asset image.

  • Generator.Generate validates the book itself and throws on any problem, with a message listing every one, so there is nothing to pre-check. Validator.Validate reports without throwing when you want to show problems yourself.
  • It holds the whole collection in memory. For a collection too big for that, use Generator.GenerateStreaming, which yields one asset at a time and hands you its ownership (abandoning the enumeration leaves the last one undisposed).
  • It rolls sequentially and renders in parallel; GenerateOptions.Parallelism sets the width. Output is byte-identical at any width.
  • Async twins (GenerateAsync, CookBookArchive.ReadAsync, SetWriter.WriteAsync) report progress and honor cancellation.

SetWriter.Write(set, dir, pack) writes the Set to disk exactly as nfty generate does; give it an IAssetModeler (Nfty.Model.SetModeler) to write models/ too.

Render a ticket

// A ticket rolled elsewhere (a server, nfty serve) renders to the identical pixels. Note
// MenuRenderer needs Nfty.Core; the Nfty.Ticket sample draws the same ticket without it.
using var menu = MenuArchive.Open(args[1]);
AssetTicket ticket = Ticketer.TryRoll(Ticketer.Prepare(menu.Book),
                                      new SplitMix64Rng(7))
                     ?? throw new InvalidOperationException("The rules rejected the roll; roll again.");
using (var image = MenuRenderer.Render(menu, ticket))       // you own the returned image
    image.SaveAsPng(Path.Combine(outDir, "ticket.png"));

MenuRenderer.Render uses the same colorizer and compositor as generate, so a client rendering a server's ticket gets identical pixels. You dispose the returned image.

Write a Menu

MenuWriter.Write(book, "book.mnu", new MenuWriteOptions { Art = MenuArtMode.Atlas });

It validates first and writes nothing for a book with problems. An atlas past the pixel ceiling throws AtlasTooLargeException; MenuArtMode.Loose avoids it.

Export and seal

SetExporter.Plan(...) computes every file an export would write without writing it, and SetExporter.Export(...) writes exactly that list; ExportPlan.Parts() renders the same summary the CLI and app print. ExportOptions and ExportPreset carry the choices from Export a Set. Exporting from a sealed .tin is refused inside the library.

Run the sample

dotnet run --project samples/Nfty.Embedding.Sample -- ./demo/ChestDemo.cbk ./demo/chest.mnu ./out

It writes three cooked PNGs, the rendered ticket, and one .glb into ./out.

See also