C#:
using HarmonyLib;
using System;
using System.Collections;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using UnityEngine;
using ProtoBuf;
using Oxide.Game.Rust.Cui;
using Oxide.Core.Configuration;
using Oxide.Core.Libraries;
using UnityEngine.UI;
namespace Oxide.Plugins
{
#region Compatibility Layer
public class ChaosPlugin : RustPlugin
{
public BaseConfigData ConfigurationData { get; set; }
protected virtual void OnConfigurationUpdated(VersionNumber oldVersion) {}
protected virtual ConfigurationFile OnLoadConfig(ref ConfigurationFile configurationFile) => null;
protected virtual T GenerateDefaultConfiguration<T>() where T : class, new() => null;
protected virtual string GetString(string key, BasePlayer player) => lang.GetMessage(key, this, player?.UserIDString);
public NoEscapeWrapper NoEscape { get; } = new NoEscapeWrapper();
public static string GetColor(string hex, float alpha)
{
if (string.IsNullOrEmpty(hex)) return "1 1 1 1";
if (hex.StartsWith("#")) hex = hex.Substring(1);
if (hex.Length == 6)
{
int r = int.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
int g = int.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
int b = int.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
string rStr = ((float)r / 255).ToString(System.Globalization.CultureInfo.InvariantCulture);
string gStr = ((float)g / 255).ToString(System.Globalization.CultureInfo.InvariantCulture);
string bStr = ((float)b / 255).ToString(System.Globalization.CultureInfo.InvariantCulture);
string aStr = alpha.ToString(System.Globalization.CultureInfo.InvariantCulture);
return $"{rStr} {gStr} {bStr} {aStr}";
}
return "1 1 1 1";
}
}
public class NoEscapeWrapper
{
private Plugin _noEscape => Oxide.Core.Interface.Oxide.RootPluginManager.GetPlugin("NoEscape");
public bool IsLoaded => _noEscape != null;
public bool IsRaidBlocked(BasePlayer player) => (bool)(_noEscape?.Call("IsRaidBlocked", player) ?? false);
public bool IsCombatBlocked(BasePlayer player) => (bool)(_noEscape?.Call("IsCombatBlocked", player) ?? false);
}
public class ConfigurationFile {}
public class ConfigurationFile<T> : ConfigurationFile
{
public ConfigurationFile(DynamicConfigFile config) {}
}
public interface IChaosUIComponent {}
public static class ChaosUI
{
public static void Show(BasePlayer player, BaseContainer root)
{
if (player == null || root == null) return;
CuiHelper.DestroyUi(player, root.Name);
CuiHelper.AddUi(player, root.Elements);
}
public static void Destroy(BasePlayer player, string name)
{
if (player == null || string.IsNullOrEmpty(name)) return;
CuiHelper.DestroyUi(player, name);
}
}
public class Style
{
public Color ImageColor { get; set; }
public string Material { get; set; }
public string Sprite { get; set; }
public UnityEngine.UI.Image.Type ImageType { get; set; }
public int FontSize { get; set; }
public TextAnchor Alignment { get; set; }
public string Font { get; set; }
public VerticalWrapMode WrapMode { get; set; }
}
public class Color
{
public string Hex { get; set; }
public float Alpha { get; set; }
public Color(string hex, float alpha) { Hex = hex; Alpha = alpha; }
public static Color Clear => new Color("000000", 0.005f);
public string ToCuiColor() => ChaosPlugin.GetColor(Hex, Alpha);
}
public class Anchor
{
public string Min { get; set; }
public string Max { get; set; }
public Anchor(string min, string max) { Min = min; Max = max; }
public static Anchor Center => new Anchor("0.5 0.5", "0.5 0.5");
public static Anchor CenterLeft => new Anchor("0 0.5", "0 0.5");
public static Anchor CenterRight => new Anchor("1 0.5", "1 0.5");
public static Anchor TopStretch => new Anchor("0 1", "1 1");
public static Anchor FullStretch => new Anchor("0 0", "1 1");
}
public class Offset
{
public string Min { get; set; }
public string Max { get; set; }
public Offset(float xMin, float yMin, float xMax, float yMax) { Min = $"{xMin} {yMin}"; Max = $"{xMax} {yMax}"; }
public static Offset zero => new Offset(0, 0, 0, 0);
}
public static class Layer { public const string Overall = "Overlay"; }
public static class Materials { public const string BackgroundBlur = "assets/content/ui/uibackgroundblur.mat"; }
public static class Sprites { public const string Background_Rounded = "assets/content/ui/ui.background.tile.psd"; }
public static class Icon {
public const string Download = "assets/icons/download.png";
public const string Close = "assets/icons/close.png";
}
public static class Font { public const string PermanentMarker = "permanentmarker.ttf"; }
public class BaseContainer
{
public string Name { get; set; }
public CuiElementContainer Elements { get; set; }
public CuiElement CurrentElement { get; set; }
public BaseContainer(string name, string parent, Anchor anchor, Offset offset, CuiElementContainer container = null)
{
Name = name;
Elements = container ?? new CuiElementContainer();
CurrentElement = new CuiElement
{
Name = name,
Parent = parent,
FadeOut = 0f,
Components = {
new CuiRectTransformComponent { AnchorMin = anchor.Min, AnchorMax = anchor.Max, OffsetMin = offset.Min, OffsetMax = offset.Max }
}
};
// Check if this is the root of the menu
if (container == null)
{
Elements.Add(CurrentElement);
}
else
{
// For children, we still add them to the main container
Elements.Add(CurrentElement);
}
}
public static BaseContainer Create(string name, string parent, Anchor anchor, Offset offset) => new BaseContainer(name, parent, anchor, offset);
public static BaseContainer Create(BaseContainer parent, Anchor anchor, Offset offset) => new BaseContainer(CuiHelper.GetGuid(), parent.Name, anchor, offset, parent.Elements);
protected T GetComponent<T>() where T : ICuiComponent
{
foreach (var comp in CurrentElement.Components)
if (comp is T t) return t;
return default(T);
}
public BaseContainer WithChildren(Action<BaseContainer> action) { action(this); return this; }
public BaseContainer NeedsCursor() {
var comp = new CuiNeedsCursorComponent();
CurrentElement.Components.Add(comp);
return this;
}
public BaseContainer NeedsKeyboard() {
var comp = new CuiNeedsKeyboardComponent();
CurrentElement.Components.Add(comp);
return this;
}
public BaseContainer DestroyExisting() { return this; }
public string ToJson() { return Elements.ToJson(); }
public BaseContainer WithOutline(OutlineComponent outline) {
CurrentElement.Components.Add(new CuiOutlineComponent { Color = outline.Color.ToCuiColor(), Distance = "1.0 -1.0", UseGraphicAlpha = true });
return this;
}
public BaseContainer WithColor(Color color) {
var img = GetComponent<CuiImageComponent>(); if (img != null) img.Color = color.ToCuiColor();
var text = GetComponent<CuiTextComponent>(); if (text != null) text.Color = color.ToCuiColor();
var btn = GetComponent<CuiButtonComponent>(); if (btn != null) btn.Color = color.ToCuiColor();
return this;
}
public BaseContainer WithSprite(string sprite) {
var img = GetComponent<CuiImageComponent>(); if (img != null) img.Sprite = sprite;
return this;
}
public BaseContainer WithMaterial(string mat) {
var img = GetComponent<CuiImageComponent>(); if (img != null) img.Material = mat;
return this;
}
public BaseContainer WithImageType(UnityEngine.UI.Image.Type type) {
var img = GetComponent<CuiImageComponent>(); if (img != null) img.ImageType = type;
return this;
}
public BaseContainer WithText(string text) {
var txt = GetComponent<CuiTextComponent>(); if (txt != null) txt.Text = text;
var input = GetComponent<CuiInputFieldComponent>(); if (input != null) input.Text = text;
return this;
}
public BaseContainer WithAlignment(TextAnchor align) {
var txt = GetComponent<CuiTextComponent>(); if (txt != null) txt.Align = align;
var input = GetComponent<CuiInputFieldComponent>(); if (input != null) input.Align = align;
return this;
}
public BaseContainer WithCharacterLimit(int limit) {
var input = GetComponent<CuiInputFieldComponent>(); if (input != null) input.CharsLimit = limit;
return this;
}
public BaseContainer WithCallback(CommandCallbackHandler handler, Action<ConsoleSystem.Arg> action, string name) {
name = name.Replace(".", "").Replace(" ", "");
var fullCmd = $"aclock_cb {name}";
var btn = GetComponent<CuiButtonComponent>(); if (btn != null) btn.Command = fullCmd;
var input = GetComponent<CuiInputFieldComponent>(); if (input != null) input.Command = fullCmd + " ";
handler?.Register(name, action);
return this;
}
public BaseContainer WithStyle(Style style) {
if (style == null) return this;
if (style.ImageColor != null) WithColor(style.ImageColor);
if (style.Material != null) WithMaterial(style.Material);
if (style.Sprite != null) WithSprite(style.Sprite);
if (style.Font != null) {
var txt = GetComponent<CuiTextComponent>(); if (txt != null) txt.Font = style.Font;
var input = GetComponent<CuiInputFieldComponent>(); if (input != null) input.Font = style.Font;
}
if (style.FontSize > 0) {
var txt = GetComponent<CuiTextComponent>(); if (txt != null) txt.FontSize = style.FontSize;
var input = GetComponent<CuiInputFieldComponent>(); if (input != null) input.FontSize = style.FontSize;
}
WithAlignment(style.Alignment);
return this;
}
}
public class ImageContainer : BaseContainer
{
public ImageContainer(string name, string parent, Anchor anchor, Offset offset, CuiElementContainer container = null) : base(name, parent, anchor, offset, container) {
CurrentElement.Components.Insert(0, new CuiImageComponent { Color = "1 1 1 1" });
}
public static new ImageContainer Create(string name, string parent, Anchor anchor, Offset offset) => new ImageContainer(name, parent, anchor, offset);
public static new ImageContainer Create(BaseContainer parent, Anchor anchor, Offset offset) => new ImageContainer(CuiHelper.GetGuid(), parent.Name, anchor, offset, parent.Elements);
}
public class TextContainer : BaseContainer
{
public TextContainer(string name, string parent, Anchor anchor, Offset offset, CuiElementContainer container = null) : base(name, parent, anchor, offset, container) {
CurrentElement.Components.Insert(0, new CuiTextComponent { Text = "", Color = "1 1 1 1", FontSize = 14, Align = TextAnchor.MiddleCenter });
}
public static new TextContainer Create(BaseContainer parent, Anchor anchor, Offset offset) => new TextContainer(CuiHelper.GetGuid(), parent.Name, anchor, offset, parent.Elements);
}
public class ButtonContainer : BaseContainer
{
public ButtonContainer(string name, string parent, Anchor anchor, Offset offset, CuiElementContainer container = null) : base(name, parent, anchor, offset, container) {
CurrentElement.Components.Insert(0, new CuiButtonComponent { Color = "1 1 1 1" });
}
public static new ButtonContainer Create(BaseContainer parent, Anchor anchor, Offset offset) => new ButtonContainer(CuiHelper.GetGuid(), parent.Name, anchor, offset, parent.Elements);
}
public class InputFieldContainer : BaseContainer
{
public InputFieldContainer(string name, string parent, Anchor anchor, Offset offset, CuiElementContainer container = null) : base(name, parent, anchor, offset, container) {
CurrentElement.Components.Insert(0, new CuiInputFieldComponent { Text = "", Color = "1 1 1 1", FontSize = 14, Align = TextAnchor.MiddleLeft });
}
public static new InputFieldContainer Create(BaseContainer parent, Anchor anchor, Offset offset) => new InputFieldContainer(CuiHelper.GetGuid(), parent.Name, anchor, offset, parent.Elements);
}
public class OutlineComponent
{
public Color Color;
public OutlineComponent(Color color) { Color = color; }
}
public class CommandCallbackHandler
{
public Dictionary<string, Action<ConsoleSystem.Arg>> Callbacks = new Dictionary<string, Action<ConsoleSystem.Arg>>();
private AutoCodeLock _plugin;
public CommandCallbackHandler(AutoCodeLock plugin) { _plugin = plugin; }
public void Register(string name, Action<ConsoleSystem.Arg> action)
{
Callbacks[name] = action;
}
}
public static class BasePlayerExtensions
{
public static bool HasPermission(this BasePlayer player, string perm)
{
if (player == null || string.IsNullOrEmpty(perm)) return false;
return Oxide.Core.Interface.Oxide.GetLibrary<Permission>().UserHasPermission(player.userID.ToString(), perm);
}
}
public class BaseConfigData { public VersionNumber Version; }
#endregion
[Info("AutoCodeLock", "k1lly0u", "3.0.10")]
[Description("Codelock & door automation tools")]
public class AutoCodeLock : ChaosPlugin
{
public static AutoCodeLock Instance { get; private set; }
public AutoCodeLock()
{
Instance = this;
}
#region Fields
private const string PERMISSION_DEPLOY_DOOR = "autocodelock.deploydoor";
private const string PERMISSION_DEPLOY_BOX = "autocodelock.deploybox";
private const string PERMISSION_DEPLOY_LOCKER = "autocodelock.deploylocker";
private const string PERMISSION_DEPLOY_CUPBOARD = "autocodelock.deploycup";
private const string PERMISSION_AUTO_LOCK = "autocodelock.autolock";
private const string PERMISSION_NO_LOCK_NEEDED = "autocodelock.nolockneed";
private const string PERMISSION_DOOR_CLOSER = "autocodelock.doorcloser";
private static readonly DateTime Epoch = new DateTime(1970, 1, 1);
#endregion
#region Oxide Hooks
private Harmony _harmony;
private void Init()
{
try
{
_harmony = new Harmony("com.chaos.autocodelock");
_harmony.PatchAll(System.Reflection.Assembly.GetExecutingAssembly());
}
catch (Exception ex)
{
Puts($"Failed to apply Harmony patches: {ex}");
}
Puts("AutoCodeLock: Init started...");
permission.RegisterPermission(PERMISSION_DEPLOY_DOOR, this);
permission.RegisterPermission(PERMISSION_DEPLOY_BOX, this);
permission.RegisterPermission(PERMISSION_DEPLOY_LOCKER, this);
permission.RegisterPermission(PERMISSION_DEPLOY_CUPBOARD, this);
permission.RegisterPermission(PERMISSION_AUTO_LOCK, this);
permission.RegisterPermission(PERMISSION_NO_LOCK_NEEDED, this);
permission.RegisterPermission(PERMISSION_DOOR_CLOSER, this);
}
private void Loaded()
{
LoadConfig();
if (Configuration == null)
{
Interface.Oxide.LogError("Configuration failed to load! Plugin features will not work correctly.");
return;
}
string cmdMain = string.IsNullOrEmpty(Configuration.Command) ? "codelock" : Configuration.Command;
string cmdCloser = string.IsNullOrEmpty(Configuration.CloserCommand) ? "closer" : Configuration.CloserCommand;
cmd.AddChatCommand(cmdMain, this, nameof(CodelockCommand));
cmd.AddChatCommand(cmdCloser, this, nameof(AutoDoorCommand));
SetupUIComponents();
LoadData();
}
protected override void LoadConfig()
{
base.LoadConfig();
try
{
ConfigData data = Config.ReadObject<ConfigData>();
if (data == null)
{
LoadDefaultConfig();
return;
}
ConfigurationData = data;
}
catch
{
LoadDefaultConfig();
}
// Ensure nested objects are initialized to prevent NREs
if (Configuration.Other == null) Configuration.Other = new ConfigData.OtherOptions();
if (Configuration.Delay == null) Configuration.Delay = new ConfigData.DelayOptions();
if (Configuration.Defaults == null) Configuration.Defaults = new ConfigData.DefaultSettings();
if (Configuration.Data == null) Configuration.Data = new ConfigData.DataManagement();
if (Configuration.Colors == null) Configuration.Colors = new ConfigData.UIColors();
SaveConfig();
}
protected override void LoadDefaultConfig()
{
Puts("Generating new configuration file");
ConfigurationData = GenerateDefaultConfiguration<BaseConfigData>();
}
protected override void SaveConfig()
{
if (Configuration != null)
Config.WriteObject(Configuration, true);
}
private void OnServerInitialized()
{
if (Configuration == null || Configuration.Data == null)
{
Interface.Oxide.LogError("Configuration is not properly initialized!");
return;
}
FindRegisterEntities();
if (Configuration.Data.PurgeAfter > 0)
PurgeOldData();
foreach (BasePlayer player in BasePlayer.activePlayerList)
OnPlayerConnected(player);
PlayerEntities.EnqueueUpdateDoorCloserDelays();
}
private void OnPlayerConnected(BasePlayer player)
{
if (HasAnyPermission(player))
{
StoredData.PlayerData playerData = storedData.FindPlayerData(player.userID);
if (playerData == null)
playerData = storedData.SetupPlayer(player.userID, Configuration);
playerData.UpdateDelays(Configuration.Delay);
playerData.SetLastOnline(UnixTimeStampUtc());
}
}
private void OnPlayerDisconnected(BasePlayer player)
{
StoredData.PlayerData playerData = storedData.FindPlayerData(player.userID);
playerData?.SetLastOnline(UnixTimeStampUtc());
}
private void OnEntitySpawned(CodeLock codeLock) => NextTick(() =>
{
if (!codeLock)
return;
PlayerEntities.GetOrCreate(codeLock.OwnerID)?.AddEntity(codeLock);
});
private void OnEntitySpawned(DoorCloser doorCloser) => NextTick(() =>
{
if (!doorCloser)
return;
PlayerEntities.GetOrCreate(doorCloser.OwnerID)?.AddEntity(doorCloser);
});
private void OnEntityKill(CodeLock codeLock)
{
if (!codeLock)
return;
PlayerEntities.Get(codeLock.OwnerID)?.RemoveEntity(codeLock, true);
}
private void OnEntityKill(DoorCloser doorCloser)
{
if (!doorCloser)
return;
PlayerEntities.Get(doorCloser.OwnerID)?.RemoveEntity(doorCloser);
}
private void OnItemDeployed(Deployer deployer, BaseEntity entity)
{
if (!deployer || !entity || entity.OwnerID == 0UL)
return;
if (deployer.GetDeployable().slot != BaseEntity.Slot.Lock || entity.GetSlot(BaseEntity.Slot.Lock) is not CodeLock)
return;
BasePlayer owner = deployer.GetOwnerPlayer();
if (!owner)
return;
if (owner.HasPermission(PERMISSION_AUTO_LOCK))
{
StoredData.PlayerData playerData = storedData.FindPlayerData(owner.userID) ?? storedData.SetupPlayer(owner.userID, Configuration);
if (!playerData.IsSet(Options.AutoLock) || !CanDeployLock(owner, entity))
return;
CodeLock codelock = entity.GetSlot(BaseEntity.Slot.Lock) as CodeLock;
if (!codelock)
return;
SetCodeLock(codelock, owner, playerData);
}
}
private void OnEntityBuilt(Planner planner, GameObject obj)
{
if (!obj)
return;
BaseEntity entity = obj.ToBaseEntity();
if (!planner || !entity || entity.OwnerID == 0UL)
return;
if (entity is not (Door or BoxStorage or Locker or BuildingPrivlidge))
return;
BasePlayer player = planner.GetOwnerPlayer();
if (!player)
return;
StoredData.PlayerData playerData = storedData.FindPlayerData(player.userID);
if (playerData == null)
return;
NextTick(() =>
{
if (!player || !entity || entity.IsDestroyed)
return;
if (entity is Door door)
{
if (door.canTakeLock && player.HasPermission(PERMISSION_DEPLOY_DOOR))
PlaceCodeLock(player, door, playerData);
if ((door.canTakeCloser || door.HasSlot(BaseEntity.Slot.UpperModifier)) &&
player.HasPermission(PERMISSION_DOOR_CLOSER) && playerData.IsSet(Options.DeployDoorCloser))
PlaceDoorCloser(player, door, playerData);
return;
}
if (entity is BoxStorage && entity.HasSlot(BaseEntity.Slot.Lock))
{
if (player.HasPermission(PERMISSION_DEPLOY_BOX))
PlaceCodeLock(player, entity, playerData);
return;
}
if (entity is Locker && entity.HasSlot(BaseEntity.Slot.Lock))
{
if (player.HasPermission(PERMISSION_DEPLOY_LOCKER))
PlaceCodeLock(player, entity, playerData);
return;
}
if (entity is BuildingPrivlidge && entity.HasSlot(BaseEntity.Slot.Lock))
{
if (player.HasPermission(PERMISSION_DEPLOY_CUPBOARD))
PlaceCodeLock(player, entity, playerData);
return;
}
});
}
private object CanPickupEntity(BasePlayer player, DoorCloser closer)
{
if (player.IsAdmin && Configuration.Other.AdminBypass)
return null;
if (Configuration.Other.PreventDoorCloserPickup)
return false;
return null;
}
private void OnServerSave() => SaveData();
private void Unload()
{
_harmony?.UnpatchAll("com.chaos.autocodelock");
if (!Interface.Oxide.IsShuttingDown)
SaveData();
foreach (BasePlayer player in BasePlayer.activePlayerList)
{
CuiHelper.DestroyUi(player, UI_MENU);
CuiHelper.DestroyUi(player, UI_BG);
}
UpdateQueue.OnUnload();
}
#endregion
#region Functions
private void FindRegisterEntities()
{
foreach (BaseNetworkable baseNetworkable in BaseNetworkable.serverEntities)
{
if (baseNetworkable is DoorCloser doorCloser)
{
AdjustDoorCloserPosition(doorCloser.GetParentEntity(), doorCloser);
PlayerEntities.GetOrCreate(doorCloser.OwnerID)?.AddEntity(doorCloser);
}
else if (baseNetworkable is CodeLock codeLock)
PlayerEntities.GetOrCreate(codeLock.OwnerID)?.AddEntity(codeLock);
}
}
private bool CanDeployLock(BasePlayer player, BaseEntity entity)
{
if (!(player.IsAdmin && Configuration.Other.AdminBypass))
{
if (player.userID != entity.OwnerID)
{
SendMessage(player, "Notification.NotLocked");
return false;
}
object externalPlugins = Interface.CallHook("CanAutoLock", player);
if (externalPlugins != null)
{
SendMessage(player, "Notification.NotLocked.Plugin", externalPlugins is string ? (string) externalPlugins : string.Empty);
return false;
}
if (NoEscape.IsLoaded)
{
if (Configuration.Other.CheckRaidBlock)
{
if (NoEscape.IsRaidBlocked(player))
{
SendMessage(player, "Notification.NotLocked.RaidBlock");
return false;
}
}
if (Configuration.Other.CheckCombatBlock)
{
if (NoEscape.IsCombatBlocked(player))
{
SendMessage(player, "Notification.NotLocked.CombatBlock");
return false;
}
}
}
}
return true;
}
private void PlaceCodeLock(BasePlayer player, BaseEntity entity, StoredData.PlayerData playerData)
{
if (!CanDeployLock(player, entity))
return;
const string CODELOCK_PREFAB = "assets/prefabs/locks/keypad/lock.code.prefab";
CodeLock codelock = GameManager.server.CreateEntity(CODELOCK_PREFAB) as CodeLock;
if (!codelock)
return;
codelock.OwnerID = player.userID;
codelock.SetParent(entity, entity.GetSlotAnchorName(BaseEntity.Slot.Lock));
if (player.HasPermission(PERMISSION_AUTO_LOCK) && playerData.IsSet(Options.AutoLock))
SetCodeLock(codelock, player, playerData);
codelock.Spawn();
entity.SetSlot(BaseEntity.Slot.Lock, codelock);
}
private void PlaceDoorCloser(BasePlayer player, BaseEntity entity, StoredData.PlayerData playerData)
{
const string DOOR_CLOSER_PREFAB = "assets/prefabs/misc/doorcloser/doorcloser.prefab";
DoorCloser doorCloser = GameManager.server.CreateEntity(DOOR_CLOSER_PREFAB) as DoorCloser;
if (!doorCloser)
return;
doorCloser.gameObject.Identity();
doorCloser.OwnerID = player.userID;
if (entity.ShortPrefabName is "floor.ladder.hatch" or "floor.triangle.ladder.hatch")
doorCloser.delay = playerData.hatchCloseDelay;
else doorCloser.delay = playerData.doorCloseDelay;
doorCloser.SetParent(entity, entity.GetSlotAnchorName(BaseEntity.Slot.UpperModifier));
doorCloser.OnDeployed(entity, null, null);
AdjustDoorCloserPosition(entity, doorCloser);
doorCloser.Spawn();
entity.SetSlot(BaseEntity.Slot.UpperModifier, doorCloser);
}
private void AdjustDoorCloserPosition(BaseEntity entity, DoorCloser doorCloser)
{
if (!entity || !doorCloser)
return;
bool isHidden = Configuration.HideDoorClosers;
if (entity.ShortPrefabName == "floor.ladder.hatch")
doorCloser.transform.localPosition = isHidden ? new Vector3(0.75f, 0f, 0f) : new Vector3(0.7f, 0f, 0f);
else if (entity.ShortPrefabName == "floor.triangle.ladder.hatch")
doorCloser.transform.localPosition = isHidden ? new Vector3(-0.85f, 0f, 0f) : new Vector3(-0.8f, 0f, 0f);
else if (entity.ShortPrefabName.StartsWith("door.double.hinged"))
{
doorCloser.transform.localPosition = isHidden ? new Vector3(0f, 2.4f, 0f) : new Vector3(0f, 2.3f, 0f);
doorCloser.transform.localRotation = isHidden ? Quaternion.Euler(0f, 0f, 90f) : Quaternion.identity;
}
else if (entity.ShortPrefabName == "wall.frame.garagedoor")
doorCloser.transform.localPosition = isHidden ? new Vector3(-0.15f, 2.9f, 0f) : new Vector3(0f, 2.85f, 0f);
else
{
doorCloser.transform.localPosition = isHidden ? new Vector3(0.01f, 0.21f, 0f) : Vector3.zero;
doorCloser.transform.localRotation = isHidden ? Quaternion.Euler(0f, 0f, 90f) : Quaternion.identity;
}
}
private void SetCodeLock(CodeLock codelock, BasePlayer owner, StoredData.PlayerData playerData)
{
codelock.code = GetOrSetPin(playerData);
codelock.hasCode = true;
codelock.whitelistPlayers.Add(owner.userID);
codelock.SetFlag(BaseEntity.Flags.Locked, true, false);
Effect.server.Run("assets/prefabs/locks/keypad/effects/lock.code.updated.prefab", codelock.transform.position);
bool streamerMode = owner.net.connection.info.GetBool("global.streamermode");
string code = streamerMode ? "****" : codelock.code;
SendMessage(owner, "Notification.CodelockSecured", code);
}
private string GetOrSetPin(StoredData.PlayerData playerData)
{
if (string.IsNullOrEmpty(playerData.pinCode))
playerData.pinCode = $"{UnityEngine.Random.Range(1, 9999)}".PadLeft(4, '0');
return playerData.pinCode;
}
private string GetOrSetGuest(StoredData.PlayerData playerData)
{
if (string.IsNullOrEmpty(playerData.guestCode))
playerData.guestCode = $"{UnityEngine.Random.Range(1, 9999)}".PadLeft(4, '0');
return playerData.guestCode;
}
private static int UnixTimeStampUtc() => (int)DateTime.UtcNow.Subtract(Epoch).TotalSeconds;
private void SendMessage(BasePlayer player, string key, params object[] args)
{
string prefix = Configuration.Other.ChatPrefix ? lang.GetMessage("Prefix", this, player.UserIDString) : string.Empty;
if (args?.Length > 0)
player.ChatMessage(prefix + string.Format(lang.GetMessage(key, this, player.UserIDString), args));
else player.ChatMessage(prefix + lang.GetMessage(key, this, player.UserIDString));
}
private void PurgeOldData()
{
List<ulong> purgeList = Facepunch.Pool.Get<List<ulong>>();
int currentTimeStamp = UnixTimeStampUtc();
foreach (KeyValuePair<ulong, StoredData.PlayerData> kvp in storedData.playerData)
{
if (currentTimeStamp - kvp.Value.lastOnline > (Configuration.Data.PurgeAfter * 86400))
purgeList.Add(kvp.Key);
}
for (int i = 0; i < purgeList.Count; i++)
storedData.playerData.Remove(purgeList[i]);
Facepunch.Pool.FreeUnmanaged(ref purgeList);
}
#endregion
#region Commands
private bool HasAnyPermission(BasePlayer player) => player.HasPermission(PERMISSION_AUTO_LOCK) || player.HasPermission(PERMISSION_DEPLOY_BOX) ||
player.HasPermission(PERMISSION_DEPLOY_DOOR) || player.HasPermission(PERMISSION_DOOR_CLOSER) ||
player.HasPermission(PERMISSION_DEPLOY_LOCKER) || player.HasPermission(PERMISSION_DEPLOY_CUPBOARD);
private void CodelockCommand(BasePlayer player, string command, string[] args)
{
if (!HasAnyPermission(player))
{
SendMessage(player, "Notification.NoPermission");
return;
}
BaseContainer bg = ImageContainer.Create(UI_BG, Layer.Overall, Anchor.FullStretch, Offset.zero)
.WithColor(new Color("000000", 0.0f))
.NeedsCursor()
.NeedsKeyboard()
.WithChildren(uiContainer =>
{
ButtonContainer.Create(uiContainer, Anchor.FullStretch, Offset.zero)
.WithColor(new Color("000000", 0.0f))
.WithCallback(m_CallbackHandler, (arg) => CloseMenu(player), $"exitbg{player.userID}");
});
ChaosUI.Show(player, bg);
CreateCodelockUI(player);
}
private void CloseMenu(BasePlayer player)
{
ChaosUI.Destroy(player, UI_MENU);
ChaosUI.Destroy(player, UI_BG);
}
private void AutoDoorCommand(BasePlayer player, string command, string[] args)
{
if (!player.HasPermission(PERMISSION_DOOR_CLOSER))
{
SendMessage(player, "Notification.NoPermission");
return;
}
if (!player.IsBuildingAuthed())
{
SendMessage(player, "Notification.NoBuildPriv");
return;
}
if (Physics.Raycast(player.eyes.HeadRay(), out RaycastHit raycastHit, 3f, 1 << (int)Rust.Layer.Construction, QueryTriggerInteraction.Ignore))
{
Door door = raycastHit.GetEntity() as Door;
if (door)
{
StoredData.PlayerData playerData = storedData.FindPlayerData(player.userID) ?? storedData.SetupPlayer(player.userID, Configuration);
if (door.OwnerID != player.userID)
{
SendMessage(player, "Notification.NotDoorOwner");
return;
}
DoorCloser doorCloser = door.GetSlot(BaseEntity.Slot.UpperModifier) as DoorCloser;
if (doorCloser)
{
doorCloser.Kill(BaseNetworkable.DestroyMode.None);
SendMessage(player, "Notification.DoorCloserRemoved");
}
else
{
PlaceDoorCloser(player, door, playerData);
SendMessage(player, "Notification.DoorCloserPlaced");
}
return;
}
}
SendMessage(player, "Notification.NoDoorFound");
}
[ConsoleCommand("aclock_cb")]
private void CuiCallback(ConsoleSystem.Arg arg)
{
if (arg != null && arg.Args != null && arg.Args.Length > 0)
{
string name = arg.Args[0];
if (m_CallbackHandler != null && m_CallbackHandler.Callbacks.TryGetValue(name, out var action))
{
action(arg);
}
}
}
#endregion
#region UI
private const string UI_MENU = "acl.menu";
private const string UI_BG = "acl.menu.bg";
private Style m_BackgroundStyle;
private Style m_PanelStyle;
private Style m_ButtonStyle;
private Style m_TitleStyle;
private Style m_CloseStyle;
private Color m_ToggleColor;
private OutlineComponent m_OutlineRed;
private CommandCallbackHandler m_CallbackHandler;
private void SetupUIComponents()
{
m_CallbackHandler = new CommandCallbackHandler(this);
m_BackgroundStyle = new Style
{
ImageColor = new Color(Configuration.Colors.Background.Hex, Configuration.Colors.Background.Alpha),
Sprite = Sprites.Background_Rounded,
ImageType = Image.Type.Tiled
};
m_PanelStyle = new Style
{
ImageColor = new Color(Configuration.Colors.Panel.Hex, Configuration.Colors.Panel.Alpha),
Sprite = Sprites.Background_Rounded,
ImageType = Image.Type.Tiled
};
m_ButtonStyle = new Style
{
ImageColor = new Color(Configuration.Colors.Button.Hex, Configuration.Colors.Button.Alpha),
Sprite = Sprites.Background_Rounded,
ImageType = Image.Type.Tiled,
Alignment = TextAnchor.MiddleCenter,
FontSize = 14
};
m_TitleStyle = new Style
{
FontSize = 18,
Font = Font.PermanentMarker,
Alignment = TextAnchor.MiddleLeft,
WrapMode = VerticalWrapMode.Overflow
};
m_CloseStyle = new Style
{
FontSize = 18,
Alignment = TextAnchor.MiddleCenter,
WrapMode = VerticalWrapMode.Overflow,
};
m_ToggleColor = new Color(Configuration.Colors.Highlight.Hex, Configuration.Colors.Highlight.Alpha);
m_OutlineRed = new OutlineComponent(new Color(Configuration.Colors.Close.Hex, Configuration.Colors.Close.Alpha));
}
private void CreateCodelockUI(BasePlayer player)
{
StoredData.PlayerData playerData = storedData.FindPlayerData(player.userID) ?? storedData.SetupPlayer(player.userID, Configuration);
const float BASE_HEIGHT = 45;
const float ELEMENT_HEIGHT = 30;
float height = BASE_HEIGHT;
bool canAutoLock = player.HasPermission(PERMISSION_AUTO_LOCK);
if (canAutoLock)
{
if (playerData.IsSet(Options.AutoLock))
{
height += ELEMENT_HEIGHT * 2; // For toggle and pin code
}
else height += ELEMENT_HEIGHT;
}
bool canDoorCloser = player.HasPermission(PERMISSION_DOOR_CLOSER);
if (canDoorCloser)
height += ELEMENT_HEIGHT * 4;
height += 5f;
BaseContainer root = BaseContainer.Create(UI_MENU, UI_BG, Anchor.FullStretch, Offset.zero)
.DestroyExisting()
.WithChildren(uiContainer =>
{
// The actual menu panel
ImageContainer.Create(uiContainer, Anchor.Center, new Offset(-165f, -(height * 0.5f), 165f, (height * 0.5f)))
.WithStyle(m_BackgroundStyle)
.WithChildren(parent =>
{
CreateHeaderBar(parent, player);
ImageContainer.Create(parent, Anchor.FullStretch, new Offset(5f, 5f, -5f, -40f))
.WithStyle(m_PanelStyle)
.WithChildren(layout =>
{
int elementIndex = 0;
if (canAutoLock)
{
CreateToggleElement(layout, player, playerData, Options.AutoLock, ++elementIndex);
if (playerData.IsSet(Options.AutoLock))
{
CreateInputElement(layout, player, playerData, Options.PinCode, ++elementIndex, 4);
}
}
if (canDoorCloser)
{
CreateToggleElement(layout, player, playerData, Options.DeployDoorCloser, ++elementIndex);
CreateInputElement(layout, player, playerData, Options.DoorDelay, ++elementIndex);
CreateInputElement(layout, player, playerData, Options.HatchDelay, ++elementIndex);
CreateToggleElement(layout, player, playerData, Options.DisableCloser, ++elementIndex);
}
});
});
});
ChaosUI.Show(player, root);
}
private void CreateHeaderBar(BaseContainer parent, BasePlayer player)
{
ImageContainer.Create(parent, Anchor.TopStretch, new Offset(5f, -35f, -5f, -5f))
.WithStyle(m_PanelStyle)
.WithChildren(titleBar =>
{
TextContainer.Create(titleBar, Anchor.CenterLeft, new Offset(5f, -15f, 285f, 15f))
.WithStyle(m_TitleStyle)
.WithText(GetString("Label.Title", player));
ImageContainer.Create(titleBar, Anchor.CenterRight, new Offset(-25f, -10f, -5f, 10f))
.WithStyle(m_ButtonStyle)
.WithOutline(m_OutlineRed)
.WithChildren(exit =>
{
ImageContainer.Create(exit, Anchor.FullStretch, new Offset(2.5f, 2.5f, -2.5f, -2.5f))
.WithColor(new Color("ff4d4d", 1f))
.WithSprite(Icon.Close);
ButtonContainer.Create(exit, Anchor.FullStretch, Offset.zero)
.WithColor(new Color("000000", 0.0f))
.WithCallback(m_CallbackHandler, (arg) => CloseMenu(player), $"{player.UserIDString}.exit");
});
});
}
private void CreateInputElement(BaseContainer layout, BasePlayer player, StoredData.PlayerData playerData, Options option, int index, int characterLimit = 0)
{
const float ELEMENT_HEIGHT = 30;
float bottom = -(ELEMENT_HEIGHT * index);
BaseContainer.Create(layout, Anchor.TopStretch, new Offset(5f, bottom, -5, bottom + 25f))
.WithChildren(inputTemplate =>
{
ImageContainer.Create(inputTemplate, Anchor.FullStretch, new Offset(0f, 0f, -95f, 0f))
.WithStyle(m_PanelStyle);
ImageContainer.Create(inputTemplate, Anchor.FullStretch, new Offset(217.5f, 0f, -27.5f, 0f))
.WithStyle(m_PanelStyle);
ImageContainer.Create(inputTemplate, Anchor.CenterRight, new Offset(-25f, -12.5f, 0f, 12.5f))
.WithStyle(m_PanelStyle);
TextContainer.Create(inputTemplate, Anchor.FullStretch, new Offset(5f, 0f, 0f, 0f))
.WithText(GetString($"Label.{option}", player))
.WithAlignment(TextAnchor.MiddleLeft);
ImageContainer.Create(inputTemplate, Anchor.CenterRight, new Offset(-90f, -10f, -30f, 10f))
.WithStyle(m_ButtonStyle)
.WithChildren(inputField =>
{
string currentValue = GetValue(player, playerData, option);
InputFieldContainer.Create(inputField, Anchor.FullStretch, Offset.zero)
.WithText(currentValue)
.WithAlignment(TextAnchor.MiddleCenter)
.WithCharacterLimit(characterLimit)
.WithCallback(m_CallbackHandler, arg =>
{
SetValue(playerData, option, arg);
CreateCodelockUI(player);
}, $"{player.UserIDString}.{option}");
});
ImageContainer.Create(inputTemplate, Anchor.CenterRight, new Offset(-22.5f, -10f, -2.5f, 10f))
.WithStyle(m_ButtonStyle)
.WithChildren(applyButton =>
{
ImageContainer.Create(applyButton, Anchor.FullStretch, new Offset(2.5f, 2.5f, -2.5f, -2.5f))
.WithSprite(Icon.Download);
ButtonContainer.Create(applyButton, Anchor.FullStretch, Offset.zero)
.WithColor(Color.Clear)
.WithCallback(m_CallbackHandler, arg => ApplyChanges(player, playerData, option), $"{player.UserIDString}.{option}.apply")
// ... [Code preview truncated due to length. Full source in attached file] ...