04 Core – Upgrading

6 min read

How Scylla Core versions, what a major, minor, or patch release means for a project already using it, and the specific steps for each release so far.

Summary

Scylla Core follows semantic versioning on its own line, independent of the other Scylla modules. The version number tells you how much attention an upgrade needs: a patch release needs none, a minor release may add things you can ignore, and a major release is the only kind that can break code you have already written. The Changelog lists what changed in each release; this page covers what to do about it.

Core is at 1.2.0. Each release step so far is documented below, newest first.

What the version number means

  • Patch (1.1.0 to 1.1.1). Fixes only, with the public API and behavior unchanged, so you can drop it in without a second thought.
  • Minor (1.0.0 to 1.1.0). New capability, and existing API keeps working. A minor release can change a default, which is the one thing worth reading the notes for: your code still compiles, but the framework may do something it did not do before.
  • Major (1.x to 2.0). Reserved for changes that can break existing code: removed or renamed public API, changed semantics, or a raised minimum Unity version, and it comes with migration notes on this page.

API that is on its way out is marked [Obsolete] with a pointer to its replacement, so the compiler tells you about it while the old call still works.

How to upgrade

Scylla Core installs as a package under Packages/com.scylla.core/, so upgrading replaces that folder rather than merging files into your project.

From the Asset Store. Open Window > Package Manager, switch to My Assets, find Scylla Core, and download and import the new version. The package folder is replaced with the new one.

From a tarball. If the project references the .tgz directly, point the existing com.scylla.core entry at the new file, or remove it and add the new tarball. Package Manager swaps the assemblies.

Two things survive an upgrade because they live in your project rather than in the package:

  • Configuration assets. Anything you created under Assets/ is yours and is never touched.
  • Imported samples. A sample is copied into Assets/Samples/ when you import it, so the copy stays on the old version. Re-import it from the package’s Samples tab if you want the newer demo scenes.

Anything you edited inside the package folder itself does not survive, which is why the package should be treated as read-only even though it is technically writable.

Compatibility

Scylla Core 1.x requires Unity 6000.5 or newer. That floor is not arbitrary: the shipped assemblies are compiled against the engine’s built-in mathematics module, which earlier Unity 6 branches do not have, so an older editor resolves the package and then fails to load the DLLs. Raising the minimum is a major-release change.

Release notes

1.1.0 to 1.2.0

This release is mostly additive, but it changes a few defaults and one data format. Read the points below before upgrading a project that already saves data.

Binary serialization changed format. In 1.1.0, SerializationFormat.Binary and the SerializationSettings.CompactBinary preset wrote UTF-8 JSON. They now write a true binary format, so bytes saved with those settings under 1.1.0 no longer load with them. If you have such data, read it once with settings that match the ones it was saved with but use SerializationFormat.JSON, and then save it again with CompactBinary. Data written with the default ToBytes and FromBytes overloads, or with ToJSON, is not affected.

Schema migrations are stricter. A stored schema version that no [ScyllaMigration] chain can reach now fails to load, where 1.1.0 skipped the missing step and loaded the data unmigrated. Check that every version step of your saved types has a migration method. Migrations that jump several versions at once, such as [ScyllaMigration(1, 3)], now run; in 1.1.0 they were never called.

Preserved references write $id. With ReferenceHandling.Preserve, JSON output now contains $id markers. Older documents without them still load. If an external tool reads the JSON and does not expect $id, set EmitReferenceIDs to false.

Play mode no longer changes configuration assets. In the Editor, the Bootstrap and the modules now work on runtime copies of their configuration assets. Values changed at runtime, console config.* commands and JSON overrides last for the Play session only. If you used Play mode to tune values and keep them, set them on the asset in Edit mode instead. A module’s configuration field also no longer equals the asset by reference during Play mode. Builds are unaffected.

Default font is standard width. FontUtil.LoadDefaultMono, LoadDefaultMonoSDF and their TryLoad variants now return the standard-width ScyllaMono font when called without arguments. Pass condensed: true to keep the condensed ScyllaMono-Cond font.

macOS documents folder. FilePathUtil.DocumentsPath now returns ~/Documents on macOS rather than the home folder, so logs, config files and saves stored under it move to ~/Documents/{Game}/. Files in the old location are not moved. Copy them across if players need to keep them.

Polymorphic data and type aliases changed shape. A derived class in a base-typed field, and any value in an abstract, interface or object field, is now written with its own $type marker and its own fields, also when IncludeTypeInfo is off. A type that declares a TypeAlias writes the alias as its marker instead of the assembly-qualified name. Data written by 1.1.0 always loads. Data written by 1.2.0 can be read by 1.1.0 unless it contains an alias in a polymorphic position or a $value envelope, which 1.1.0 does not understand. Monomorphic data without aliases is byte-identical to 1.1.0. If a save holds a $type that is not assignable to the type it is loaded into, the load now fails instead of producing a wrongly typed object. Keep types that appear in polymorphic positions of a save under IL2CPP with [Preserve], or register their alias with TypeMetadataCache.RegisterAlias.

Per-settings serializers now reach collection elements. A serializer registered through the settings builder applies to the elements of arrays, lists, hash sets, queues, stacks and the keys and values of dictionaries. If you registered a serializer per settings for a type that also appears in a collection and relied on the collection using the default shape, the collection now uses your serializer for both writing and reading.

Palette entries are identified by ID. ColorPaletteEntry.Name and ColorPaletteEntryData.Name are now ID, and GetColorName, ContainsName and MutableColorPalette.Rename are now GetColorID, ContainsID and SetID. The old members still compile with an obsolete warning and will be removed in a later version, so switch to the new names when convenient. Existing palette assets load unchanged.

Two interfaces gained members. A class of your own that implements IScyllaModuleManager needs CheckDependencies(), and one that implements IUITextField needs RestoreOriginalTextOnEscape, SelectAllTextOnFocus and SetPlaceholder(). Code that only uses these interfaces is not affected.

Smaller changes. BSP maps can differ from 1.1.0 for the same seed where a partition used to stop splitting too early. Script changes made during Play mode are now applied when Play mode ends, while the editor preference Script Changes While Playing is at its default. Setting ScyllaTime.TimeScale while paused now survives Resume().

See the Changelog for the full list.

1.0.0 to 1.1.0

Scene management is the headline addition in 1.1.0. The upgrade is additive in API terms, and there is one default worth knowing about.

One behavior change. The bootstrap hierarchy now persists across scene loads by default, controlled by PersistFrameworkAcrossScenes on the scene configuration. Before 1.1.0 every scene needed its own bootstrap and the framework was rebuilt on each load. Now the first bootstrap survives scene changes, and a scene that carries its own bootstrap has that duplicate removed in favor of the persistent one rather than logging an error.

For most projects this is what you already wanted, and it means every scene stays directly playable in the Editor while a single framework instance runs the session. Two cases deserve a look:

  • Code that assumed a fresh framework per scene. Module state now survives a scene load. If a module was relying on being rebuilt to reset itself, reset it explicitly instead.
  • Projects that want the old behavior. Set PersistFrameworkAcrossScenes to false on the scene configuration asset, or override it through the JSON config file, and the framework is rebuilt per scene as before.

What is new and optional. This release adds managed asynchronous scene loading with progress and an activation gate, along with transition hygiene that kills tweens and clears the timescale stack before a destructive load. It also adds pre-load and post-load task hooks, six typed scene events, and Addressables scene routing when that package is installed. None of it is required: a project that ignores the scene subsystem entirely keeps working.

See the Changelog for the full list.