Fix consumer-reported blockers in the 0.0.1-alpha.1 preview - #2
Draft
VPDPersonal wants to merge 3 commits into
Draft
VPDPersonal wants to merge 3 commits into
VPDPersonal wants to merge 3 commits into
Conversation
- Declare MIT in package.json and add LICENSE / LICENSE.md. The first preview shipped without any license, which by default reserves all rights and blocks shipping a game that depends on the package. - Re-include ~-suffixed directories in .gitignore. A global '*~' backup pattern matched the Samples~ directory itself, so the sample that package.json advertises was absent from every commit, and therefore from the subtree split the release workflow publishes. - Stop claiming UniTask is "pulled in automatically as a package dependency". UPM does not resolve git dependencies transitively, so it never was; document it as an explicit first install step instead. - Drop the upm branch install instructions until the first stable release creates that branch, and bump the version to 0.0.1-alpha.2. Co-Authored-By: Claude <noreply@anthropic.com>
…anges StateFactory.CreateState returned a live reference to its own field, which the next call cleared and rewrote. A ChangeState issued from a state's Enter / IEnterController.OnEnter therefore rewrote the chain the outer ChangeState was still iterating: states were entered twice and duplicates were left in CurrentStates. The async path was affected too, where the chain is held across await. - CreateState now returns a list the caller owns; the machine rents a separate buffer per in-flight transition. - A re-entrant ChangeState is queued and applied once the running change completes (run-to-completion), with guards re-resolved at apply time. - Add IsTransitionEnabled(source, target), the edge-level guard every state change funnels through, and opt-in StrictTransitions, which rejects a path no registered transition fully covers instead of transitioning silently. A partially covered path no longer counts as covered. - Forward IsStateEnabled / IsControllerEnabled / IsTransitionEnabled / StrictTransitions through MonoStateMachine, which composes the core rather than inheriting it and so could not override them at all (CS0115). - ChangeStateAsync called from an async enter/exit callback deadlocked on the transition it was running inside; it now throws instead. - Replace the MethodInfo.Invoke dispatch with Type-based overloads. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Acts on an audit of the published
upm-previewbranch (0.0.1-alpha.1) from a consumer project. Every item below was reproduced against the sources before being fixed.Summary
StateFactory.CreateStatehanded out a live reference to its own field, which the next call cleared and rewrote. AChangeStateissued from a state'sEnter/IEnterController.OnEnterrewrote the chain the outerChangeStatewas still iterating — states entered twice, duplicates left inCurrentStates. The async path was affected too, where the chain is held acrossawait.ChangeStateis now queued and applied once the running change completes (run-to-completion), instead of mutating the chain underneath it. Guards are re-resolved at apply time.IsTransitionEnabled(source, target)— the edge-level guard every state change funnels through.IsStateEnabledonly ever received the target, so edge legality could not be expressed without readingCurrentStates[^1]by hand.StrictTransitions(opt-in) makes the transition registry the declaration of allowed edges: an uncovered path throws instead of transitioning silently. A partially covered path no longer counts as covered.MonoStateMachinenow forwardsIsStateEnabled/IsControllerEnabled/IsTransitionEnabled/StrictTransitions. It composes the core rather than inheriting it, so a subclass previously could not override them at all (CS0115).ChangeStateAsyncfrom an async enter/exit callback deadlocked — it awaited the very transition it was running inside. Now throws with an explanation.LICENSE, packageLICENSE.md,licensefield. The first preview shipped with none, which by default reserves all rights and blocks shipping a game built on the package.package.jsonadvertisedSamples~/GameLoop, but a*~backup pattern in a global gitignore matched theSamples~directory itself — it was absent from every commit, and therefore from thegit subtree splitthe release workflow publishes. The workflow was fine; the files were never there.upmbranch instructions are gone until the first stable release creates that branch.TransitionViaand the async transition entry points no longer dispatch throughMethodInfo.Invoke.Notes for review
CreateStatereturns a chain that shrinks from 2 to 1 after a second call, andCurrentStatesends up with 3 entries but only 2 distinct.MonoStateMachineis not covered by the xUnit suite — it needsUnityEngineand is not in the test csproj. The forwarding was verified by compiling all 36 package sources against minimalMonoBehaviour/Time/UniTaskstubs: clean build, zero warnings. Worth a look in the Editor before release.dependenciesentry was added on purpose. A git URL there is ignored by UPM, and the registry form resolves only for consumers who have the OpenUPM scoped registry — for everyone else the install would fail outright on resolution. Documenting the step beats failing loudly.ChangeStatenow rejects a call made during an async transition before consultingIsStateEnabled, so an in-flight async transition throws regardless of the target. Previously a targetIsStateEnabledrefused returned silently.LICENSE.mdships without a.meta; Unity generates it on the next Editor open.Aspid.Core.HSM.Generators.dllwas deliberately left untouched — no generator source changed, so the rebuild diff was pure noise.StrictTransitionsis off,IsTransitionEnabledreturnstrue, and the permissive partial-chain path still transitions.🇷🇺 Описание на русском
Закрывает аудит опубликованной ветки
upm-preview(0.0.1-alpha.1), присланный из проекта-потребителя. Каждый пункт перед починкой воспроизведён по исходникам.Что сделано
StateFactory.CreateStateотдавал живую ссылку на собственное поле, которое следующий вызов очищал и перезаписывал.ChangeState, вызванный изEnter/IEnterController.OnEnterсостояния, перезаписывал цепочку, по которой внешнийChangeStateещё шёл: состояния входили дважды, вCurrentStatesоставались дубликаты. Async-путь задет тоже — там цепочка живёт черезawait.ChangeStateтеперь ставится в очередь и применяется после завершения текущего перехода (run-to-completion), а не правит цепочку под ним. Гарды разрешаются в момент применения.IsTransitionEnabled(source, target)— гард по ребру, через который проходит каждая смена состояния.IsStateEnabledполучал только цель, поэтому легальность ребра нельзя было выразить, не читаяCurrentStates[^1]вручную.StrictTransitions(по желанию) делает реестр переходов объявлением разрешённых рёбер: непокрытый путь бросает исключение вместо молчаливого перехода. Частично покрытый путь больше не считается покрытым.MonoStateMachineпробрасываетIsStateEnabled/IsControllerEnabled/IsTransitionEnabled/StrictTransitions. Он композирует ядро, а не наследует его, поэтому наследник раньше не мог их переопределить вообще (CS0115).ChangeStateAsyncиз async-колбэка входа/выхода уходил в дедлок — ждал тот самый переход, внутри которого выполнялся. Теперь бросает исключение с объяснением.LICENSE,LICENSE.mdв пакете, полеlicense. Первый превью вышел без всего этого, то есть по умолчанию all rights reserved — выпуск игры на пакете юридически заблокирован.package.jsonобъявлялSamples~/GameLoop, но паттерн*~в глобальном gitignore матчил саму папкуSamples~— её не было ни в одном коммите, а значит и вgit subtree split, который делает релизный workflow. Workflow был исправен, файлов просто не существовало.upmубраны до первого стабильного релиза.TransitionViaи async-переходы больше не ходят черезMethodInfo.Invoke.На что смотреть при ревью
CreateStateотдаёт цепочку, схлопывающуюся с 2 до 1 после второго вызова, а вCurrentStatesоказывается 3 элемента при 2 уникальных.MonoStateMachineне покрыт xUnit-набором — ему нуженUnityEngine, и в тестовый csproj он не включён. Проброс проверен компиляцией всех 36 исходников пакета против минимальных стабовMonoBehaviour/Time/UniTask: сборка чистая, ноль предупреждений. Перед релизом стоит открыть в редакторе.dependenciesне добавлена намеренно. Git-URL там UPM игнорирует, а реестровая форма резолвится только у тех, у кого подключён scoped-registry OpenUPM — у остальных установка упала бы на резолве. Документированный шаг лучше громкого падения.ChangeStateтеперь отклоняет вызов во время async-перехода до проверкиIsStateEnabled, то есть активный async-переход бросает независимо от цели. Раньше цель, отклонённаяIsStateEnabled, возвращалась молча.LICENSE.mdидёт без.meta— Unity сгенерирует при следующем открытии редактора.Aspid.Core.HSM.Generators.dllнамеренно не тронут: исходники генератора не менялись, дифф пересборки был чистым шумом.StrictTransitionsвыключен,IsTransitionEnabledвозвращаетtrue, частично покрытый путь по-прежнему переходит.