-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathRpcHandler.cs
More file actions
137 lines (118 loc) · 5.08 KB
/
Copy pathRpcHandler.cs
File metadata and controls
137 lines (118 loc) · 5.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Cysharp.Threading.Tasks;
using Mirage.Logging;
using Mirage.Serialization;
using UnityEngine;
namespace Mirage.RemoteCalls
{
internal class RpcHandler
{
private static readonly ILogger logger = LogFactory.GetLogger<RpcHandler>();
private readonly Dictionary<int, Action<NetworkReader>> _callbacks = new Dictionary<int, Action<NetworkReader>>();
private int _nextReplyId;
/// <summary>
/// Object locator required for deserializing the reply
/// </summary>
private readonly IObjectLocator _objectLocator;
/// <summary>
/// Invoke type for validation
/// </summary>
private readonly RpcInvokeType _invokeType;
public RpcHandler(MessageHandler messageHandler, IObjectLocator objectLocator, RpcInvokeType invokeType)
{
messageHandler.RegisterHandler<RpcReply>(OnReply);
messageHandler.RegisterHandler<RpcMessage>(OnRpcMessage);
messageHandler.RegisterHandler<RpcWithReplyMessage>(OnRpcWithReplyMessage);
_objectLocator = objectLocator;
_invokeType = invokeType;
}
/// <summary>
/// Handle ServerRpc from specific player, this could be one of multiple players on a single client
/// </summary>
/// <param name="player"></param>
/// <param name="msg"></param>
private void OnRpcWithReplyMessage(INetworkPlayer player, RpcWithReplyMessage msg)
{
HandleRpc(player, msg.NetId, msg.FunctionIndex, msg.Payload, msg.ReplyId);
}
internal void OnRpcMessage(INetworkPlayer player, RpcMessage msg)
{
HandleRpc(player, msg.NetId, msg.FunctionIndex, msg.Payload, default);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void HandleRpc(INetworkPlayer player, uint netId, int functionIndex, ArraySegment<byte> payload, int replyId)
{
if (!_objectLocator.TryGetIdentity(netId, out var identity))
{
if (logger.WarnEnabled()) logger.LogWarning($"Spawned object not found when handling ServerRpc message [netId={netId}]");
return;
}
var remoteCall = identity.RemoteCallCollection.GetAbsolute(functionIndex);
if (remoteCall.InvokeType != _invokeType)
ThrowInvalidRpc(remoteCall);
// for ServerRpc we need to check if the player has authority
if (_invokeType == RpcInvokeType.ServerRpc)
{
var ok = CheckAuthority(remoteCall, identity, player);
if (!ok)
return;
}
if (logger.LogEnabled()) logger.Log($"Rpc for {identity} from {player}");
using (var reader = NetworkReaderPool.GetReader(payload, _objectLocator))
{
remoteCall.Invoke(reader, player, replyId);
}
}
private bool CheckAuthority(RemoteCall remoteCall, NetworkIdentity identity, INetworkPlayer player)
{
// not required, return ok
if (!remoteCall.RequireAuthority)
return true;
// is owner, return ok
if (identity.Owner == player)
return true;
// not ok
if (logger.WarnEnabled()) logger.LogWarning($"ServerRpc for object without authority {identity}");
return false;
}
private void ThrowInvalidRpc(RemoteCall remoteCall)
{
throw new MethodInvocationException($"Invalid Rpc for index {remoteCall.Name}. Expected {_invokeType} but was {remoteCall.InvokeType}");
}
/// <summary>
/// Creates a task that waits for a reply from the server
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>the task that will be completed when the result is in, and the id to use in the request</returns>
public (UniTask<T> task, int replyId) CreateReplyTask<T>()
{
var newReplyId = _nextReplyId++;
var completionSource = AutoResetUniTaskCompletionSource<T>.Create();
void Callback(NetworkReader reader)
{
var result = reader.Read<T>();
completionSource.TrySetResult(result);
}
_callbacks.Add(newReplyId, Callback);
return (completionSource.Task, newReplyId);
}
private void OnReply(INetworkPlayer player, RpcReply reply)
{
// find the callback that was waiting for this and invoke it.
if (_callbacks.TryGetValue(reply.ReplyId, out var action))
{
_callbacks.Remove(_nextReplyId);
using (var reader = NetworkReaderPool.GetReader(reply.Payload, _objectLocator))
{
action.Invoke(reader);
}
}
else
{
throw new MethodAccessException("Received reply but no handler was registered");
}
}
}
}