From 8aa24bbc8a749acea4c872b7bd157dd1c4c24467 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 22 Feb 2021 03:54:23 +0000 Subject: [PATCH] chore(release): 1.4.0 [skip ci] # [1.4.0](https://github.com/RebeccaStevens/uMod-Rust-Plugin-AutoCode/compare/v1.3.0...v1.4.0) (2021-02-22) ### Bug Fixes * display guest code as well as code when code lock is placed with one ([18b587c](https://github.com/RebeccaStevens/uMod-Rust-Plugin-AutoCode/commit/18b587ce5f6d63294ed13a822bf8056500442efa)) * formatting on message when a code lock is placed ([659e0bf](https://github.com/RebeccaStevens/uMod-Rust-Plugin-AutoCode/commit/659e0bf98058b4f4c19c7be3f5dd5902f5527981)) * update user messages ([1a064f2](https://github.com/RebeccaStevens/uMod-Rust-Plugin-AutoCode/commit/1a064f2a9dd261941e8048e015377e064f083ed3)) ### Features * add extended help message ([3ca82a0](https://github.com/RebeccaStevens/uMod-Rust-Plugin-AutoCode/commit/3ca82a059591694540a5771d9f190d88aa5c1423)) * add quiet mode ([38b87b6](https://github.com/RebeccaStevens/uMod-Rust-Plugin-AutoCode/commit/38b87b6d9d748b78b83f983bddf404fb6809e802)) * allow changing the icon displayed to the user in the in-game chat ([8874903](https://github.com/RebeccaStevens/uMod-Rust-Plugin-AutoCode/commit/88749039acf28c499b5704b3abef60c1d3ef0640)) * allow for optional "set" before code in command ([281f71d](https://github.com/RebeccaStevens/uMod-Rust-Plugin-AutoCode/commit/281f71dc2b0f328fe4abf9b47df8e27a8d4da790)) --- AutoCode.cs | 487 +++++++++++++++++++++++++++++++++++++++++++-------- CHANGELOG.md | 17 ++ 2 files changed, 432 insertions(+), 72 deletions(-) diff --git a/AutoCode.cs b/AutoCode.cs index 1e49dac..17cdfd5 100644 --- a/AutoCode.cs +++ b/AutoCode.cs @@ -8,11 +8,12 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; using UnityEngine; namespace Oxide.Plugins { - [Info("Auto Code", "slaymaster3000", "1.3.0")] + [Info("Auto Code", "BlueBeka", "1.4.0")] [Description("Automatically sets the code on code locks placed.")] class AutoCode : RustPlugin { @@ -21,6 +22,8 @@ class AutoCode : RustPlugin private Data data; private Dictionary tempCodeLocks; + private const string HiddenCode = "****"; + #region Hooks void Init() @@ -118,13 +121,17 @@ void OnEntitySpawned(CodeLock codeLock) // Lock the lock. codeLock.SetFlag(BaseEntity.Flags.Locked, true); - // Don't display code if in streamer mode. - if (!player.net.connection.info.GetBool("global.streamermode")) + if (!settings.quietMode) { - player.ChatMessage( + Message( + player, string.Format( - lang.GetMessage("CodeAutoLocked", this, player.UserIDString), - settings.code + lang.GetMessage( + codeLock.hasGuestCode ? "CodeAutoLockedWithGuest" : "CodeAutoLocked", + this, + player.UserIDString), + Formatter.Value(Utils.ShouldHideCode(player, settings) ? HiddenCode : codeLock.code), + Formatter.Value(Utils.ShouldHideCode(player, settings) ? HiddenCode : codeLock.guestCode) ) ); } @@ -159,24 +166,44 @@ object CanUseLockedEntity(BasePlayer player, CodeLock codeLock) protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary - { - { "NoPermission", "You don't have permission." }, - { "CodeAutoLocked", "Code lock placed with code {0}." }, - { "CodeUpdated", "Your new code is {0}." }, - { "GuestCodeUpdated", "Your new guest code is {0}." }, - { "CodeRemoved", "Your code has been removed." }, - { "GuestCodeRemoved", "Your guest code has been removed." }, - { "InvalidArgsTooMany", "Too many arguments supplied." }, - { "Info", "Code: {0}\nGuest Code: {1}\n\nUsage: {2}" }, - { "NotSet", "Not set." }, - { "SyntaxError", "Syntax Error: expected command in the form:\n{0}" }, - { "SpamPrevention", "Too many recent code sets. Please wait {0} and try again." }, - { "InvalidArguments", "Invalid arguments supplied." }, - { "ErrorNoPlayerFound", "Error: No player found." }, - { "ErrorMoreThanOnePlayerFound", "Error: More than one player found." }, - { "ResettingAllLockOuts", "Resetting lock outs for all players." }, - { "ResettingLockOut", "Resetting lock outs for {0}." }, - }, this); + { + { "Help", "Usage:\n{0}" }, + { "Description", "Automatically set the code on code locks you place." }, + { "Info", "Code: {0}\nGuest Code: {1}\nQuiet Mode: {2}" }, + { "NoPermission", "You don't have permission." }, + { "CodeAutoLocked", "Code lock placed with code {0}." }, + { "CodeAutoLockedWithGuest", "Code lock placed with code {0} and guest code {1}." }, + { "CodeUpdated", "Your auto-code has changed to {0}." }, + { "CodeUpdatedHidden", "New auto-code set." }, + { "GuestCodeUpdated", "Your guest auto-code has changed to {0}." }, + { "GuestCodeUpdatedHidden", "New guest auto-code set." }, + { "CodeRemoved", "Your auto-code has been removed." }, + { "GuestCodeRemoved", "Your guest auto-code has been removed." }, + { "InvalidArgsTooMany", "Too many arguments supplied." }, + { "NotSet", "Not set" }, + { "SyntaxError", "Syntax Error: expected command in the form:\n{0}" }, + { "SpamPrevention", "Too many recent auto-code sets. Please wait {0} and try again." }, + { "InvalidArguments", "Invalid arguments supplied." }, + { "ErrorNoPlayerFound", "Error: No player found." }, + { "ErrorMoreThanOnePlayerFound", "Error: More than one player found." }, + { "ResettingAllLockOuts", "Resetting lock outs for all players." }, + { "ResettingLockOut", "Resetting lock outs for {0}." }, + { "QuietModeEnable", "Quiet mode now enabled." }, + { "QuietModeDisable", "Quiet mode now disabled." }, + { "Enabled", "Enabled" }, + { "Disabled", "Disabled" }, + { "HelpExtendedCoreCommands", "Core Commands:" }, + { "HelpExtendedOtherCommands", "Other Commands:" }, + { "HelpExtendedInfo", "Show your settings:\n{0}" }, + { "HelpExtendedSetCode", "Set your auto-code to 1234:\n{0}" }, + { "HelpExtendedPickCode", "Open the code lock interface to set your auto-code:\n{0}" }, + { "HelpExtendedRandomCode", "Set your auto-code to a randomly generated code:\n{0}" }, + { "HelpExtendedRemoveCode", "Remove your set auto-code:\n{0}" }, + { "HelpExtendedCoreGuestCommands", "Each core command is also avalibale in a guest code version. e.g.\n{0}" }, + { "HelpExtendedQuietMode", "Toggles on/off quiet mode:\n{0}" }, + { "HelpExtendedQuietModeDetails", "Less messages will be shown and your auto-code will be hidden." }, + { "HelpExtendedHelp", "Displays this help message:\n{0}" }, + }, this); } #endregion @@ -210,7 +237,9 @@ public string GetCode(BasePlayer player, bool guest = false) /// The player to set the code for. /// The code to set for the given player. /// If true, the guest code will be set instead of the main code. - public void SetCode(BasePlayer player, string code, bool guest = false) + /// If true, no output message will be shown to the player. + /// If true, the new code won't be displayed to the user. Has no effect if quiet is true. + public void SetCode(BasePlayer player, string code, bool guest = false, bool quiet = false, bool hideCode = false) { if (!data.Inst.playerSettings.ContainsKey(player.userID)) { @@ -255,14 +284,18 @@ public void SetCode(BasePlayer player, string code, bool guest = false) // Locked out? if (lockedOut) { - player.ChatMessage( - string.Format( - lang.GetMessage("SpamPrevention", this, player.UserIDString), - TimeSpan.FromSeconds(Math.Ceiling(settings.lockedOutUntil - currentTime)).ToString(@"d\d\ h\h\ mm\m\ ss\s").TrimStart(' ', 'd', 'h', 'm', 's', '0'), - config.Options.SpamPrevention.LockOutTime, - config.Options.SpamPrevention.WindowTime - ) - ); + if (!quiet) + { + Message( + player, + string.Format( + lang.GetMessage("SpamPrevention", this, player.UserIDString), + TimeSpan.FromSeconds(Math.Ceiling(settings.lockedOutUntil - currentTime)).ToString(@"d\d\ h\h\ mm\m\ ss\s").TrimStart(' ', 'd', 'h', 'm', 's', '0'), + config.Options.SpamPrevention.LockOutTime, + config.Options.SpamPrevention.WindowTime + ) + ); + } return; } @@ -285,14 +318,18 @@ public void SetCode(BasePlayer player, string code, bool guest = false) settings.lastSet = currentTime; - // Don't display code if in streamer mode. - if (!player.net.connection.info.GetBool("global.streamermode")) + if (!quiet) { - player.ChatMessage( - string.Format( - lang.GetMessage(guest ? "GuestCodeUpdated" : "CodeUpdated", this, player.UserIDString), - code - ) + hideCode = hideCode || Utils.ShouldHideCode(player, settings); + + Message( + player, + hideCode + ? lang.GetMessage(guest ? "GuestCodeUpdatedHidden" : "CodeUpdatedHidden", this, player.UserIDString) + : string.Format( + lang.GetMessage(guest ? "GuestCodeUpdated" : "CodeUpdated", this, player.UserIDString), + Formatter.Value(code) + ) ); } } @@ -312,7 +349,8 @@ public void ToggleEnabled(BasePlayer player) /// /// The player to remove the code of. /// If true, the guest code will be removed instead of the main code. - public void RemoveCode(BasePlayer player, bool guest = false) + /// If true, no output message will be shown to the player. + public void RemoveCode(BasePlayer player, bool guest = false, bool quiet = false) { if (!data.Inst.playerSettings.ContainsKey(player.userID)) { @@ -330,7 +368,13 @@ public void RemoveCode(BasePlayer player, bool guest = false) // Remove the guest code both then removing the main code and when just removing the guest code. settings.guestCode = null; - player.ChatMessage(lang.GetMessage(guest ? "GuestCodeRemoved" : "CodeRemoved", this, player.UserIDString)); + if (!quiet) + { + Message( + player, + lang.GetMessage(guest ? "GuestCodeRemoved" : "CodeRemoved", this, player.UserIDString) + ); + } } [ObsoleteAttribute("This method is deprecated. Call IsValidCode instead.", false)] @@ -421,6 +465,41 @@ public void OpenCodeLockUI(BasePlayer player, bool guest = false) }); } + /// + /// Remove all lock outs caused by spam protection. + /// + /// The player to toggle quiet mode for. + /// If true, no output message will be shown to the player. + public void ToggleQuietMode(BasePlayer player, bool quiet = false) + { + // Load the player's settings. + Data.Structure.PlayerSettings settings = data.Inst.playerSettings[player.userID]; + + settings.quietMode = !settings.quietMode; + + if (!quiet) + { + if (settings.quietMode) + { + Message( + player, + string.Format( + "{0}\n" + Formatter.SmallLineGap + "{1}", + lang.GetMessage("QuietModeEnable", this, player.UserIDString), + lang.GetMessage("HelpExtendedQuietModeDetails", this, player.UserIDString) + ) + ); + } + else + { + Message( + player, + lang.GetMessage("QuietModeDisable", this, player.UserIDString) + ); + } + } + } + /// /// Reset (remove) all lock outs caused by spam protection. /// @@ -508,6 +587,22 @@ private void UnsubscribeFromUnneedHooks() } } + /// + /// Message the given player via the in-game chat. + /// + private void Message(BasePlayer player, string message) + { + if (string.IsNullOrEmpty(message) || !player.IsConnected) + return; + + player.SendConsoleCommand( + "chat.add", + 2, + config.Commands.ChatIconId, + message + ); + } + /// /// The Config for this plugin. /// @@ -534,6 +629,7 @@ public AutoCodeConfig(AutoCode plugin) public class CommandsDef { public string Use = "code"; + public ulong ChatIconId = 76561199143387303u; }; public OptionsDef Options = new OptionsDef(); @@ -697,6 +793,7 @@ public class PlayerSettings { public string code = null; public string guestCode = null; + public bool quietMode = false; public double lastSet = 0; public int timesSetInSpamWindow = 0; public double lockedOutUntil = 0; @@ -749,6 +846,9 @@ private class Commands public string PickCode = "pick"; public string RandomCode = "random"; public string RemoveCode = "remove"; + public string SetCode = "set"; + public string QuietMode = "quiet"; + public string Help = "help"; public Commands(AutoCode plugin) { @@ -851,7 +951,10 @@ private void HandleUse(BasePlayer player, string label, string[] args) { if (plugin.config.Options.DisplayPermissionErrors) { - player.ChatMessage(plugin.lang.GetMessage("NoPermission", plugin, player.UserIDString)); + plugin.Message( + player, + plugin.lang.GetMessage("NoPermission", plugin, player.UserIDString) + ); } return; } @@ -868,27 +971,55 @@ private void HandleUse(BasePlayer player, string label, string[] args) plugin.data.Inst.playerSettings.Add(player.userID, new Data.Structure.PlayerSettings()); } - string operation = args[0].ToLower(); + int nextArg = 0; + int argsRemainingCount = args.Length; + + string operation = args[nextArg++].ToLower(); + argsRemainingCount--; + bool guest = false; if (operation == Guest) { - if (args.Length < 2) + if (argsRemainingCount < 1) { SyntaxError(player, label, args); return; } guest = true; - operation = args[1].ToLower(); + operation = args[nextArg++].ToLower(); + argsRemainingCount--; + } + + if (operation == SetCode) + { + if (argsRemainingCount < 1) + { + SyntaxError(player, label, args); + return; + } + + operation = args[nextArg++].ToLower(); + argsRemainingCount--; + } + + // Help. + if (operation == Help) + { + plugin.Message(player, GetHelpExtended(player, label)); + return; } // Pick code. if (operation == PickCode) { - if ((guest && args.Length > 2) || (!guest && args.Length > 1)) + if (argsRemainingCount > 0) { - player.ChatMessage(string.Format(plugin.lang.GetMessage("InvalidArgsTooMany", plugin, player.UserIDString), label)); + plugin.Message( + player, + string.Format(plugin.lang.GetMessage("InvalidArgsTooMany", plugin, player.UserIDString), label) + ); return; } @@ -896,12 +1027,36 @@ private void HandleUse(BasePlayer player, string label, string[] args) return; } + // Toggle quiet mode. + if (operation == QuietMode) + { + if (guest) + { + SyntaxError(player, label, args); + return; + } + if (argsRemainingCount > 0) + { + plugin.Message( + player, + string.Format(plugin.lang.GetMessage("InvalidArgsTooMany", plugin, player.UserIDString), label) + ); + return; + } + + plugin.ToggleQuietMode(player); + return; + } + // Remove? if (operation == RemoveCode) { - if ((guest && args.Length > 2) || (!guest && args.Length > 1)) + if (argsRemainingCount > 0) { - player.ChatMessage(string.Format(plugin.lang.GetMessage("InvalidArgsTooMany", plugin, player.UserIDString), label)); + plugin.Message( + player, + string.Format(plugin.lang.GetMessage("InvalidArgsTooMany", plugin, player.UserIDString), label) + ); return; } @@ -912,22 +1067,35 @@ private void HandleUse(BasePlayer player, string label, string[] args) // Use random code? if (operation == RandomCode) { - if ((guest && args.Length > 2) || (!guest && args.Length > 1)) + if (argsRemainingCount > 0) { - player.ChatMessage(string.Format(plugin.lang.GetMessage("InvalidArgsTooMany", plugin, player.UserIDString), label)); + plugin.Message( + player, + string.Format(plugin.lang.GetMessage("InvalidArgsTooMany", plugin, player.UserIDString), label) + ); return; } - plugin.SetCode(player, plugin.GenerateRandomCode(), guest); + var hideCode = false; + if (plugin.data.Inst.playerSettings.ContainsKey(player.userID)) + { + Data.Structure.PlayerSettings settings = plugin.data.Inst.playerSettings[player.userID]; + hideCode = settings.quietMode; + } + + plugin.SetCode(player, plugin.GenerateRandomCode(), guest, false, hideCode); return; } // Use given code? if (plugin.IsValidCode(operation)) { - if ((guest && args.Length > 2) || (!guest && args.Length > 1)) + if (argsRemainingCount > 0) { - player.ChatMessage(string.Format(plugin.lang.GetMessage("InvalidArgsTooMany", plugin, player.UserIDString), label)); + plugin.Message( + player, + string.Format(plugin.lang.GetMessage("InvalidArgsTooMany", plugin, player.UserIDString), label) + ); return; } @@ -945,27 +1113,34 @@ private void ShowInfo(BasePlayer player, string label, string[] args) { string code = null; string guestCode = null; + bool quietMode = false; if (plugin.data.Inst.playerSettings.ContainsKey(player.userID)) { Data.Structure.PlayerSettings settings = plugin.data.Inst.playerSettings[player.userID]; code = settings.code; guestCode = settings.guestCode; + quietMode = settings.quietMode; - // Hide codes for those in streamer mode. - if (player.net.connection.info.GetBool("global.streamermode")) + if (Utils.ShouldHideCode(player, settings)) { - code = code == null ? null : "****"; - guestCode = guestCode == null ? null : "****"; + code = code == null ? null : HiddenCode; + guestCode = guestCode == null ? null : HiddenCode; } } - player.ChatMessage( + plugin.Message( + player, string.Format( - plugin.lang.GetMessage("Info", plugin, player.UserIDString), - code ?? plugin.lang.GetMessage("NotSet", plugin, player.UserIDString), - guestCode ?? plugin.lang.GetMessage("NotSet", plugin, player.UserIDString), - UsageInfo(label) + "{0}\n" + Formatter.SmallLineGap + "{1}\n" + Formatter.SmallLineGap + "{2}", + Formatter.H2(plugin.lang.GetMessage("Description", plugin, player.UserIDString)), + string.Format( + plugin.lang.GetMessage("Info", plugin, player.UserIDString), + code != null ? Formatter.Value(code) : Formatter.NoValue(plugin.lang.GetMessage("NotSet", plugin, player.UserIDString)), + guestCode != null ? Formatter.Value(guestCode) : Formatter.NoValue(plugin.lang.GetMessage("NotSet", plugin, player.UserIDString)), + quietMode ? Formatter.Value(plugin.lang.GetMessage("Enabled", plugin, player.UserIDString)) : Formatter.NoValue(plugin.lang.GetMessage("Disabled", plugin, player.UserIDString)) + ), + GetHelp(player, label) ) ); } @@ -975,30 +1150,113 @@ private void ShowInfo(BasePlayer player, string label, string[] args) /// private void SyntaxError(BasePlayer player, string label, string[] args) { - player.ChatMessage( + plugin.Message( + player, string.Format( plugin.lang.GetMessage("SyntaxError", plugin, player.UserIDString), - UsageInfo(label) + GetUsage(label) ) ); } + /// + /// Get help for the "use" command. + /// + public string GetHelp(BasePlayer player, string label) + { + return string.Format( + plugin.lang.GetMessage("Help", plugin, player.UserIDString), + GetUsage(label) + ); + } + /// /// Show how to use the "use" command. /// /// - private string UsageInfo(string label) + private string GetUsage(string label) { - return string.Format("/{0} {1}", label, HelpGetAllUseCommandArguments()); + return Formatter.Command( + string.Format( + "{0} [<{1}>]", + label, + string.Join("|", new string[] { + string.Format( + "[{0}] <{1}>", + Guest, + string.Join("|", new string[] { + string.Format( + "[{0}] {1}", + SetCode, + "1234" + ), + PickCode, + RandomCode, + RemoveCode + }) + ), + QuietMode, + Help + }) + ) + ); } /// - /// Get all the arguments that can be supplied to the "use" command. + /// Display an extended help messsage to the player. /// - /// - private string HelpGetAllUseCommandArguments() + public string GetHelpExtended(BasePlayer player, string label) { - return string.Format("[{0}] {1}", Guest, string.Join("|", new string[] { "1234", RandomCode, PickCode, RemoveCode })); + return string.Format( + "{0}", + string.Join( + "\n" + Formatter.SmallLineGap, + string.Join( + "\n" + Formatter.SmallLineGap, + Formatter.H2(plugin.lang.GetMessage("HelpExtendedCoreCommands", plugin, player.UserIDString)), + Formatter.UL( + string.Format( + plugin.lang.GetMessage("HelpExtendedInfo", plugin, player.UserIDString), + Formatter.Indent(Formatter.Command(string.Format("{0}", label))) + ), + string.Format( + plugin.lang.GetMessage("HelpExtendedSetCode", plugin, player.UserIDString), + Formatter.Indent(Formatter.Command(string.Format("{0} {1}", label, "1234"))) + ), + string.Format( + plugin.lang.GetMessage("HelpExtendedPickCode", plugin, player.UserIDString), + Formatter.Indent(Formatter.Command(string.Format("{0} {1}", label, PickCode))) + ), + string.Format( + plugin.lang.GetMessage("HelpExtendedRandomCode", plugin, player.UserIDString), + Formatter.Indent(Formatter.Command(string.Format("{0} {1}", label, RandomCode))) + ), + string.Format( + plugin.lang.GetMessage("HelpExtendedRemoveCode", plugin, player.UserIDString), + Formatter.Indent(Formatter.Command(string.Format("{0} {1}", label, RemoveCode))) + ) + ), + string.Format( + plugin.lang.GetMessage("HelpExtendedCoreGuestCommands", plugin, player.UserIDString), + Formatter.Indent(Formatter.Command(string.Format("{0} {1} {2}", label, Guest, "5678"))) + ) + ), + string.Join( + "\n" + Formatter.SmallLineGap, + Formatter.H2(plugin.lang.GetMessage("HelpExtendedOtherCommands", plugin, player.UserIDString)), + Formatter.UL( + string.Format( + plugin.lang.GetMessage("HelpExtendedQuietMode", plugin, player.UserIDString), + Formatter.Indent(Formatter.Command(string.Format("{0} {1}", label, QuietMode))) + ), + string.Format( + plugin.lang.GetMessage("HelpExtendedHelp", plugin, player.UserIDString), + Formatter.Indent(Formatter.Command(string.Format("{0} {1}", label, Help))) + ) + ) + ) + ) + ); } } @@ -1012,6 +1270,91 @@ private static class Utils /// /// The number of seconds that have passed since 1970-01-01. public static double CurrentTime() => DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds; + + /// + /// Should the code for the given player be hidden in messages? + /// + public static bool ShouldHideCode(BasePlayer player, Data.Structure.PlayerSettings settings) + { + return settings.quietMode || player.net.connection.info.GetBool("global.streamermode"); + } + } + + /// + /// Text formatting functions for in-game chat. + /// + private static class Formatter + { + /// + /// A line break in a very small font. + /// + public const string SmallestLineGap = "\n"; + + /// + /// A line break in a small font. + /// + public const string SmallLineGap = "\n"; + + /// + /// Format the given text as a header level 1. + /// + public static string H1(string text) + { + return string.Format("{0}", text); + } + + /// + /// Format the given text as a header level 2. + /// + public static string H2(string text) + { + return string.Format("{0}", text); + } + + /// + /// Small text. + /// + public static string Small(string text) + { + return string.Format("{0}", text); + } + + /// + /// Format the items as an unordered list. + /// + public static string UL(params string[] items) + { + return string.Join( + "\n" + SmallestLineGap, + items.Select(item => string.Format(" - {0}", item)) + ); + } + + /// + /// Indent the given text. + /// + public static string Indent(string text) + { + return string.Format(" {0}", text); + } + + /// + /// Format the given text as a command. + /// + public static string Command(string text) + { + return string.Format("{0}", text); + } + + public static string Value(string text) + { + return string.Format("{0}", text); + } + + public static string NoValue(string text) + { + return string.Format("{0}", text); + } } /// diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b50f69..af04eeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,23 @@ # Changelog All notable changes to this project will be documented in this file. Dates are displayed in UTC. +# [1.4.0](https://github.com/RebeccaStevens/uMod-Rust-Plugin-AutoCode/compare/v1.3.0...v1.4.0) (2021-02-22) + + +### Bug Fixes + +* display guest code as well as code when code lock is placed with one ([18b587c](https://github.com/RebeccaStevens/uMod-Rust-Plugin-AutoCode/commit/18b587ce5f6d63294ed13a822bf8056500442efa)) +* formatting on message when a code lock is placed ([659e0bf](https://github.com/RebeccaStevens/uMod-Rust-Plugin-AutoCode/commit/659e0bf98058b4f4c19c7be3f5dd5902f5527981)) +* update user messages ([1a064f2](https://github.com/RebeccaStevens/uMod-Rust-Plugin-AutoCode/commit/1a064f2a9dd261941e8048e015377e064f083ed3)) + + +### Features + +* add extended help message ([3ca82a0](https://github.com/RebeccaStevens/uMod-Rust-Plugin-AutoCode/commit/3ca82a059591694540a5771d9f190d88aa5c1423)) +* add quiet mode ([38b87b6](https://github.com/RebeccaStevens/uMod-Rust-Plugin-AutoCode/commit/38b87b6d9d748b78b83f983bddf404fb6809e802)) +* allow changing the icon displayed to the user in the in-game chat ([8874903](https://github.com/RebeccaStevens/uMod-Rust-Plugin-AutoCode/commit/88749039acf28c499b5704b3abef60c1d3ef0640)) +* allow for optional "set" before code in command ([281f71d](https://github.com/RebeccaStevens/uMod-Rust-Plugin-AutoCode/commit/281f71dc2b0f328fe4abf9b47df8e27a8d4da790)) + # [1.3.0](https://github.com/RebeccaStevens/uMod-Rust-Plugin-AutoCode/compare/v1.2.1...v1.3.0) (2021-02-13)