logo SERENITY
Game Systems Foundation
Search Results for

    Show / Hide Table of Contents

    Class TombstonePlanner

    Plans the tombstones that make the first upgrade over an old install fully self-contained. For every path the previous release shipped under the owned framework roots that the current version no longer ships, the package carries an inert file at that exact path with that exact previous-release GUID: on import Unity overwrites the customer's orphan in place (same path + GUID = a normal asset update), so no stale code survives to break compilation, the just-imported cleaner loads in the SAME session, and it silently sweeps the tombstones — they are deliberately absent from the shipped snapshot. One exception: a removed .asmref/.asmdef is NOT tombstoned when the current release ships another assembly-definition file in the same folder (a folder that graduated from an .asmref fold to a standalone .asmdef). A tombstone there would collide with the shipped assembly def ("multiple assembly definition files"); the scrubber and cleaner remove the consumer's orphan instead. Second exception, and the reason a "removed" path is judged by GUID and not just by path: a previous- release entry whose GUID still appears somewhere in the CURRENT shipped set is not a removal — it is a GUID-preserving rename or move (e.g. the ProceduralExpression → ProceduralAnimator rename). Tombstoning it at the OLD path would ship the SAME GUID twice in one package (once at the tombstone's old path, once at the real file's new path). Field evidence (a real import of the renamed package into a consumer project, confirmed via its Editor.log) shows the observed behavior on that GUID collision is a REDIRECT-WRITE: Unity writes the incoming content into the path that already owns the GUID — i.e. the customer's OLD path — updating it in place rather than creating the NEW path; some import flows may instead silently skip the incoming file entirely. Either way the NEW path is never created by the import itself, and the cleaner would then find the tombstoned old path listed in the import record and, before this planner excluded renames, abort the whole sweep as a stale-snapshot suspect (see SerenityObsoleteAssetCleaner.ClassifySweep). The upgrade pipeline is deliberately robust to BOTH observed behaviors: a redirect-write leaves a husk that enters the import record and is swept via the relocated-rename classification once its GUID resolves to a different path in the shipped snapshot; a silent skip leaves a husk that is either uncorrelated (an ordinary previous-version leftover) or removed by the import scrubber — either way the restore payload below still materializes the new path. A renamed/moved file is the exclusive business of SelectHostageRiskAssets(IReadOnlyList<ShippedAssetEntry>, IReadOnlyList<ShippedAssetEntry>, string), which ships it a second time as a GUID-proof .bytes payload the cleaner restores after the old husk is swept: the two planners partition rename handling between them by construction — one path a previous-release GUID can take must never be selected by both. Pure: no Unity API calls, so it is unit-testable without a running editor.

    Inheritance
    object
    TombstonePlanner
    Inherited Members
    object.ToString()
    object.Equals(object)
    object.Equals(object, object)
    object.ReferenceEquals(object, object)
    object.GetHashCode()
    object.GetType()
    object.MemberwiseClone()
    Namespace: Serenity.Editor.PackageBootstrapper
    Assembly: Serenity.PackageBootstrapper.Editor.dll
    Syntax
    public static class TombstonePlanner
    Remarks

    A tombstone means "this version REMOVED that path". A path this version never ships in the first place (SerenityPackageExporter.ExcludedPaths — Tests, EXAMPLES, the previous-release baseline) was never a removal, so it must never be tombstoned, no matter what the baseline lists. Distinguishing the two is not cosmetic: the diff input is the manually maintained previous-release baseline, and a baseline that lists never-shipped paths would otherwise select every one of them — in the development project those paths hold LIVE SOURCE, and writing a tombstone there destroys it. Nothing is lost on the customer side: SerenityObsoleteAssetCleaner.KnownNonProductPrefixes covers the same paths and its sweep removes such orphans unconditionally, so tombstoning them only ever duplicated work the cleaner already does.

    Fields

    OwnedRootPrefixes

    Must stay equal to SerenityFrameworkOwnership.OwnedRoots (cleaner assembly — not referencable from here); a test asserts the two lists match so the scrubber's scorch and the tombstones can never cover different trees.

    Declaration
    public static readonly string[] OwnedRootPrefixes
    Field Value
    Type Description
    string[]

    Methods

    BuildDummyAssemblyName(string)

    Deterministic unique dummy assembly name derived from the tombstone's GUID.

    Declaration
    public static string BuildDummyAssemblyName(string guid)
    Parameters
    Type Name Description
    string guid
    Returns
    Type Description
    string

    BuildTombstoneContent(string, string, string)

    Inert content by extension: a .cs tombstone is a single comment (compiles empty); an .asmdef tombstone is a valid Editor-only assembly definition with a deterministic dummy name and no references; an .asmref tombstone points at the cleaner's assembly definition by GUID so it always resolves; every other extension becomes an empty file.

    Declaration
    public static string BuildTombstoneContent(string path, string guid, string cleanerAssemblyDefinitionGuid)
    Parameters
    Type Name Description
    string path
    string guid
    string cleanerAssemblyDefinitionGuid
    Returns
    Type Description
    string

    SelectPathsOccupiedByLiveSource(IReadOnlyList<ShippedAssetEntry>, string, Func<string, string>)

    Selects the planned tombstone paths that are already occupied by content OTHER than that tombstone's own — that is, by real source. This is the last line of defence for the development project, where the owned roots hold the live Serenity source tree: writing a tombstone over one of those files destroys it, and the only recovery is version control. A path holding exactly the content this tombstone would write is NOT reported: that is a leftover tombstone from an interrupted run, and overwriting it with itself changes nothing. Pure: the caller supplies the file reads, which returns null for a path that holds no file.

    Declaration
    public static List<string> SelectPathsOccupiedByLiveSource(IReadOnlyList<ShippedAssetEntry> tombstones, string cleanerAssemblyDefinitionGuid, Func<string, string> readExistingFileContent)
    Parameters
    Type Name Description
    IReadOnlyList<ShippedAssetEntry> tombstones
    string cleanerAssemblyDefinitionGuid
    Func<string, string> readExistingFileContent
    Returns
    Type Description
    List<string>

    SelectTombstones(IReadOnlyList<ShippedAssetEntry>, IEnumerable<ShippedAssetEntry>, IEnumerable<string>)

    Declaration
    public static List<ShippedAssetEntry> SelectTombstones(IReadOnlyList<ShippedAssetEntry> previousSnapshotEntries, IEnumerable<ShippedAssetEntry> currentShippedEntries, IEnumerable<string> neverShippedPathPrefixes)
    Parameters
    Type Name Description
    IReadOnlyList<ShippedAssetEntry> previousSnapshotEntries
    IEnumerable<ShippedAssetEntry> currentShippedEntries

    The current release's full shipped set — path AND guid, mirroring SelectHostageRiskAssets(IReadOnlyList<ShippedAssetEntry>, IReadOnlyList<ShippedAssetEntry>, string)'s currentShippedEntries — so a previous-release entry can be recognized as a rename (its GUID resurfaces somewhere in this set) and not just as "path no longer present".

    IEnumerable<string> neverShippedPathPrefixes

    The paths this version deliberately excludes from the package (SerenityPackageExporter.ExcludedPaths), matched as prefixes exactly as the exporter matches them. Required, not optional: a caller that forgets them plans tombstones over its own source tree.

    Returns
    Type Description
    List<ShippedAssetEntry>
    In this article
    © 2026 Serenity. All Rights Reserved