05 Core – Changelog

16 min read

What shipped in each Scylla Core release, from the initial framework foundation that landed at 1.0.0 down through every fix that follows.

Release notes for Scylla Core, the foundational module that every other Scylla module depends on. Versions are listed newest at the top; each entry groups its bullets under Added, Removed, Changed, and Fixed. Core follows its own semver line, independent of the other modules in the framework.

1.2.0

This release adds a true binary serialization format, atomic file writes, the save contracts the Persistence module builds on, and a set of color analysis tools. In the Editor, configuration assets are no longer changed by Play mode. A few defaults changed along the way, and the Upgrading page lists what to check.

Added

  • Binary serialization. SerializationFormat.Binary and the SerializationSettings.CompactBinary preset now write a compact, tagged binary stream through the new ScyllaBinaryWriter and ScyllaBinaryReader. The stream is little-endian on every platform and stores NaN and Infinity as they are.
  • SerializationSettings.EmitReferenceIDs and Builder.WithEmitReferenceIDs(bool), which control whether preserved references carry an explicit $id marker. The $id and $ref property names are now public constants on SerializationSettings.
  • ReferenceTracker.TryTrackWithID and TypeMetadata.TryBuildMigrationPath, which finds the shortest chain of [ScyllaMigration] methods between two schema versions.
  • Atomic writes. ScyllaFileUtil.WriteTextAtomic, WriteBytesAtomic and WriteBytesAtomicAsync write to a temporary file in the same folder and swap it in, so a crash leaves either the old file or the new one, never half of each. FileSettings gains TempFileExtension and KeepReplaceBackup, which these methods use, and UseAtomicWrite, which makes the regular WriteText, WriteBytes and WriteLines methods and their async versions write atomically too. With it set, those writes throw on WebGL, like the atomic methods.
  • Save contracts in Scylla.Core.Persistence: IScyllaPersistable and the typed IScyllaPersistable<TState> for systems that save state, ScyllaPersistenceRegistry for registering them, and ScyllaPartitionID. The Persistence module uses these, and any module can implement them without depending on it.
  • Runtime configuration copies. ScyllaConfiguration.CreateRuntimeCopy<T>() and ReleaseRuntimeCopy(), plus IsRuntimeCopy and RuntimeSource, give Play mode in the Editor a copy of a configuration asset to work on. ScyllaModule.PrepareConfiguration<T>() copies a module’s configuration, applies JSON overrides, logs where it came from and registers it in one call.
  • IScyllaModuleManager.CheckDependencies(), with ScyllaModuleDependencyIssue and ScyllaModuleDependencyIssueType. It checks the module dependency graph at any time without logging or changing module state.
  • ScyllaModuleID.PERSISTENCE.
  • Color analysis tools:
    • ColorAnalysis: relative luminance, WCAG contrast ratios and checks against WCAGLevel (AA, AAA), Oklab color difference, and correlated color temperature.
    • ColorBlindnessSimulator: simulates the eight ColorBlindnessType values on one color or a span.
    • ColorHarmony: generates complementary, triadic, analogous and seven other ColorHarmonyType schemes, in Oklch, HSV or OkHSL.
    • ColorRamps: tints, shades, tones, ramps and material tones, each with an allocation-free span overload.
    • ColorUtil: IsInSRGBGamut, MaxOklabChromaAtHue and ClampToSRGBGamut for Oklch colors.
  • WaitForSettledFrames, a coroutine wait that holds until the frame rate steadies, so delta-time work does not start during a loading hitch.
  • ScyllaSafeScheduler for UI Toolkit, with RecurringSafe, DebouncedSafe and OnceSafe. A scheduled item pauses when its element leaves the panel, and a callback that throws is stopped and logged once instead of failing every tick. The editor ScyllaScheduler forwards to it and gains OnceSafe too.
  • TweenVisualElementExtensions, which tweens UI Toolkit elements: opacity, fades, translation, scale, rotation, background and text color, width and height.
  • ScyllaEditorSceneUtil in the Editor, with FindBootstrap() and FindOrCreateBootstrap(). Module setup wizards use it to create the Scylla Bootstrap themselves when the scene has none.
  • ScyllaModuleTier and ScyllaModuleInfo.Tier, set through a new ScyllaModuleInfo constructor that takes the tier. The existing constructor is unchanged.
  • [ScyllaMenuItem] in the Editor, which adds a method to the Scylla menus by group and path instead of a menu priority, and [ScyllaCreateAsset], which adds a ScriptableObject type to Assets/Create/Scylla instead of [CreateAssetMenu]. The Scylla menu registry places every item and draws every divider, so a module adding an item no longer moves the items of other modules.
  • A newly created Scylla Bootstrap gets the Core, Logger, Time, Event and Scene configuration assets from Resources/Config assigned to its empty slots. This applies to adding the component in the Inspector, Tools > Scylla > Create > Scylla Core, the Setup Wizard and the module wizards. Before, those slots stayed empty until each Assign button was clicked.
  • ScyllaUISafeArea.Compute(), which turns the screen safe area and a margin into anchors and offsets, with an override rectangle for simulating device cutouts.
  • IUITextField gains RestoreOriginalTextOnEscape, SelectAllTextOnFocus and SetPlaceholder().
  • TimeUtil.UniqueUTCNow(), a UTC clock whose results always increase within a process, for timestamps that things are ordered by. DateTime.UtcNow only advances about every 15.6 ms on Windows, so two saves made in quick succession could get the same time. It is safe to call from any thread.
  • StringUtil.StripANSISequences(), and the Char.BELL and Char.ESCAPE constants.
  • Editor: ScyllaChoiceCardGroup, a selectable list of title-and-description cards; ScyllaEditorAssetUtil.LoadCachedStyleSheet() for modules that ship their own style sheets; ScyllaEditorBase.BuildDescriptionLabel(); new page, card and problem-box helpers on ScyllaWizardWindowBase; and menu constants for the Persistence, TextMode and UI modules on ScyllaEditorConstants.

Changed

  • The Tools/Scylla, GameObject/Scylla and Assets/Create/Scylla menus are built by the Scylla menu registry. Module submenus are sorted alphabetically, the Create and GameObject entries are grouped by module tier, the Assets/Create/Scylla entries are sorted alphabetically, and menu paths and keyboard shortcuts are unchanged.
  • The menu priority constants in ScyllaEditorConstants are obsolete, except PRIORITY_GETTING_STARTED and PRIORITY_CONTEXT_GAMEOBJECT.
  • SerializationFormat.Binary now produces binary data. In 1.1.0 it wrote UTF-8 JSON, so bytes saved with the Binary format under 1.1.0 no longer load with Binary settings. The default ToBytes and FromBytes overloads still use JSON and are unchanged.
  • With ReferenceHandling.Preserve, the first occurrence of a shared object now carries an explicit $id, so $ref back-references resolve whatever order the properties are read in. Documents without $id still load. Set EmitReferenceIDs to false to write the old shape.
  • A schema version gap that no [ScyllaMigration] method bridges now fails deserialization instead of being skipped. Direct migrations that span several versions, such as [ScyllaMigration(1, 3)], are now applied.
  • In the Editor, configuration assets are no longer changed by Play mode. The Bootstrap and every module work on runtime copies, so values set at runtime, console config.* commands and JSON overrides last for the session only. Builds are unaffected. This also applies in projects that installed Scylla Core as a package.
  • FontUtil now defaults to the standard-width ScyllaMono font instead of the condensed ScyllaMono-Cond. Pass condensed: true to LoadDefaultMono, LoadDefaultMonoSDF and their TryLoad variants to keep the old look.
  • On macOS, FilePathUtil.DocumentsPath now returns ~/Documents instead of the home folder. Logs, config files and saves stored under it move to ~/Documents/{Game}/. Existing files are not moved.
  • Script changes made during Play mode are now applied when Play mode ends. A reload in the middle of a session cleared the framework’s state without starting it again, which left the game running with nothing behind it. This follows the editor preference Script Changes While Playing and applies only while it is set to Recompile And Continue Playing, the default.
  • Module dependency validation now reports every hard dependency cycle in one pass instead of stopping at the first.
  • The ScyllaShapeTweenExtensions helpers now set the shape as the tween target, like the other tween extensions. ScyllaTween.KillTweensOf(shape) now stops a shape’s tweens, and a managed tween is dropped when its shape is destroyed instead of writing to the destroyed shape.
  • Opening a wizard that is already open now returns it to the first step. The Finish button grows to fit longer labels.
  • The Persistence module now has editor menu priority 204. Stats moved to 205 and YM2612 to 208, which makes room for TextMode (206) and UI (207).
  • Types that declare a [ScyllaSerializable(TypeAlias = ...)] now write the alias as their $type marker instead of the assembly-qualified name. Types without an explicit alias still write the assembly-qualified name, and the FullName default is never written. A document with an alias in a polymorphic position cannot be read by 1.1.0.
  • A value whose runtime type differs from its declared type now always carries a $type marker, also when IncludeTypeInfo is off. The flag then only leaves out the marker of objects whose runtime type equals the declared type. Output for monomorphic, non-aliased data is unchanged.
  • A $type marker that resolves to a type not assignable to the declared type now fails the load. A marker that cannot be resolved still falls back to the declared type when that type is concrete, and fails with an error naming the marker when it is abstract or an interface.
  • TypeMetadataCache.ResolveAlias discovers aliases it has not seen yet by scanning the loaded assemblies once, and registering an alias that another type already holds logs a warning. TypeMetadata gains HasExplicitAlias and TypeMarkerName.
  • Per-settings serializers registered through SerializationSettings.Builder.RegisterSerializer<T> now apply to the elements, keys and values of arrays, lists, hash sets, queues, stacks and dictionaries. A collection that was written with the default shape of its element type and is read with a custom serializer that cannot parse that shape no longer loads. Persistence registers no custom serializers, so its saves are unaffected.
  • A palette entry’s identifier is now called its ID. ColorPaletteEntry.ID, ColorPaletteEntryData.ID, ColorPalette.GetColorID and ContainsID, and MutableColorPalette.SetID and ContainsID replace the members named after a name. The old Name, GetColorName, ContainsName and Rename members still work but are marked obsolete. Palette assets saved with the old field name still load and are rewritten with ID the next time they are saved. Lookups by ID, such as palette["Background"], behave as before.

Fixed

  • Core failed to compile in projects without the optional SharpZipLib package.
  • In projects that installed Scylla Core as a package, AssemblyUtil.OnBeforeReload and OnAfterReload did not exist and were never raised, so IsReloading and ReloadCount never changed. The events are now declared in every build and raised by the Core editor assembly. Scylla Core’s tests now compile in such projects as well.
  • TimeDebugOverlay was missing from projects that installed Scylla Core as a package and from development builds. It now works in the Editor and in development builds and switches itself off in release builds, so a scene that contains it no longer shows a missing script there.
  • Setup Wizard, module wizards and Tools > Scylla > Create > Scylla Core failed to create a Scylla Bootstrap in projects that installed Scylla Core as a package.
  • Setting ScyllaTime.TimeScale while paused was lost on Resume(). The new value is now kept.
  • An auto-assigned reference ID could collide with an ID read from a document.
  • A boxed struct behind an object or interface field was treated as a shared reference during serialization.
  • The BSP map generator turned a partition into a single room when only one of its sides was too short to split. Maps generated from the same seed can differ from 1.1.0 where that happened.
  • A [MonoTextArea] field could lose its edited text on entering Play mode or after a domain reload.
  • The ScyllaFileUtil.SaveObject, LoadObject, SaveSecureObject and LoadSecureObject overloads without serialization settings, their async versions, and the SaveToFile and SaveSecureToFile extensions always failed with “Settings cannot be null.” Passing null serialization settings now uses SerializationSettings.Default, as documented.
  • ScyllaSerialization.ToJSON and ToBytes ignored SerializationSettings.Indent, so pretty-printed JSON always used a tab. The configured indent is now used.
  • SerializationSettings.IncludePrivateFields was never read. It now includes every non-public field and every non-public property with a getter and a setter, including those of base classes; [ScyllaIgnore] still excludes a member.
  • 3D shapes did not rebuild when their parameters were edited in the Inspector during Play mode. They now rebuild in Play mode as they do in Edit mode.
  • A 3D shape with a custom material rendered in its tint color, white by default, instead of the material’s own color. A white tint now leaves a custom material’s color alone, as documented. On the Built-in Standard shader, which has _Color instead of _BaseColor, tints now take effect.
  • The Mesh and MatchingPrimitive collider modes of 3D shapes removed every collider on the GameObject, including colliders added by hand, and MatchingPrimitive replaced its collider on every rebuild. A shape now manages only the collider it attached and resizes it in place. In scenes saved with an earlier version, a shape adopts its existing collider rather than adding a second one. Custom shapes should obtain their primitive collider with the new protected GetOrAddManagedCollider<T>() in TryAttachPrimitiveCollider.
  • 3D shape properties now clamp fixed ranges when set, so ScyllaSphereShape.Segments = 1000 reads back as 256, and assigning a property its current value no longer rebuilds the mesh. Limits that depend on another property, such as a door against its wall, are still applied when the mesh is built.
  • ScyllaShapes.CreateMesh assigned a destroyed material passed to it instead of falling back to the default material.
  • Collection serializers ignored per-settings custom serializers for their elements, keys and values, and ignored a global serializer registered after the collection type had first been serialized. They now resolve the element serializer on every call.
  • A null element in a collection of a nullable element type, such as List<int?>, threw during serialization. It now round-trips as null.
  • A derived class stored in a base-typed field, or any value in an abstract, interface or object field, lost its derived fields on serialization and could not be read back. The runtime type is now written with a $type marker, so the value round-trips as its own type. Values behind object and interface fields that are not objects with properties, such as boxed numbers, enums and collections, use a {"$type", "$value"} envelope.
  • TypeAlias had no effect on the written output and aliases could not be resolved on a cold start. Aliases are now written as the type marker and discovered on demand when a document is loaded.

1.1.0

Scene management joins the Core subsystems: framework persistence across scene loads plus a managed loading pipeline.

Added

  • Scene management subsystem (ScyllaSceneManager), initialized by the Bootstrap alongside Time and the event bus. Keeps the bootstrap hierarchy alive across scene changes via DontDestroyOnLoad and deduplicates per-scene bootstraps automatically, so every scene stays playable directly from the Editor.
  • Managed asynchronous scene loading: LoadSceneAsync (by name or build index), ReloadActiveSceneAsync, and UnloadSceneAsync return a SceneLoadOperation with progress, state, a Completed event, and an Awaitable. A synchronous LoadScene fallback is included.
  • Activation gate and minimum load duration for loading screens: WithManualActivation() holds the load at 90% until code opens the gate; WithMinDuration(seconds) (or the config default) keeps fast loads from flashing.
  • Transition hygiene before destructive scene changes: active tweens are killed, the timescale-modifier stack is cleared, and destroyed event-bus subscribers are pruned. Each step is a configuration toggle with a per-call HygieneOverride.
  • Transition tasks: RegisterPreLoadTask / RegisterPostLoadTask accept awaitable hooks that the pipeline waits for (autosaves, fades, warm-ups).
  • Six typed scene events on the event bus: will-load, loaded, will-unload, unloaded, active-scene-changed, and throttled load progress.
  • Addressables scene routing (when the Addressables package is installed): LoadAddressableSceneAsync by address key or AssetReference, through the same pipeline; unloads route back through Addressables automatically.
  • ScyllaSceneConfiguration asset with a custom inspector, a Scene Configuration slot on the Bootstrap inspector, and Setup Wizard integration.
  • PruneInvalidSubscribers() on the event bus, removing subscriptions whose Unity objects have been destroyed.
  • SceneManager demo: a hub scene exercising single, additive, gated, and addressable loads with a live status overlay and scene event log.

Changed

  • The bootstrap hierarchy now persists across scene loads by default (PersistFrameworkAcrossScenes, on by default). A scene that carries its own bootstrap no longer logs an error in a running session; the duplicate is removed in favor of the persistent instance.

1.0.0

This is the initial release of Scylla Core, the module every other Scylla module builds on.

Added

  • Bootstrap and module manager: a scene-level ScyllaBootstrap component plus the ScyllaCore singleton and ScyllaModuleManager that together discover, validate, and drive every module through the framework lifecycle (Discovered, Validated, Initialized, Started, Enabled, Disabled, Shutdown) with fail-fast validation of the dependency graph.
  • Scylla Event eXchange (SEX) bus, a typed publish-subscribe channel with priority ordering, cancellable propagation, and lifecycle-safe subscription management that cleans up automatically when subscribers are destroyed.
  • Dual-layer configuration system: ScriptableObject assets for design-time defaults paired with JSON files for runtime overrides, resolved through a four-tier layered fallback (User Documents, Application, Unity Asset, Defaults) by ConfigFileManager, and exportable straight from the Inspector via an “Export Config File” button.
  • Logging subsystem, with module categories, pluggable receivers (Unity console and file system), and uniform formatting across the framework.
  • Time subsystem (ScyllaTime), initialized by the Bootstrap before any module runs, owning framework-aware time, timescale control, and pause behaviour independent of Time.timeScale.
  • Utility library, covering math and interpolation, noise (Perlin, OpenSimplex2, Value, ValueCubic, Cellular) with domain warping and curl flows, random (PCG32, Xoshiro256, Mersenne Twister, SplitMix64, Xoroshiro128+) with helpers for dice and IDs, strings and tabular text, files and paths, sprites and fonts, colors, lights, and cameras, threading, platform, assemblies, and type helpers.
  • Data structures library, with generic and DOTS-compatible queue, priority queue, deque, stack, map, type-map, ring buffer, object pool, trie, quadtree, octree, BVH, KD-tree, bit array, disjoint set, graphs (adjacency list, CSR, grid adapter, algorithms), and grid utilities for hex, square, and triangle layouts.
  • First-party types: the ColorAssetRegistry with ColorPalette, ColorGradient, and ColorSwatchCollection; the ConfigFile family (ConfigFile, ConfigFileGroup, ConfigFileProperty) that backs the JSON configuration tier; and eight strongly-typed unit structs (LengthUnit, MassUnit, TimeUnit, AreaUnit, SpeedUnit, PressureUnit, TemperatureUnit, DataUnit) with conversion properties and validated math.
  • Serialization, compression, crypto, and checksum stack: a first-party JSON serialization engine with attribute-driven configuration, reference tracking, and type serializers; compression providers for GZip, Deflate, BZip2, and Zip; authenticated encryption via AES-CBC-HMAC, AES-GCM, and ChaCha20-Poly1305 with PBKDF2 key derivation; and non-cryptographic checksums (Adler32, CRC16, CRC32, Fletcher32, XXHash32, XXHash64).
  • Tween system, a pooled and easing-aware engine for floats, vectors, colors, and quaternions with sequence composition and one-line extensions for Transform, Material, AudioSource, CanvasGroup, SpriteRenderer, and RectTransform.
  • Procedural map generation toolkit: BSP, cellular automata, drunkard walk, maze, maze-with-rooms, room-and-corridor, Voronoi regions, noise threshold, and winding corridor, plus pluggable post-processing for connectivity repair, dead-end removal, room detection, door placement, and spawn points.
  • Debug-draw subsystem, runtime visualization for primitives and meshes that works in both edit and play mode, across the Built-in and SRP render pipelines.
  • Editor foundation, a UI Toolkit base for the rest of the framework’s editor experience with ScyllaEditorBase for custom inspectors, ScyllaToolWindowBase for windows, ScyllaScheduler for safe recurring jobs, a USS token system for theming, and the bundled setup and getting-started wizards.