C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Spectate", "Wulf/lukespragg", "0.2.0", ResourceId = 1426)]
[Description("Allows only players with permission to spectate")]
class Spectate : RustPlugin
{
// Do NOT edit this file, instead edit Spectate.en.json in the oxide/lang directory,
// or create a new language file for another language using the 'en' file as a default.#region Initialization
const string permSpectate = "spectate.use";
void Init()
{
LoadDefaultMessages();
permission.RegisterPermission(permSpectate, this);
cmd.AddChatCommand("spectate", this, "ChatCommand");
}
#endregion
#region Localization
void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["NotAllowed"] = "Sorry, you're not allowed to use 'spectate'",
["NoTargets"] = "No valid spectate targets!",
["SpectateStart"] = "Started spectating: {0}",
["SpectateStop"] = "Stopped spectating: {0}"
}, this);
}
#endregion
#region Chat Command
readonly MethodInfo entitySnapshot = typeof(BasePlayer).GetMethod("SendEntitySnapshot", BindingFlags.Instance | BindingFlags.NonPublic);
readonly Dictionary<string, Vector3> lastPositions = new Dictionary<string, Vector3>();
readonly Dictionary<string, string> spectating = new Dictionary<string, string>();
void ChatCommand(BasePlayer player, string command, string[] args)
{
if (!IsAllowed(player.UserIDString, permSpectate))
{
PrintToChat(player, Lang("NotAllowed", player.UserIDString));
return;
}
if (!player.IsSpectating())
{
var target = (args.Length > 0 ? BasePlayer.Find(args[0]) : null);
if (target == null || target == player)
{
PrintToChat(player, Lang("NoTargets", player.UserIDString));
return;
}
// Store current location before spectating
lastPositions.Add(player.UserIDString, player.transform.position);
// Put player in spectate mode
player.SetPlayerFlag(BasePlayer.PlayerFlags.Spectating, true);
player.gameObject.SetLayerRecursive(10);
player.CancelInvoke("MetabolismUpdate");
player.CancelInvoke("InventoryUpdate");
player.ClearEntityQueue();
entitySnapshot.Invoke(player, new object[] { target });
player.gameObject.Identity();
player.SetParent(target, 0);
// Notify player and store target name
PrintToChat(player, Lang("SpectateStart", player.UserIDString, target.displayName));
spectating.Add(player.UserIDString, target.displayName);
}
else
{
// Restore player to normal mode
player.SetParent(null, 0);
player.SetPlayerFlag(BasePlayer.PlayerFlags.Spectating, false);
player.gameObject.SetLayerRecursive(17);
player.metabolism.Reset();
player.InvokeRepeating("InventoryUpdate", 1f, 0.1f * UnityEngine.Random.Range(0.99f, 1.01f));
// Teleport to original location after spectating
var lastPosition = lastPositions[player.UserIDString];
rust.ForcePlayerPosition(player, lastPosition.x, lastPosition.y, lastPosition.z);
if (lastPositions.ContainsKey(player.UserIDString)) lastPositions.Remove(player.UserIDString);
// Notify player and clear target name
PrintToChat(player, Lang("SpectateStop", player.UserIDString, spectating[player.UserIDString]));
if (spectating.ContainsKey(player.UserIDString)) spectating.Remove(player.UserIDString);
}
}
#endregion
#region Command Redirect
object OnServerCommand(ConsoleSystem.Arg arg)
{
if (arg?.cmd?.namefull != "global.spectate" || arg.connection == null) return null;
var player = arg.connection.player as BasePlayer;
var target = new[] { arg.GetString(0) };
ChatCommand(player, null, target);
return true;
}
#endregion
#region Helper Methods
T GetConfig<T>(string name, T defaultValue)
{
if (Config[name] == null) return defaultValue;
return (T)Convert.ChangeType(Config[name], typeof(T));
}
bool IsAllowed(string id, string perm) => permission.UserHasPermission(id, perm);
string Lang(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args);
#endregion
}
}