Skip to content

Commit

Permalink
refactor: moving the invoke check to ClientRpcSender
Browse files Browse the repository at this point in the history
BREAKING CHANGE: ClientRpc are now only invoked locally if in host mode
  • Loading branch information
James-Frowen committed Aug 1, 2022
1 parent 3b7a5ae commit 3465258
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 49 deletions.
55 changes: 39 additions & 16 deletions Assets/Mirage/Runtime/RemoteCalls/ClientRpcSender.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Mirage.Logging;
using Mirage.Serialization;
Expand Down Expand Up @@ -72,6 +73,40 @@ private static void Validate(NetworkBehaviour behaviour, RemoteCall rpc)
}
}

/// <summary>
/// Used by weaver to check if ClientRPC should be invoked locally in host mode
/// </summary>
/// <param name="behaviour"></param>
/// <param name="target"></param>
/// <param name="player">player used for RpcTarget.Player</param>
/// <returns></returns>
public static bool ShouldInvokeLocally(NetworkBehaviour behaviour, RpcTarget target, INetworkPlayer player)
{
// not server? error
if (!behaviour.IsServer)
{
throw new InvalidOperationException("Client RPC can only be called when server is active");
}

// not host? never invoke locally
if (!behaviour.IsClient)
return false;

// check if host player should receive
switch (target)
{
case RpcTarget.Observers:
return IsLocalPlayerObserver(behaviour);
case RpcTarget.Owner:
return IsLocalPlayerTarget(behaviour, behaviour.Owner);
case RpcTarget.Player:
return IsLocalPlayerTarget(behaviour, player);
}

// should never get here
throw new InvalidEnumArgumentException();
}

/// <summary>
/// Checks if host player can see the object
/// <para>Weaver uses this to check if RPC should be invoked locally</para>
Expand All @@ -80,14 +115,8 @@ private static void Validate(NetworkBehaviour behaviour, RemoteCall rpc)
/// <returns></returns>
public static bool IsLocalPlayerObserver(NetworkBehaviour behaviour)
{
if (behaviour.Server != null)
{
var local = behaviour.Server.LocalPlayer;
return behaviour.Identity.observers.Contains(local);
}

// todo should ClientRpc be called in client only mode
return true;
var local = behaviour.Server.LocalPlayer;
return behaviour.Identity.observers.Contains(local);
}

/// <summary>
Expand All @@ -98,14 +127,8 @@ public static bool IsLocalPlayerObserver(NetworkBehaviour behaviour)
/// <returns></returns>
public static bool IsLocalPlayerTarget(NetworkBehaviour behaviour, INetworkPlayer target)
{
if (behaviour.Server != null)
{
var local = behaviour.Server.LocalPlayer;
return local == target;
}

// todo should ClientRpc be called in client only mode
return true;
var local = behaviour.Server.LocalPlayer;
return local == target;
}
}
}
Expand Down
51 changes: 18 additions & 33 deletions Assets/Mirage/Weaver/Processors/ClientRpcProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,40 +192,25 @@ private static MethodReference GetSendMethod(MethodDefinition md, RpcTarget targ
: md.Module.ImportReference(() => ClientRpcSender.SendTarget(default, default, default, default, default));
}

private void IsClient(ILProcessor worker, RpcTarget target, Action body)
private void InvokeLocally(ILProcessor worker, RpcTarget target, Action body)
{
// if (IsLocalClient) {
var endif = worker.Create(OpCodes.Nop);
worker.Append(worker.Create(OpCodes.Ldarg_0));
worker.Append(worker.Create(OpCodes.Call, (NetworkBehaviour nb) => nb.IsClient));
worker.Append(worker.Create(OpCodes.Brfalse, endif));

switch (target)
{
case RpcTarget.Observers:
// if (this.IsLocalPlayerObserver())
worker.Append(worker.Create(OpCodes.Ldarg_0));
worker.Append(worker.Create(OpCodes.Call, () => ClientRpcSender.IsLocalPlayerObserver(default)));
worker.Append(worker.Create(OpCodes.Brfalse, endif));
break;
// behaviour
worker.Append(worker.Create(OpCodes.Ldarg_0));
// rpcTarget
worker.Append(worker.Create(OpCodes.Ldc_I4, (int)target));
// networkPlayer (or null)
if (target == RpcTarget.Player)
// target will be arg1
worker.Append(worker.Create(OpCodes.Ldarg_1));
else
worker.Append(worker.Create(OpCodes.Ldnull));

case RpcTarget.Owner:
// if (this.IsLocalPlayerTarget(this.Owner))
worker.Append(worker.Create(OpCodes.Ldarg_0));
worker.Append(worker.Create(OpCodes.Ldarg_0));
worker.Append(worker.Create(OpCodes.Call, (NetworkBehaviour nb) => nb.Owner));
worker.Append(worker.Create(OpCodes.Call, () => ClientRpcSender.IsLocalPlayerTarget(default, default)));
worker.Append(worker.Create(OpCodes.Brfalse, endif));
break;
case RpcTarget.Player:
// target will be arg1
// if (this.IsLocalPlayerTarget(target))
worker.Append(worker.Create(OpCodes.Ldarg_0));
worker.Append(worker.Create(OpCodes.Ldarg_1));
worker.Append(worker.Create(OpCodes.Call, () => ClientRpcSender.IsLocalPlayerTarget(default, default)));
worker.Append(worker.Create(OpCodes.Brfalse, endif));
break;
}
// call function
worker.Append(worker.Create(OpCodes.Call, () => ClientRpcSender.ShouldInvokeLocally(default, default, default)));
worker.Append(worker.Create(OpCodes.Brfalse, endif));

body();

Expand All @@ -236,10 +221,10 @@ private void IsClient(ILProcessor worker, RpcTarget target, Action body)

private void CallBody(ILProcessor worker, MethodDefinition rpc, RpcTarget target)
{
IsClient(worker, target, () =>
{
InvokeBody(worker, rpc);
});
InvokeLocally(worker, target, () =>
{
InvokeBody(worker, rpc);
});
}

private void InvokeBody(ILProcessor worker, MethodDefinition rpc)
Expand Down

0 comments on commit 3465258

Please sign in to comment.