Skip to content

chore: runtime/timing classes moved to unity.netcode.gameobjects.timing namespace - #4150

Open
NoelStephensUnity wants to merge 14 commits into
develop-3.x.xfrom
chore/timing-namespace-to-gameobjects
Open

chore: runtime/timing classes moved to unity.netcode.gameobjects.timing namespace#4150
NoelStephensUnity wants to merge 14 commits into
develop-3.x.xfrom
chore/timing-namespace-to-gameobjects

Conversation

@NoelStephensUnity

Copy link
Copy Markdown
Member

NOTE:

N4E migration of NetworkTime and NetworkTimeSystem must be merged before taking this project out of draft!

Purpose of this PR

Since NGO will be dependent upon N4E and N4E is going to be updating their root namespace from Unity.NetCode to Unity.Netcode, there is a conflict with NetworkTime and NetworkTimeSystem.
This PR migrates NGO's time related classes into Unity.Netcode.GameObjects.Timing.

Jira ticket

MTT-15570

Changelog

  • Changed: All classes within the runtime/timing folder have been migrated to the Unity.Netcode.GameObjects.Timing namespace.

Documentation

  • Minor update to documentation.

Testing & QA (How your changes can be verified during release Playtest)

Manual testing can be performed by migrating an existing NGO v2.x.x project to NGO v3.x.x.
(Once N4E has made the namespace change)

Functional Testing

Manual testing :

  • Manual testing done

Automated tests:

  • Covered by modified APIUpdater automated test
  • Covered by new automated tests

Does the change require QA team to:

  • Review automated tests?
  • Execute manual tests?
  • Provide feedback about the PR?

If any boxes above are checked the QA team will be automatically added as a PR reviewer.

Up-port

N/A

Backports

N/A

Adds the input and the assertions for moving NetworkTime, NetworkTimeSystem and NetworkTickSystem
out of Unity.Netcode, ahead of the move itself.

Assets/Runtime/DeprecatedTimingUsage.cs names all three in every reference form the editor project
already covers, plus a constructor call, and names each one fully qualified at least once so a
blocked run can tell "was not rewritten" from "was never referenced".

EXPECTED_TYPES becomes EXPECTED_MOVES, grouped by relocation. The old list derived the 3.x name by
substituting the namespace prefix, which only works while every move shares one destination; stating
the namespace pair once per move keeps a type's two names from drifting and admits a second
destination. Both match counts now take a trailing-token boundary, without which
Unity.Netcode.NetworkTime also counts every Unity.Netcode.NetworkTimeSystem.

--collision-stub is the regression test for why the move exists. It installs an assembly occupying
the two names Netcode for Entities would take, and inverts the expectation for exactly those two: a
reference that still resolves never reaches the MovedFrom data, so it cannot be migrated.
NetworkTickSystem is deliberately absent from the stub and must still migrate, so a pass proves both
halves rather than merely failing. The stub lives in a folder ending in '~' and is inert until the
flag copies it in.

Not run locally: there is no Python on the development machine, so this is verified by
/ci apiupdater. The namespace-only form of MovedFrom is also unmeasured until then - every case in
the AGENTS.md table moved the assembly too - and AGENTS.md now records that gap along with the one
reference form the assertions deliberately do not depend on.
Runtime/Timing moves out of the Unity.Netcode root so that correcting the casing of Netcode for
Entities' Unity.NetCode namespace does not collide with it. Two of the names are the collision:
NetworkTime and NetworkTimeSystem exist in both SDKs as unrelated types - a time value here, an
IComponentData carrying prediction-loop state there - and two assemblies exporting one fully
qualified name is CS0433, which no user can work around in source.

The three public types carry [MovedFrom(true, "Unity.Netcode", null, null)], so existing scripts are
rewritten on upgrade. sourceAssembly is null because Unity.Netcode.Runtime keeps its name and a null
argument is read as "unchanged"; the editor relocation passed an assembly name because that one
genuinely moved assemblies. The three internal types in the folder carry nothing - the attribute
only matters for API the updater has to migrate.

Consumers take an import rather than a qualified name at each site. Qualification was the first
approach and it is wrong here: .editorconfig sets IDE0001 to error, so a fully qualified name that
the simplifier can shorten fails the Standards job. That leaves NGO's own references reading the
bare name, which is correct as long as nothing else occupies Unity.Netcode.NetworkTime - if Netcode
for Entities takes those names without vacating them, this assembly stops compiling wherever both
packages are installed. That is deliberate: it fails early and loudly in our own CI rather than
silently in user projects, and the recommendation both halves of this work rest on is that neither
SDK keeps those names in the shared root.

Files whose declared namespace is exactly Unity.Netcode cannot use an import for the two colliding
names at all - the enclosing namespace's members beat both using directives and using aliases
(CS0576) - so if the one-sided case ever has to be supported, those five files need qualified names
and the rest do not. Sub-namespaces such as Unity.Netcode.Components never walk that far up and are
fine either way.

NetworkTimeSystem and AnticipationSystem now import Unity.Netcode themselves, and the five
Components.NetworkTransform doc references in NetworkTimeSystem are spelled in full, since that
prefix was only reachable from inside the root namespace.

Compile-checked: runtime (with and without UNITY_EDITOR), editor, runtime tests and editor tests all
clean, the last except the known environmental CS0656 on BytePackerTests. The UNIFIED_NETCODE path
is not covered - the harness has no Entities or NetCode references - so UnifiedNetcodeTransport was
checked by reading it: IRealTimeProvider at line 207 is a type position, so its import is needed.
…mespace

xref targets are fully qualified UIDs, so the one in networktime-ticks.md stopped resolving when the
type moved. DocFX renders an unresolved xref as its raw text, which reads as a broken link on the
published page rather than failing anything in this repo - there is no docfx job here.

Nothing else in Documentation~ needs changing. Every code sample that touches these types reaches
them through NetworkManager (LocalTime.TimeAsFloat, NetworkTickSystem.Tick), which never names the
type and is unaffected; the remaining mentions are prose or links to this same page. The old names in
apiupdaterproject/README.md and the CHANGELOG are the 2.x side of before/after tables and are correct
as they stand.

No upgrade note added. The samples on that page do not need the new import, so a note about it would
be advice for a reader the page does not have, and the landed editor relocation set the precedent of
updating the affected sample without prose. The CHANGELOG entry covers the move.
The --collision-stub mode was added with the timing relocation but never wired into the job, so
/ci apiupdater only ever exercised the default path and the regression test for the reason the move
exists could not actually run in CI.

Two sequential commands rather than one invocation: each needs its own cold import, since the
assertion is meaningless against a Library that already holds rewritten sources. The script removes
the stub and restores the 2.x sources on every exit path, so the second run starts from the state the
first one did.

The on-demand trigger is `pull_request.comment eq "apiupdater"` with no draft exclusion, so this runs
on #4150 while it sits in draft. Neither mode needs Netcode for Entities present - the default mode
tests NGO's own 2.x to 3.x migration, and the stub mode uses the local stub rather than the real
package - so the run does not have to wait on the N4E namespace work.
Netcode for Entities 6.7.0 corrected the casing of its namespace, so it now declares
Unity.Netcode.NetworkTimeSystem. The enclosing namespace is searched ahead of any using
directive, so once the timing types leave Unity.Netcode the five files that still spell
the name bare bind to theirs instead, and Unity.Netcode.Runtime stops compiling in any
project that has both packages: 16 errors across 6 files, all of them cascading from the
NetworkManager.NetworkTimeSystem property picking up the wrong type.

The collision stub is corrected to match what N4E actually shipped. It sub-namespaced
NetworkTime into Unity.Netcode.NetcodeTime but left NetworkTimeSystem in the shared root,
so only that one name is occupied.
@NoelStephensUnity

Copy link
Copy Markdown
Member Author

/ci apiupdater

…dundant

Unity.Netcode.GameObjects.Timing is nested inside Unity.Netcode, so the root namespace is
already in scope there through the enclosing namespace chain. The two imports the move added to
AnticipationSystem.cs and NetworkTimeSystem.cs were never needed, and the Unity.Netcode. prefix
on the five NetworkTransform doc references shortens to Components. for the same reason - both
are IDE0005 and IDE0001, which the Standards job treats as errors.

NetworkManagerEventsTests.cs took an import it does not use: its only mention of
NetworkTimeSystem is inside a comment.

Matches the diff the Standards job produced. Running the tool locally finds nothing on dotnet
10.0.400, which is the version divergence its own failure message warns about, so this was
applied from the CI output rather than reproduced here.
@NoelStephensUnity

Copy link
Copy Markdown
Member Author

/ci apiupdater

The job description still said the stub occupies both NetworkTime and NetworkTimeSystem, which
is what the stub looked like before 6013f45 corrected it. N4E 6.7.0 sub-namespaced NetworkTime
into Unity.Netcode.NetcodeTime and left NetworkTimeSystem in the shared root, so only one name
is occupied and only that one expectation inverts.
@NoelStephensUnity
NoelStephensUnity marked this pull request as ready for review September 7, 2026 03:30

@u-pr u-pr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Harness Review

Needs changes

The runtime namespace migration is consistently applied internally, but the tested upgrade path still leaves a colliding legacy type bound to the other package and has gaps in how that limitation is validated and documented.

Reviewed commit ce53a03

🤖 Helpful? 👍/👎

Comment thread apiupdaterproject/run_upgrade_test.py
Comment thread apiupdaterproject/Assets/Runtime/DeprecatedTimingUsage.cs
Comment thread apiupdaterproject/README.md Outdated
@u-pr

u-pr Bot commented Sep 7, 2026

Copy link
Copy Markdown

ℹ️ Skipped — a harness review for this commit (ce53a03) already ran or is in flight. Same-commit re-runs are paused for 10 min; push a new commit, or re-comment /harness-review in ~8 min to force a fresh one.

🤖 Helpful? 👍/👎

Two review findings, both accurate.

The README still described the stub as occupying both NetworkTime and NetworkTimeSystem, which is
what it looked like before 6013f45 corrected it to match N4E 6.7.0. The expectation table was the
harmful part: it listed NetworkTime as not-rewritten when it now migrates, so a correct run would
read as a failure. The CLI help was already right.

The CHANGELOG claimed migration is automatic without qualification. It is not, for exactly the case
this move exists to address: when another installed package still declares the name in
Unity.Netcode the old reference resolves, never reaches the MovedFrom data and is left alone. The
--collision-stub run asserts that as a deliberate negative result, so the test was right and the
user-facing sentence was wrong.
DeprecatedTimingUsage.cs reaches NetworkTickSystem through 'using TimeNs = Unity.Netcode;', and
the per-type counts cannot see that site: 'TimeNs.NetworkTickSystem' contains neither the old nor
the new fully qualified name, so it contributes to neither updated nor stale. The editor runs with
-ignoreCompilerErrors, so an updater that left it unresolved would still produce a passing run off
the other reference forms. The type alias on the line above is fine - it spells
Unity.Netcode.NetworkTime in full, so it already counts toward stale.

Separate commit because it cannot be verified here - there is no Python on the machine this was
written on, so the next /ci apiupdater run is the first execution. Either outcome is informative:
a pass closes the hole, and a failure means the updater does not follow namespace aliases, which
is a finding rather than a defect in this assertion.
@codecov-github-com

codecov-github-com Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

@@                Coverage Diff                @@
##           develop-3.x.x    #4150      +/-   ##
=================================================
+ Coverage          78.01%   78.18%   +0.17%     
=================================================
  Files                153      153              
  Lines              26260    26272      +12     
=================================================
+ Hits               20486    20542      +56     
+ Misses              5774     5730      -44     
Flag Coverage Δ
NGOv2_project_testproject_ubuntu_pinnedTrunk 77.85% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
.../Runtime/Components/AnticipatedNetworkTransform.cs 80.41% <ø> (ø)
...ponents/Interpolator/BufferedLinearInterpolator.cs 86.61% <ø> (+0.29%) ⬆️
...gameobjects/Runtime/Components/NetworkTransform.cs 88.75% <ø> (+0.33%) ⬆️
...tcode.gameobjects/Runtime/Core/ComponentFactory.cs 95.45% <ø> (ø)
...netcode.gameobjects/Runtime/Core/NetworkManager.cs 79.97% <100.00%> (+0.18%) ⬆️
...me/Messaging/Messages/ConnectionApprovedMessage.cs 66.26% <ø> (ø)
...ects/Runtime/Messaging/Messages/TimeSyncMessage.cs 88.88% <ø> (ø)
...time/NetworkVariable/AnticipatedNetworkVariable.cs 76.64% <ø> (ø)
...Runtime/NetworkVariable/Collections/NetworkList.cs 79.75% <ø> (-0.07%) ⬇️
...cts/Runtime/NetworkVariable/NetworkVariableBase.cs 87.17% <ø> (ø)
... and 7 more

... and 2 files with indirect coverage changes

Components Coverage Δ
com.unity.netcode.gameobjects 78.18% <96.96%> (+0.17%) ⬆️

ℹ️ Need help interpreting these results?

The unified job failed to compile with two CS0104 on NetworkManager.LocalTime and ServerTime:
'NetworkTime' is ambiguous between Unity.Netcode.GameObjects.Timing.NetworkTime and
Unity.NetCode.NetworkTime.

This is a second, distinct mechanism from the one 6013f45 addressed, and the stub used there
could not produce it. That stub modelled N4E after its casing correction, where the colliding
names are members of the enclosing Unity.Netcode namespace and therefore win over any import.
The N4E that the pinned editor actually bundles is still Unity.NetCode, so both names arrive as
imports instead - Unity.NetCode alongside Unity.Netcode.GameObjects.Timing - and two imports
offering the same simple name is an ambiguity rather than a silent rebind.

Before the move NGO's own NetworkTime was a member of Unity.Netcode, which is why the two
imports coexisted for as long as they did.

The alias fixes both mechanisms, so this holds whichever casing is installed. NetworkTime and
NetworkTimeSystem are the only two names at risk: they are the sole intersection between
Runtime/Timing/ and what N4E declares, confirmed against the 6000.7.0a5 bundled package.
Only NetworkManager.cs is affected, being the only file that imports both namespaces and names
either type in a type position.
@NoelStephensUnity

Copy link
Copy Markdown
Member Author

/ci apiupdater

@NoelStephensUnity

Copy link
Copy Markdown
Member Author

/harness-review

@u-pr u-pr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Harness Review

LGTM

The namespace relocation and its runtime/test call sites are consistently updated, but the public XML documentation for the newly aliased timing properties does not follow the collision-safe type resolution.

Reviewed commit 3ded452

🤖 Helpful? 👍/👎

Comment thread com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs Outdated
…space

Supersedes the aliasing of NGO's own timing types added in the previous commit, which was
treating the symptom.

Netcode for Entities' namespace differs from this one only by the casing of a single letter, so
a blanket 'using Unity.NetCode;' from inside 'namespace Unity.Netcode' competes with our own
namespace on every name the two happen to share. That is what produced the CS0104 on LocalTime
and ServerTime, and it will produce the same on any future N4E type whose name we also use.
Importing the two types this file actually needs removes the class of failure rather than the
instance, and the timing types then resolve from the Timing import with no alias, no
qualification, and no IDE0001 suppression on their doc comments.

NetcodeWorld was not obvious from reading: it is declared without an access modifier, so a
search for public or internal declarations misses it. The compiler found it. Anything reached
through an extension method defined in that namespace would have needed the same treatment,
since a using alias does not bring extension methods into scope - there are none here.

Verified against the N4E the pinned editor actually bundles, not a stub: compiled under
UNIFIED_NETCODE in a 6000.7.0a5 project with com.unity.netcode 6.7.0 installed, 0 errors, and
UnifiedNetworkTransformTest passes. Reverting the change in the same project reproduces the two
CS0104 the unified job reported.

This addresses the ambiguity that the shipped casing produces. The shadowing that the corrected
casing produces is a different mechanism, unaffected by imports, and belongs with the branch
that adapts to it.
…yout

Netcode for Entities has now moved NetworkTimeSystem into Unity.Netcode.NetcodeTime alongside
NetworkTime, so describing the stub as 'what N4E does' is about to be wrong for the third time.

What the --collision-stub run actually asserts is a property of Unity's API updater: it is driven
by resolution failure, so a relocated type whose old name still resolves somewhere else is never
rewritten. That is worth regression-testing whether or not any package occupies the name today,
and it is the reason the timing types were moved at all. Described that way in all four places
the stub is documented, so it stops drifting every time another SDK rearranges itself.
@NoelStephensUnity

Copy link
Copy Markdown
Member Author

/ci unified
/ci apiupdater

@NoelStephensUnity

Copy link
Copy Markdown
Member Author

/ci apiupdater

@NoelStephensUnity

Copy link
Copy Markdown
Member Author

/ci ngo
/ci unified

@NoelStephensUnity

Copy link
Copy Markdown
Member Author

/harness-review

@u-pr u-pr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Harness Review

LGTM

The namespace migration updates the checked-in runtime consumers consistently, but the new updater test can still report success when several unqualified timing references were not migrated.

Reviewed commit 2df9bdb

🤖 Helpful? 👍/👎

# The old name still resolves to the stub, so the reference must have been left alone.
passed = updated == 0 and stale > 0
else:
passed = updated > 0 and stale == 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2

This success condition only examines fully-qualified old/new type names. The fixture's simple NetworkTime/NetworkTimeSystem/NetworkTickSystem usages (including the generic, typeof, constructor, and parameter forms) contain neither string, so an updater regression that rewrites the fully-qualified sites but leaves using Unity.Netcode; and these unqualified references unchanged still produces updated > 0 and stale == 0. Because Unity is invoked with -ignoreCompilerErrors, that broken upgraded source also does not fail the import. Assert the expected transformation for the simple-name forms (or require the rewritten project to compile) so this coverage cannot green-light that regression.

🤖 Helpful? 👍/👎

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants