Skip to content

Conversation

@andy013
Copy link
Contributor

@andy013 andy013 commented Dec 12, 2025

Fix incorrect Caller after nested Rpc calls.

Issue: Facepunch/sbox-issues#7755

Changes

  • Consume Caller in the outer Rpc so that any internal calls use Connection.Local
  • Restore correct Caller once the nested Rpc has finished
  • Renamed Calling to IsRemoteCall for clarity
  • Removed PreCall as it is no longer needed

Notes

There are 2 bugs in the current code.

  1. If you call an Rpc that is nested within another one, it will permanently change the Caller even once you exit back to the original Rpc scope.
  2. The Calling bool that should be true when the Rpc was invoked remotely is always false when accessed within an Rpc.

I found the name Calling to be quite confusing so I renamed it to IsRemoteCall which I think is clearer. I can change it back if you disagree. (Maybe I still need to keep Calling and mark it as obsolete for people who are currently using it? I doubt anyone will be using it since it's broken)

I'm not sure if this is the best way to solve this but it's the only way I could come up with after thinking about it for a while. If you have any better ideas let me know and I can try and rework it.

How It Works

  1. Remote RPC arrives → InvokeInstanceRpc/IncomingStaticRpcMsg sets PendingRpcCaller
  2. User's RPC method executes → __rpc_Wrapper/OnCallRpc consumes pending caller and creates WithCaller scope
  3. Any nested RPC calls → See null pending caller, correctly use Connection.Local
  4. When the nested Rpc is exited, we restore the previous values so the outer Rpc has the correct Caller again

Demo Project: rpc_caller_fix.zip

This is small project I made for testing the changes. To use it, just hit play, connect with a client and then look at the server logs.

The code is setup like this so that the broadcast is called in the middle of the Rpc:

	[Rpc.Host]
	public void ComponentRpc()
	{
		Log.Info( $"[Component] CallerId={Rpc.CallerId} isLocal={Rpc.Caller == Connection.Local} IsRemoteCall={Rpc.IsRemoteCall}" );
		ComponentBroadcast();
		Log.Info( $"[Component] CallerId={Rpc.CallerId} isLocal={Rpc.Caller == Connection.Local} IsRemoteCall={Rpc.IsRemoteCall}" );
	}

	[Rpc.Broadcast]
	public void ComponentBroadcast()
	{
		Log.Info( $"[Component Broadcast] CallerId={Rpc.CallerId} isLocal={Rpc.Caller == Connection.Local} IsRemoteCall={Rpc.IsRemoteCall}" );
	}

Current Behavior:

[Component] CallerId=6080a588-fd6e-4609-8d16-241481fe1967 isLocal=False Calling=False        // Calling is ALWAYS false
[Component Broadcast] CallerId=4210ea28-b854-453b-b2ef-bc1bf5eda6a7 isLocal=True Calling=False
[Component] CallerId=4210ea28-b854-453b-b2ef-bc1bf5eda6a7 isLocal=True Calling=False        // CallerId incorrectly stays set to nested Rpc value, even once we return to previous scope

[Static] CallerId=6080a588-fd6e-4609-8d16-241481fe1967 isLocal=False Calling=False
[Static Broadcast] CallerId=4210ea28-b854-453b-b2ef-bc1bf5eda6a7 isLocal=True Calling=False
[Static] CallerId=4210ea28-b854-453b-b2ef-bc1bf5eda6a7 isLocal=True Calling=False

[GOS] CallerId=6080a588-fd6e-4609-8d16-241481fe1967 isLocal=False Calling=False
[GOS Broadcast] CallerId=4210ea28-b854-453b-b2ef-bc1bf5eda6a7 isLocal=True Calling=False
[GOS] CallerId=4210ea28-b854-453b-b2ef-bc1bf5eda6a7 isLocal=True Calling=False

After Fix:

[Component] CallerId=6f6ef388-8a62-48d4-8447-8877e90884f2 isLocal=False IsRemoteCall=True        // IsRemoteCall is set correctly
[Component Broadcast] CallerId=d2c4917d-f7ca-4ee4-a7f9-35b713f53f7d isLocal=True IsRemoteCall=False
[Component] CallerId=6f6ef388-8a62-48d4-8447-8877e90884f2 isLocal=False IsRemoteCall=True        // CallerId correctly resets once inner Rpc has finished

[Static] CallerId=6f6ef388-8a62-48d4-8447-8877e90884f2 isLocal=False IsRemoteCall=True
[Static Broadcast] CallerId=d2c4917d-f7ca-4ee4-a7f9-35b713f53f7d isLocal=True IsRemoteCall=False
[Static] CallerId=6f6ef388-8a62-48d4-8447-8877e90884f2 isLocal=False IsRemoteCall=True

[GOS] CallerId=6f6ef388-8a62-48d4-8447-8877e90884f2 isLocal=False IsRemoteCall=True
[GOS Broadcast] CallerId=d2c4917d-f7ca-4ee4-a7f9-35b713f53f7d isLocal=True IsRemoteCall=False
[GOS] CallerId=6f6ef388-8a62-48d4-8447-8877e90884f2 isLocal=False IsRemoteCall=True

@andy013 andy013 marked this pull request as ready for review December 18, 2025 20:09
@garrynewman
Copy link
Member

One for @kurozael to look at

@kurozael kurozael self-assigned this Dec 22, 2025
@kurozael
Copy link
Contributor

I'll take a look but at a glance it seems like a complicated way to solve the problem

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.

3 participants