diff --git a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
index ca7742fda..860fe60d9 100644
--- a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
+++ b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
@@ -16,32 +16,24 @@ namespace Exiled.API.Extensions
using System.Text;
using AdminToys;
-
using AudioPooling;
-
using Cassie;
-
using CustomPlayerEffects;
-
using Exiled.API.Enums;
using Exiled.API.Features.Items;
using Exiled.API.Features.Items.Keycards;
using Exiled.API.Features.Pickups.Keycards;
-
using Features;
using Features.Pools;
-
+ using HarmonyLib;
using InventorySystem;
using InventorySystem.Items;
using InventorySystem.Items.Autosync;
using InventorySystem.Items.Firearms;
using InventorySystem.Items.Firearms.Modules;
using InventorySystem.Items.Keycards;
-
using MEC;
-
using Mirror;
-
using PlayerRoles;
using PlayerRoles.Blood;
using PlayerRoles.FirstPersonControl;
@@ -49,13 +41,10 @@ namespace Exiled.API.Extensions
using PlayerRoles.PlayableScps.Scp1507;
using PlayerRoles.Spectating;
using PlayerRoles.Voice;
-
using RelativePositioning;
-
using Respawning;
-
+ using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
-
using Utils.Networking;
///
@@ -107,8 +96,8 @@ public static ReadOnlyDictionary SyncVarDirtyBits
if (SyncVarDirtyBitsValue.Count == 0)
{
foreach (PropertyInfo property in typeof(ServerConsole).Assembly.GetTypes()
- .SelectMany(x => x.GetProperties())
- .Where(m => m.Name.StartsWith("Network")))
+ .SelectMany(x => x.GetProperties())
+ .Where(m => m.Name.StartsWith("Network")))
{
MethodInfo setMethod = property.GetSetMethod();
@@ -141,8 +130,8 @@ public static ReadOnlyDictionary RpcFullNames
if (RpcFullNamesValue.Count == 0)
{
foreach (MethodInfo method in typeof(ServerConsole).Assembly.GetTypes()
- .SelectMany(x => x.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
- .Where(m => m.GetCustomAttributes(typeof(ClientRpcAttribute), false).Length > 0 || m.GetCustomAttributes(typeof(TargetRpcAttribute), false).Length > 0))
+ .SelectMany(x => x.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
+ .Where(m => m.GetCustomAttributes(typeof(ClientRpcAttribute), false).Length > 0 || m.GetCustomAttributes(typeof(TargetRpcAttribute), false).Length > 0))
{
MethodBody methodBody = method.GetMethodBody();
@@ -181,6 +170,12 @@ public static ReadOnlyDictionary RpcFullNames
/// Target to play sound to.
public static void PlayBeepSound(this Player player) => SendFakeTargetRpc(player, ReferenceHub._hostHub.networkIdentity, typeof(AmbientSoundPlayer), nameof(AmbientSoundPlayer.RpcPlaySound), 7);
+ ///
+ /// Plays a beep sound that only the target can hear.
+ ///
+ /// Target to play sound to.
+ public static void PlayBeepSound(this NetworkConnection connection) => connection.SendFakeTargetRpc(ReferenceHub._hostHub.networkIdentity, typeof(AmbientSoundPlayer), nameof(AmbientSoundPlayer.RpcPlaySound), 7);
+
///
/// Set on the player that only the can see.
///
@@ -194,7 +189,7 @@ public static ReadOnlyDictionary RpcFullNames
///
/// Target to play.
/// Position to play on.
- /// Weapon' sound to play.
+ /// Weapon's sound to play.
/// Sound's volume to set.
/// GunAudioMessage's audioClipId to set (default = 0).
[Obsolete("This method is not working. Use PlayGunSound(Player, Vector3, FirearmType, float, int, bool) overload instead.")]
@@ -306,7 +301,14 @@ public static void PlaceBlood(this Player player, Vector3 position, Vector3 orig
///
/// Only this player can see Display Text.
/// Text displayed to the player.
- public static void SetIntercomDisplayTextForTargetOnly(this Player target, string text) => target.SendFakeSyncVar(IntercomDisplay._singleton.netIdentity, typeof(IntercomDisplay), nameof(IntercomDisplay.Network_overrideText), text);
+ public static void SetIntercomDisplayTextForTargetOnly(this Player target, string text) => target.Connection.SetIntercomDisplayTextForTargetOnly(text);
+
+ ///
+ /// Sets that only the connection can see.
+ ///
+ /// Only this connection can see Display Text.
+ /// Text displayed to the player.
+ public static void SetIntercomDisplayTextForTargetOnly(this NetworkConnection connection, string text) => connection.SendFakeSyncVar(IntercomDisplay._singleton.netIdentity, typeof(IntercomDisplay), nameof(IntercomDisplay.Network_overrideText), text);
///
/// Resync .
@@ -321,6 +323,30 @@ public static void PlaceBlood(this Player player, Vector3 position, Vector3 orig
/// Color to set.
public static void SetRoomColorForTargetOnly(this Room room, Player target, Color color) => target.SendFakeSyncVar(room.RoomLightControllerNetIdentity, typeof(RoomLightController), nameof(RoomLightController.NetworkOverrideColor), color);
+ ///
+ /// Sets of a that only the can see.
+ ///
+ /// Room to modify.
+ /// Only this connection can see room color.
+ /// Color to set.
+ public static void SetRoomColorForTargetOnly(this Room room, NetworkConnection connection, Color color) => connection.SendFakeSyncVar(room.RoomLightControllerNetIdentity, typeof(RoomLightController), nameof(RoomLightController.NetworkOverrideColor), color);
+
+ ///
+ /// Sets of a that only the can see.
+ ///
+ /// Only this player can see room color.
+ /// Room to modify.
+ /// Color to set.
+ public static void SetRoomColorForTargetOnly(this Player player, Room targetRoom, Color color) => player.Connection.SetRoomColorForTargetOnly(targetRoom, color);
+
+ ///
+ /// Sets of a that only the can see.
+ ///
+ /// Only this connection can see room color.
+ /// Room to modify.
+ /// Color to set.
+ public static void SetRoomColorForTargetOnly(this NetworkConnection connection, Room targetRoom, Color color) => connection.SendFakeSyncVar(targetRoom.RoomLightControllerNetIdentity, typeof(RoomLightController), nameof(RoomLightController.NetworkOverrideColor), color);
+
///
/// Sets the lights of a to be either on or off, visible only to the player.
///
@@ -335,10 +361,7 @@ public static void PlaceBlood(this Player player, Vector3 position, Vector3 orig
/// Only this player can see the name changed.
/// Player that will desync the CustomName.
/// Nickname to set.
- public static void SetName(this Player target, Player player, string name)
- {
- target.SendFakeSyncVar(player.NetworkIdentity, typeof(NicknameSync), nameof(NicknameSync.Network_displayName), name);
- }
+ public static void SetName(this Player target, Player player, string name) => target.SendFakeSyncVar(player.NetworkIdentity, typeof(NicknameSync), nameof(NicknameSync.Network_displayName), name);
///
/// Change character model for appearance.
@@ -556,14 +579,21 @@ public static void MessageTranslated(this Player player, string words, string tr
///
/// The player to send the Scene.
/// The new Scene the client will load.
- public static void SendFakeSceneLoading(this Player player, ScenesType newSceneName)
+ public static void SendFakeSceneLoading(this Player player, ScenesType newSceneName) => player.Connection.SendFakeSceneLoading(newSceneName);
+
+ ///
+ /// Sends to the player a Fake Change Scene.
+ ///
+ /// The to send the Scene.
+ /// The new Scene the client will load.
+ public static void SendFakeSceneLoading(this NetworkConnection connection, ScenesType newSceneName)
{
SceneMessage message = new()
{
sceneName = newSceneName.ToString(),
};
- player.Connection.Send(message);
+ connection.Send(message);
}
///
@@ -587,6 +617,13 @@ public static void ChangeSceneToAllClients(ScenesType scene)
/// The to spawn.
public static void SpawnNetworkIdentity(this Player player, NetworkIdentity identity) => SendSpawnMessageMethodInfo?.Invoke(null, new object[] { identity, player.Connection });
+ ///
+ /// Sends a spawn message for the specified to the given .
+ ///
+ /// The which should receive the spawn message.
+ /// The to spawn.
+ public static void SpawnNetworkIdentity(this NetworkConnection connection, NetworkIdentity identity) => SendSpawnMessageMethodInfo?.Invoke(null, new object[] { identity, connection });
+
///
/// Sends a destroy message for the specified to the given .
///
@@ -594,18 +631,32 @@ public static void ChangeSceneToAllClients(ScenesType scene)
/// The to destroy.
public static void DestroyNetworkIdentity(this Player player, NetworkIdentity identity) => player.DestroyNetworkId(identity.netId);
+ ///
+ /// Sends a destroy message for the specified to the given .
+ ///
+ /// The which should receive the destroy message.
+ /// The to destroy.
+ public static void DestroyNetworkIdentity(this NetworkConnection connection, NetworkIdentity identity) => connection.DestroyNetworkId(identity.netId);
+
///
/// Sends a destroy message for the specified network ID to the given .
///
/// The player who should receive the destroy message.
/// The network ID of the object to destroy.
- public static void DestroyNetworkId(this Player player, uint netId) => player.Connection.Send(new ObjectDestroyMessage() { netId = netId });
+ public static void DestroyNetworkId(this Player player, uint netId) => player.Connection.DestroyNetworkId(netId);
+
+ ///
+ /// Sends a destroy message for the specified network ID to the given .
+ ///
+ /// The which should receive the destroy message.
+ /// The network ID of the object to destroy.
+ public static void DestroyNetworkId(this NetworkConnection connection, uint netId) => connection.Send(new ObjectDestroyMessage() { netId = netId });
///
/// Respawns the specified for the given .
/// This sends a destroy message followed by a spawn message to the player's client.
///
- /// The player who should receive the respawn messages.
+ /// The who should receive the respawn messages.
/// The to respawn.
public static void RespawnNetworkIdentity(this Player player, NetworkIdentity identity)
{
@@ -613,6 +664,18 @@ public static void RespawnNetworkIdentity(this Player player, NetworkIdentity id
player.SpawnNetworkIdentity(identity);
}
+ ///
+ /// Respawns the specified for the given .
+ /// This sends a destroy message followed by a spawn message to connection.
+ ///
+ /// The who should receive the respawn messages.
+ /// The to respawn.
+ public static void RespawnNetworkIdentity(this NetworkConnection connection, NetworkIdentity identity)
+ {
+ connection.DestroyNetworkIdentity(identity);
+ connection.SpawnNetworkIdentity(identity);
+ }
+
///
/// Moves object for the player.
///
@@ -670,14 +733,42 @@ public static void EditNetworkObject(this Player player, NetworkIdentity identit
resetAction?.Invoke(identity);
}
+ ///
+ /// Edit 's parameter and sync.
+ ///
+ /// Target to send.
+ /// Target object.
+ /// Edit function.
+ /// Reback function for reset object to original state.
+ public static void EditNetworkObject(this NetworkConnection connection, NetworkIdentity identity, Action customAction, Action resetAction)
+ {
+ if (identity == null)
+ return;
+
+ customAction?.Invoke(identity);
+
+ connection.RespawnNetworkIdentity(identity);
+
+ resetAction?.Invoke(identity);
+ }
+
///
/// Sends a spawn message for the specified to a targeted collection of players.
///
/// The to serialize and spawn.
/// The collection of who will receive the spawn message.
- public static void SendSpawnMessageForPlayers(this NetworkIdentity identity, IEnumerable players)
+ public static void SendSpawnMessageForPlayers(this NetworkIdentity identity, IEnumerable players) => identity.SendSpawnMessageForConnections(players.Where(p => p.IsConnected).Select(p => p.Connection));
+
+ ///
+ /// Sends a spawn message for the specified to a targeted collection of s."/>.
+ ///
+ /// The to serialize and spawn.
+ /// The collection of who will receive the spawn message.
+ public static void SendSpawnMessageForConnections(this NetworkIdentity identity, IEnumerable connections)
{
- if (identity == null || identity.netId == 0 || !players.Any())
+ IEnumerable networkConnections = connections as NetworkConnection[] ?? connections.ToArray();
+
+ if (identity == null || identity.netId == 0 || !networkConnections.Any())
return;
using NetworkWriterPooled ownerWriter = NetworkWriterPool.Get();
@@ -705,25 +796,25 @@ public static void SendSpawnMessageForPlayers(this NetworkIdentity identity, IEn
NetworkMessages.Pack(spawnMessage, prepackedObserverWriter);
ArraySegment segment = prepackedObserverWriter.ToArraySegment();
- foreach (Player player in players)
+ foreach (NetworkConnection connection in networkConnections)
{
- if (!player.IsConnected)
+ if (connection == null)
continue;
- bool isOwner = identity.connectionToClient == player.Connection;
+ bool isOwner = identity.connectionToClient == connection;
if (!isOwner)
{
- player.Connection.Send(segment);
+ connection.Send(segment);
continue;
}
SpawnMessage ownerMessage = spawnMessage;
ownerMessage.isOwner = true;
- ownerMessage.isLocalPlayer = player.NetworkIdentity == identity;
+ ownerMessage.isLocalPlayer = connection.identity == identity;
ownerMessage.payload = ownerPayload;
- player.Connection.Send(ownerMessage);
+ connection.Send(ownerMessage);
}
}
@@ -824,13 +915,29 @@ public static void EditNetworkObject(this NetworkIdentity identity, ActionValue of send to target.
public static void SendFakeSyncVar(this Player target, NetworkIdentity behaviorOwner, Type targetType, string propertyName, T value)
{
- if (!target.IsConnected || behaviorOwner == null)
+ if (!target.IsConnected)
+ return;
+ target.Connection.SendFakeSyncVar(behaviorOwner, targetType, propertyName, value);
+ }
+
+ ///
+ /// Send fake values to client's .
+ ///
+ /// Target SyncVar property type.
+ /// to send.
+ /// of object that owns .
+ /// 's type.
+ /// Property name starting with Network.
+ /// Value of send to connection.
+ public static void SendFakeSyncVar(this NetworkConnection connection, NetworkIdentity behaviorOwner, Type targetType, string propertyName, T value)
+ {
+ if (connection == null || behaviorOwner == null)
return;
NetworkWriterPooled writer = NetworkWriterPool.Get();
NetworkWriterPooled writer2 = NetworkWriterPool.Get();
MakeCustomSyncWriter(behaviorOwner, targetType, null, CustomSyncVarGenerator, writer, writer2);
- target.Connection.Send(new EntityStateMessage
+ connection.Send(new EntityStateMessage
{
netId = behaviorOwner.netId,
payload = writer.ToArraySegment(),
@@ -838,6 +945,7 @@ public static void SendFakeSyncVar(this Player target, NetworkIdentity behavi
NetworkWriterPool.Return(writer);
NetworkWriterPool.Return(writer2);
+
void CustomSyncVarGenerator(NetworkWriter targetWriter)
{
bool isAdminToy = targetType.BaseType == typeof(AdminToyBase);
@@ -888,9 +996,25 @@ public static void ResyncSyncVar(NetworkIdentity behaviorOwner, Type targetType,
/// 's type.
/// Property name starting with Rpc.
/// Values of send to target.
- public static void SendFakeTargetRpc(Player target, NetworkIdentity behaviorOwner, Type targetType, string rpcName, params object[] values)
+ public static void SendFakeTargetRpc(this Player target, NetworkIdentity behaviorOwner, Type targetType, string rpcName, params object[] values)
{
- if (!target.IsConnected || behaviorOwner == null)
+ if (!target.IsConnected)
+ return;
+
+ target.Connection.SendFakeTargetRpc(behaviorOwner, targetType, rpcName, values);
+ }
+
+ ///
+ /// Send fake values to client's .
+ ///
+ /// Connection to send.
+ /// of object that owns .
+ /// 's type.
+ /// Property name starting with Rpc.
+ /// Values of send to connection.
+ public static void SendFakeTargetRpc(this NetworkConnection connection, NetworkIdentity behaviorOwner, Type targetType, string rpcName, params object[] values)
+ {
+ if (connection == null || behaviorOwner == null)
return;
NetworkWriterPooled writer = NetworkWriterPool.Get();
@@ -906,7 +1030,7 @@ public static void SendFakeTargetRpc(Player target, NetworkIdentity behaviorOwne
payload = writer.ToArraySegment(),
};
- target.Connection.Send(msg);
+ connection.Send(msg);
NetworkWriterPool.Return(writer);
}
@@ -929,20 +1053,46 @@ public static void SendFakeTargetRpc(Player target, NetworkIdentity behaviorOwne
/// });
///
///
- public static void SendFakeSyncObject(Player target, NetworkIdentity behaviorOwner, Type targetType, Action customAction)
+ public static void SendFakeSyncObject(this Player target, NetworkIdentity behaviorOwner, Type targetType, Action customAction)
{
if (!target.IsConnected)
return;
+ target.Connection.SendFakeSyncObject(behaviorOwner, targetType, customAction);
+ }
+
+ ///
+ /// Send fake values to client's .
+ ///
+ /// Target to send.
+ /// of object that owns .
+ /// 's type.
+ /// Custom writing action.
+ ///
+ /// EffectOnlySCP207.
+ ///
+ /// MirrorExtensions.SendFakeSyncObject(player, player.NetworkIdentity, typeof(PlayerEffectsController), (writer) => {
+ /// writer.WriteULong(1ul); // DirtyObjectsBit
+ /// writer.WriteUInt(1); // DirtyIndexCount
+ /// writer.WriteByte((byte)SyncList<byte>.Operation.OP_SET); // Operations
+ /// writer.WriteUInt(17); // EditIndex
+ /// });
+ ///
+ ///
+ public static void SendFakeSyncObject(this NetworkConnection connection, NetworkIdentity behaviorOwner, Type targetType, Action customAction)
+ {
+ if (connection == null || behaviorOwner == null)
+ return;
+
NetworkWriterPooled writer = NetworkWriterPool.Get();
NetworkWriterPooled writer2 = NetworkWriterPool.Get();
MakeCustomSyncWriter(behaviorOwner, targetType, customAction, null, writer, writer2);
- target.Connection.Send(new EntityStateMessage() { netId = behaviorOwner.netId, payload = writer.ToArraySegment() });
+ connection.Send(new EntityStateMessage() { netId = behaviorOwner.netId, payload = writer.ToArraySegment() });
NetworkWriterPool.Return(writer);
NetworkWriterPool.Return(writer2);
}
- // Get components index in identity.(private)
+ // Get components' index in identity.(private)
private static int GetComponentIndex(NetworkIdentity identity, Type type)
{
return Array.FindIndex(identity.NetworkBehaviours, (x) => x.GetType() == type);
@@ -996,4 +1146,4 @@ private static void MakeCustomSyncWriter(NetworkIdentity behaviorOwner, Type tar
observer.WriteBytes(owner.ToArraySegment().Array, position, owner.Position - position);
}
}
-}
+}
\ No newline at end of file