-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathClientRpcSender.cs
More file actions
161 lines (134 loc) · 5.93 KB
/
Copy pathClientRpcSender.cs
File metadata and controls
161 lines (134 loc) · 5.93 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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Cysharp.Threading.Tasks;
using Mirage.Logging;
using Mirage.Serialization;
using UnityEngine;
namespace Mirage.RemoteCalls
{
public static class ClientRpcSender
{
private static readonly ILogger logger = LogFactory.GetLogger(typeof(ClientRpcSender));
public static void Send(NetworkBehaviour behaviour, int relativeIndex, NetworkWriter writer, Channel channelId, bool excludeOwner)
{
var index = behaviour.Identity.RemoteCallCollection.GetIndexOffset(behaviour) + relativeIndex;
Validate(behaviour, index);
var message = CreateMessage(behaviour, index, writer);
// The public facing parameter is excludeOwner in [ClientRpc]
// so we negate it here to logically align with SendToReady.
var includeOwner = !excludeOwner;
behaviour.Identity.SendToRemoteObservers(message, includeOwner, channelId);
}
public static void SendTarget(NetworkBehaviour behaviour, int relativeIndex, NetworkWriter writer, Channel channelId, INetworkPlayer player)
{
var index = behaviour.Identity.RemoteCallCollection.GetIndexOffset(behaviour) + relativeIndex;
Validate(behaviour, index);
var message = CreateMessage(behaviour, index, writer);
player = GetTarget(behaviour, player);
player.Send(message, channelId);
}
public static UniTask<T> SendTargetWithReturn<T>(NetworkBehaviour behaviour, int relativeIndex, NetworkWriter writer, INetworkPlayer player)
{
var index = behaviour.Identity.RemoteCallCollection.GetIndexOffset(behaviour) + relativeIndex;
Validate(behaviour, index);
(var task, var id) = behaviour.ServerObjectManager._rpcHandler.CreateReplyTask<T>();
var message = new RpcWithReplyMessage
{
NetId = behaviour.NetId,
FunctionIndex = index,
ReplyId = id,
Payload = writer.ToArraySegment()
};
player = GetTarget(behaviour, player);
// reply rpcs are always reliable
player.Send(message, Channel.Reliable);
return task;
}
private static INetworkPlayer GetTarget(NetworkBehaviour behaviour, INetworkPlayer player)
{
// player parameter is optional. use owner if null
if (player == null)
player = behaviour.Owner;
// if still null throw to give useful error
if (player == null)
throw new InvalidOperationException("Player target was null for Rpc");
return player;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static RpcMessage CreateMessage(NetworkBehaviour behaviour, int index, NetworkWriter writer)
{
var message = new RpcMessage
{
NetId = behaviour.NetId,
FunctionIndex = index,
Payload = writer.ToArraySegment()
};
return message;
}
private static void Validate(NetworkBehaviour behaviour, int index)
{
var server = behaviour.Server;
if (server == null || !server.Active)
{
var rpc = behaviour.Identity.RemoteCallCollection.GetRelative(behaviour, index);
throw new InvalidOperationException($"RPC Function {rpc} called when server is not active.");
}
}
/// <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, bool excludeOwner)
{
// 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, excludeOwner);
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>
/// </summary>
/// <param name="player"></param>
/// <returns></returns>
public static bool IsLocalPlayerObserver(NetworkBehaviour behaviour, bool excludeOwner)
{
var local = behaviour.Server.LocalPlayer;
// if local player is the owner, skip
if (excludeOwner && behaviour.Owner == local)
return false;
return behaviour.Identity.observers.Contains(local);
}
/// <summary>
/// Checks if host player is the target player
/// <para>Weaver uses this to check if RPC should be invoked locally</para>
/// </summary>
/// <param name="player"></param>
/// <returns></returns>
public static bool IsLocalPlayerTarget(NetworkBehaviour behaviour, INetworkPlayer target)
{
var local = behaviour.Server.LocalPlayer;
return local == target;
}
}
}