Skip to content

Fix RegisterSyncField(Type, string) preserving caller targetType#2

Closed
sviyh wants to merge 1 commit intomasterfrom
fix/registersyncfield-targettype
Closed

Fix RegisterSyncField(Type, string) preserving caller targetType#2
sviyh wants to merge 1 commit intomasterfrom
fix/registersyncfield-targettype

Conversation

@sviyh
Copy link
Copy Markdown
Owner

@sviyh sviyh commented Apr 12, 2026

Summary

  • MP.RegisterSyncField(Type, string) routed through RegisterSyncField(FieldInfo), which keyed everything off FieldInfo.ReflectedType. AccessTools.Field walks the inheritance chain, so for a private field declared on a base class the ReflectedType collapses to the base — silently discarding the concrete Type the compat caller passed in.
  • Downstream, SyncField stored that base targetType and DoSync serialized target as the base, so SyncWorkers registered on the concrete subclass stopped matching. This is Registering sync field thhrough API doesn't allow for specifying the exact type of the target rwmt/Multiplayer#880 and is why the VGE oxygen gizmo compat patch currently needs a reflection SetValue hack on targetType.
  • Fix: in the (Type, string) overload, look up the FieldInfo only for validation and the static branch, but pass the caller's targetType through to Sync.Field and the memberPath key. RegisterSyncField(FieldInfo) behavior is unchanged.

Test plan

  • dotnet build Source/Client/Multiplayer.csproj -c Release — 0 errors
  • Verify VGE oxygen gizmo sync works once the compat-side SetValue hack is removed in a follow-up
  • Spot-check existing callers of RegisterSyncField(Type, string) where caller Type == declaring type (no behavior change expected)

AccessTools.Field walks the inheritance chain, so the returned
FieldInfo.ReflectedType collapses to the base type that declared the
field. Routing the (Type, string) overload through RegisterSyncField(
FieldInfo) silently discarded the caller's concrete Type, causing
SyncField to store the base targetType and breaking SyncWorker lookups
registered on the concrete subclass (rwmt#880).

Keep the caller's Type for the instance path; use the FieldInfo only
for existence validation and the static branch. Collapse the
RegisterSyncField(FieldInfo) overload into a one-line delegate through
(field.ReflectedType, field.Name), preserving its prior behavior.
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.

1 participant