logo SERENITY
Game Systems Foundation
Search Results for

    Show / Hide Table of Contents

    Namespace Serenity.Editor.PackageBootstrapper

    Classes

    GhostAssetPathFilter

    Splits a set of AssetDatabase-enumerated paths into the ones that still exist on disk and the ones that do not — "ghost" entries the AssetDatabase still reports for a file a manual delete (outside Unity) or an interrupted import already removed. SerenityPackageExporter feeds every path it exports through this before it reaches ShippedAssetsSnapshot.json: a ghost path left in that snapshot makes TombstonePlanner.SelectTombstones treat the already-gone file as still shipped (it is absent from BOTH the previous and current snapshot, so no removal is ever detected), and SerenityObsoleteAssetCleaner then trusts the snapshot's word that a consumer's copy of that file is current. Pure: the caller supplies the existence check, so this is unit-testable without a running editor.

    ReoccupiedMovedPath

    One occurrence of the export-breaking combination found by ReoccupiedMovedPathPlanner: a previous-release GUID now ships at NewPath, while its OldPath is occupied by a different GUID (OccupantGuid).

    ReoccupiedMovedPathPlanner

    Finds the one combination that corrupts an additive package upgrade: a GUID that the PREVIOUS release shipped at one path now ships at a different path, while the OLD path is occupied by a different GUID in the current shipped set. Unity imports by GUID — an incoming entry whose GUID already exists in the customer project is written onto the path that currently OWNS that GUID, not onto the incoming path. On an upgrading project the moved file's content is therefore redirect-written onto the old path, the new path is never created, and the different-GUID occupant of the old path is dropped as an unknown GUID collision — compilation breaks, and the cleaner cannot heal it because the old path is still a shipped path (so the stranded file is not an orphan). Each half alone is safe: a GUID-preserving move is covered by the restore payloads, and a path that gets a new GUID is covered by the tombstones. Only the combination is a defect; the remedy is to keep the previous-release GUID at the old path and give the moved file the new GUID, or to stop re-occupying the old path. A blank-GUID occupant is never reported: AssetDatabase.AssetPathToGUID yields a blank GUID only for a path that does not exist, so a guid-less entry is not a shipped file and cannot take part in a redirect-write. A GUID the previous snapshot lists at several paths is checked against every one of them. Pure: no Unity API calls, so it is unit-testable without a running editor.

    RestorePayloadEntry

    One restorable file shipped as a payload: where it belongs, its GUID, and its original meta.

    RestorePayloadIndex

    JsonUtility-compatible wrapper for the restore-payload index shipped next to the payloads.

    RestorePayloadPlanner

    Selects the shipped assets at hostage risk during an additive package upgrade and names their restore payloads. A shipped file is at hostage risk when its GUID appears in the PREVIOUS release's snapshot at a different path — a moved or renamed file: in an upgrading project the old copy still holds that GUID during import. 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 typically a REDIRECT-WRITE — Unity writes the incoming content into the OLD path that already owns the GUID, updating it in place rather than creating the NEW path — though some import flows may instead silently skip the incoming file entirely. Either way the NEW path is never created by the import itself, which is exactly why this planner ships it a second time as a payload. Plain payload copies (.bytes) always import — no GUID can block them — which lets the cleaner restore the blocked files afterwards without a package re-import, regardless of which of the two observed behaviors actually occurred. Pure: no Unity API calls, so it is unit-testable without a running editor.

    SerenityAiAgentDirectory

    SerenityAiAgentDirectoryCatalog

    SerenityAiAgentSkillsInstaller

    Installs the Serenity agent skills bundled under Assets/Serenity/Documentation/Skills into the project-level skill directories of the selected AI coding agents. Every agent reads the same open Agent Skills format (a folder with a SKILL.md manifest); only the destination directory differs per agent. The bundled copies are inert where they sit; agents never scan under Assets/.

    SerenityPackageDependencyInstaller

    Standalone dependency installer that lives in a zero-dependency Editor assembly. Detects missing Unity packages required by Serenity and installs them sequentially.

    This assembly (Serenity.PackageBootstrapper.Editor) has no references to any Serenity runtime or editor assembly and no references to optional Unity packages. It compiles even when the rest of Serenity cannot, which is exactly the scenario it exists to fix.

    • UnityEditor.InitializeOnLoadMethodAttribute startup hook runs once per editor session and shows a confirmation dialog when packages are missing.
    • Menu item Tools/Serenity/Install Required Unity Packages for manual use.
    • Packages install one at a time via UnityEditor.PackageManager.Client.Add(System.String).

    State is stored in UnityEditor.SessionState so it survives domain reloads but resets each editor session, preventing repeated popup spam.

    SerenityPackageExporter

    Exports the Serenity .unitypackage for Asset Store distribution, automatically excluding paths that should not ship to customers (tests, examples, documentation build tools, etc.).

    SerenityShippedSnapshotSentinel

    Warns the Serenity development project when ShippedAssetsSnapshot.json is stale relative to the current shipped asset set. ExportPackage() and RefreshUpgradeArtifacts() both regenerate the snapshot, but Asset Store Tools uploads and Unity's built-in Assets > Export Package bypass the exporter entirely, so a snapshot can go stale before an upload without anyone noticing. That matters downstream: SerenityObsoleteAssetCleaner diffs the framework folders against this exact snapshot in customer projects, and a snapshot missing a whole module makes that module look like an orphan from a previous version. Runs only in the development project (this file never ships — see SerenityPackageExporter's ExcludedPaths) and never in batch mode, mirroring the guard checks in SerenityObsoleteAssetCleaner.

    ShippedAssetEntry

    A shipped asset's identity as of a package export: its GUID and project path.

    ShippedAssetsManifest

    JsonUtility-compatible wrapper for the shipped-assets snapshot committed at each export.

    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.

    Structs

    GhostAssetPathFilter.Result

    In this article
    © 2026 Serenity. All Rights Reserved