C#:
using CompanionServer.Handlers;
using Facepunch;
using HarmonyLib;
using Network;
using Newtonsoft.Json;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using ProtoBuf;
using Rust;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using Time = UnityEngine.Time;
namespace Oxide.Plugins {
[Info("AdminMap", "0xF // dsc.gg/0xf-plugins", "2.0.4")]
partial class AdminMap : RustPlugin {
#region References
[PluginReference]
private Plugin AdminMenu;
#endregion
#region Consts
private const string PERMISSION_ALLOW = "adminmap.allow";
private const string PERMISSION_TPMARKER = "adminmap.teleport2marker";
private const string PERMISSION_INVIS = "adminmap.invis";
private const string PERMISSION_RUSTPLUS = "adminmap.rust+";
private const string PERMISSION_RUSTPLUSINVIS = "adminmap.rust+.invis";
#endregion
#region Vars
private static AdminMap Instance;
private static string CapsuleBackgroundVerticalCRC, LetterTCRC;
private List<string> permissions = new() {
PERMISSION_ALLOW, PERMISSION_TPMARKER, PERMISSION_INVIS, PERMISSION_RUSTPLUS, PERMISSION_RUSTPLUSINVIS
}
;
private Dictionary<(ulong userID, string permissionName), bool> permissionCache = new();
#endregion
#region Enums
public enum MapLayer {
None, Player = 1 << 0, Sleeper = 1 << 2, TC = 1 << 3, Stash = 1 << 4, SleepingBag = 1 << 5, Text = 1 << 6,
}
public enum MapLayerModification {
None, NoLabels = 1 << 0
}
#endregion
#region Hooks
void Init() {
Instance = this;
foreach (var button in config.Buttons) if (!string.IsNullOrEmpty(button.Permission)) permissions.Add($"adminmap.{button.Permission}");
foreach (var perm in permissions) permission.RegisterPermission(perm, this);
}
void Unload() {
permissions.Clear();
permissionCache.Clear();
foreach (ConnectionData connectionData in ConnectionData.all.Values.ToArray()) connectionData.Dispose();
}
void OnServerInitialized(bool initial) {
CapsuleBackgroundVerticalCRC = FileStorage.server.Store(Convert.FromBase64String("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TYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoU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"), FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID).ToString();
LetterTCRC = FileStorage.server.Store(Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAThSURBVHgB7d3Pi1V1GMDh96RoZYmWRUW1KggtComgH1ZYFC2kIFq0bdH/IEEUtHIV7aJli0CCFtG+RbSoFi0kLRgRSSSFMCkxU0/v4U4YUTD3Xhq/57zPAy9nFjMgOO9nzjl37pkIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAZfR9f3fOIzldANOWi35bzqs5H+Ss9Fe8H0yOqheXi709D0/l7F2dXf/xqRdztnRddyGYjI1BKbnwW/KwJ64s/EM5G9bwpcP3yh05x4LJEICJy4Uf/o8fzHl2dYbl3xyLWUsoGBEBmJhc+GFJh5/qw7I/kfNkztaAfyEAE5BLP1y3PxezU3oLz5oJwIjl4t+Shw9zng9YwDXBmB0Iy88SBGDcdgcsQQDGzV15liIAUJgAQGECAIUJABQmAFCYAEBhAgCFCQAUJgBQmABAYQIAhQkAFCYAUJgAQGECAIV5JFhD+r7flofXY/Zs/uvW8CV3xfp6J/+NZ2M+fc7JnM+6rvsmaIo/DNKQXK4v8vB4TNPlnD0ZgS+DZrgEaEQu/00x3eUfDN9r+4KmCEA7bozp87jyxggAFCYAUJgAQGECAIUJABQmAFCYAEBhAgCFCQAUJgBQmABAYQIAhQkAFCYAUJgAtONcTN/5oCkC0Iiu607n4WhM21dBUzwTsC0v5ryZszPW9kzAO3M2xfr5JWaP9prH8EzAEzkf5xwMmuKZgCPW9/2hmD1AdL3ck2cqK8FkuASAwgQAChMAKEwAoDABgMIEAAoTAChMAKAwAYDCBAAKEwAoTACgMAGAwgQAChMAKEwAoDABgMIEAAoTAChMAKAwAYDCBAAKEwAoTACgMAGAwgQAChMAKEwAoDABgMIEAAoTAChMAKAwAYDCBAAKEwAoTACgMAGAwgSAeWwIJkUAmMeWYFIEgHlsDyZFAJjHA8GkCADzeDqYFAFgHvv6vr83mAwBYB7DqwCfZARuDSZBAJjXrpwjGYE3cu4LRq0LRisX8FDMFvJq+jXnWM5POX3OmdXjP13KOZzzXtd1Z4ImbAxYzg0596/OWuzOeSlogkuAcetjfPYGzRCAcfs9xsdvEzZEAMbtfMASBGDcBIClCMC4/RywBAEYtx8DliAA43YiYAkCMG5HA5YgAOP2dcASBGDEuq4b7gGcDFiQAIzf5wELEoDxOxiwIO8GHLm+76+N2WXAthiHy3np4unCjXAGMHK5TMNvA74bsABnABOQZwFb87CSsyPa5wygIc4AJiAX6mweXotxvj2Yq0gAJiIj8GkeDgTMQQCmZX/OW9H2mcCFoBkCMCF5FtDnvJ0fvpxzPNr0XQD/r7wxeH3O/pzjfTvO5TwTNMOrABOXCzfccX8h55WcfbG+f9/vYszesPR9zrc5H+UZyuGgGQJQSMZguOTbmfNozmM5D+fcnnNzLG6433AqZi9DHsn5IWYLP3y8kgv/R9AsAeCvs4QhAjtWj5tj9vDOTX/7tOF7ZVj232J2I+90zBb/VC75pQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKbpT6jRW4l52+/oAAAAAElFTkSuQmCC"), FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID).ToString();
foreach (BasePlayer player in BasePlayer.allPlayerList) UpdatePermissionCache(player);
foreach (BasePlayer player in BasePlayer.activePlayerList) OnPlayerConnected(player);
}
void Loaded() {
foreach (Type type in this.GetType().GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) {
object[] attribute = type.GetCustomAttributes(typeof(HarmonyPatch), false);
if (attribute.Length >= 1) {
PatchClassProcessor patchClassProcessor = this.HarmonyInstance.CreateClassProcessor(type);
patchClassProcessor.Patch();
}
}
}
void OnPlayerConnected(BasePlayer player) {
UpdatePermissionCache(player);
CheckConnectionDataConditions(player);
if (config.AutoShowPanel) ConnectionData.Get(player.Connection)?.UI.RenderSidebar();
}
void OnPlayerDisconnected(BasePlayer player) {
ConnectionData connectionData = ConnectionData.Get(player.Connection);
if (connectionData != null) connectionData.Dispose();
}
void OnUserPermissionGranted(string userID, string permName) {
if (!permissions.Contains(permName)) return;
BasePlayer player = BasePlayer.FindAwakeOrSleepingByID(ulong.Parse(userID));
if (player == null) return;
UpdatePermissionCache(player);
CheckConnectionDataConditions(player);
}
void OnUserPermissionRevoked(string userID, string permName) {
if (!permissions.Contains(permName)) return;
BasePlayer player = BasePlayer.FindAwakeOrSleepingByID(ulong.Parse(userID));
if (player == null) return;
UpdatePermissionCache(player);
CheckConnectionDataConditions(player);
}
void OnUserGroupAdded(string userID, string groupName) {
BasePlayer player = BasePlayer.FindAwakeOrSleepingByID(ulong.Parse(userID));
if (player == null) return;
UpdatePermissionCache(player);
CheckConnectionDataConditions(player);
}
void OnUserGroupRemoved(string userID, string groupName) {
BasePlayer player = BasePlayer.FindAwakeOrSleepingByID(ulong.Parse(userID));
if (player == null) return;
UpdatePermissionCache(player);
CheckConnectionDataConditions(player);
}
object OnMapMarkerAdd(BasePlayer player, ProtoBuf.MapNote note) {
if (HasPermission(player.userID, PERMISSION_TPMARKER)) {
if (player.serverInput.WasDown(BUTTON.DUCK)) {
float y = TerrainMeta.HeightMap.GetHeight(note.worldPosition) + 2.5f;
float highestPoint = TerrainMeta.HighestPoint.y + 250f;
RaycastHit[] hits = Physics.RaycastAll(note.worldPosition.WithY(highestPoint), Vector3.down, ++highestPoint, Layers.Mask.World | Layers.Mask.Terrain | Layers.Mask.Default, QueryTriggerInteraction.Ignore);
if (hits.Length > 0) {
GamePhysics.Sort(hits);
y = hits.Max(hit => hit.point.y);
}
if (player.IsFlying) y = Mathf.Max(y, player.transform.position.y);
player.Teleport(note.worldPosition.WithY(y));
return false;
}
}
if (HasPermission(player.userID, PERMISSION_ALLOW)) {
ConnectionData connectionData = ConnectionData.Get(player.Connection);
if (connectionData == null) return null;
if (connectionData.IsLayerActive(MapLayer.Text)) {
(BasePlayer player, float distance) minDistancePlayer = (null, float.MaxValue);
foreach (BasePlayer basePlayer in connectionData.textPlayers) {
float distance = Vector2.Distance(basePlayer.transform.position.XZ2D(), note.worldPosition.XZ2D());
if (distance < minDistancePlayer.distance) minDistancePlayer = (basePlayer, distance);
}
if (minDistancePlayer.distance < 75) {
Instance.InvokeActionMenu(connectionData, player, minDistancePlayer.player);
return false;
}
}
}
return null;
}
void OnPlayerPingsSend(BasePlayer player, MapNoteList mapNoteList) {
ConnectionData connectionData = ConnectionData.Get(player.Connection);
if (connectionData == null) return;
mapNoteList.notes.AddRange(connectionData.notes);
}
object OnTeamLeave(RelationshipManager.PlayerTeam team, BasePlayer player) {
return BasePlayer_TeamUpdate_Patch.Prefix(player) ? null : false;
}
#endregion
#region Methods
private void UpdatePermissionCache(BasePlayer player) {
foreach (string permissionName in permissions) permissionCache[(player.userID, permissionName)] = permission.UserHasPermission(player.UserIDString, permissionName);
}
private bool HasPermission(ulong userID, string permission) {
if (!Instance.permissionCache.TryGetValue((userID, permission), out bool result)) return false;
return result;
}
public void SendPingsToClient(BasePlayer player, List<MapNote> notes) {
using (MapNoteList mapNoteList = Facepunch.Pool.Get<MapNoteList>()) {
mapNoteList.notes = Facepunch.Pool.Get<List<MapNote>>();
mapNoteList.notes.AddRange(notes);
player.ClientRPC<MapNoteList>(global : : RpcTarget.Player("Client_ReceivePings", player), mapNoteList);
Facepunch.Pool.FreeUnmanaged(ref mapNoteList.notes);
}
}
private void CheckConnectionDataConditions(BasePlayer player) {
if (!player.IsConnected) return;
Connection connection = player.Connection;
ConnectionData connectionData = ConnectionData.Get(connection);
bool hasPermission = HasPermission(player.userID, PERMISSION_ALLOW);
if (hasPermission && connectionData == null) ConnectionData.GetOrCreate(connection);
else if (!hasPermission && connectionData != null) connectionData.Dispose();
}
private void InvokeActionMenu(ConnectionData connectionData, BasePlayer from, BasePlayer to) {
if (config.AdminMenuInsteadOfActionMenu && Instance.AdminMenu != null && Instance.AdminMenu.IsLoaded) {
from.SendConsoleCommand($"adminmenu userinfo.open {to.UserIDString}");
return;
}
connectionData.selectedPlayer = to;
connectionData.UI.RenderPlayerActionUI(to);
}
private static string GetHexColorForTeam(ulong teamID) {
return Convert.ToString((int)Math.Floor((Math.Abs(Math.Sin(teamID) * 16777215))), 16);
}
private void ExceptPlayers(List<BasePlayer> list, BasePlayer @for = null, bool rustPlus = false) {
if (@for != null) list.Remove(@for);
for (int i = 0; i < list.Count; i++) {
BasePlayer player = list[i];
if (HasPermission(player.userID, PERMISSION_INVIS) || rustPlus && HasPermission(player.userID, PERMISSION_RUSTPLUSINVIS)) if (list.Remove(player)) i--;
}
}
private static AppMarker GetPlayerMarker(global : : BasePlayer player) {
AppMarker appMarker = Facepunch.Pool.Get<AppMarker>();
Vector2 vector = CompanionServer.Util.WorldToMap(player.transform.position);
appMarker.id = player.net.ID;
appMarker.type = AppMarkerType.Player;
appMarker.x = vector.x;
appMarker.y = vector.y;
appMarker.steamId = player.userID;
return appMarker;
}
private static bool TryAddAppMarkers(ulong userId, List<AppMarker> markers) {
if (!Instance.HasPermission(userId, PERMISSION_RUSTPLUS)) return false;
List<BasePlayer> list = Facepunch.Pool.Get<List<BasePlayer>>();
list.AddRange(BasePlayer.activePlayerList);
Instance.ExceptPlayers(list, null, true);
foreach (BasePlayer player in list) markers.Add(GetPlayerMarker(player));
Facepunch.Pool.FreeUnmanaged(ref list);
return true;
}
private static AppTeamInfo GetAppTeamInfo(ulong userId) {
if (!Instance.HasPermission(userId, PERMISSION_RUSTPLUS)) return null;
AppTeamInfo appTeamInfo = Pool.Get<AppTeamInfo>();
appTeamInfo.members = Pool.Get<List<AppTeamInfo.Member>>();
List<BasePlayer> list = Facepunch.Pool.Get<List<BasePlayer>>();
list.AddRange(BasePlayer.activePlayerList);
Instance.ExceptPlayers(list, null, true);
foreach (BasePlayer player in list) {
AppTeamInfo.Member member = Pool.Get<AppTeamInfo.Member>();
Vector2 vector = CompanionServer.Util.WorldToMap(player.transform.position);
member.steamId = player.userID;
member.name = (player.displayName ?? "");
member.x = vector.x;
member.y = vector.y;
member.isOnline = player.IsConnected;
AppTeamInfo.Member member2 = member;
PlayerLifeStory lifeStory = player.lifeStory;
member2.spawnTime = ((lifeStory != null) ? lifeStory.timeBorn : 0U);
member.isAlive = player.IsAlive();
AppTeamInfo.Member member3 = member;
PlayerLifeStory previousLifeStory = player.previousLifeStory;
member3.deathTime = ((previousLifeStory != null) ? previousLifeStory.timeDied : 0U);
appTeamInfo.members.Add(member);
}
Facepunch.Pool.FreeUnmanaged(ref list);
appTeamInfo.leaderSteamId = 0UL;
appTeamInfo.mapNotes = Pool.Get<List<AppTeamInfo.Note>>();
appTeamInfo.leaderMapNotes = Pool.Get<List<AppTeamInfo.Note>>();
return appTeamInfo;
}
#region Notes
private void AddTempNote(BasePlayer player, ulong noteId, MapNote note, float time) {
if (!player.IsConnected) return;
ConnectionData connectionData = ConnectionData.Get(player.Connection);
if (connectionData == null) return;
AddTempNote(connectionData, noteId, note, time);
}
private void AddTempNote(ConnectionData data, ulong noteId, MapNote note, float time) {
note.totalDuration = Time.realtimeSinceStartup + time;
if (data.tempNotes.TryGetValue(noteId, out MapNote note2)) note2.Dispose();
data.tempNotes[noteId] = note;
}
private MapNote GetNote(int icon, int color, Vector3 worldPosition, NetworkableId associatedId = default(NetworkableId)) {
return new MapNote {
noteType = 1, isPing = true, icon = icon, colourIndex = color, worldPosition = worldPosition, associatedId = associatedId
}
;
}
private void GetPlayerNotes(IEnumerable<BasePlayer> players, List<MapNote> list, bool withoutLabels = false) {
const int icon = 6;
foreach (BasePlayer player in players) {
int color = player.IsSleeping() ? 3 : 2;
MapNote note = GetNote(icon, color, player.transform.position, player.net.ID);
if (!withoutLabels) note.label = player.displayName;
list.Add(note);
}
}
private void GetTCNotes(IEnumerable<BuildingPrivlidge> privlidges, List<MapNote> list) {
const int icon = 2;
foreach (BuildingPrivlidge privlidge in privlidges) {
int color = Mathf.Clamp(privlidge.authorizedPlayers.Count - 1, 0, 5);
MapNote note = GetNote(icon, color, privlidge.transform.position, privlidge.net.ID);
list.Add(note);
}
}
private void GetTCNotes(IEnumerable<SimplePrivilege> privlidges, List<MapNote> list) {
const int icon = 5;
foreach (SimplePrivilege privlidge in privlidges) {
int color = Mathf.Clamp(privlidge.authorizedPlayers.Count - 1, 0, 5);
MapNote note = GetNote(icon, color, privlidge.transform.position, privlidge.net.ID);
list.Add(note);
}
}
private void GetTCNotes(List<MapNote> list) {
GetTCNotes(BaseNetworkable.serverEntities.OfType<BuildingPrivlidge>(), list);
GetTCNotes(BaseNetworkable.serverEntities.OfType<SimplePrivilege>(), list);
}
private void GetStashNotes(IEnumerable<StashContainer> stashes, List<MapNote> list) {
const int icon = 11;
foreach (StashContainer stash in stashes) {
if (stash.inventory.itemList.Count == 0) continue;
int color = Mathf.Clamp(stash.inventory.itemList.Count - 1, 0, 5);
MapNote note = GetNote(icon, color, stash.transform.position, stash.net.ID);
list.Add(note);
}
}
private void GetStashNotes(List<MapNote> list) {
GetStashNotes(BaseNetworkable.serverEntities.OfType<StashContainer>(), list);
}
private void GetSleepingBagNotes(IEnumerable<SleepingBag> sleepingBags, List<MapNote> list) {
foreach (SleepingBag sleepingBag in sleepingBags) list.Add(GetSleepingBagNote(sleepingBag));
}
private MapNote GetSleepingBagNote(SleepingBag sleepingBag) {
const int icon = 7;
const int color = 3;
return GetNote(icon, color, sleepingBag.transform.position, sleepingBag.net.ID);
}
private void GetSleepingBagNotes(List<MapNote> list) {
GetSleepingBagNotes(BaseNetworkable.serverEntities.OfType<SleepingBag>(), list);
}
#endregion
#endregion
#region Commands
[ChatCommand("amap")]
private void amap(BasePlayer player, string command, string[] args) {
ConnectionData connectionData = ConnectionData.Get(player.Connection);
if (connectionData != null) {
if (args.Length > 0 && args[0] == "off") connectionData.Reset();
else connectionData.UI.RenderSidebar();
}
}
[ConsoleCommand("adminmap.cmd")]
private void cmd(ConsoleSystem.Arg arg) {
if (arg.Args.Length < 1) return;
BasePlayer player = arg.Player();
if (player == null) return;
if (!HasPermission(player.userID, PERMISSION_ALLOW)) return;
ConnectionData connectionData = ConnectionData.Get(player.Connection);
if (connectionData == null) return;
switch (arg.GetString(0)) {
case "player_action" : {
Configuration.Button button = config.Buttons.ElementAtOrDefault(arg.GetInt(1, -1));
if (!string.IsNullOrEmpty(button.Permission) && !HasPermission(player.userID, $"adminmap.{button.Permission}")) break;
string[] commands = button.Command.Split(';');
for (int i = 0; i < commands.Length; i++) {
string command = commands[i];
if (command.First() == '/') command = command.Replace(command, $"chat.say \"{command}\"");
command = command.Replace("{steamid}", connectionData.selectedPlayer.UserIDString).Replace("{username}", connectionData.selectedPlayer.displayName).Replace("{admin.steamid}", player.UserIDString).Replace("{admin.username}", player.displayName);
player.SendConsoleCommand(command);
}
break;
}
case "teleport" : {
NetworkableId networkableId = arg.GetEntityID(1);
if (!networkableId.IsValid) break;
BaseEntity entity = BaseNetworkable.serverEntities.Find(networkableId) as BaseEntity;
if (entity) player.Teleport(entity.transform.position);
break;
}
case "toggle_layer" : {
MapLayer mapLayer = (MapLayer)arg.GetInt(1);
if (mapLayer == MapLayer.None) break;
if (connectionData.IsLayerActive(mapLayer)) {
connectionData.Layers &= ~mapLayer;
if (mapLayer == MapLayer.Text) connectionData.RestoreTeam();
}
else {
connectionData.Layers |= mapLayer;
}
connectionData.UI.RenderSidebar();
break;
}
case "toggle_mod" : {
MapLayerModification layerModification = (MapLayerModification)arg.GetInt(1);
if (layerModification == MapLayerModification.None) break;
if (connectionData.HasModification(layerModification)) connectionData.Modifications &= ~layerModification;
else connectionData.Modifications |= layerModification;
connectionData.UI.RenderSidebar();
break;
}
case "show_player_teammates" : {
BasePlayer argPlayer = arg.GetPlayerOrSleeper(1);
if (argPlayer == null) break;
if (argPlayer.currentTeam == 0) {
player.ShowToast(GameTip.Styles.Red_Normal, $"The player is not a member of the team");
break;
}
RelationshipManager.PlayerTeam team = RelationshipManager.ServerInstance.FindTeam(argPlayer.currentTeam);
if (team == null) {
player.ShowToast(GameTip.Styles.Red_Normal, $"The team #{argPlayer.currentTeam} not found");
break;
}
List<MapNote> notes = Facepunch.Pool.Get<List<MapNote>>();
GetPlayerNotes(team.members.Select(userId => BasePlayer.FindAwakeOrSleepingByID(userId)), notes);
foreach (MapNote note in notes) {
if (note.colourIndex != 3) note.colourIndex = 5;
AddTempNote(connectionData, note.associatedId.Value, note, 15);
}
Facepunch.Pool.FreeUnmanaged(ref notes);
connectionData.updater.SendUpdate();
player.ShowToast(GameTip.Styles.Blue_Normal, $"Showing {team.members.Count} teammates for 15 sec.");
break;
}
case "show_player_privlidges" : {
BasePlayer argPlayer = arg.GetPlayerOrSleeper(1);
if (argPlayer == null) break;
var privlidges = BaseNetworkable.serverEntities.OfType<BuildingPrivlidge>().Where(p => p.IsAuthed(argPlayer));
var privlidges2 = BaseNetworkable.serverEntities.OfType<SimplePrivilege>().Where(p => p.IsAuthed(argPlayer));
int privlidgesCount = privlidges.Count() + privlidges2.Count();
if (privlidgesCount > 0) {
List<MapNote> notes = Facepunch.Pool.Get<List<MapNote>>();
GetTCNotes(privlidges, notes);
GetTCNotes(privlidges2, notes);
foreach (MapNote note in notes) AddTempNote(connectionData, note.associatedId.Value, note, 10);
Facepunch.Pool.FreeUnmanaged(ref notes);
connectionData.updater.SendUpdate();
player.ShowToast(GameTip.Styles.Blue_Normal, $"Showing {privlidgesCount} privlidges for 10 sec.");
}
else {
player.ShowToast(GameTip.Styles.Red_Normal, $"No privlidges of player {argPlayer.displayName} found");
}
break;
}
case "show_player_sleepingbags" : {
BasePlayer argPlayer = arg.GetPlayerOrSleeper(1);
if (argPlayer == null) break;
var sleepingBags = BaseNetworkable.serverEntities.OfType<SleepingBag>().Where(p => p.deployerUserID == argPlayer.userID);
int sleepingBagsCount = sleepingBags.Count();
if (sleepingBagsCount > 0) {
List<MapNote> notes = Facepunch.Pool.Get<List<MapNote>>();
GetSleepingBagNotes(sleepingBags, notes);
foreach (MapNote note in notes) AddTempNote(connectionData, note.associatedId.Value, note, 10);
Facepunch.Pool.FreeUnmanaged(ref notes);
connectionData.updater.SendUpdate();
player.ShowToast(GameTip.Styles.Blue_Normal, $"Showing {sleepingBagsCount} sleeping bags for 10 sec.");
}
else {
player.ShowToast(GameTip.Styles.Red_Normal, $"No sleeping bags of player {argPlayer.displayName} found");
}
break;
}
case "show_player_stashes" : {
BasePlayer argPlayer = arg.GetPlayerOrSleeper(1);
if (argPlayer == null) break;
var stashes = BaseNetworkable.serverEntities.OfType<StashContainer>().Where(p => p.OwnerID == argPlayer.userID);
int stashesCount = stashes.Count();
if (stashesCount > 0) {
List<MapNote> notes = Facepunch.Pool.Get<List<MapNote>>();
GetStashNotes(stashes, notes);
foreach (MapNote note in notes) AddTempNote(connectionData, note.associatedId.Value, note, 10);
Facepunch.Pool.FreeUnmanaged(ref notes);
connectionData.updater.SendUpdate();
player.ShowToast(GameTip.Styles.Blue_Normal, $"Showing {stashesCount} stashes for 10 sec.");
}
else {
player.ShowToast(GameTip.Styles.Red_Normal, $"No stashes of player {argPlayer.displayName} found");
}
break;
}
case "show_owned_item" : {
int icon = arg.GetInt(3, 10);
int color = arg.GetInt(4, 4);
BasePlayer argPlayer = arg.GetPlayerOrSleeper(1);
if (argPlayer == null) break;
string filter = arg.GetString(2, "");
var entities = BaseEntity.Util.FindTargetsOwnedBy(argPlayer.userID, filter);
int entitiesCount = entities.Count();
if (entitiesCount > 0) {
foreach (BaseEntity entity in entities) {
MapNote note = GetNote(icon, color, entity.transform.position, entity.net.ID);
AddTempNote(connectionData, note.associatedId.Value, note, 15);
}
connectionData.updater.SendUpdate();
player.ShowToast(GameTip.Styles.Blue_Normal, $"Showing {entitiesCount} entities for 15 sec.");
}
else {
player.ShowToast(GameTip.Styles.Red_Normal, $"No entities of player {argPlayer.displayName} found");
}
break;
}
}
}
#endregion
#region Classes
public class AdminMapper : FacepunchBehaviour {
private BasePlayer player;
void Awake() {
player = GetComponent<BasePlayer>();
InvokeRepeating(SendUpdateInternal, 2f, 2f);
}
private bool HasPermission() {
return Instance.HasPermission(player.userID, PERMISSION_ALLOW);
}
public void SendUpdate() {
CancelInvoke(SendUpdateInternal);
InvokeRepeating(SendUpdateInternal, 0, 2f);
}
private List<BasePlayer> GetPooledPlayerList() {
List<BasePlayer> list = Facepunch.Pool.Get<List<BasePlayer>>();
list.AddRange(BasePlayer.activePlayerList);
Instance.ExceptPlayers(list, player);
return list;
}
private void SendUpdateInternal() {
if (player == null || !player.IsConnected) {
Destroy(this);
return;
}
ConnectionData connectionData = ConnectionData.Get(player.Connection);
if (!HasPermission()) {
Destroy(this);
connectionData.Reset();
return;
}
connectionData.notes.Clear();
if (connectionData.Layers == MapLayer.None && connectionData.tempNotes.Count == 0) return;
for (int i = 0; i < connectionData.tempNotes.Count; i++) {
var pair = connectionData.tempNotes.ElementAt(i);
MapNote note = pair.Value;
if (note.totalDuration < Time.realtimeSinceStartup) {
connectionData.tempNotes.Remove(pair.Key);
i--;
continue;
}
}
List<BasePlayer> players = null;
bool withoutLabels = connectionData.HasModification(MapLayerModification.NoLabels);
if (connectionData.IsLayerActive(MapLayer.Player)) Instance.GetPlayerNotes(players ??= GetPooledPlayerList(), connectionData.notes, withoutLabels);
if (connectionData.IsLayerActive(MapLayer.Sleeper)) Instance.GetPlayerNotes(BasePlayer.sleepingPlayerList, connectionData.notes, withoutLabels);
if (connectionData.IsLayerActive(MapLayer.TC)) Instance.GetTCNotes(connectionData.notes);
if (connectionData.IsLayerActive(MapLayer.Stash)) Instance.GetStashNotes(connectionData.notes);
if (connectionData.IsLayerActive(MapLayer.SleepingBag)) Instance.GetSleepingBagNotes(connectionData.notes);
connectionData.notes.AddRange(connectionData.tempNotes.Values);
if (connectionData.IsLayerActive(MapLayer.Text)) {
connectionData.textPlayers.Clear();
players ??= GetPooledPlayerList();
using (PlayerTeam playerTeam = Facepunch.Pool.Get<PlayerTeam>()) {
playerTeam.members = Facepunch.Pool.Get<List<PlayerTeam.TeamMember>>();
for (int i = 0; i < players.Count; i++) {
BasePlayer player = players[i];
PlayerTeam.TeamMember teamMember = Facepunch.Pool.Get<PlayerTeam.TeamMember>();
string color = config.TextMap.SoloColor;
if (player.IsSleeping()) color = config.TextMap.SleeperColor;
else if (player.currentTeam > 0) color = (config.TextMap.UseGeneratedTeamsColors ? GetHexColorForTeam(player.currentTeam) : config.TextMap.TeamColor);
teamMember.displayName = $"\t\t\t<size={config.TextMap.FontSize * 10}><color=#{color}>{player.displayName}</color></size>\t\t\t";
teamMember.healthFraction = 1;
teamMember.online = true;
teamMember.wounded = true;
teamMember.position = player.transform.position;
teamMember.userID = (ulong)i;
playerTeam.members.Add(teamMember);
connectionData.textPlayers.Add(player);
}
player.ClientRPC<PlayerTeam>(global : : RpcTarget.PlayerAndSpectators("CLIENT_ReceiveTeamInfo", player), playerTeam);
}
}
player.SendPingsToClient();
if (players != null) Facepunch.Pool.FreeUnmanaged(ref players);
}
}
public class ConnectionData : IDisposable {
public Connection connection;
public List<MapNote> notes = new List<MapNote>();
public Dictionary<ulong, MapNote> tempNotes = new Dictionary<ulong, MapNote>();
public List<BasePlayer> textPlayers = new List<BasePlayer>();
public BasePlayer selectedPlayer;
public AdminMapper updater;
private MapLayer layers = MapLayer.None;
private MapLayerModification modifications = MapLayerModification.None;
public ConnectionUI UI {
get;
}
public ConnectionData(Connection connection) {
this.connection = connection;
this.UI = new ConnectionUI(this);
BasePlayer player = this.Player;
if (player == null || !player.IsConnected) return;
updater = player.gameObject.AddComponent<AdminMapper>();
}
public BasePlayer Player {
get {
return connection.player as BasePlayer;
}
}
public MapLayer Layers {
get {
return layers;
}
set {
layers = value;
if (layers == MapLayer.None) Reset();
updater.SendUpdate();
}
}
public MapLayerModification Modifications {
get {
return modifications;
}
set {
modifications = value;
updater?.SendUpdate();
}
}
public void Reset() {
notes.Clear();
textPlayers.Clear();
layers = MapLayer.None;
modifications = MapLayerModification.None;
selectedPlayer = null;
RestoreTeam();
Player.SendPingsToClient();
UI.Dispose();
}
public void RestoreTeam() {
BasePlayer player = this.Player;
if (player.currentTeam != 0UL) player.TeamUpdate();
else player.ClientRPC(RpcTarget.PlayerAndSpectators("CLIENT_ClearTeam", player));
}
public bool IsLayerActive(MapLayer mapType) {
return (this.layers & mapType) == mapType;
}
public bool HasModification(MapLayerModification modification) {
return (this.modifications & modification) == modification;
}
public void Dispose() {
UnityEngine.Object.Destroy(updater);
Reset();
all.Remove(connection);
}
public static Dictionary<Connection, ConnectionData> all = new Dictionary<Connection, ConnectionData>();
public static ConnectionData Get(Connection connection) {
if (connection == null) return null;
ConnectionData data;
if (all.TryGetValue(connection, out data)) return data;
return null;
}
public static ConnectionData GetOrCreate(Connection connection) {
if (connection == null) return null;
ConnectionData data = Get(connection);
if (data == null) data = all[connection] = new ConnectionData(connection);
return data;
}
}
public class ConnectionUI : IDisposable {
private Connection connection;
private ConnectionData connectionData;
private static (Func<ConnectionData, bool> condition, string command, string icon, (string min, string max) offset)[] sidebarButtonsProperties = new (Func<ConnectionData, bool>, string, string, (string, string))[7] {
((ConnectionData cd) => cd.IsLayerActive(MapLayer.Player), $"adminmap.cmd toggle_layer {(int)MapLayer.Player}", "assets/content/ui/map/icon-map_pin.png", ("0 1", "0 1")), ((ConnectionData cd) => cd.IsLayerActive(MapLayer.Text), $"adminmap.cmd toggle_layer {(int)MapLayer.Text}", LetterTCRC, ("-0.5 0", "-0.5 0")), ((ConnectionData cd) => cd.IsLayerActive(MapLayer.Sleeper), $"adminmap.cmd toggle_layer {(int)MapLayer.Sleeper}", "assets/content/ui/map/icon-map_sleep.png", ("0 0", "0 0")), ((ConnectionData cd) => cd.IsLayerActive(MapLayer.TC), $"adminmap.cmd toggle_layer {(int)MapLayer.TC}", "assets/content/ui/map/icon-map_home.png", ("-4 -5", "4 3")), ((ConnectionData cd) => cd.IsLayerActive(MapLayer.Stash), $"adminmap.cmd toggle_layer {(int)MapLayer.Stash}", "assets/prefabs/deployable/small stash/small_stash.png", ("6 6", "-6 -6")), ((ConnectionData cd) => cd.IsLayerActive(MapLayer.SleepingBag), $"adminmap.cmd toggle_layer {(int)MapLayer.SleepingBag}", "assets/prefabs/deployable/sleeping bag/sleepingbag.png", ("6 6", "-6 -6")), ((ConnectionData cd) => cd.HasModification(MapLayerModification.NoLabels), $"adminmap.cmd toggle_mod {(int)MapLayerModification.NoLabels}", "assets/content/ui/hypnotized.png", ("4 4", "-4 -4")),
}
;
public ConnectionUI(ConnectionData connectionData) {
this.connectionData = connectionData;
this.connection = connectionData.connection;
}
public void RenderSidebar() {
const string lockedButtonBackground = "0.723 0.035 0 1";
const string lockedButtonIcon = "0.902 0.282 0.2 1";
const string disabledButtonBackground = "0.31 0.302 0.294 1";
const string disabledButtonIcon = "0.565 0.545 0.529 1";
const string enabledButtonBackground = "0.3568628 0.4431373 0.2235294 1";
const string enabledButtonIcon = "0.5490196 0.7764707 0.1921569 1";
CUI.Root root = new CUI.Root("OverlayNonScaled");
float height = 7 * 32.8f;
float halfHeight = height * 0.5f;
var container = root.AddContainer(anchorMin : "0 0.5", anchorMax : "0 0.5", offsetMin : $"3 -{halfHeight}", offsetMax : $"39 {halfHeight}", name : "ADMINMAP_SIDEBAR").AddDestroySelfAttribute();
container.AddHImage(CapsuleBackgroundVerticalCRC, color : "0 0 0 1");
for (int i = 0; i < sidebarButtonsProperties.Length; i++) {
var prop = sidebarButtonsProperties[i];
string command = prop.command;
string icon
// ... [Code preview truncated due to length. Full source in attached file] ...