logo SERENITY
Game Systems Foundation
Search Results for

    Show / Hide Table of Contents

    Class SingletonSettingsCreationEngine<TSettings, TInput, TResult>

    Template-method engine shared by every "create or update the ONE project-wide settings asset of this module" wizard: identity validation, target validation (folder shape and path collision), the transaction-backed creation with rollback, Addressables registration under the module's label, and the in-place update that restores the asset when a step after it fails. A concrete engine supplies only what genuinely differs between modules — its label, its default asset name, how it reads in a message, and which fields the input writes onto the asset.

    Extracted from the ten near-identical *SettingsCreator classes (MenuServiceSettingsCreator and ModalServiceSettingsCreator matched 85-94% line for line; five of the ten declared a byte-identical ResolveNewAssetPath). ModalCreator is deliberately not one of them: it authors many drafts, not one singleton, and belongs to ResolveOrCreateByIdInFolder<TSettings>(string, string, string, SerenityAssetCreationTransaction, out bool)'s shape instead.

    Adopt it the way BatchSoundEffectImporter adopts BatchAudioAssetImportEngine{TItem,TInput,TResult,TAsset}: keep the module's public static class as the caller-facing surface, hold one private sealed class Engine instance behind it, and forward each public member in one line — the windows and tests that already call the static creator never change.

    The Id and the Guid are the engine's business, not a subclass's: it writes Id on both the create and the update path and seeds a fresh Guid exactly once, on creation. ApplyFields(TInput, TSettings) is therefore only ever handed the module's own fields to write, which is why the same override serves both paths.

    Inheritance
    object
    SingletonSettingsCreationEngine<TSettings, TInput, TResult>
    Inherited Members
    object.ToString()
    object.Equals(object)
    object.Equals(object, object)
    object.ReferenceEquals(object, object)
    object.GetHashCode()
    object.GetType()
    object.MemberwiseClone()
    Namespace: Serenity.StructuredPersistence.Infrastructure.Editor.Authoring
    Assembly: Serenity.UnityStructuredPersistence.Infrastructure.Editor.dll
    Syntax
    public abstract class SingletonSettingsCreationEngine<TSettings, TInput, TResult> where TSettings : ScriptableObject, IFoundationSettings where TInput : SingletonSettingsInput where TResult : SingletonSettingsResult, new()
    Type Parameters
    Name Description
    TSettings

    The module's concrete settings asset type.

    TInput

    The module's concrete request type.

    TResult

    The module's concrete outcome type.

    Constructors

    SingletonSettingsCreationEngine()

    Declaration
    protected SingletonSettingsCreationEngine()

    Properties

    AssetTypeDisplayName

    What the asset is called in a message an author reads, e.g. "Menu service settings" — it completes the output-folder rejection and the Undo label. The settings type's own singleton display name (Serenity.Global.Infrastructure.Editor.SerenitySingletonSettingsCatalog.AuthorFacingNameOf(System.Type)), so a module never declares a second name for the asset its ambiguity and folder sentences already name.

    Declaration
    protected virtual string AssetTypeDisplayName { get; }
    Property Value
    Type Description
    string

    DefaultAssetName

    File name (without extension) used when the request names none, or names one that sanitizes away entirely.

    Declaration
    protected abstract string DefaultAssetName { get; }
    Property Value
    Type Description
    string

    FailureMessagePrefix

    The subject of the rollback sentence, e.g. "Menu service settings" for "Menu service settings generation failed and was rolled back: …". The same name as AssetTypeDisplayName: every singleton display name already reads at the start of a sentence.

    Declaration
    protected virtual string FailureMessagePrefix { get; }
    Property Value
    Type Description
    string

    ModuleLabel

    The Addressables module label stamped on everything this engine creates.

    Declaration
    protected abstract string ModuleLabel { get; }
    Property Value
    Type Description
    string

    Methods

    ApplyFields(TInput, TSettings)

    Writes the module's own fields from the request onto the asset. Not the Id and not the Guid — the engine owns both. Called directly by the create path; the update path calls ApplyFieldsToExisting(TInput, TSettings) instead, which forwards here by default.

    Declaration
    protected abstract void ApplyFields(TInput input, TSettings settings)
    Parameters
    Type Name Description
    TInput input
    TSettings settings

    ApplyFieldsToExisting(TInput, TSettings)

    Writes the request onto an asset that already exists. Defaults to ApplyFields(TInput, TSettings) — override only when an update must treat a field differently from a create, e.g. a collection field that can be authored outside the wizard and so must be merged into the existing asset's value on update rather than overwritten the way a fresh asset's field is simply assigned.

    Declaration
    protected virtual void ApplyFieldsToExisting(TInput input, TSettings settings)
    Parameters
    Type Name Description
    TInput input
    TSettings settings

    Create(TInput)

    Validates the request, then either updates the asset named by ExistingSettingsAssetPath or creates a brand-new one. Either way a failure leaves the project as it found it: a created asset is deleted, and an existing one edited before the failure is put back.

    Declaration
    public TResult Create(TInput input)
    Parameters
    Type Name Description
    TInput input
    Returns
    Type Description
    TResult

    CreateExtraAssets(TInput, TSettings, SerenityAssetCreationTransaction, TResult)

    Assets a module creates alongside its settings singleton, inside the same transaction so one failure rolls the whole run back. Track every created path with Serenity.Global.Infrastructure.Editor.SerenityAssetCreationTransaction.TrackCreatedAsset(System.String), and every pre-existing asset edited on the way with SerenityAssetCreationTransaction.TrackExistingAsset.

    Declaration
    protected virtual void CreateExtraAssets(TInput input, TSettings settings, SerenityAssetCreationTransaction transaction, TResult result)
    Parameters
    Type Name Description
    TInput input
    TSettings settings
    SerenityAssetCreationTransaction transaction
    TResult result

    CreateFromDraft(TSettings, string, string, bool)

    Draft-based counterpart of Create(TInput): commits every field draft carries onto the resolved-or-created asset via ResolveAndCommit<TSettings>(TSettings, string, string, string, SerenityAssetCreationTransaction), instead of the narrower ApplyFields(TInput, TSettings) field list — so a wizard built on Serenity.Global.Infrastructure.Editor.SerenityWizardDraftLifecycle`1 (a SerenityWizardScratchDraft.FromExisting seed) can never silently revert a field it does not draw to its declared default, the same guarantee SettingsAssetDraftCommit provides for every one of its callers. The engine still owns identity through that shared call: draft's own Guid is overwritten regardless of what it already carried, and its Id travels with everything else UnityEditor.EditorUtility.CopySerialized(UnityEngine.Object,UnityEngine.Object) copies — trimmed first here, the same rule every other SettingsAssetDraftCommit caller applies to its own Id-shaped fields before committing. Skips ApplyFields(TInput, TSettings)/ApplyFieldsToExisting(TInput, TSettings) entirely — a draft-based caller owns the whole asset, not a per-module field subset.

    Declaration
    public TResult CreateFromDraft(TSettings draft, string explicitSettingsAssetPath, string newSettingsFolderPath, bool registerAsAddressables)
    Parameters
    Type Name Description
    TSettings draft

    Every field to write: an already-seeded scratch draft from an existing asset for an update-in-place run, or a freshly populated one for a create.

    string explicitSettingsAssetPath

    Explicit target path — empty resolves the project-wide singleton.

    string newSettingsFolderPath

    Folder a brand-new asset is created under when none exists.

    bool registerAsAddressables

    Whether to register a newly created asset as Addressable.

    Returns
    Type Description
    TResult

    CreateNewWithin(TInput, SerenityAssetCreationTransaction)

    The part of a creation run that happens inside a transaction: ensures the output folder, creates the asset with its Id, a fresh Guid and the module's own fields, and verifies it landed on disk — without committing or rolling back anything itself. Public so a module's larger creator can fold the singleton's creation into ITS transaction alongside its other assets, rather than have this engine commit a separate one first. The created asset is at ResolveNewAssetPath(TInput), which is also the transaction's Serenity.Global.Infrastructure.Editor.SerenityAssetCreationTransaction.LastCreatedPath until the caller tracks something else.

    Declaration
    public TSettings CreateNewWithin(TInput input, SerenityAssetCreationTransaction transaction)
    Parameters
    Type Name Description
    TInput input
    SerenityAssetCreationTransaction transaction
    Returns
    Type Description
    TSettings

    ResolveNewAssetPath(TInput)

    The path a brand-new asset would be created at. Pure — computes the path without resolving, creating or ensuring anything, so a window's read-only preview and CreateNewWithin(TInput, SerenityAssetCreationTransaction) both read one formula.

    Declaration
    public string ResolveNewAssetPath(TInput input)
    Parameters
    Type Name Description
    TInput input
    Returns
    Type Description
    string

    UpdateExistingInPlace(TInput)

    Updates the already-validated asset named by ExistingSettingsAssetPath in place. Public so a module's larger creator can update the singleton it resolved itself, without paying for a second Validate(TInput) pass over a request it already validated.

    Declaration
    public TResult UpdateExistingInPlace(TInput input)
    Parameters
    Type Name Description
    TInput input
    Returns
    Type Description
    TResult

    Validate(TInput)

    Full validation, re-checked at creation time so Create(TInput) is safe to call independently of a window's live validation.

    Declaration
    public SerenityCreationValidationResult Validate(TInput input)
    Parameters
    Type Name Description
    TInput input
    Returns
    Type Description
    SerenityCreationValidationResult

    ValidateBeyondIdentity(TInput, SerenityCreationValidationResult)

    Validation beyond identity that must NOT leak into ValidateIdentity(TInput)'s live, folder-less check: a value rule that reads other fields (a bounds check, a duplicate-name scan) or a non-blocking warning about an optional reference. Runs only inside Validate(TInput), once identity itself passed — skipped when it did not, so a full validation never reports a module error on top of an identity error.

    Declaration
    protected virtual void ValidateBeyondIdentity(TInput input, SerenityCreationValidationResult result)
    Parameters
    Type Name Description
    TInput input
    SerenityCreationValidationResult result

    ValidateExtra(TInput, SerenityCreationValidationResult)

    Validation beyond identity: the module's own fields (a controller id, a backend choice). Runs inside ValidateIdentity(TInput) — and therefore inside Validate(TInput) too, since Validate(TInput) begins by calling ValidateIdentity(TInput) — so a window's live identity check sees it as well as a full validation. Contrast ValidateBeyondIdentity(TInput, SerenityCreationValidationResult), which runs only on a full Validate(TInput).

    Declaration
    protected virtual void ValidateExtra(TInput input, SerenityCreationValidationResult result)
    Parameters
    Type Name Description
    TInput input
    SerenityCreationValidationResult result

    ValidateIdentity(TInput)

    Id-only validation, usable before a folder choice has been made.

    Declaration
    public SerenityCreationValidationResult ValidateIdentity(TInput input)
    Parameters
    Type Name Description
    TInput input
    Returns
    Type Description
    SerenityCreationValidationResult
    In this article
    © 2026 Serenity. All Rights Reserved