Changelog¶
All notable changes to the OxHeart package will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[1.1.2] - 2026-08-20¶
Documentation¶
Third Party Notices.mdcorrected — it understated the packages OxHeart declares and gave the wrong import order. Addressables was listed as 2.5.0; the declared dependency is 2.9.1. Two declared built-in modules,com.unity.modules.audioandcom.unity.modules.physics, were missing from the table entirely. The introduction also stated that Unity installs OxHeart's package dependencies automatically — true for a UPM install, false for the Asset Store.unitypackage, which is how you received OxHeart: Addressables and Newtonsoft JSON must be installed through Package Manager before importing, or the package will not compile. The publisher line also still carried pre-rebrand product naming. Nothing about your licence obligations changed; the file now describes the dependency set accurately.Documentation~/README.mdSystem Requirements corrected — same stale Addressables 2.5.0 claim, no uGUI entry, no mention of the built-in Audio and Physics modules, and no import-order warning. It now matches the package README and the notices file.- A hardcoded version was removed from the Deterministic RNG sample's README, which used
.../1.1.0/Rng/as a literal example of the imported-sample path.
Documentation only. No code, public API, serialized field, dependency, or link.xml entry changed. Migration note: none.
[1.1.1] - 2026-08-20¶
Fixed¶
- Importing the package into a Unity 6000.6 project logged eight deprecation warnings. Unity 6000.6 ships two new compiler analyzers that the engine tripped on every compile —
UAC0009(theDEVELOPMENT_BUILDpreprocessor directive is deprecated in favour of the managed-code-variant directives) andUAC0005(AppDomain.CurrentDomain.GetAssemblies()may return assemblies Unity has already unloaded, risking errors or leaks). Warnings only — the package compiled and ran correctly on 6000.6 throughout — and nothing here changes behaviour on any Unity version. EveryDEVELOPMENT_BUILDguard is nowDEBUG, including the six[Conditional]attributes onOxLogger'sLog/DebugLog/LogEmphasis(andGameLogger's). Unity definesDEBUGin the editor and in development player builds and leaves it undefined in release players — exactly whereDEVELOPMENT_BUILDwas defined — so the ADR-006 release-stripping contract documented in the Logger guide is unchanged: those call sites, argument evaluation included, still compile to nothing in a release build. The reflection scans that enumerate loaded assemblies (externalIServiceRegistrardiscovery, runtime service-interface discovery, and the editor's dependency checker, save-type validator and sample-scene builder) now route through a single helper that calls Unity'sCurrentAssemblies.GetLoadedAssemblies()on 6000.6+ and keeps the previousAppDomaincall, unchanged, on 6000.0–6000.5 where that API does not exist — the declared 6000.0 floor is untouched. Migration note: none — but if your own game code guards debug-only paths with#if DEVELOPMENT_BUILD, you will see the sameUAC0009warning on 6000.6; the fix is the same swap to#if DEBUG, orDebug.isDebugBuildfor a runtime check.
Documentation¶
- Corrected the
link.xmlplacement guidance, which promised protection on an install path that would not have had it. The bundledlink.xmlcomment and the IL2CPP guide both stated that Unity honours alink.xmlat a UPM package root. It does not — package-root link files are intentionally ignored by the linker, and the supported mechanisms for a UPM package are an assembly-embeddedlink.xmlorIUnityLinkerProcessor.GenerateAdditionalLinkXmlFile. Your project is unaffected in OxHeart's shipped form: the Asset Store.unitypackagerestores toAssets/OxHeart/, and Unity honours alink.xmlanywhere underAssets/— which is the mechanism the file has always actually relied on. What was wrong was the promise made for a different install path: the Quick Start also documented an "Add package from disk / git URL" route, and on that route the bundled file would have been silently ignored while the IL2CPP guide said engine types were "safe at any stripping level with no action on your part" — the linker would then have stripped the engine's reflection-created service constructors, surfacing at boot asCannot register null service of type I…in an IL2CPP player only, with no compile error and no Editor symptom. The Quick Start no longer documents that route (.unitypackageimport is the supported install), and both the comment and the guide now state the real rule plus what to do if you relocate the folder intoPackages/yourself: copy the four<assembly>entries into alink.xmlunder your ownAssets/. Documentation and one XML comment only — no code, public API, serialized field, or<assembly>entry changed. Migration note: none. - Dropped stale "in 1.0" phrasing from the cheat sheet, config guide, docs README and Time Management page, and removed the
1.0.xsections from this changelog. Those versions were internal pre-release checkpoints that were never published, so they described releases no buyer received.
[1.1.0] - 2026-08-14¶
Added¶
Tools > OxHeart > Validate Save Types— catches save fields IL2CPP stripping would silently drop. Scans everyPersistableDatasubclass in your project and reports public auto-properties, the one save-data shape the managed linker can remove without any runtime signal. If nothing in your own code ever calls such a getter — ordinary for data written during play and read back only by the loader — the linker strips it, the serializer never sees the member, and its key is simply absent from the save file: no exception, no warning, no quarantine entry. The report names each type and property and gives the two fixes that work (a[SerializeField]backing field, or alink.xmlentry), including the caveat that changing shape renames the serialized key and so needs aSchemaVersionbump plus a migration. This is deliberately an editor check: after stripping, reflection has no baseline to compare against, so a player build cannot detect what it lost — the shape has to be caught before the linker runs. Types in the engine's own assemblies are skipped, since the package-rootlink.xmlalready preserves them. Editor tooling only — no runtime service, noServiceConfigurationtoggle, no runtime cost. Migration note: none — additive.- Scene Management service (
ISceneService). A new optional, opt-in service (off by default — enable "Scene Service" inServiceConfiguration, under Game Services) for async scene load/unload with progress.LoadSceneAsync(nameOrKey, options)streams a scene from the Build Settings list or Addressables — oneSceneSource.Autooption resolves Addressables-then-build-settings and falls back quietly with no error-log noise when Addressables has no entry for the key (the P1 quiet-fallback contract). Progress is reported as a monotonic0 → 1(Unity'sallowSceneActivation0.9 activation quirk is mapped) through both a directOnLoadProgressevent and pooledSceneEvents.LoadStarted/LoadProgress/LoadCompletedbus events. Optional CanvasGroup fade-out/fade-in is driven by the service's ownDontDestroyOnLoadoverlay canvas — it never touches your camera or game UI — and an optionalAutoSaveBeforeLoadrequests an immediate save through the AutoSave service before the load (warn-and-skip if AutoSave is disabled). The service is graceful: a missing scene, an empty key, or a load superseded by a newer one returns a failedSceneLoadResultand never throws; a second load cancels the first (single in-flight load, no queue — deliberately no scene-dependency graph).SceneServiceConfigsupplies the fade colour, opt-in fade durations, and overlay sort order. Ships with the Scene Management sample (Samples/SceneManagement/):SceneTransitionSample+SceneTransitionA/SceneTransitionBscenes demonstrating Auto / Build Settings / Addressables loads with fades and an on-screen progress bar. See the "Scene Management" documentation and ADR-024. Migration note: none — additive and off by default. - Game Settings service (
IGameSettingsService). A new optional, opt-in service (off by default — enable "Game Settings Service" inServiceConfiguration) that owns the standard graphics/display options — quality tier, resolution + fullscreen mode, vsync, target frame rate — persisting them across restarts (PlayerPrefs), applying them on init, and raisingSettingsChanged. Resolution changes go through a revert/confirm safety:ApplyResolution(...)applies the new mode immediately and returns anIResolutionChangeHandle; unless youConfirm()it before the timeout it auto-reverts to the last confirmed resolution. The countdown runs on the Time service's unscaled clock, so it still fires while the game is paused (timeScale = 0), and an unconfirmed change is reverted on the next launch — so a bad mode can never strand the player, even across a crash or quit mid-countdown. Per-setting "manage" toggles on the newGameSettingsConfiglet a game hand only some settings to the service. Requires the Time service (on by default). Ships with a click-and-runGameSettings.unityscene wrapping the existing runtime-built options panel — the "Screen now" readout reports the actual measured frame rate (aTime.unscaledDeltaTime-based counter, averaged and refreshed twice a second) alongside the Target FPS setting, so stepping the target visibly caps the real rate rather than only showing the value you chose.CurrentResolutiontracks the resolution the service itself last applied rather than readingScreen.currentResolutionback — in windowed /FullScreenWindow(the default fullscreen mode), that Unity API reports the display's native mode and never reflects aScreen.SetResolutioncall, which made a confirmed resolution change look like it silently failed to persist even though it had genuinely saved and was correctly re-applied on the next launch.CurrentResolutionstill falls back to the raw display read when resolution is unmanaged. See the "Game Settings" documentation and ADR-023. Migration note: none — additive and off by default. - Default notification toast presenter (
NotificationPresenter). A drop-in, sample-adjacent MonoBehaviour that rendersINotificationServicenotifications as stacked, priority-coloured toasts — so buyers see notifications on screen without building UI first. Add it to any GameObject in an initialized scene (Notification + Event services enabled) and it builds its own uGUI canvas at runtime; no prefab or scene wiring required. It is a pure view: the notification service owns expiry/dismissal (and already never auto-dismisses Critical or persistent notifications), so the presenter runs no timers of its own — it adds a toast onNotificationSentand removes it onNotificationExpired/NotificationDismissed, honouring stacking, a configurableMaxVisibleToasts, priority ordering, and persistence. It lives in theOxHeart-Samplesassembly; the engine core gains no dependency on it, and the NotificationSystem sample now spawns it automatically. Migration note: none — additive. - Third-Party Notices generator (editor tooling) + display sample. New editor tools under
Tools > OxHeart > Legal/that scan resolved UPM packages, read each package's license file (LICENSE.md/LICENSE.txt/LICENSE/Third Party Notices.md), merge them with a buyer-curated text file, and write a consolidatedTHIRD_PARTY_NOTICES.txt(default output:Assets/StreamingAssets/THIRD_PARTY_NOTICES.txt). A committedThirdPartyNoticesManifestScriptableObject holds paths, product name, exclude / force-include lists, and two opt-in build flags (RegenerateOnBuild,ValidateOnBuild— both off by default so the engine is never hostile-by-default). The Package Manifest window exposes one checkbox per package whose state mirrors the generator's include decision. This is editor tooling only (like the Setup Wizard) — no runtime service, noServiceConfigurationtoggle. Ships with the Third-Party Notices uGUI sample (Samples/ThirdPartyNotices/) that loads the generated file from StreamingAssets cross-platform and displays it in a scroll view, plus a click-and-runThirdPartyNotices.unityscene (camera + component; no engine initializer). Docs carry an explicit "convenience tool, not legal advice — you own your attribution obligations" disclaimer. See the Third-Party Notices documentation. Migration note: none — additive.
Fixed¶
- The Data Persistence sample silently saved
0when a numeric field held non-numeric text. Typing something likeabcinto Level or Gold fell through aTryParse-or-zero helper, so the save reported success while the value read0— indistinguishable from the save system having dropped it. The sample now says so: the status line names the offending field and its rejected text, and a warning goes to the console.0is still stored so Save never blocks, and an empty field stays a quiet0(that is the ordinary "nothing typed yet" case, not a mistake). No engine change — the save system stored exactly the value it was handed; only the sample's input handling was silent about coercing it. - The Scene Management sample gave no hint that an Addressable scene's address must match the key. Ticking Addressable on a scene makes Unity default its address to the full asset path (
Assets/…/Scenes/SceneTransitionA.unity), butLoadSceneAsyncis called with the keySceneTransitionA— so the Addressables load path failed with nothing explaining why.ISceneServiceitself now returns an actionable error instead of a bare "not found": a failedSceneSource.Addressablesload names the exact address the scene needs and warns that Unity's default address (the full asset path) will never match, and a failedSceneSource.BuildSettingsload points at File > Build Settings. The sample's own failure message names the address too, and the Scene Management guide states the rule for your own scenes — including the wrinkle thatSceneSource.Autohides the mismatch by design (its Addressables probe is silent, so a wrongly-addressed scene quietly falls back to Build Settings and looks like it worked). Diagnostic text only —SceneLoadResult.Success, the failure conditions, and every API signature are unchanged. ClearNotifications()left cleared notifications on screen forever. The method emptied the active set directly, so it raised no event at all — unlikeDismissNotification()(which raisesNotificationDismissed) and the auto-expire path (which raisesNotificationExpired). Worse, clearing also suppressed the already-scheduledNotificationExpired: that path is guarded by an internal "was it still active?" check, which now found nothing to remove, so the event never fired either. Any UI driven by the documented notification events — including the bundledNotificationPresentertoast presenter new in this release — was therefore never told the notifications had gone, and kept displaying them permanently, for every notification regardless of duration. Reproducible in the Notification System sample: pressing Clear All emptied the service but left every toast on screen. A bulk clear is a manual removal, so it now raises oneNotificationDismissedper cleared notification, exactly as a single dismiss does; theDismissed-vs-Expireddistinction that lets consumers tell a user action from a timeout is unchanged. Routing the clear through the shared removal helper also cancels each notification's pending auto-dismiss task, which the previous implementation left scheduled to fire and quietly no-op. Migration note: none — if you subscribe toNotificationDismissedyou will now receive one event per notification whenClearNotifications()is called, which is the behaviour the event contract always implied.- The declared Addressables dependency floor (
2.5.0) does not compile on Unity 6000.5+. Confirmed by importing the package into a clean project on Unity 6000.5.8f1 withcom.unity.addressablesat the declared2.5.0floor: compilation failed witherror CS0619: 'Object.GetInstanceID()' is obsolete: 'Use GetEntityId instead.'inside Addressables' own source (AsyncOperationBase.cs,VirtualAssetBundle.cs). Unity 6000.5 hard-obsoletesObject.GetInstanceID()(an error, not a warning), and Addressables 2.5.0 still calls it — a genuine compile-time incompatibility baked into that package version, not a soft recommendation.2.5.0continues to compile fine on Unity 6000.0.81f1;2.9.1was confirmed clean on both 6000.0.81f1 and 6000.5.8f1 in the same clean-project test. The floor inpackage.jsonis now2.9.1. Every EditMode/PlayMode/IL2CPP run in this repository has in fact compiled against2.9.1all along — the shipped dependency declaration was the only place still advertising the older, now-broken floor. Migration note: none — a strictly higher dependency floor, and no buyers have imported the incorrect one. - Every scene logged red Addressables console errors on boot in a fresh buyer project. The shipped
DefaultResourceManagementConfig.asset— which every sample and every fresh consuming project resolvesResourceManagementConfigto by convention, sinceServiceConfiguration.resourceManagementConfigNameis blank everywhere — carriedenableAddressables: 1. Any project that had not built Addressables groups (the default out-of-box state for anyone evaluating the free samples) gotAddressables.InitializeAsync()called anyway, and Addressables logs its own "Unable to load runtime data" errors via internalDebug.LogError/LogWarningFormatcalls as a side effect of running the operation — before the engine's owntry/catcharound the awaited handle ever gets control back, so no amount of exception handling inResourceServicecould suppress it. Confirmed live in a clean buyer-simulation project: a built player was console-clean with the flag off and produced three Addressables errors on every affected scene with it on.enableAddressablesnow shipsfalseon the default config. All 13 samples load their assets fromResourcesand were unaffected by the change.ResourceManagementConfig.enableAddressables's C# field default is unchanged (true) — that default is the intentional ADR-001 fallback used only when no config is registered at all — this fix is to the shipped asset's serialized value, not the class default. Flip it on once you have built Addressables groups for your project. Migration note: none — 1.1.0 is the debut, and any project that already built Addressables groups and wants the shipped default to demonstrate them can flip the one checkbox back on. - Sample scenes required the optional Input System package, breaking 8 of the 13 samples without it. Every EventSystem-bearing sample scene serialized an
InputSystemUIInputModule, because the scene generator attached whichever input module existed on the machine that authored the scene — and that machine hadcom.unity.inputsysteminstalled. In a project without the package the component resolved to a missing script, leaving the EventSystem with no input module at all, so the on-screen buttons in Getting Started, Audio, Save/Load, Events, Localization, Notifications, Object Pooling and RNG did nothing. This contradicted the README's promise that with neither input backend available the samples "still compile, run, and work from their on-screen UI buttons" — the one configuration it explicitly guaranteed was the one that broke. The scenes now carrySampleEventSystemInstaller, which adds the right module at runtime:InputSystemUIInputModulewhen the package is genuinely installed and the new backend is active,StandaloneInputModuleotherwise, and a clear warning when neither backend is usable.com.unity.inputsystemremains undeclared inpackage.json— the engine still never reads input. Migration note: none. - A registrar-name conflict between two
IServiceRegistrarimplementations sharing a type name was undiagnosable.09_GameServiceRegistrarSample.csexplicitly recommends copying itsGameServiceRegistrarpattern into your own game code — if that copy keeps the sample's original class name (an easy copy-first-rename-later slip) and the bundled sample stays compiled too,ConfigurableServiceInitializer's reflection-based registrar scan finds and runs both, and the second one's registrations collide with the first's. The resulting log,Service conflict: ... already registered by 'GameServiceRegistrar', now being overwritten by 'GameServiceRegistrar', named both sides identically, giving no way to tell that two different assemblies — not one buggy registrar calling itself — were involved. The conflict log now includes each registrar's assembly ('GameServiceRegistrar [YourGame]'vs'GameServiceRegistrar [OxHeart-Samples]'), so the actual cause is visible immediately. Diagnostic-only change — no behavior, timing, or registration outcome is different. Migration note: none. - BasicAudio sample: music stopped responding to mute/volume, and couldn't be restarted, after playing for a few seconds.
PlayerController.PlayMusicSound()played the bundledDemo_Musicclip (8s) withoutloop: true, so it played once and completed naturally during ordinary testing — well within the time it takes to toggle mute and drag the volume slider.AudioServicecorrectly disposes a completed one-shot's handle per its documented lifecycle (ADR-004), but the sample's_musicHandlefield was never cleared, onlyIsValidflipped to false:ApplyVolumeToMusic()silently no-op'd on the stale handle (mute/volume appeared dead) andPlayMusicSound()'s dedup guard (_musicHandle != null) permanently blocked restarting. Fixed by playing the music bed withloop: true(matching its intended "keeps playing until stopped" role) and changing the guard to_musicHandle != null && _musicHandle.IsValid, so the sample recovers even if a future clip swap makes it non-looping again. Sample-only change — no engine API affected. Migration note: none. - Saving a
Vector3(orQuaternion,Color,Rect,Bounds, …) threw instead of saving. Storing any Unity math struct in aPersistableDatafailed withJsonSerializationException: Self referencing loop detected for property 'normalized'. The cause:Vector3.normalizedreturns aVector3, which has its ownnormalized, so a general-purpose serializer reflecting over properties never terminates. The08_DataPersistenceSamplesample stored aVector3position and hit this, and no test covered it.JsonDataSerializernow registers converters forVector2/Vector3/Vector4,Vector2Int/Vector3Int,Quaternion,Color,Color32,RectandBounds, each writing only the type's real components — which terminates the recursion and keeps payloads small (aVector3is three numbers, not the dozen derived members reflection would otherwise walk). Deserialization tolerates missing components (they read as zero) and ignores unknown ones, so a save written by an older or newer build still loads. Migration note: none — the previous behaviour was an exception, so no existing save file can contain one of these values. The written shapes ({"x":…,"y":…,"z":…}) are now part of the save format and are frozen under the same rule asModelIdstrings and the checksum scheme.
Changed¶
- Removed: the Asset Updater / asset-versioning subsystem.
AssetUpdaterService,IAssetUpdaterService,VersionCatalog,SemanticVersion,AssetVersionInfo, the resource-service versioning extensions, theenableAssetUpdaterServicetoggle onServiceConfiguration, the three Asset Updater fields onResourceManagementConfig(remoteCatalogUrl,localCatalogFileName,assetDownloadSubPath), and theVersionedAssetsExamplesample have all been deleted. The subsystem could not do what it advertised: the three config fields were never read (the initializer hardcoded an empty catalog URL and fixedpersistentDataPathlocations), and — more fundamentally — wiring them would not have helped, because downloaded files landed in a directory no resource loader ever consults, while the versioning extensions synthesizedassetId_versionkeys and passed them through the ordinary Resources/Addressables/StreamingAssets loaders. There was no path from downloaded bytes to a loadable asset. The bundled sample also resolved a concrete type that was never registered, and the remote catalog's JSON schema was documented nowhere. Rather than ship a service whose core promise cannot be met, it is removed. Teams needing remote content today should use Addressables' own remote catalog support directly. No compatibility stubs or[Obsolete]shims were left behind — they would preserve exactly the dead surface this removal exists to eliminate. Migration note: none — this subsystem never reached a buyer. It was disabled by default, and no release of this package was ever public (see ADR-026);1.1.0is the debut. If you enabled the toggle while evaluating a pre-release build, remove yourIAssetUpdaterServiceusages — nothing else in the engine referenced it, and every otherResourceManagementConfigandServiceConfigurationsetting is unchanged. - The engine is now OxHeart. The package has been renamed from its previous identity throughout: package id (
games.moomoo.oxheart), display name, namespace root (OxHeart.*), assembly definitions (OxHeart-Core,-Runtime,-Helpers,-Utility,-Editor,-Samples), every public type that carried the old product name (OxHeartConfig,IOxHeartService, ...), the compile define symbols (OXHEART_DEV,OXHEART_TESTS_ENABLED,OXHEART_STARTUP_DIAGNOSTICS), the editor menu roots (Tools > OxHeart,Assets → Create → OxHeart,Add Component → OxHeart), the documentation site, and the repository and support URLs inpackage.json. The logger keeps the short formOxLogger. The brand is spelledOxHearteverywhere — internal capital, neverOxheart. - Nothing in the save contract moved. No save file format,
ModelId, checksum scheme, or quarantine reason code (JSN/CHK/SCH, including the-BAKvariants) changed. Five string literals deliberately keep their pre-rename spelling because they are save-format contract rather than branding: the package-default obfuscation salt and its companion secret inSecureKeyProvider, the AES key-derivation suffix inAesEncryptionService, the fallback XOR key inXorObfuscationService, and thePlayerPrefskey inGameSettingsService. The first four feedSHA256key derivation — renaming them would produce a valid-looking wrong key and fail silently, so they are frozen, marked in source, and pinned by a regression suite replaying key vectors captured before the rename. See ADR-025. - Migration note: none. No version of this package was ever publicly released — 1.0.0 and every 1.0.x tag after it were internal checkpoints. 1.1.0 is the debut release, so there is no upgrade path from an older name and no deprecation shims for it. This is why a rename ships in a minor rather than a major; see ADR-026. From 1.1.0 onward the normal SemVer contract applies in full.
- Sample save types now use
[SerializeField]backing fields instead of auto-properties.DemoProfileData(DataPersistence sample) andPlayerData(08_DataPersistenceSample) changed shape so the samples model the pattern that is safe under IL2CPP managed stripping — see the IL2CPP entry under Documentation. Public API is unchanged: the samePlayerName/Level/Gold/Position/PlayTimeproperties exist with the same types and accessibility, so sample code and anything copied from it compiles and behaves identically. Only the serialized key names inside the samples' own save files change (Gold→_gold), which affects nothing but a sample save written by a pre-release build. Migration note: none. - Sample asset renamed:
Samples/TestConfig.asset→Samples/SampleOxHeartConfig.asset. It is anOxHeartConfigsample asset, not a test artifact, and the old name read as one inside a shipped package. The asset GUID is unchanged, so any reference to it keeps resolving. Migration note: none — nothing in the package referenced this asset by GUID or by name, and 1.1.0 is the debut release. - Release tooling: releases are now cut from persistent
release/vX.Y.xline branches, never fromdevelop.Scripts/create-release.shcreates the line on the first cut of a minor; from then on every rc, final, and patch of that minor is a version-bump commit + annotated tag on the line.mainis merged only for a final (non-rc) cut of the newest line, and the line back-merges todeveloponly when it is the newest line — patches to older lines touch neither (their fixes originate ondevelopand reach the line viaScripts/backport-commit.sh). New guards refuse malformed versions, existing tags, dirty trees on branch switches, and behind-origin branches. A new interactiveScripts/release-wizard.shshows the live pipeline state (git, Asset Store review status via_internal/RELEASE_STATE.json, docs site) with a suggested next step, and drives cuts, store-submission prep, verdict recording, and docs publishing. Engine-internal only — no package code, public API, or asset changes. Migration note: none for buyers; maintainers cut from line branches from now on (see_internal/Documentation/RELEASE_PIPELINE.md). - Release tooling:
backport-commit.shno longer offers its inline patch release;publish-theme-only.shspeaks the versioned publisher. The backport script's "create patch release?" path bumped onlypackage.json(skippingOxHeartVersion.cs,CHANGELOG.md, and the README badges — a tag that fails the release checklist's own version sweep); it now ends by pointing atcreate-release.shfor the cut.publish-theme-only.shderives the docs minor from its frozen snapshot and passes it to the now-versionedpublish-docs-local.sh(preserving the site'slatestalias), instead of calling it with the removed argument-less signature. Maintainer tooling only — no package code changes.
Documentation¶
- IL2CPP guide: corrected the save-type stripping advice, which was insufficient. The guide told you to add
[Preserve]to aPersistableDatasubclass that is only constructed by deserialization. That is true as far as it goes, but it misses the case that actually causes damage: stripping applies to members, not only whole types. A public auto-property whose getter is never called anywhere in your own code — ordinary for save data written during play and read back only by the loader — can be removed by the linker even though its class survives. Newtonsoft then never sees the member and the key is silently absent from the save file: no exception, no warning, no quarantine entry. Verified on Unity 6000.0 / IL2CPP / Managed Stripping High with the data class in a game assembly:[Preserve]on the class does not rescue it — the object still serializes, minus that property. What does work, and is now documented with a comparison table: a[SerializeField]private backing field (the recommended shape — the engine's contract resolver picks these up on purpose, including private fields declared on base classes) or an explicitlink.xmlentry for the type. Also notes that switching an existing auto-property to a backing field changes the serialized key (Coins→_coins) and therefore needs aSchemaVersionbump and a migration step. No code changed — public fields,[SerializeField]fields, and auto-properties your code does read were already safe, and Newtonsoft itself was never at risk. - The hosted docs site is now versioned per minor. The site publishes one folder per minor (
/1.0/,/1.1/, ...) with a rootversions.jsondriving Material's header version selector (extra.version.provider: mikeinmkdocs.yml), and the root URL redirects to the latest published version.Tools/publish-docs-local.shnow takes the docs version (<X.Y> [--latest]), stages into that version's folder only, and refuses a version that does not match the tree'spackage.json— so docs builds stay anchored to the released tag. Publishing (commit + push ofgh-pages) remains a manual, owner-reviewed step. Pages built before this change (the/1.0/content) do not show the version selector; new versions do.
1.1.0 is the first public release of OxHeart on the Unity Asset Store. Earlier
1.0.x versions were internal pre-release checkpoints and were never published,
so no changelog history precedes 1.1.0.