Skip to content

[Client] check skipped IsClientInitialized on networked objects - #1084

Open
jimdroberts wants to merge 1 commit into
FirstGearGames:mainfrom
jimdroberts:fix/client-attribute-check
Open

jimdroberts wants to merge 1 commit into
FirstGearGames:mainfrom
jimdroberts:fix/client-attribute-check

Conversation

@jimdroberts

Copy link
Copy Markdown
Contributor

Problem

Since 4.6.16, a [Client] method on a NetworkBehaviour runs whenever its object is networked, whether or not the
client is initialized for that object. This happens on a dedicated server, on a server-only instance, and before
client initialization or after deinitialization. [Client] guards server-side code paths from running client
logic, so any such method now executes on servers. The warning is never logged, because the check never fails.

[Server] is not affected.

Cause

4.6.16 changed CreateIsNetworkedCheck to take a branch opcode and target, as the fix for #939 ("Server/Client
attribute not blocking execution when IsNetworked was set to false"). The two callers were given opposite
branches:

  • CreateIsServerCheck (CodeGenerating/Helpers/NetworkBehaviourHelper.cs:396):
    CreateIsNetworkedCheck(methodDef, OpCodes.Brfalse, conditionFailedInst), meaning "not networked means fail,
    otherwise check IsServerInitialized". This is correct.
  • CreateIsClientCheck (NetworkBehaviourHelper.cs:340):
    CreateIsNetworkedCheck(methodDef, OpCodes.Brtrue, conditionPassedInst), meaning "networked means pass
    without checking IsClientInitialized". Otherwise it falls through to IsClientInitialized, which is false for
    a non-networked object.

So the #939 case (not networked) is blocked, but only by accident, and the main case (networked object, client
not initialized) is now allowed. In 4.6.12 the same helper emitted brfalse endIf, which skipped the
initialization check for non-networked objects. #939 inverted that correctly for [Server] only.

checkIsNetworked is true only for the instance path (!useStatic) from QOLAttributeProcessor.cs:141/149.
The RPC callers pass false and are unaffected.

Fix

Emit the same guard as CreateIsServerCheck: brfalse to conditionFailedInst. The emitted check becomes
if (!GetIsNetworked() || !IsClientInitialized) { log; return default; }.

Evidence

Verified by reading the emitted instruction sequences, plus a small interpreter that executes them
(useStatic = false, checkIsNetworked = true):

                 net=0 init=0    net=0 init=1    net=1 init=0    net=1 init=1
Client 4.6.12            runs            runs         blocked            runs
Client main           blocked            runs            runs            runs
Client this PR        blocked         blocked         blocked            runs
Server 4.6.12            runs            runs         blocked            runs
Server main           blocked         blocked         blocked            runs

net = GetIsNetworked(), init = IsClientInitialized / IsServerInitialized. With this change [Client]
matches [Server] on main. The column that matters is net=1 init=0: a networked object on a server, or not
yet initialized on the client. Main runs the method there. (net=0 init=1 is probably unreachable, and is shown
for completeness.)

Compiled with Unity 6000.4's Roslyn with no new errors or warnings.

Behavior change

[Client] methods on networked objects no longer run when the client is not initialized for that object. They
return default and log at the attribute's Logging level, as documented ("Prevents this method from running if
client is not active") and as 4.6.15 and earlier behaved. Projects that unknowingly relied on these methods
running server-side since 4.6.16 will now see that warning.

The IsNetworked guard in CreateIsClientCheck branched to the passed
label when the object was networked, so the IsClientInitialized check
never ran for networked objects. Branch to the failed label when not
networked, matching CreateIsServerCheck.
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