Skip to content

Commit

Permalink
feat: adding allowServerToCall option to ServerRpc
Browse files Browse the repository at this point in the history
Adding option to allow ServerRpc to be called on server and bypass authority check
  • Loading branch information
James-Frowen committed Jan 21, 2025
1 parent cc80a33 commit 01721fe
Show file tree
Hide file tree
Showing 5 changed files with 405 additions and 12 deletions.
5 changes: 5 additions & 0 deletions Assets/Mirage/Runtime/CustomAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ public class ServerRpcAttribute : Attribute
{
public Channel channel = Channel.Reliable;
public bool requireAuthority = true;

/// <summary>
/// Allows the server to invoke the method locally. Note: this will bypass any authority checks on host.
/// </summary>
public bool allowServerToCall = false;
}

/// <summary>
Expand Down
10 changes: 5 additions & 5 deletions Assets/Mirage/Runtime/RemoteCalls/ServerRpcSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,23 +77,23 @@ private static void Validate(NetworkBehaviour behaviour, int index, bool require
/// <param name="target"></param>
/// <param name="player">player used for RpcTarget.Player</param>
/// <returns></returns>
public static bool ShouldInvokeLocally(NetworkBehaviour behaviour, bool requireAuthority)
public static bool ShouldInvokeLocally(NetworkBehaviour behaviour, bool requireAuthority, bool allowServerToCall)
{
// if allowServerToCall, then just check server because we ignore all other checks
if (behaviour.IsServer && allowServerToCall)
return true;

// not client? error
if (!behaviour.IsClient)
{
throw new InvalidOperationException("Server RPC can only be called when client is active");
}

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

// check if auth is required and that host has auth over the object
if (requireAuthority && !behaviour.HasAuthority)
{
throw new InvalidOperationException($"Trying to send ServerRpc for object without authority.");
}

return true;
}
Expand Down
14 changes: 8 additions & 6 deletions Assets/Mirage/Weaver/Processors/ServerRpcProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ private MethodDefinition GenerateStub(MethodDefinition md, CustomAttribute serve
{
var channel = serverRpcAttr.GetField(nameof(ServerRpcAttribute.channel), 0);
var requireAuthority = serverRpcAttr.GetField(nameof(ServerRpcAttribute.requireAuthority), true);
var allowServerToCall = serverRpcAttr.GetField(nameof(ServerRpcAttribute.allowServerToCall), false);

var cmd = SubstituteMethod(md);

Expand All @@ -61,7 +62,7 @@ private MethodDefinition GenerateStub(MethodDefinition md, CustomAttribute serve
// call the body
// return;
// }
CallBody(worker, cmd, requireAuthority);
CallBody(worker, cmd, requireAuthority, allowServerToCall);

// NetworkWriter writer = NetworkWriterPool.GetWriter()
var writer = md.AddLocal<PooledNetworkWriter>();
Expand Down Expand Up @@ -91,13 +92,14 @@ private MethodDefinition GenerateStub(MethodDefinition md, CustomAttribute serve
return cmd;
}

public void InvokeLocally(ILProcessor worker, bool requiredAuthority, Action body)
public void InvokeLocally(ILProcessor worker, bool requireAuthority, bool allowServerToCall, Action body)
{
// if (IsServer) {
var endif = worker.Create(OpCodes.Nop);
worker.Append(worker.Create(OpCodes.Ldarg_0));
worker.Append(worker.Create(requiredAuthority.OpCode_Ldc()));
worker.Append(worker.Create(OpCodes.Call, () => ServerRpcSender.ShouldInvokeLocally(default, default)));
worker.Append(worker.Create(requireAuthority.OpCode_Ldc()));
worker.Append(worker.Create(allowServerToCall.OpCode_Ldc()));
worker.Append(worker.Create(OpCodes.Call, () => ServerRpcSender.ShouldInvokeLocally(default, default, default)));
worker.Append(worker.Create(OpCodes.Brfalse, endif));

body();
Expand All @@ -107,9 +109,9 @@ public void InvokeLocally(ILProcessor worker, bool requiredAuthority, Action bod

}

private void CallBody(ILProcessor worker, MethodDefinition rpc, bool requiredAuthority)
private void CallBody(ILProcessor worker, MethodDefinition rpc, bool requireAuthority, bool allowServerToCall)
{
InvokeLocally(worker, requiredAuthority, () =>
InvokeLocally(worker, requireAuthority, allowServerToCall, () =>
{
InvokeBody(worker, rpc);
worker.Append(worker.Create(OpCodes.Ret));
Expand Down
Loading

0 comments on commit 01721fe

Please sign in to comment.